Files
Siro/knowledge/syria_driver_registration_report.md
2026-06-19 01:47:48 +03:00

14 KiB
Raw Permalink Blame History

تقرير نظام تسجيل السائق — سوريا 🇸🇾

ملف المحاكاة التفاعلية: siro_driver_registration_simulation.html


🗺️ خريطة النظام الكاملة

flowchart TD
    A[📱 السائق يفتح التطبيق] --> B[RegisterCaptainController<br/>إدخال رقم الهاتف]
    B --> C{التحقق من الدولة}
    C -->|🇸🇾 سوريا| D[Phone Formatting<br/>09xx → 963xx]
    D --> E[POST /auth/otp/request.php<br/>country: Syria]
    E --> F{OTP Router}
    F -->|Primary| G[Intaleq WhatsApp]
    F -->|Failover 1| H[Nabeh JWT]
    F -->|Failover 2| I[SMS]
    G --> J[DB: phone_verification<br/>AES-GCM encrypted × 5min]
    J --> K[OtpVerificationController<br/>countdown 120s]
    K --> L[POST /auth/otp/verify.php]
    L --> M{✅ صحيح؟}
    M -->|نعم| N[RegistrationController<br/>3-Step Wizard]
    M -->|لا| K
    N --> O[Step 1: Driver Info<br/>الاسم + HID + DOB + License]
    O --> P[Step 2: Car Info<br/>Plate + Make + Model + VIN]
    P --> Q[Step 3: Doc Upload × 8]
    Q --> R[uploadToSyria × 8<br/>MultipartRequest + JWT]
    R --> S[uploadSyrianDocs.php<br/>private_uploads Signed URL 48h]
    S --> T[submitRegistration<br/>POST /register_driver_and_car.php]
    T --> U[🤖 Gemini Flash<br/>Vision AI Analysis]
    U --> V{AI Face Match}
    V -->|✅ high| W[DB Transaction<br/>driver + CarRegistration]
    V -->|❌ mismatch| X[jsonError - رفض]
    W --> Y[FCM → topic:service<br/>Admin Notification]
    Y --> Z[status: yet ⏳]
    Z --> AA[Admin Review]
    AA --> AB[status: active ✅]
    AB --> AC[JWT + driverToken<br/>السائق جاهز للعمل]

📱 مراحل التسجيل (8 خطوات)

1 إدخال رقم الهاتف — RegisterCaptainController

منطق تنسيق الرقم السوري:

// من: register_driver_and_car.php
if (strpos($phone, '00963') === 0) {
    $phone = substr($phone, 2);          // 00963 → 963
} elseif (strpos($phone, '09') === 0) {
    $phone = '963' . substr($phone, 1);  // 09xx → 9639xx
} elseif (strpos($phone, '9') === 0 && strlen($phone) == 9) {
    $phone = '963' . $phone;             // 9xxxxxxxx → 9639xxxxxxxx
}
// التأكد من وجود 9 بعد 963
if (strpos($phone, '963') === 0 && strpos($phone, '9639') !== 0) {
    $phone = '9639' . substr($phone, 3);
}

2 نظام OTP — OtpVerificationController

الدولة المزود الأساسي Failover 1 Failover 2
🇸🇾 سوريا Intaleq WhatsApp Nabeh JWT SMS
🇪🇬 مصر Kazumi SMS Intaleq WhatsApp Nabeh JWT
🇯🇴 الأردن Intaleq SMS Nabeh JWT

مخطط DB:

-- جدول التحقق للسائق
INSERT INTO phone_verification (
  phone_number,     -- مشفر AES-GCM
  driverId,
  email,            -- مشفر AES-GCM
  token_code,       -- مشفر AES-GCM (3 أرقام)
  expiration_time,  -- NOW() + 5 دقائق
  is_verified       -- 0 → 1 عند النجاح
)

Note

Rate Limiting: 3 محاولات كل 5 دقائق لكل IP عبر Redis


3 معلومات السائق — RegistrationController Page 0

الحقول المطلوبة:

  • first_name, last_name, national_number, birthdate, expiry_date
  • التحقق عبر driverInfoFormKey.validate()

ملاحظة تاريخ الميلاد:

// من register_driver_and_car.php
$data['birthdate'] = trim($data['birthdate']) . '-01-01';
// "1990" → "1990-01-01"

4 معلومات المركبة — RegistrationController Page 1

الحقول الجديدة (vehicle_category_id + fuel_type_id):

