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):- تحقق الخدمة
is_activeوالكمية ضمن[min, max]. - احسب
chargeفي الخادم فقط (لا تثق بأي سعر من الواجهة). chargeBalance()من المرحلة 3 (خصم + إنشاء order بحالةpending+ transaction، الكل atomic).- أرسل الطلب لمزوّد الخدمة عبر
provider.service.js. - نجاح → خزّن
provider_order_id، حدّثstatus=processing. - فشل → استرجاع فوري عبر
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,codeUNIQUE,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 وقناة شام كاش من الواجهة، وتأكد التعليمات تظهر صح لكل وحدة.