Files

20 KiB

TASKS.md — خطة تنفيذ SMM Panel للوكيل البرمجي

نفّذ المهام بالترتيب. كل مرحلة لازم "تشتغل وتُختبر" قبل ما تنتقل للي بعدها. المرجع الكامل: AGENTS.md (السباق العام) وWALLET.md (تفاصيل الحساب والمحفظة — إلزامي قراءته قبل مرحلة 2 و3) وFRONTEND.md (شرح الواجهة الحالية + مواصفات دقيقة للمراحل 9 و10 — إلزامي قراءته قبلهن). علّم كل مهمة [x] بعد ما تخلص وتتأكد إنها تشتغل فعلياً (شغّلها، ما تفترض).


المرحلة 0 — الإعداد

  • إنشاء مشروع Node.js + Express، هيكلة المجلدات حسب AGENTS.md § 3.
  • إعداد الاتصال بقاعدة بيانات MySQL/MariaDB (connection pool، ليس اتصال مفتوح لكل طلب).
  • إعداد نظام migrations (Knex / Prisma / أي أداة) — ممنوع تعديل السكيمة يدوياً بدون migration.
  • إنشاء ملف .env.example بكل المتغيرات المذكورة في AGENTS.md § 9.
  • إعداد error handler مركزي (middleware) يرجّع رسائل موحدة وما يسرّب stack trace بالإنتاج.
  • إعداد logging أساسي (بدون تسجيل باسوردات أو مفاتيح API إطلاقاً).

معيار القبول: السيرفر يشتغل، يتصل بقاعدة البيانات، ويرجّع 200 على GET /health.


المرحلة 1 — قاعدة البيانات

  • إنشاء جدول users حسب AGENTS.md § 4 (بما فيها balance DECIMAL(12,4) DEFAULT 0).
  • إنشاء جدول services.
  • إنشاء جدول orders.
  • إنشاء جدول transactions — مع UNIQUE على (user_id, reference, type) لمنع التكرار (WALLET.md § 2.2).
  • إنشاء جدول deposits — مع UNIQUE على tx_ref (WALLET.md § 2.3-أ).
  • (اختياري لكن موصى) جدول idempotency_keys حسب WALLET.md § 3.
  • (اختياري) جدول refresh_tokens لإدارة الجلسات حسب WALLET.md § 1.2.

معيار القبول: كل الجداول موجودة، الـ FKs والـ UNIQUE constraints فعّالة (جرّب إدخال قيمة مكررة وتأكد إنها تُرفض).


المرحلة 2 — نظام الحساب (Auth)

  • POST /auth/register — تشفير الباسورد بـ bcrypt، تحقق من تفرّد username/email.
  • POST /auth/login — إصدار JWT قصير المدة + refresh token، رسالة خطأ موحدة (WALLET.md § 1.2).
  • Rate limiting على /auth/login و/auth/register (مثلاً 5 محاولات/15 دقيقة).
  • Middleware auth (تحقق JWT) وrequireActive (يرفض المستخدم المحظور) وadmin (تحقق role).
  • POST /auth/logout — إبطال الـ refresh token.
  • POST /auth/forgot-password + POST /auth/reset-password (token عشوائي مؤقت، يبطل الجلسات القديمة عند إعادة التعيين).
  • GET /me — يرجّع بيانات الحساب + الرصيد الحالي.

معيار القبول:

  • تسجيل حساب جديد ثم دخول فيه يرجّع JWT صالح.
  • محاولة دخول بباسورد غلط 6 مرات متتالية تُحظر مؤقتاً.
  • طلب لمسار محمي بدون توكن يرجّع 401.

المرحلة 3 — المحفظة (Wallet) — أهم مرحلة، لا تستعجل فيها

