Javis OS
Javis OS는 기업가를 돕는 에이전시 AI로, 업무를 지원하고 함께 성장합니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 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 قبل أول نشر.