🔐 توثيق APIs الدنكل — الرائد بلس

كل نقاط النهاية الخاصة بالدناكل: تطبيق سطح المكتب (تفعيل/قراءة) + إدارة المخزون والتفعيلات، مع شرح بسيط وأمثلة جاهزة.

الأساس: http://alraed.space/api/alraed نسخة التطبيق المطلوبة: 1.6.2.5 المصادقة: Bearer Token الرمز الثابت للشركة: alraed

i نظرة عامة

النظام صار شركة واحدة (single-tenant)، فرمز الشركة ثابت alraed في كل المسارات. جهاز الدنكل (تطبيق ويندوز القديم) يتكلّم مع الخادم عبر HTTP، ويكفي تغيير عنوان الخادم في التطبيق إلى http://alraed.space/api/alraed/.

غلاف الرد الموحّد

كل رد من الـAPI يرجع بنفس الشكل (نجاح أو فشل):

{
  "success": true,          // نجحت العملية؟
  "message": "تم التفعيل",   // رسالة عربية للعرض
  "data": { ... }          // الحمولة (تختلف حسب النقطة)
}
عند الفشل يرجع success:false ورسالة الخطأ في message، مع رمز HTTP مناسب (400 تحقق، 401 غير مصرّح، 403 صلاحية ناقصة، 404 غير موجود).

الفهرس

1 الدخول والمصادقة

التطبيق يسجّل الدخول مرة، يأخذ accessToken، ويرسله في كل طلب لاحق بترويسة Authorization: Bearer <token>.

POST /api/alraed/auth/login
تسجيل الدخول بحساب النظام نفسه. الرد مُثرى بحقلين يستعملهما تطبيق الدنكل: version (نسخة التطبيق المطلوبة — بوابة «حدّث النظام») وapplicationlist (الأنظمة التي لها «تفعيل دنكل» بأرقامها).
الصلاحية: بلا توكن (نقطة عامة) — يحتاج فقط تفعيل وحدة التفعيل.
الطلب
{ "username": "admin", "password": "******" }
الرد (data)
{
  "accessToken": "eyJhbGciOi...",
  "accessTokenExpiresAt": "2026-07-24T12:00:00Z",
  "refreshToken": "...",
  "user": { ... },
  "permissions": ["dongles.activate", "dongles.view"],
  "version": "1.6.2.5",               // لو نسخة التطبيق أقدم ⇒ يطلب تحديث
  "applicationlist": [
    { "id": 12, "name": "الرائد لإدارة العقود", "number": 3,
      "editions": [
        { "id": 7, "name": "Professional", "number": 3 },
        { "id": 8, "name": "Lite", "number": 4 }
      ] }
  ]
}
number = رقم النظام (Product.DongleAppNumber) — يُرسَل لاحقاً بالتفعيل في حقل appNumber. الأنظمة التي بلا «تفعيل دنكل» لا تظهر هنا.
editions = إصدارات النظام (النسخ) بأرقامهاnumber هنا هو الذي يُرسَل بالتفعيل في حقل versionType. تُدار من صفحة «البرامج» بالنظام (لكل إصدار حقل «رقم الدنكل»)، فبدل ما تكون النسخ مكتوبة داخل التطبيق، عبّي قائمة «نوع النسخة» من هنا حسب النظام المختار. الإصدار بلا رقم يرجع number: null (لا يصلح للإرسال).
POST /api/alraed/auth/refresh
تجديد التوكن عند انتهائه (بلا إعادة إدخال كلمة السر).
الطلب
{ "refreshToken": "..." }

2 جدول ملخّص لكل النقاط

كل المسارات تبدأ بـ /api/alraed.