اقرأ WALLET.md كامل قبل البدء هون.

  • بناء wallet.service.js — نقطة الدخول الوحيدة لأي تعديل على balance. لا كود ثاني يلمس عمود balance مباشرة.
  • دالة chargeBalance(userId, amount, reference, note) — قفل الصف (FOR UPDATE) + تحقق الرصيد + خصم + تسجيل transaction، الكل بـ DB transaction واحدة.
  • دالة creditBalance(userId, amount, reference, note) — نفس الآلية للإضافة (شحن/استرجاع).
  • GET /transactions — كشف حساب المستخدم مرتب زمنياً مع balance_after لكل حركة.
  • POST /deposits — تقديم طلب شحن (amount, method, tx_ref)، رفض فوري لو tx_ref مكرر.
  • GET /deposits — طلبات الشحن الخاصة بالمستخدم وحالتها.
  • PATCH /admin/deposits/:id — موافقة/رفض، بقفل الصف ومنع الموافقة المزدوجة (WALLET.md § 2.3-ب).
  • PATCH /admin/users/:id (تعديل رصيد يدوي) — يمر حصراً عبر wallet.service.js.
  • اختبارات إلزامية (WALLET.md § 4):
    • خصم أكبر من الرصيد يُرفض ولا يغيّر الرصيد.
    • عمليتان متزامنتان (parallel) على نفس الرصيد → وحدة بس تنجح.
    • موافقتان متزامنتان على نفس طلب شحن → إضافة وحدة بس.
    • tx_ref مكرر → رفض بدون لمس الرصيد.
    • بعد سلسلة عمليات عشوائية: SUM(transactions.amount) == users.balance.
  • job دوري (cron) للتسوية (Reconciliation) — يقارن SUM(transactions) مع users.balance ويرسل تنبيه عند أي فرق (WALLET.md § 2.4).

معيار القبول: كل الاختبارات أعلاه خضراء فعلياً (شغّلها، شوف النتيجة)، ولا يوجد أي مسار كود يعدّل balance خارج wallet.service.js (تأكد بالبحث النصي بالمشروع).


المرحلة 4 — الخدمات (Services)

  • provider.service.js — طبقة موحّدة للتواصل مع API المزوّد (services / add / status / balance) حسب AGENTS.md § 5.
  • POST /admin/services/sync — سحب الخدمات من المزوّد وتخزينها/تحديثها محلياً.
  • منطق حساب sell_price من provider_price + الهامش (DEFAULT_MARKUP_PERCENT أو هامش لكل خدمة — حسب القرار المفتوح في AGENTS.md § 11).
  • GET /services — قائمة الخدمات النشطة فقط، بسعر البيع (لا تُظهر provider_price للمستخدم العادي أبداً).
  • PATCH /admin/services/:id — تعديل السعر/الهامش/التفعيل من لوحة الأدمن.

معيار القبول: مزامنة الخدمات تعمل، والمستخدم العادي ما يقدر يشوف سعر المزوّد الحقيقي بأي استجابة API.


المرحلة 5 — الطلبات (Orders) — الجزء اللي يربط المحفظة بالمزوّد

  • POST /orders — تنفيذ التدفق الكامل خطوة بخطوة (AGENTS.md § 6):
    1. تحقق الخدمة is_active والكمية ضمن [min, max].
    2. احسب charge في الخادم فقط (لا تثق بأي سعر من الواجهة).
    3. chargeBalance() من المرحلة 3 (خصم + إنشاء order بحالة pending + transaction، الكل atomic).
    4. أرسل الطلب لمزوّد الخدمة عبر provider.service.js.
    5. نجاح → خزّن provider_order_id، حدّث status=processing.
    6. فشل → استرجاع فوري عبر creditBalance() (type=refund)، حدّث status=failed.
  • GET /orders وGET /orders/:id — طلبات المستخدم (تأكد إنه يشوف طلباته هو فقط، لا طلبات غيره).
  • GET /admin/orders — كل الطلبات لعرض الأدمن.

معيار القبول: طلب ناجح يخصم المبلغ الصحيح بالضبط ويوصل provider_order_id، وطلب فاشل (مزوّد down مثلاً) يرجع الرصيد تلقائياً بدون تدخل يدوي.


