بناء من الصفر على باك إند NestJS، بقرار المالك 2026-08-04 الذي يعكس قرارَي
نقل GetX (2026-07-21) واعتماد باك إند PHP (2026-07-27). لا يُورَّث سطر دارت
من apps/*-archive-cubit، وسيرو مرجع بصري وسلوكي لا مصدر نسخ.
الوثائق:
- docs/37: الخطة الكاملة بستّ مراحل وبواباتها
- docs/38: عقد الـAPI من 37 controller، معظمه متحقَّق حيّاً من السيرفر المنشور
- docs/39: جرد 202 شاشة في تطبيقَي سيرو، مصنّفة داخل/خارج النطاق
الطبقة الأصلية منقولة من *-archive-cubit وحدها لأنها هوية النشر:
- rider_new → com.mobileapp.store.ride · shorebird 496cb3ac
- driver_new → com.sefer_driver (أندرويد) · com.sefer.driver (iOS) · 68cc9345
- أُصلح تعارض: هدف RunnerTests في driver_new كان يحمل bundle الراكب
الأصول مصدرها *-archive-cubit لا سيرو: أصول الأرشيف مجموعة أشمل (كل صور
سيرو + صور تريبز) وخطوطها هي خطوط docs/26. استُكمل السائق بعشرة ملفات
ناقصة من سيرو (شعارات مزوّدي الدفع + صوتان).
lib/ مكتوب من الصفر (11 ملف لكل تطبيق):
- AppConfig بأعلام const — أساس نموذج lite/pro/max
- TokenStore على التخزين الآمن، يقرأ exp محليّاً بلا حزمة خارجية
- AuthInterceptor بتجديد استباقي وطلقة واحدة — عمر التوكن 15 دقيقة فقط
- ApiClient و ApiException يفهم مصفوفة message في NestJS
flutter analyze نظيف في التطبيقين. البناء الفعلي لم يُجرَّب بعد: بوابة
المرحلة 1 تتطلّب البناء على السيرفر، والوصول إليه غير متاح حالياً.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
apps/driver كان يشير إلى `../../Intaleq/packages/get` خارج المستودع — وهو
مسار يُحل من apps/driver إلى Tripz/Intaleq/ غير الموجود، فلا يمرّ
`flutter pub get` إطلاقاً. (فجوة docs/30 §2.6.)
ضُمّت نسخة **انطلق** تحديداً (App/Intaleq/packages) لا فرع apps/rider:
الفرعان يختلفان فعلاً — snackbar_controller.dart و get_queue.dart وغيرهما
مختلفة بين النسختين، فاستعارة فرع rider كانت ستغيّر سلوك السائق صامتة.
apps/driver/packages/get (3.5 م.ب، 202 ملف dart مع get_storage)
apps/driver/packages/get_storage
pubspec: ../../Intaleq/packages/* → ./packages/* (3 مواضع، منها
dependency_overrides سطر 142)
المستودع صار مكتفياً ذاتياً: لا تبعية مسار خارج شجرته. لم يُمسّ App/Intaleq.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
قرار المالك 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 (كوميت fff76c9 أرشف الباك إند):
apps/rider → apps/rider-archive-cubit
apps/driver → apps/driver-archive-cubit
dashboards/admin-web → dashboards/admin-web-archive
dashboards/service-web → dashboards/service-web-archive
إعادة تسمية بلا تعديل محتوى. rider/driver كانا إعادة بناء Cubit مبنية على
عقد NestJS المؤرشف. admin-web و service-web نموذجان بملف HTML واحد (164 و
128 سطراً) — يحلّ محلهما siro_admin و siro_service المكتملان.
يُحمل من المؤرشف ولا يُعاد اكتشافه: عقد map-saas المثبَّت بالتجربة في
apps/rider-archive-cubit/lib/core/api/ (x-api-key ترويسة، reverse مصفوفة،
route في points) ودرس مسار FCM.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
قرار المالك 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>
- marketing: index.html من 2274 إلى 775 سطراً، والباقي إلى
styles/ (tokens·base·components·sections·responsive) و
js/ (app·i18n·calculator·analytics) و i18n/ بخمس لغات
(ar·en·fr·es·tr) عبر سمة data-i18n.
- docs/05 + docs/33 + لوحة السوبر-أدمن: الباقات صارت ثلاثاً
(إطلاق · نمو · سيادة) بدل الأربع، وتحديث docs/31 بما يطابقها.
- docs/36: توثيق سيرو إكسبرس للتوصيل.
- تنظيف استيرادات غير مستعملة في apps/rider و apps/driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
البند ٢ — استخبارات التسعير (docs/35 جديد):
- توصيف تقني دقيق من كود سيرو: المصدر ليس "كشط أسعار معلنة" بل
بوت أندرويد على أجهزة حقيقية يشغّل 7 تطبيقات منافسة مُسمّاة
بمعرّف الحزمة (standalone_worker.php:665) بدفعات حتى 100 مهمة
- فصل ثلاثة أخطار مستقلة: الوصول الآلي · قانون المنافسة · البيانات
- الخطر الأكبر (لم يكن موثّقاً): بيع المحرّك لمشغّلَين في نفس المدينة
= ترتيب hub-and-spoke تقع مسؤوليته على المحور أي Tripz
- ضابط مقترح: مشغّل واحد لكل مدينة، في العقد وفي الكود
- تنظيف صياغة الموقع: صفر أسماء منافسين، وحُذفت "اعرف كيف يسعّر
منافسك قبل أن يعرف هو" لأنها تصلح دليل نيّة في نزاع
البند ٣ — طبقة القياس (docs/32 §5):
- شريط موافقة بأربع لغات، الرفض بنفس بروز القبول
- البكسل لا يُحمَّل قبل الموافقة (مطلوب لزوّار الاتحاد الأوروبي:
الموقع بالفرنسية والإسبانية)
- 8 أحداث تغطي القمع كاملاً حتى Lead بمعامل القناة
- PIXEL_ID فارغ عمداً: لا شيء ينكسر ولا شيء يُقاس حتى يُملأ
تحقُّق: صفر أخطاء console · صفر عناصر i18n فارغة في اللغات الأربع ·
fbq غير معرّف قبل الموافقة · الأحداث تُطلق بعدها
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
تنفيذ توصيات المراجعة النقدية التي لم تُنفَّذ في الجولة السابقة:
- حذف «الأمان والامتثال» $79 كوحدة → النواة (سبب قانوني: بيع الأمان
يُنشئ وثيقة إدانة يوم يقع حادث عند مستأجر لم يشترِ الوحدة)
- حذف «حزمة المدفوعات» $99 → النواة (الحلقة الأساسية لا ترقية)
- دمج «ذكاء الأسطول» $99 في باقة أسطول+ (تناقض تسموي)
- «المواصلات» تخرج من الشبكة → قسم #transit مستقل بجمهوره وCTA خاص
- شريط .core-band يعرض ما لا يُباع كحجة بيع
- إصلاح الانقلاب السعري: كانت سيادة $879 أرخص من أسطول+ محمّلة $903
الآن: علامة $585 < أسطول+ $599 < سيادة $879
- الشارات ← «متاح الآن» بتأكيد المالك أنها تعمل في إنتاج سيرو
- تصحيح سردية docs/33 §0 المستحيلة (كانت تبني التوسّع على باقة
تنتهي بعد 90 يوماً)
الترجمات مكتملة في اللغات الأربع — تحقُّق برمجي: صفر عناصر فارغة.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✨ تحسينات رئيسية:
1️⃣ إصلاحات توثيقية (docs/21, docs/33):
- S18 ليست مشكلة — النظام يفرض سحب الرصيد الكامل فقط
- إضافة تنبيه حالة عن عدم جاهزية الوحدات
2️⃣ إصلاحات الموقع (marketing/index.html):
- حذف قسم #ai-modules المكرر (التكرار المحرج)
- إضافة رابط «الإضافات» في القائمة العلوية (4 لغات)
- تغيير جميع الشارات إلى «قريباً الربع القادم» (حماية قانونية)
- 7 وحدات مدفوعة واضحة مع أسعار + وصف
3️⃣ النموذج التجاري:
- النواة رخيصة = سلاح اكتساب
- الوحدات = هامش الربح الحقيقي (land-and-expand)
- بدون تكرار محرج للزائر
⚠️ الحد الأدنى قبل التسويق:
- S4 و S5 تُصلح قبل أول استنساخ على سيرفر مستأجر
- جرد صدق بند بند على إنتاج سيرو
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same phone can now hold distinct rider and driver accounts (and wallets)
within a tenant. Unique constraint moves from (tenant, phone_bidx) to
(tenant, phone_bidx, role); login creates/finds the record matching the
app's x-app-role header. Fixes rider-app login returning role:driver for
a phone previously registered as a driver.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
الدورة السابقة أعلنت الفشل والنظام سليم — العطل كان في السكربت:
1. **`fixed` تُرفض بـ400 وهذا هو المطلوب** (أُخرجت من الزرع عمداً)، لكن
السكربت كان يعدّها فئة متوقَّعة فأسقط التغطية إلى 72/81. صارت في
`REJECTED_CLASSES`: الرفض يُتحقَّق منه صراحةً، وغيابه هو الفشل.
2. **429 على `send-otp`** (10 لكل 5 دقائق) عند تشغيلتين متتاليتين — يُنتظر
ويُعاد بدل الإبلاغ عن فشل ليس في النظام.
3. **إبطال كاش التعرفة صار خطوة في الدورة.** `getActive` تكاشي 900 ثانية،
و**Redis حاوية منفصلة لا تتأثر بإعادة تشغيل الـapi** — وتطبيق
`tariffs.sql` مباشرةً على القاعدة يتجاوز التطبيق فلا يُبطل شيئاً. النتيجة
أن النظام خدم أسعاراً قديمة عبر دورتَي نشر كاملتين وهو يعلن النجاح.
المسح الآن مبنيّ في السكربت لا متروك للتذكّر.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## الأرقام صارت من جدول `kazan` الحيّ لا من تقدير
العمولة 11٪/12٪/14٪ مطابقة حرفياً. وثلاثة تصحيحات على الخام:
- **محورا الأردن مقلوبان**: أعمدة الفئات تحمل سعر الدقيقة (0.043) وأعمدة
النوافذ سعر الكيلومتر (0.184) — عكس مصر وسوريا. قراءتها كما هي تجعل
رحلة 27 كم بأقلّ من دينارين.
- **نِسَب الفئات في مصر وسوريا معطوبة**: الكهربائي 0.23 والفان 0.25–0.40 من
الاقتصادي، أي فان أرخص من اقتصادي. نِسَب الأردن وحدها متماسكة فاعتُمدت.
- سعر الكيلو في مصر 4 جنيهات لا 3.5 (قرار المالك).
## البنية صارت مطابقة لسيرو — وهي ما سيكتب فيه بوت استخبارات الأسعار
سعر الكيلو يتغيّر بالفئة فقط (رقم يعرفه السوق)، وسعر الدقيقة بالنافذة فقط.
**لا فتحة عدّاد** (`startPrice = 0` في كل صفوف سيرو): الحدّ الأدنى أرضية لا
رسم يُضاف — «ما في رحلة أقل من 20 جنيهاً». `fuelPrice` محفوظ كمدخل للبوت.
## تسريبان أغلقهما الاختبار الحيّ (بموافقة المالك)
1. **فئة مجهولة = رحلة مجانية**: `service_class` يأتي من الطلب بلا تحقّق،
والاستثناء كان يُبتلع فتُنشأ رحلة بـ`quoted_fare = null` ثم تُسوّى بصفر.
صار الطلب يُرفض بـ400 — الفشل الصريح أأمن من الصامت.
2. **`fixed` لا تُزرع**: سعر ثابت بلا جدول خطوط يعني رقماً واحداً لكل
الوجهات — أظهر الاختبار رحلة 55.8 كم بـ2.5 ديناراً مقابل 23.2 للاقتصادي.
300 اختبار خضر.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ضاعت دورتان كاملتان على السبب نفسه: تسلسل `up -d && restart && <اختبار>`
كان يشغّل الاختبار بينما الـapi ساقطة، فيظهر خطأ الإقلاع كـ`EAI_AGAIN` داخل
الاختبار — أي في المكان الخطأ تماماً، فيُشخَّص كعطل شبكة أو عطل سكربت.
`verify-and-test.sh` يجعل الدورة واحدة ومحروسة:
- يفحص `.env` قبل الإقلاع ويصلّح التعارض القاتل (OTP_DEV_MODE=true مع
NODE_ENV=production — حارس `main.ts` ينهي العملية لا يحذّر، فتدخل الحاوية
حلقة إعادة تشغيل).
- يميّز «حلقة إعادة تشغيل» عن «بطء إقلاع»: الأولى خطأ إقلاع يُطبع سببه من
اللوغ فوراً بدل انتظار عقيم.
- **لا اختبار قبل ردّ /health فعلياً** — هذه هي البوابة التي كانت غائبة.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
نصيحتي السابقة بتفعيل `OTP_DEV_MODE=true` على السيرفر كانت خاطئة: حارس في
`main.ts` يرفض الإقلاع عند `OTP_DEV_MODE=true` مع `NODE_ENV=production`،
فسقطت حاوية الـapi في حلقة إعادة تشغيل وظهر الفشل كـ`EAI_AGAIN` في السكربت.
الحارس صحيح ولا يُضعَف لأجل اختبار: رمز ثابت على سيرفر متاح للإنترنت يعني
أن معرفة رقم هاتف تكفي لدخول أي حساب.
السكربت صار يقرأ الرمز الحقيقي من Redis (`send-otp` يخزّنه قبل محاولة
الإرسال، فيبقى محفوظاً حتى حين يفشل الإرسال لرقم وهمي). عميل RESP أدنى على
`node:net` بلا تبعيات — الحاوية عابرة بلا `node_modules`. المفتاح يستعمل
UUID المستأجر لا الـslug، فتُجلب خريطة الـslug→UUID من `/admin/tenants`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
عطلان في سكربت التسعير، كشفهما أول تشغيل حقيقي:
1. **إعلان نجاح على العدم** — الأخطر. فشل تسجيل الدخول فجُمع صفر صفّ، فمرّ
كل تحقّق فراغاً وطبع «الاختبار ناجح». اختبار يعلن النجاح وهو لم ينفّذ
شيئاً أسوأ من اختبار يفشل: الفشل يُرى. أُضيف حارس عدم + تحقّق تغطية
(المنفَّذ مقابل المتوقَّع)، وكلاهما يُخرج برمز فشل.
2. **تسجيل الدخول تجاوز `send-otp`** — تجاوز التطوير في `verifyOtp` مشروط
بـ`OTP_DEV_MODE=true`، وبدونه لا رمز في Redis فيُرفض الثابت 1234 بـ401.
صار يمرّ بـ`send-otp` ويأخذ `dev_code` من استجابتها، ويقول صراحةً حين
يكون الوضع التطويري مطفأً بدل أن يفشل بلا تفسير.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>