الأسلوبالمسارالوظيفةالصلاحية
POST/auth/loginدخول (توكن + نسخة + الأنظمة)
تطبيق الدنكل (سطح المكتب)
POST/activation/app-activateتفعيل/إعادة تفعيل دنكلactivate / reactivate / manage
POST/activation/app-tabletتفعيل تابلت (الخادم يولّد الكود)activate / manage
GET/activation/dongles/by-key?key=قراءة آخر تفعيل دنكل بالمفتاحview
مخزون الدناكل
GET/activation/dongles?q=قائمة بسيطة (بحث بالمفتاح)view
GET/activation/dongles/pagedقائمة مُرقّمة + إحصائياتview
POST/activation/donglesإضافة دنكل للمخزونmanage
PUT/activation/dongles/{id}تعديل دنكلmanage
DEL/activation/dongles/{id}حذف دنكلmanage
POST/activation/dongles/importاستيراد مخزون (ملف)manage
تفعيلات الدناكل
GET/activation/dongle-activationsقائمة التفعيلات (فلاتر + ترقيم)view
GET/activation/dongle-activations/{id}تفاصيل تفعيلview
POST/activation/dongle-activationsإنشاء تفعيل (من الويب)activate / reactivate / manage
POST/activation/dongle-activations/{id}/actionإيقاف/قفل/فتح/ترقية/مطابقةmanage
DEL/activation/dongle-activations/{id}حذف تفعيلmanage
مساعدة وتقارير
GET/activation/dongles/{id}/lifecycleالخط الزمني لدنكلview
GET/activation/dongle-dashboardلوحة إحصائيات الدناكلview
GET/activation/reports?year=تقرير سنويview
GET/activation/cities?country=قائمة المدنview / requests.view
GET/activation/countriesقائمة الدولview / requests.view

3 APIs تطبيق الدنكل (سطح المكتب)

هذي النقاط التي يناديها تطبيق الدنكل (WinForms) بعد الدخول. كلها تحتاج ترويسة Authorization: Bearer.