المرحلة 6 — متابعة الحالات (Status Tracking)

  • job دوري (مثلاً كل 5-10 دقائق) يمر على الطلبات بحالة processing/in_progress ويستعلم عن حالتها من المزوّد (action: status).
  • تحديث status وremains بناءً على رد المزوّد.
  • معالجة الحالات الجزئية (partial) — قرار: هل يُسترجع فرق المبلغ تلقائياً أم يحتاج مراجعة أدمن؟ (وضّح القرار بكود وتوثيق).

معيار القبول: طلب تجريبي يتحدث تلقائياً لحالته الحقيقية عند المزوّد خلال دورة الـ job التالية.


المرحلة 7 — لوحة الأدمن

  • GET /admin/users + PATCH /admin/users/:id (حظر/رفع حظر، تعديل رصيد).
  • GET /admin/deposits (المعلّقة أولاً) + الموافقة/الرفض.
  • GET /admin/stats — إجمالي المبيعات، الأرباح (sell - provider cost)، عدد المستخدمين النشطين، طلبات معلّقة.
  • كل مسارات هذا القسم خلف middleware admin بدون استثناء — تحقق فعلياً بمحاولة وصول بحساب عادي وتأكد من 403.

معيار القبول: حساب role=user يحاول الوصول لأي مسار أدمن → 403، وحساب أدمن يشوف كل البيانات صح.


المرحلة 8 — الواجهة (Frontend)

  • صفحات: تسجيل / دخول / لوحة المستخدم (الرصيد + كشف حساب) / الخدمات / إنشاء طلب / طلباتي / شحن رصيد.
  • لوحة أدمن منفصلة (خدمات، طلبات شحن، طلبات، مستخدمين، إحصائيات).
  • عرض الرصيد بشكل بارز دائماً (Header) ويتحدث فور أي عملية.
  • رسائل خطأ واضحة بالعربية (رصيد غير كافٍ، رابط غير صالح، إلخ).

معيار القبول: مستخدم جديد يقدر يسوي التدفق الكامل من الواجهة: تسجيل → طلب شحن → (موافقة أدمن) → شراء خدمة → متابعة الحالة، بدون لمس الـ API مباشرة.


المرحلة 9 — نظام الألوان والتصميم المتجاوب

اقرأ FRONTEND.md § 3 كامل قبل البدء هون.

  • وحّد متغيرات اللون الأساسي بـ styles.css تحت --primary-h / --primary-s / --primary-l واشتق منها --primary / --primary-hover / --primary-soft (استبدل كل استخدام لـ --accent / --accent-strong / --blue بالمتغيرات الجديدة).
  • أضف زر Theme Picker بالـ topbar: 6 ألوان جاهزة (Presets) + خيار لون مخصص (<input type="color">).
  • عند اختيار لون: حوّل HEX إلى HSL بـ JS، حدّث المتغيرات الثلاث على document.documentElement، واحفظ الاختيار بـ localStorage.
  • طبّق اللون المحفوظ فوراً بأول تنفيذ للسكربت (قبل أول رسم) لمنع أي وميض بلون افتراضي.
  • (تحسين مطلوب — راجع FRONTEND.md § 3.3 "نسخة منقّحة") الزر الحالي دائرة صغيرة بدون تسمية وحسّه بدائي — بدّله بزر فيه أيقونة (SVG بسيط) + نص "المظهر" (يختفي على الموبايل، أيقونة بس).
  • لوحة الألوان: كبّر الشبكة (44px بدل 34px) وأضف اسم تحت كل لون (تركواز/أزرق/بنفسجي/أخضر/برتقالي/وردي).
  • بيّن اللون المفعّل حالياً بعلامة واضحة (حلقة حد + ✓) على الـ Preset المطابق.
  • قسم اللون المخصص: زد حجم <input type="color">، أضف حقل نصي HEX مرتبط ثنائياً معه، وشريحة معاينة حيّة تتحدث أثناء السحب قبل التثبيت.
  • اللوحة تنغلق بالضغط خارجها، بزر × صريح، أو بمفتاح Escape — مع انتقال فتح/إغلاق ناعم (transition).
  • لا تغيّر آلية الحفظ/التطبيق بـ localStorage ولا الـ HEX→HSL — هذا الجزء شغّال صح، خلّيه زي ما هو.
  • الألوان الدلالية (نجاح/تحذير/خطر لحالات الطلبات والحظر) تبقى ثابتة ومنفصلة عن اللون الأساسي — لا تتأثر بالثيمنج.
  • أضف كسرين إضافيين للتجاوب (640px و1024px) بدل الكسر الواحد الحالي (900px).
  • القائمة الجانبية: ثابتة ≥1024px، صف علوي بالتابلت، Drawer منزلق بزر ☰ تحت 640px مع خلفية قابلة للإغلاق.
  • الجداول الكثيفة (الطلبات، كشف الحساب، طلبات الشحن) تتحول لعرض بطاقات (Card view) تحت 640px بدل التمرير الأفقي.
  • .inline-form تتكدس تدريجياً (عمودين بالتابلت، عمود واحد بالموبايل).
  • ارتفاع الأزرار وحقول الإدخال لا يقل عن 44px بالموبايل.
  • topbar يلتف (flex-wrap) بدل ما يفيض خارج الشاشة على الموبايل.

