12 Commits
Author SHA1 Message Date
Hamza-AyedandClaude Opus 5 4d8414c96b feat: استيراد كود سيرو إلى تريبز (سيرو @ecfe7568) — بلا تعديل
قرار المالك 2026-07-27: باك إند سيرو PHP هو المعتمد، وتطبيقاته المجرّبة
ميدانياً تحل محل إعادة البناء المؤرشفة. سيرو نفسه لم يُمسّ.

الخريطة:
  backend · payment_server · loction_server · ride_server ·
  passenger_server · docker · dashboard · stress_test  → الجذر
  siro_rider  → apps/rider          siro_driver  → apps/driver
  siro_admin  → dashboards/admin    siro_service → dashboards/service
  android_bot → apps/android_bot    socialBot    → apps/socialBot

نُسخ المتعقَّب في git سيرو فقط عبر `git archive` (3,198 ملفاً / ~169 م.ب)
لا `cp -r` — فاستُثنيت مخلفات البناء تلقائياً. بلا أي تعديل محتوى عمداً:
كل ما يلي يصير فرقاً مقروءاً مقابل المصدر.

لم يُستورد وسببه: siromove.com (الموقع التسويقي يبقى marketing/ في تريبز،
سيرو فيه 8 ملفات) · docs و planning (تريبز له docs/ الخاص) · deploy.sh
(ليس نشراً على سيرفر بل `git add . && git push origin --all` — فخّ في
مستودع آخر) · transit_dashboard (بانتظار قرار مصير backend-transit و
dashboards/transit-web).

⚠️ لا يبني بعد — ثلاثة نواقص متوقعة ومقصودة:
1. `.env` و `lib/env/env.g.dart` غير متعقَّبين في سيرو (أسرار لكل مستأجر):
   كل تطبيق فلاتر يحتاج .env خاصاً ثم توليد env.g.dart بـ build_runner.
2. إعدادات Firebase (9 ملفات google-services.json و GoogleService-Info.plist)
   يستبعدها .gitignore تريبز — ولكل مستأجر مشروع Firebase خاص أصلاً.
3. apps/driver في سيرو يشير إلى `../../Intaleq/packages/get` خارج المستودع →
   يجب ضمّ الحزم داخله أسوة بـ apps/rider.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:14:13 +03:00
Hamza-AyedandClaude Opus 5 fff76c949f chore: أرشفة باك إند NestJS إلى backend-archive
قرار المالك 2026-07-27: الباك إند المعتمد صار باك إند سيرو PHP، ويُنقل
إلى هذا المستودع كنسخة جديدة باسم «انطلق» على api.intaleqapp.com.

backend/ → backend-archive/ (305 ملفاً، إعادة تسمية بلا تعديل محتوى)
+ README-ARCHIVE.md يوضّح سبب الأرشفة وسبب عدم الحذف: scripts/e2e-test.mjs
  هو أدق توثيق سلوكي لدورة الرحلة كاملة، ويبقى مرجعاً عند بناء الوحدات
  الناقصة في باك إند PHP (المحفظتان · الاستحقاقات · محرّك التسعير · الوحدات).

