尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗منصة "شركة المندوب للتوصيل السريع" التي تربط قنوات المراسلة (واتساب، إنستغرام، ماسنجر) بنظام إدارة الطلبات والشحن، من أول رسالة للزبون حتى التسوية المالية مع التاجر.
الدخول: بريد واحد وزر واحد — لا كلمة مرور ولا خطوة تهيئة قبل أول تشغيل. بريد مالك المنصة (يُضبط بـ
PLATFORM_OWNER_EMAILS) يدخل أدمِناً إلى/admin، وكل بريد آخر يدخل مشتركاً على باقة Spark المجانية. التفاصيل فيlib/platform-owner.ts. وحالة الأمان الكاملة فيdocs/security-open-items.md.
قواعد التطوير الملزمة في
CLAUDE.md— اقرأها قبل أي تعديل. أهمّها: منع تخمين المعرّفات والمبالغ المالية، واعتماد هيكل البيانات قبل كتابة منطق الأعمال.
npm install
cp .env.example .env.local # ثم املأ المفاتيح والأسرار
npm run dev
يفتح على http://localhost:3000.
npm run typecheck # tsc --noEmit
npm run lint # eslint
npm test # vitest run
npm run build # next build
الأربعة تعمل على كل دفعة في .github/workflows/ci.yml.
| المتغيّر | الاستخدام |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | عنوان مشروع Supabase |
NEXT_PUBLIC_SUPABASE_ANON_KEY | مفتاح المتصفح — محكوم بسياسات RLS |
SUPABASE_SERVICE_ROLE_KEY | مفتاح الخادم حصراً — لا يُستورد أبداً في كود المتصفح |
BARIQ_BOT_WEBHOOK_SECRET | سرّ توقيع /api/webhooks/bot |
BARIQ_DELIVERY_SYNC_SECRET | سرّ توقيع /api/delivery/sync |
N8N_WEBHOOK_URL | نقطة إرسال ردود اللوحة إلى واتساب عبر n8n |
BARIQ_N8N_OUTBOUND_SECRET | السرّ المُرسل مع كل رد إلى n8n — docs/n8n-dashboard-reply.md |
الأسرار إلزامية: المسار الذي يغيب سرّه يردّ ٥٠٣ ولا يقبل شيئاً. نسيان الضبط يجب أن يوقف المسار لا أن يفتحه.
| المسار | الوصف |
|---|---|
/ | صفحة التعريف بالمنصة والباقات — عامة |
/login | تسجيل الدخول — الطريق الوحيد إلى ما تحته |
/operations | لوحة العمليات: الطلبات، التجار، المروّجون، الحملات |
/operations/chats | خدمة العملاء: محادثات القنوات الثلاث عبر Supabase Realtime |
/operations/whatsapp | لوحة واتساب المستقلة |
/workspace | مساحة التاجر: الكتالوج، الموظفون الأذكياء، الإعدادات |
/admin | لوحة مالك المنصة |
/dashboard | تتبّع الشحنات الميداني وحالاتها المالية |
الدور (profiles.role) | يرى |
|---|---|
platform_owner | كل شيء بما فيه /admin، وله فتح مساحة أي تاجر (مع تقييد في admin_impersonation_log) |
staff | /operations و /operations/chats و /dashboard |
merchant | /workspace — تاجره وحده |
الدور يُحسم بالبريد لحظة أول دخول، لا بإنشاء يدوي: بريد في
PLATFORM_OWNER_EMAILS ← platform_owner، وأي بريد آخر ← merchant
بتاجر واشتراك Spark يُنشآن معه. دور staff وحده يُمنح يدوياً من
supabase/seed/001_bootstrap_platform_owner.sql
أو من /admin/users.
جلسة صالحة بلا صفّ في profiles تعني حساباً جديداً يُوجَّه إلى /onboarding
فيُمنح دوره هناك — لا تُنهى جلسته.
middleware.ts يمنع غير الداخل، وrequireRole في الصفحة تفحص الدور.
الفحصان مفصولان عمداً: فحص الدور يحتاج قراءة من القاعدة، ووضعه على حافة
الشبكة يُبطّئ كل طلب ويجعل الصلاحية تُفحص في مكانين فيفترقان.
POST /api/orders — إنشاء طلب من نظام التاجرcurl -X POST https://<host>/api/orders \
-H "Authorization: Bearer brq_live_xxxxxxxx" \
-H "Idempotency-Key: order-2026-09-15-0042" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "أحمد الجبوري",
"customer_phone": "07701234567",
"governorate": "بغداد",
"district": "الكرادة",
"full_address": "شارع ٦٢، قرب صيدلية النور",
"order_content": "حذاء رياضي أسود قياس ٤٢",
"cod_amount_iqd": 35000,
"delivery_fee_iqd": 5000
}'
merchant_id من المُرسِل.Idempotency-Key إلزامية. إعادة الإرسال بنفس المفتاح تُعيد الشحنة
الأصلية ولا تُنشئ ثانية.cod_amount_iqd وdelivery_fee_iqd حقلان مستقلان مطلوبان. لا قيمة
افتراضية لأيّهما: الحقل الغائب يُرفض برد ٤٢٢ ولا يُخمَّن.GET /api/orders بنفس المفتاح يُعيد شحنات صاحبه فقط، مع ?status= و?search=.
POST /api/delivery/sync — تحديث حالة شحنةموقَّع بـ x-bariq-signature. يقبل tracking_number وaction من:
pickup · in_transit · out_for_delivery · delivered · postponed ·
returned · settled. التأجيل والإرجاع يلزمهما reason.
الانتقال المخالف للتسلسل يُرفض بـ ٤٠٩ مع قائمة الانتقالات المسموحة.
الحالة نفسها مرّتين ترجع unchanged: true بلا أثر مكرر.
POST /api/webhooks/bot — أحداث البوتموقَّع بـ x-bariq-signature. الأحداث: message_received · message_sent ·
order_created · order_status_query · human_handover.
/api/payments/* تردّ ٥٠١. التكامل غير مبنيّ — التفاصيل في
lib/payments/index.ts.
docs/الأرقام العربية موحّدة في كل الواجهات. استخدم دوال lib/formatters.ts
(toArabicDigits, formatArabicCurrency, formatArabicNumber) ولا تطبع أرقاماً خاماً.
فصل الخادم عن المتصفح. ملفات *-server.ts تستعمل service_role ويُمنع استيرادها
في مكوّنات العميل. ما يُقرأ بمفتاح anon يجب أن تحميه سياسة RLS.
لا مبلغ افتراضي ولا رقم هاتف افتراضي. الحقل المالي الغائب يُرفض
(lib/order-validation.ts)، ولا يُملأ بقيمة مُقدَّرة.
الاختبارات في tests/order-validation.test.ts تحرس هذا.
هوية التاجر من الجلسة لا من الرابط. في المسارات استعمل
requireMerchantScope، وفي الصفحات
loadWorkspaceContext. searchParams.get('merchant_id')
ليس هوية — هو طلب يُمحَّص، ويُتجاهل لغير مالك المنصة.
لا يُطبع رقم هاتف زبون في السجلات. استعمل lib/log.ts: maskPhone
لرقم الهاتف وmaskText لنص الرسالة. سجلات Vercel تُقرأ خارج حماية RLS.
آلة حالات الشحنة تتبع تدفقاً صارماً لا يقبل القفز العشوائي:
ORDER_RECEIVED → PICKED_UP_SAME_DAY → IN_TRANSIT_HUB → OUT_FOR_DELIVERY
→ DELIVERED | POSTPONED | RETURNED → SETTLED_FINANCIALLY
الفرض في مُحفّز قاعدة البيانات، ونسخة lib/shipments.ts لتوجيه
الواجهة فقط. اختبار في tests/shipment-state-machine.test.ts
يقرأ خريطة المُحفّز من ملف الترحيل ويقارنها بنسخة الكود، فيكشف أي افتراق.
الفصل المالي إلزامي: قيمة البضاعة (COD) وأجرة التوصيل حقلان منفصلان، وصافي
مستحق التاجر عمود محسوب في القاعدة (GENERATED ALWAYS) لا يُخزَّن مُخمَّناً.
كل المخطط موثَّق في supabase/migrations/. الترحيل
010_document_live_schema.sql يوثّق الجداول الـ ١٨ التي كانت تعيش في لوحة
Supabase فقط — مُستخرَجة من القاعدة الحية لا مكتوبة من الذاكرة.
أي تغيير في المخطط يبدأ بملف ترحيل، لا بلوحة Supabase. التعديل في اللوحة لا يمرّ بمراجعة ولا يترك أثراً، ويجعل إنشاء بيئة اختبار مستحيلاً.
تُقرأ من جدول plans وتُعرض في / فوراً عند أي تعديل، بلا إعادة نشر.
الباقة التي لم يُعتمد سعرها بعد (price_iqd_monthly IS NULL) تظهر "قريباً" بدل رقم مُخمَّن.
حدود الباقة مفروضة بمُحفّز enforce_plan_limit في القاعدة، لا في الكود.
يُنشر تلقائياً على Vercel عند الدفع إلى main بعد نجاح CI. تأكّد من ضبط كل
متغيّرات البيئة أعلاه في إعدادات المشروع على Vercel قبل أول نشر.