معيار القبول: غيّر اللون الأساسي وأعد تحميل الصفحة — نفس اللون يرجع فوراً بدون وميض. افتح الموقع بعرض موبايل (375px) وتابلت (768px) ودسكتوب — كل صفحة (بما فيها الجداول والفورمات ولوحة الأدمن) قابلة للاستخدام بدون تمرير أفقي مزعج أو عناصر متراكبة على بعض.


المرحلة 10 — طرق شحن رصيد حقيقية (PayPal / شام كاش / USDT ...)

اقرأ FRONTEND.md § 4 كامل قبل البدء هون. هذا تعديل على WALLET.md § 2.3 — يبقى نفس مبدأ "شحن يدوي بموافقة أدمن"، بس بقنوات محددة وتعليمات واضحة بدل حقل نص حر.

  • Migration: جدول payment_methods (id, code UNIQUE, label_ar, instructions, destination, is_active, sort_order, created_at, updated_at).
  • Migration/Seed: أضف قنوات ابتدائية على الأقل PayPal وشام كاش وUSDT (تعليمات placeholder، الأدمن يعبّي البيانات الحقيقية لاحقاً).
  • GET /payment-methods — القنوات النشطة فقط (يتطلب تسجيل دخول، أي مستخدم).
  • GET /admin/payment-methods — كل القنوات (نشطة وموقوفة).
  • POST /admin/payment-methods — إضافة قناة جديدة.
  • PATCH /admin/payment-methods/:id — تعديل/تفعيل/إيقاف قناة.
  • عدّل تحقق POST /deposits (zod): method لازم يطابق code قناة نشطة فعلياً، وإلا رفض فوري — لا تقبل أي نص حر بعد اليوم.
  • الواجهة: بدّل حقل "الطريقة" النصي بـ <select> معبّى من GET /payment-methods بالاسم العربي.
  • الواجهة: عند اختيار قناة، اعرض instructions وdestination بصندوق بارز فوق حقل "مرجع التحويل"، وغيّر تسمية الحقل حسب القناة المختارة.
  • لوحة الأدمن: قسم جديد لإدارة قنوات الدفع (عرض/إضافة/تعديل/تفعيل-إيقاف) يستخدم مسارات /admin/payment-methods.
  • أعد تشغيل اختبارات WALLET.md § 4 (npm run test:wallet) للتأكد إن تعديل التحقق على method ما كسر منطق المحفظة أو الـ UNIQUE على tx_ref.