ID نوع المركبة ID نوع الوقود
1 سيارة (Car) 1 بنزين (Petrol)
2 دراجة نارية 2 ديزل (Diesel)
3 فان / باص 3 كهربائي
4 هايبرد
// Flutter → submitRegistration()
_addField(fields, 'vehicle_category_id', selectedVehicleCategoryId.toString());
_addField(fields, 'fuel_type_id', selectedFuelTypeId.toString());
_addField(fields, 'fuel', fuelObj['name'].toString()); // للتوافق

5 رفع الوثائق — uploadToSyria() × 8

الوثائق المطلوبة لسوريا:

المستند الحقل إلزامي سوريا؟
هوية — وجه id_front
هوية — خلف id_back
رخصة القيادة — وجه driver_license
رخصة القيادة — خلف driver_license_back 🇸🇾 إلزامي فقط!
صورة شخصية profile_picture
لا حكم عليه criminal_record
ترخيص سيارة — وجه car_license_front
ترخيص سيارة — خلف car_license_back

آلية الرفع:

// Flutter: uploadToSyria()
final req = http.MultipartRequest('POST', syrianUploadUri);
req.headers.addAll({
  'Authorization': 'Bearer JWT',
  'X-HMAC-Auth': hmacHeader,
});
req.fields['driver_id'] = driverId;
req.fields['doc_type'] = docType; // e.g., 'driver_license_back'
// timeout: 120 ثانية — 3 محاولات تلقائية

Backend — الرد (Signed URL):

{
  "status": "success",
  "file_url": "https://api-syria.siromove.com/siro/secure_image.php?driver_id=DRV...&doc_type=id_front&ext=jpg&expires=1720000000&signature=sha256...",
  "mime_type": "image/jpeg",
  "size_bytes": 98340,
  "expires_at": "2024-06-21T..."
}

Important

الملفات تُحفظ في private_uploads/ (خارج الويب العام) ولا يمكن الوصول إليها إلا عبر رابط موقّع صالح لمدة 48 ساعة.


6 الذكاء الاصطناعي Gemini Flash — Vision AI

البرومبت الكامل يطلب:

{
  "status": "success|failure",
  "reason": "إذا فشل",
  "face_match_confidence": "high|low",
  "driver": {
    "full_name": "",        // الاسم الكامل بالعربي
    "national_number": "",  // أرقام لاتينية فقط
    "dob": "YYYY-MM-DD",
    "governorate": "",
    "license_expiry_date": "YYYY-MM-DD",
    "license_category": "B|D1|..."
  },
  "car": {
    "car_plate": "",        // e.g., "155186 درعا"
    "vin": "",              // أحرف وأرقام لاتينية
    "color": "",
    "color_hex": "#FFFFFF",
    "make": "", "model": "", "year": ""
  }
}

قواعد الذكاء الاصطناعي الحرجة:

  1. FACE MATCHING: مقارنة الصورة الشخصية ↔ الهوية ↔ رخصة القيادة
  2. OCR ذكي: المسح من كلا وجهي كل وثيقة
  3. تحويل الأرقام العربية (٠١٢) إلى لاتينية (012)
  4. تطبيع ألوان السيارات أبيض → White → #FFFFFF
  5. الفشل الكلي فقط عند: وجه غير متطابق / وثائق مزورة

Warning

SSRF Protection: URLs يُسمح بها فقط من allowedHosts — يُمنع تحميل أي صورة من مصادر خارجية.


7 إدراج قاعدة البيانات — Transaction Atomique

الحقول المشفرة في جدول driver:

$toEncryptDriver = [
    "phone", "email", "first_name", "last_name",
    "name_arabic", "gender", "national_number",
    "address", "site", "fullNameMaritial", "birthdate"
];
// كلها تُشفَّر بـ AES-GCM قبل الإدراج

كلمة المرور (HMAC + bcrypt):

$baseString = implode('|', [$data['id'], $data['phone'], $data['national_number']]);
$rawSecret  = hash_hmac('sha256', $baseString, $pepper, true);
$pwdHashed  = password_hash($rawSecret, PASSWORD_DEFAULT);

8 جداول قاعدة البيانات (schema_primary.sql)

CarRegistration — الجدول الرئيسي للمركبات:

CREATE TABLE `CarRegistration` (
  `id`                 int AUTO_INCREMENT PRIMARY KEY,
  `driverID`           varchar(100) NOT NULL,          -- FK → driver.id
  `vin`                varchar(100) NOT NULL,           -- مشفر
  `car_plate`          varchar(150),                   -- مشفر
  `make`               varchar(255) NOT NULL,
  `model`              varchar(255) NOT NULL,
  `year`               int NOT NULL,
  `expiration_date`    varchar(30) NOT NULL,
  `color`              varchar(255) NOT NULL,
  `owner`              varchar(255) NOT NULL,           -- مشفر
  `color_hex`          varchar(20) NOT NULL,
  `fuel`               varchar(100) NOT NULL,
  `vehicle_category_id` tinyint DEFAULT 1,             -- 1=Car,2=Moto,3=Van
  `fuel_type_id`       tinyint DEFAULT 1,              -- 1=Petrol,2=Diesel...
  `status`             varchar(20) DEFAULT 'yet',      -- yet|active|suspended
  `isDefault`          tinyint DEFAULT 0,
  KEY `idx_driverID` (`driverID`)
)

driver — الجدول الرئيسي للسائقين:

CREATE TABLE `driver` (
  `id`              varchar(100) NOT NULL,            -- DRV{timestamp}{random}
  `phone`           varchar(255) NOT NULL,            -- مشفر AES-GCM
  `email`           varchar(255) NOT NULL,
  `password`        varchar(255) NOT NULL,            -- bcrypt(HMAC)
  `first_name`      varchar(255) NOT NULL,            -- مشفر
  `last_name`       varchar(255) NOT NULL,            -- مشفر
  `national_number` varchar(255),                     -- مشفر (UNIQUE)
  `name_arabic`     varchar(255),                     -- مشفر — من AI
  `birthdate`       varchar(255),                     -- مشفر
  `status`          varchar(20) DEFAULT 'notDeleted', -- yet|active|notDeleted
  `expiry_date`     date,                             -- انتهاء رخصة القيادة
  UNIQUE KEY `national_number` (`national_number`)
)

driver_documents:

CREATE TABLE `driver_documents` (
  `id`          int AUTO_INCREMENT PRIMARY KEY,
  `driverID`    varchar(64) NOT NULL,
  `doc_type`    varchar(64) NOT NULL,  -- id_front, driver_license_back, ...
  `image_name`  varchar(255) NOT NULL,
  `link`        varchar(512) NOT NULL, -- Signed URL
  `upload_date` datetime NOT NULL,
  KEY `driverID` (`driverID`)
)

🔒 طبقات الأمان

الطبقة التقنية التفاصيل
Authentication JWT Bearer يُعاد التوليد عند كل دخول
Transport HMAC-SHA256 X-HMAC-Auth header
Encryption at Rest AES-GCM جميع البيانات الحساسة
Password HMAC + bcrypt pepper من env
Rate Limiting Redis 3 OTP / 5 دقائق لكل IP
File Access Signed URLs HMAC-SHA256 صالح 48 ساعة
SSRF Protection Allowlist hosts منع URL injection في AI
SQL Injection PDO Prepared Statements كل الاستعلامات
File Upload MIME detection + finfo ليس Content-Type فقط

النتيجة: هل النظام يعمل صح؟

العملية السلوك التقييم
تنسيق الهاتف السوري 09xx9639xx تلقائياً
OTP سوريا Intaleq WhatsApp → Nabeh Failover
خلف رخصة القيادة إلزامي فقط لـ countryCode == Syria
رفع الوثائق Retry × 3 + Timeout 120s
AI Face Match Gemini Flash Vision — تحقق ذكي
AI يعيد كتابة البيانات اسم، DOB، لوحة، VIN من الوثائق
Transaction Atomic: driver + CarRegistration + documents
تشفير الحقول phone, name, national_number → AES-GCM
vehicle_category_id و fuel_type_id يُرسلان من Flutter ويُخزنان في CarRegistration
إشعار خدمة العملاء FCM → topic:service بعد التسجيل
Signed URL صالح 48 ساعة + HMAC signed

Caution

ملاحظة: حقل vin في submitRegistration() يُرسل كـ 'yet' افتراضياً ولا يُرسل من حقل carVinController:

_addField(fields, 'vin', 'yet'); // ← يجب ربطه بـ carVinController.text

AI سيُصحح هذا من خلال استخراج VIN من صورة ترخيص السيارة، لكن إذا لم يعمل AI فسيُخزن 'yet'.


📁 الملفات المرجعية

الملف الدور
registration_controller.dart Controller رئيسي — رفع الوثائق + تقديم التسجيل
register_captin_controller.dart تسجيل مبدئي + OTP
opt_token_controller.dart إدارة OTP (120 ثانية countdown)
register_driver_and_car.php Backend — Gemini AI + DB Transaction
uploadSyrianDocs.php Backend — رفع الوثائق + Signed URL
request.php Backend — إرسال OTP حسب الدولة
verify.php Backend — التحقق من OTP
schema_primary.sql DB Schema — driver + CarRegistration + driver_documents