قاعدة جوهرية: الخادم لا يولّد كود تفعيل الدنكل — التطبيق يحسبه على جهاز العميل ويرسله جاهزاً في activationCode، والخادم يخزّنه كما هو (مطابقة للنظام القديم). أمّا التابلت فالخادم هو من يولّد الكود ويرجّعه.
متى يكون baseCode/activationCode مطلوباً؟ فقط في الترقية من النظام القديم (isUpgrade:true) وفي تفعيل التابلت (app-tablet — لأنه مُدخَل التوليد). التفعيل العادي وإعادة التفعيل يُقبلان بلا الكودين ويُخزَّنان فارغين.
رقم الإصدار (versionType): يُؤخذ من editions الخاصة بالنظام (من ردّ الدخول). إذا كان النظام معرَّفة له أرقام إصدارات، فأي رقم خارجها يُرفض برسالة تذكر الأرقام المتاحة. 0 = غير محدَّد ومقبول دائماً. والأنظمة التي لم تُضبط أرقام إصداراتها بعد تقبل أي رقم (توافقاً مع القديم: 1=Economy، 2=Standard، 3=Professional، 4=Lite).
محارف التحكّم (0x00): سيريال الدنكل المقروء من الجهاز غالباً يحمل بايتات 0x00 ملتصقة به (مخزن ثابت الطول) — وإرسالها خاماً داخل JSON يجعل الطلب غير صالح كلياً. الخادم صار ينظّفها تلقائياً من جسم الطلب ومن القيم قبل التخزين، لكن يُفضَّل تنظيفها بالتطبيق أيضاً: id.Split('\0')[0].Trim() (لاحظ أن Trim() وحده لا يشيل \0).
POST /activation/app-activate
تفعيل أو إعادة تفعيل دنكل. سيريال الدنكل (dongleKey) يُربط بالمخزون تلقائياً، وإن لم يكن موجوداً يُضاف تلقائياً. النظام يُحدَّد برقمه (appNumber).
دورة قفل الدنكل (مهمة):
  • تفعيل عادي (isReactivation:false) على دنكل مفعّل سابقاً ⇒ يفشل: «هذا الدنكل مفعّل سابقاً (التفعيل #…) — استعمل «إعادة تفعيل» بدل التفعيل».
  • إعادة تفعيل على دنكل تفعيله مقفول ⇒ يفشل: «هذا الدنكل مقفول (التفعيل #…) — افتح القفل من النظام أولاً ثم أعد المحاولة».
  • عند النجاح: كل تفعيلات هذا الدنكل السابقة تُؤرشَف (حذف منطقي مع مَن أرشفها ومتى) — تبقى مرئية بدورة حياة الدنكل.
  • التفعيل الجديد يُقفل تلقائياً ⇒ أي تفعيل لاحق لنفس الدنكل مرفوض حتى يفتح القفل مخوَّل عبر dongle-activations/{id}/action بـ{"action":"unlock"}.
الصلاحية: dongles.activate (للتفعيل) أو dongles.reactivate (لو isReactivation:true) أو dongles.manage أو مدير.
الطلب
{
  "appNumber": 3,               // رقم النظام (من applicationlist)
  "dongleKey": "b897e1404c27ba...", // سيريال الدنكل (يُربط/يُضاف تلقائياً)
  "baseCode": "ABC123",        // كود أساسي من جهاز العميل (مطلوب للترقية فقط)
  "activationCode": "XYZ789",  // الكود جاهزاً من التطبيق (مطلوب للترقية فقط)
  "isServer": true,           // سيرفر (true) أم شبكي (false)
  "isUpgrade": false,
  "isReactivation": false,
  "versionType": 3,            // رقم الإصدار من editions النظام (0 = غير محدَّد)
  "cityValue": 5,              // رقم المدينة (من /cities)
  "countryValue": 0,           // 0=العراق 1=ليبيا
  "companyName": "شركة النور",
  "address": "بغداد - الكرادة",
  "phone": "07701234567",
  "job": "محاسب",
  "note": null
}
الرد (data)
{
  "id": 64867,               // معرّف التفعيل الجديد
  "activationCode": "XYZ789",   // كما أُرسل
  "productId": 12, "productName": "الرائد لإدارة العقود",
  "dongleId": 68586, "dongleKey": "b897e1404c27ba...",
  "dongleCreated": false       // true لو أُضيف الدنكل للمخزون الآن
}
POST /activation/app-tablet
تفعيل تابلت من التطبيق. هنا الخادم يولّد الكود بخوارزمية التابلت (SHA256) ويرجّعه.
الصلاحية: dongles.activate أو dongles.manage أو مدير.
الطلب
{
  "appNumber": 5,
  "baseCode": "IMEI-or-BaseCode",  // مُدخَل التوليد (مطلوب)
  "keyCode": "device-key",
  "cityValue": 5,
  "companyName": "...", "address": "...", "phone": "...", "job": "...", "note": null
}
الرد (data)
{ "id": 771, "activationCode": "a1b2c3d4e5f60718", "productId": 14, "productName": "..." }
GET /activation/dongles/by-key?key={سيريال}
قراءة آخر تفعيل لدنكل بمفتاحه (زر «قراءة من السيرفر» / «مطابقة» في التطبيق). لو الدنكل غير موجود يرجّع found:false.
الصلاحية: dongles.view
الرد (data) عند وجود تفعيل
{
  "found": true, "dongleId": 68586, "dongleKey": "b897...", "dongleActive": true,
  "hasActivation": true, "activationId": 64867,
  "appNumber": 3, "productId": 12, "productName": "الرائد لإدارة العقود",
  "isServer": true, "versionType": 3, "isUpgrade": false,
  "isStop": false, "isLocked": false,
  "companyName": "شركة النور", "address": "...", "phone": "...", "job": "...", "note": null,
  "cityValue": 5, "cityName": "النجف", "countryValue": 0, "countryName": "العراق",
  "activatedAt": "2026-01-15T09:00:00Z"
}
لو الدنكل موجود بالمخزون لكن بلا أي تفعيل: found:true وhasActivation:false.
POST /activation/unlock-requests
طلب فتح قفل دنكل بسبب. يظهر الطلب بصفحة «طلبات فتح الدنكل» بالنظام، ويصل إشعار لكل مَن يملك صلاحية الموافقة. الموافقة تفتح القفل فوراً فيقدر التطبيق يعيد التفعيل.
الصلاحية: dongles.view أو activate أو reactivate أو manage.
الطلب
{ "dongleKey": "b897e1404c27ba...", "reason": "الزبون غيّر الجهاز بعد فورمات" }
الرد (data)
{ "id": 14, "dongleKey": "b897...", "status": "pending", "reason": "...",
  "requestedByName": "أحمد", "createdAt": "...", "productName": "..." }
الطلب المعلّق واحد لكل دنكل: إعادة الإرسال ترجّع الطلب المعلّق نفسه بلا تكرار. ولو التفعيل غير مقفول أصلاً يُرفض الطلب برسالة واضحة.
GET /activation/unlock-requests/by-key?key={سيريال}
حالة آخر طلب فتح لهذا الدنكل — يستعملها التطبيق ليعرف هل تمّت الموافقة (status: pending · approved · rejected). بعد approved يكون القفل مفتوحاً فعلاً.
الصلاحية: نفس صلاحيات إنشاء الطلب.

4 إدارة المخزون

مخزون الدناكل = المفاتيح الفريدة (سيريالات) المتاحة للربط بتفعيل.

GET /activation/dongles?q={بحث}
قائمة بسيطة (بحث بالمفتاح). كل عنصر: { id, dongleKey, isActive, note, createdAt }.
الصلاحية: dongles.view
GET /activation/dongles/paged?q=&flag=&page=1&pageSize=50
قائمة مُرقّمة + إحصائيات. flag = active | inactive | used | unused (تصفية).
الصلاحية: dongles.view
الرد (data)
{
  "items": [{ "id":1, "dongleKey":"...", "isActive":true, "note":null, "createdAt":"...", "activationsCount":1 }],
  "total": 1240,
  "stats": { "total":1240, "active":1200, "inactive":40, "used":1100, "unused":140 }
}
POST /activation/dongles  ·  PUT /activation/dongles/{id}
إضافة/تعديل دنكل. الطلب واحد للاثنين.
الصلاحية: dongles.manage
الطلب
{ "dongleKey": "NEW-SERIAL-001", "isActive": true, "note": null }
DEL /activation/dongles/{id}
حذف دنكل من المخزون.
الصلاحية: dongles.manage
POST /activation/dongles/import
استيراد مخزون دفعة من ملف (multipart، الحقل file). الرد: { imported, skipped, duplicates, errors[] }.
الصلاحية: dongles.manage

5 إدارة التفعيلات

GET /activation/dongle-activations
قائمة التفعيلات مع فلاتر وترقيم وإحصائيات (facet).
الصلاحية: dongles.view
معاملات الاستعلام
customerIdتصفية بعميلproductIdتصفية بنظام
qبحث (كود/شركة/هاتف…)flagserver/stop/locked/upgrade/matching…
from / toمدى تاريخpage / pageSizeترقيم (افتراضي 1 / 50)
GET /activation/dongle-activations/{id}
تفاصيل تفعيل واحد (العميل، النظام، الدنكل، الأكواد، الحالات، الشركة، المدينة/الدولة، منفّذ التفعيل، صورة العقد…).
الصلاحية: dongles.view
POST /activation/dongle-activations
إنشاء تفعيل من الويب (يدوياً). يقبل ربط عميل/نظام/دنكل بالمعرّف، وفتح بوابة اختياري.
الصلاحية: dongles.activate / reactivate (حسب isReactivation) / manage.
الطلب
{
  "customerId": 31908,   // اختياري
  "productId": 12,       // اختياري
  "dongleId": 68586,     // اختياري (من المخزون)
  "isServer": true,
  "baseCode": "ABC123",       // مطلوب
  "activationCode": "XYZ789", // مطلوب
  "isUpgrade": false, "isReactivation": false,
  "cityValue": 5, "countryValue": 0,
  "companyName": "...", "address": "...", "phone": "...", "job": "...", "note": null,
  "openGateway": false       // افتح بوابة زبون لهذا الدنكل (بلا اشتراك)
}
الرد (data)
{ "id": 64868, "activationCode": "XYZ789", "gatewayId": null, "gatewayCode": null }
POST /activation/dongle-activations/{id}/action
إجراء على التفعيل (يُسجَّل بدورة الحياة).
الصلاحية: dongles.manage
الطلب
{ "action": "stop", "value": true }
actionالمعنىvalue
stopإيقاف/إلغاء إيقاف التفعيلtrue=إيقاف، false=إلغاء
lockقفل التفعيل
unlockفتح القفل
upgradeتعيين حالة الترقيةtrue/false
matchingتعيين حالة المطابقةtrue/false
DEL /activation/dongle-activations/{id}
حذف تفعيل (يُسجَّل بدورة الحياة قبل الحذف).
الصلاحية: dongles.manage

6 دورة الحياة · اللوحة · التقارير

GET /activation/dongles/{id}/lifecycle
الخط الزمني لدنكل: كل أحداثه (إنشاء/تفعيل/إيقاف/قفل…) بالترتيب مع منفّذها.
الصلاحية: dongles.view
الرد (data)
{ "id":68586, "dongleKey":"...", "isActive":true, "activationsCount":2,
  "events": [{ "id":9, "type":"activated", "userName":"أحمد", "details":"BaseCode: ABC", "createdAt":"..." }] }
GET /activation/dongle-dashboard
لوحة إحصائيات: المخزون، التفعيلات، الحالات، الدول، وآخر التفعيلات.
الصلاحية: dongles.view
GET /activation/reports?year={سنة}
تقرير سنوي للتفعيلات (تجميع شهري وحسب النظام…).
الصلاحية: dongles.view

7 قوائم مساعدة (مدن / دول)

GET /activation/countries
قائمة الدول. الرد: [{ value, name }]0=العراق، 1=ليبيا.
GET /activation/cities?country={0|1}
قائمة مدن الدولة. بلا معامل = مدن العراق فقط (توافقاً مع صفحة التابلت القديمة). الرد: [{ value, name }].
الصلاحية: dongles.view أو activationrequests.view (مشتركة مع التابلت).

8 الصلاحيات

الدناكل تُحكَم حصرياً بصلاحيات فئة «الدناكل» (فُصلت عن activation.* القديمة).

المفتاحيسمح بـ
dongles.viewعرض المخزون والتفعيلات واللوحة والتقارير ودورة الحياة والقراءة بالمفتاح.
dongles.activateتفعيل دنكل/تابلت جديد (app-activate / app-tablet / إنشاء تفعيل غير مُعاد).
dongles.reactivateإعادة التفعيل (isReactivation:true) — جهاز مبدّل/فورمات.
dongles.manageكل شيء: إدارة المخزون، الإجراءات (إيقاف/قفل…)، الحذف — ويشمل ضمناً التفعيل وإعادته.
تنبيه تشغيلي: الصلاحيات تُخبز في التوكن عند الدخول. أي صلاحية جديدة تُمنح لحساب لا تظهر إلا بعد تسجيل خروج ودخول يُجدّد التوكن.

9 أمثلة عملية كاملة

أ) التدفّق كاملاً بـ curl

# 1) دخول والحصول على التوكن
curl -s -X POST https://alraed.space/api/alraed/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"******"}'

# خذ data.accessToken من الرد، ثم:
TOKEN="eyJhbGciOi..."

# 2) تفعيل دنكل
curl -s -X POST https://alraed.space/api/alraed/activation/app-activate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"appNumber":3,"dongleKey":"b897...","baseCode":"ABC123","activationCode":"XYZ789","isServer":true,"versionType":3,"cityValue":5,"countryValue":0,"companyName":"شركة النور","phone":"07701234567"}'

# 3) قراءة آخر تفعيل بالمفتاح
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://alraed.space/api/alraed/activation/dongles/by-key?key=b897..."

ب) بـ C# (RestSharp — مثل تطبيق الدنكل القديم)

var client = new RestClient("https://alraed.space/api/alraed/");

// 1) دخول
var login = new RestRequest("auth/login", Method.Post)
    .AddJsonBody(new { username = "admin", password = "******" });
var res = client.Execute(login);
// استخرج data.accessToken (الغلاف {success,message,data})
string token = JObject.Parse(res.Content)["data"]["accessToken"].ToString();

// 2) أرسل التوكن بكل طلب لاحق
var act = new RestRequest("activation/app-activate", Method.Post);
act.AddHeader("Authorization", "Bearer " + token);
act.AddJsonBody(new {
    appNumber = 3, dongleKey = "b897...",
    baseCode = "ABC123", activationCode = "XYZ789",
    isServer = true, versionType = 3, cityValue = 5, countryValue = 0
});
var actRes = client.Execute(act);
خلاصة التعامل: ادخل مرة → خزّن التوكن → أرسله في ترويسة Authorization: Bearer بكل طلب → اقرأ data من الغلاف الموحّد. للدنكل أنت تحسب الكود وترسله؛ للتابلت الخادم يحسبه ويرجّعه لك.
توثيق APIs الدنكل — نظام الرائد بلس · single-tenant (alraed) · نسخة تطبيق الدنكل 1.6.2.5