مسارات backend/ في docs/*.md لم تُعدَّل عمداً — سجلّ تاريخي.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 04:54:35 +03:00
Hamza-Ayed 959e2fa758 Update backend to use firebase-admin SDK for FCM 2026-07-19 16:21:43 +03:00
Hamza-Ayed 4aeab5275a feat: Add tenant wallet, billing tables, and transit microservice 2026-07-18 18:15:23 +03:00
Hamza-AyedandClaude Sonnet 5 d4ac38ca87 feat: P1 — بوابات دفع حقيقية (PayMob) + إغلاق ثغرة webhook حرجة
الأهم أولاً: `PaymentsService.webhook()` كان يقبل أي جسم `{ payment_id }`
بلا أي تحقّق توقيع — من يعرف معرّف دفعة معلَّقة كان يستطيع تحويلها «ناجحة»
ويشحن رصيداً من عدم (محفظة راكب أو رصيد سائق تشغيلي). الآن كل تغيير حالة
محروس بـ`adapter.verifyWebhook(headers, payload, tenant)`، ولا شيء يُقرأ من
الحمولة قبل ذلك كقرار ثقة — قراءة المرجع لتحديد المستأجر ليست قراراً.

البنية (docs/07 · docs/24 — P1):
- `PaymentAdapter`: charge() يعيد instant (كاش) · redirect (بوابة API حقيقية)
  · invoice (بلا API، تسوية عبر P2).
- PayMob (مصر): تسلسل auth→order→payment_key→iframe حقيقي عبر fetch،
  وتحقّق HMAC-SHA512 على تسلسل حقول ثابت (بروتوكول PayMob الرسمي بالضبط)
  بمقارنة ثابتة الزمن. مفاتيح كل مستأجر مستقلة — حساب تاجر خاص به.
- كليق/شام كاش/MTN/سيرياتيل/زين كاش: محوّل مشترك واحد لأن سلوكها متطابق
  فعلياً في سيرو (`create_*_invoice.php` تُنشئ فاتورة فقط، لا نداء بوابة
  حيّاً) — مرجع + حساب استلام معروض، والتسوية عبر رسالة SMS لا webhook.
  MTN/سيرياتيل الحقيقيَّين (توكن+OTP) موثَّقان كبند مفتوح: لا نبني تكاملاً
  لا نملك اعتماداً حيّاً للتحقّق منه.
- `PATCH /payments/settings` لأدمن المستأجر: مفاتيح PayMob · حسابات
  الاستلام · سرّ webhook الرسائل — الاستجابة لا تُعيد الأسرار.

تنظيف: إزالة الإشارات المتبقّية لحاوية `martin` من docs/07 (أُزيلت فعلياً
سابقاً)، وتحديث هيكل الكود الموثَّق ليطابق ما هو مبنيّ فعلاً.

25 اختباراً جديداً (226 إجمالاً) — منها توقيع PayMob محسوب فعلياً ومُتحقَّق،
وتلاعبٌ بالحمولة بعد التوقيع يُرفض، وسبع حالات تثبت إغلاق ثغرة الـwebhook.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 15:50:05 +03:00
Hamza-AyedandClaude Opus 4.8 8f6c46f0ac feat: D5 — OTP متعدد المزوّدين حسب الدولة (من سيرو) + قرار بلا كلمة مرور
مراجعة سيرو (auth/otp/): مصر تستعمل Kazumi SMS مع failover لواتساب،
وسوريا/الأردن Nabeh. عندنا كان مزوّد واحد فقط.

- integrations/otp/: واجهة OtpProvider + OtpDispatcher يوجّه حسب
  tenant.countryPack بسلسلة failover (eg: [kazumi, nabeh] · jo/sy: [nabeh])
- KazumiSmsProvider (مصر SMS) + NabehOtpProvider (غلاف على NabehService)
- كل مزوّد يرسل رمزاً نولّده نحن (Redis) — استبعدنا نمط Intaleq (يولّد الرمز
  بنفسه) حفاظاً على مصدر واحد للرمز
- موحَّد: AuthService.sendOtp و PayoutsService.issueOtp يمرّان بنفس المُوزِّع
  الآن — أُزيل كل استدعاء Nabeh مباشر. أي مسار يحتاج رسالة يستدعي الخدمة
  الموحّدة (طلب المالك)
- فشل السلسلة كاملة = ServiceUnavailable صريح، لا صمت

قرار المالك (2026-07-17): بلا كلمة مرور إطلاقاً. المصادقة = هاتف + OTP مرة
→ جلسة مربوطة بالجهاز (D2). التسجيل بالهاتف لا Google/Apple (محظوران في بعض
الدول). سيرو نفسه لا كلمة مرور حقيقية له (password = hash(email) وهمي،
ومقارنة نصّية غير آمنة في مسار التجربة). يطابق أوبر/كريم/inDrive.

مراجعة حدّ الطلبات في سيرو (RateLimiter.php): حدود مسمّاة لكل نوع + fallback
بملف عند تعطّل Redis (fail-closed). عندنا مسار OTP fail-closed أصلاً؛
تخزين Throttler على Redis مؤجَّل للتوسّع الأفقي الفعلي (موثّق في D4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:24:01 +03:00
Hamza-AyedandClaude Opus 4.8 d2a34f753c feat: TLS — إغلاق المنافذ المكشوفة + الثقة بالبروكسي + دليل الإصدار
النطاق: tripz-api.intaleqapp.com (قرار المالك) — مملوك وجاهز، ومحايد عن
اسم العلامة الذي لم يُحسم فلا يحتاج تغييراً لاحقاً.

ثغرتان كانتا ستُبطلان TLS من أصله:
- API كان منشوراً على 0.0.0.0:4010 → صار 127.0.0.1. بلا هذا يبقى
  http://IP:4010 مفتوحاً فيتجاوز TLS كلياً وتصير الشهادة زينة
- **Postgres كان مكشوفاً على 0.0.0.0:55432** — القاعدة على الإنترنت مباشرة
  → صار 127.0.0.1. المنفذ غير القياسي إخفاء لا أمان. الوصول من الماك عبر
  نفق SSH (docs/20 §4)

trust proxy = 1: خلف Nginx كان req.ip يصير 127.0.0.1 للجميع فيمتلئ سجل
التدقيق المالي (I7) بعنوان البروكسي بدل السائق — أثرٌ بلا قيمة عند النزاع.
قفزة واحدة فقط: الثقة المفتوحة تسمح للعميل بتزوير X-Forwarded-For.

docs/20: DNS → موقع reverse proxy → شهادة → **إعداد ترقية WebSocket**
(بدونه يسقط التتبّع الحيّ) + مهلة 3600s للنبضات طويلة العمر + فحص تحقّق
أهمّه أن http://IP:4010 يجب أن **يفشل**.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:36:14 +03:00
HamzaandClaude Opus 4.8 6234e66471 feat: Gemini AI (doc OCR + face match) + role-assignment endpoints
- GeminiService (integrations/gemini, @Global): extractDocument (OCR fields) + faceMatch; graceful if no GEMINI_API_KEY
- documents: face-match now real via Gemini; POST /admin/documents/:id/ocr extracts fields for CS auto-fill
- roles: PATCH /admin/users/:id/role (admin) + POST /platform/users/role (bootstrap via x-platform-secret)
- config: gemini + platform.secret; .env.example entries

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 20:01:26 +03:00
HamzaandClaude Opus 4.8 f3383c3cf8 feat(security): field encryption AES-256-GCM + random IV (at-rest)
- common/crypto/crypto.util.ts: encrypt/decrypt (GCM, random 96-bit IV), format v1:base64(iv|tag|ct)
- EncryptedTransformer applied to users.name + drivers.vehicle_plate (auto, no service change)
- blindIndex (HMAC) helper for future searchable-field encryption (phone)
- tolerant decrypt for legacy plaintext → no migration needed (varchar holds base64)
- ENCRYPTION_KEY env + boot warning if unset; docs/16-encryption.md
- fixes Siro's documented CBC+fixed-IV flaw

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 19:24:27 +03:00
HamzaandClaude Opus 4.8 16df6258c1 P2: ride types + class-aware matching + round-trip + dispatch + wallet + FCM + WS live location/WebRTC
- ride-types: per-tenant catalog (economy/comfort/electric/family_van/women/scooter), seeded
- matching now class-scoped (geo:drivers:{tenant}:{class}); trips match by requested type
- trips: is_round_trip (doubles distance for fare) + payment_method + wallet settlement on paid
- dispatch: operator creates trip for phone customer (role-guarded)
- wallet: balance + credit/debit + txns + self topup; wallet-paid trips settle rider→driver
- notifications: FCM device tokens + best-effort push (assign event)
- realtime: trip:join, live driver:location broadcast, WebRTC call signaling (offer/answer/ice/end)
- migration InitP2 (ride_types, wallets, wallet_txns, device_tokens, trips.is_round_trip)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:39:56 +03:00
HamzaandClaude Opus 4.8 65a2e5e275 P1 complete: real انطلق maps + Nabeh WhatsApp OTP + chat + ratings + cancel(both) + fraud detection
- maps: real map-saas route/geocode/reverse/places (x-api-key per country) + straight-line fallback
- auth: Redis-backed OTP + Nabeh WhatsApp send (dev mode keeps fixed 1234 bypass)
- chat: per-trip messages (participant-guarded) + socket broadcast
- ratings: post-trip rating + driver avg recompute + no double-rate
- cancel: from rider or driver + stage-based cancel fee + notify both
- fraud: cancel-abuse (soft/hard block via Redis), arrived-far-from-pickup, completed-too-fast → fraud_flags
- migration InitP1b (chat_messages, ratings, fraud_flags, trips.cancelled_by/cancel_fee)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:12:38 +03:00
HamzaandClaude Opus 4.8 95fea546f5 first commit: منصة Tripz — خطط كاملة + سكافولد باك إند NestJS/Docker
- docs/00-15: دراسة، بنية، محرك تعرفة، تسعير، نموذج استئجار، تكاملات، بيانات، realtime، خطة، devops، لاندنج، مخاطر، اصطلاحات سيرفر، تدفق نشر
- backend/: NestJS 11 على Docker (health + tenants + عزل tenant_id + بادئة tripz_ + Redis DB 3)
- apps/rider, apps/driver, dashboards/admin-web, dashboards/superadmin-web (هياكل)
- sync-to-server.sh + .gitignore

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:22:06 +03:00