معيار القبول: فورم شحن الرصيد يعرض قنوات حقيقية بدل حقل فاضي، اختيار أي قناة يعرض تعليماتها، وإرسال طلب بقناة موقوفة أو غير موجودة يُرفض من الباك-إند (جرّبها مباشرة بـ curl/Postman مو بس من الواجهة). الأدمن يقدر يضيف/يوقف قناة من لوحة الأدمن وتنعكس فوراً بفورم المستخدم.


المرحلة 11 — إغلاق ثغرات أمان لُقطت بفحص شامل (audit مباشر على السيرفر الحقيقي)

  • PATCH /admin/users/:id: امنع الأدمن من حظر حسابه هو نفسه (req.params.id == req.user.id وstatus=banned → ارفض بـ 400).
  • PATCH /admin/users/:id: امنع حظر آخر أدمن نشط متبقي بالنظام (عدّ role=admin AND status=active قبل التنفيذ، إذا صار العدد صفر بعد الحظر ارفض العملية).
  • src/app.js: قيّد cors() بدومين الموقع الحقيقي عبر متغير بيئة جديد (CORS_ORIGIN)، بدل السماح لأي دومين افتراضياً. خلّي القيمة الافتراضية بالتطوير مفتوحة بس بالإنتاج (NODE_ENV=production) إجباري تحديد الدومين، وإلا ارفض الإقلاع (زي validateEnv).
  • src/routes/auth.routes.js: أضف rate limiter لـ POST /auth/forgot-password (مثلاً 5 محاولات/15 دقيقة لكل IP، بنفس نمط loginLimiter).
  • src/routes/deposit.routes.js: أضف rate limiter على POST /deposits (مثلاً 10 طلبات/15 دقيقة لكل مستخدم) لمنع إغراق قائمة الأدمن.
  • (تحسين تجربة إضافي، اختياري بس مستحسن): بعد نجاح "حظر/تنشيط" من لوحة الأدمن، خلّي رسالة النجاح توضح التغيير فعلياً (مثال: "تم حظر المستخدم فلان" بدل رسالة عامة) عشان يكون واضح للأدمن إنه الإجراء نفّذ فعلاً.

معيار القبول: محاولة أدمن حظر نفسه أو آخر أدمن متبقي تُرفض برسالة واضحة. تشغيل السيرفر بـ NODE_ENV=production بدون CORS_ORIGIN يفشل بالإقلاع (زي باقي متغيرات الإنتاج الإلزامية). تكرار POST /auth/forgot-password أو POST /deposits أكتر من الحد المسموح يرجّع 429. شغّل npm test بعدها للتأكد ما انكسر شي بالمسارات الموجودة.


قبل ما تعتبر المشروع "جاهز"

  • راجع القرارات المفتوحة بـ AGENTS.md § 11 — لازم تكون كلها محسومة (مو افتراضات).
  • راجع كل بند بـ AGENTS.md § 8 (الأمان) وتأكد إنه مطبّق فعلياً، وحدة وحدة.
  • شغّل كل اختبارات المحفظة من المرحلة 3 مرة أخيرة بعد ربط كل الأجزاء مع بعض.
  • اعمل جولة يدوية كاملة (end-to-end) بحساب تجريبي: تسجيل → شحن → شراء → فشل متعمد (مزوّد وهمي يرجع خطأ) → تأكد الاسترجاع صحيح.
  • شغّل اختبارات المحفظة مرة أخيرة بعد المرحلة 10 (تعديل تحقق method) للتأكد إنه ما كسر شي.
  • جرّب الموقع فعلياً بثلاث أحجام شاشة (موبايل/تابلت/دسكتوب) بعد المرحلة 9 وتأكد ما في عناصر متكسرة أو نص مقطوع.
  • جرّب تغيير اللون الأساسي، أعد تحميل الصفحة، وتأكد إنه محفوظ.
  • جرّب طلب شحن بقناة PayPal وقناة شام كاش من الواجهة، وتأكد التعليمات تظهر صح لكل وحدة.