Update: 2026-06-12 01:23:54
This commit is contained in:
@@ -46,7 +46,9 @@ if (!move_uploaded_file($file['tmp_name'], $uploadPath)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$imageUrl = "https://intaleq.xyz/siro/auth/uploads/documents/" . $uniqueName ;
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'api-syria.siromove.com';
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
|
||||
$imageUrl = "$protocol://$host/siro/auth/uploads/documents/" . $uniqueName ;
|
||||
$imageData = file_get_contents($uploadPath);
|
||||
$imageBase64 = base64_encode($imageData);
|
||||
|
||||
@@ -196,7 +198,7 @@ EOT
|
||||
$prompt = $prompts[$type] ?? $prompts["id_front_sy"];
|
||||
|
||||
$apiKey = getenv("GEMINI_API_KEY");
|
||||
$apiURL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent?key=$apiKey";
|
||||
$apiURL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=$apiKey";
|
||||
|
||||
$headers = ["Content-Type: application/json"];
|
||||
$payload = [
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
uploadLog("🚀 [uploadDocSyria.php] Document upload script started.");
|
||||
|
||||
$driverId = trim((string) filterRequest("driver_id"));
|
||||
$type = trim((string) filterRequest("type"));
|
||||
|
||||
@@ -13,8 +15,23 @@ $type = trim((string) filterRequest("type"));
|
||||
if ($driverId === "") { $driverId = "unknown"; }
|
||||
if ($type === "") { $type = "generic"; }
|
||||
|
||||
uploadLog("📥 Request parameters: driver_id=$driverId, type=$type");
|
||||
|
||||
// ✅ التحقق من ملف الصورة
|
||||
if (isset($_FILES['image'])) {
|
||||
uploadLog("$_FILES['image'] metadata", 'INFO', [
|
||||
'name' => $_FILES['image']['name'] ?? 'unknown',
|
||||
'type' => $_FILES['image']['type'] ?? 'unknown',
|
||||
'size' => $_FILES['image']['size'] ?? 0,
|
||||
'upload_error_code' => $_FILES['image']['error'] ?? UPLOAD_ERR_OK
|
||||
]);
|
||||
} else {
|
||||
uploadLog("No 'image' file was sent in the request.", 'WARNING');
|
||||
}
|
||||
|
||||
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
|
||||
$err = $_FILES['image']['error'] ?? 'missing_file';
|
||||
uploadLog("❌ File upload validation failed. Code: $err", 'ERROR');
|
||||
error_log("Upload error: Image not provided or upload failed.");
|
||||
jsonError("Image upload failed");
|
||||
exit;
|
||||
@@ -26,6 +43,7 @@ $file = $_FILES['image'];
|
||||
$allowedExt = ['jpg', 'jpeg', 'png'];
|
||||
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if (!in_array($extension, $allowedExt, true)) {
|
||||
uploadLog("❌ Unsupported file extension: $extension", 'ERROR');
|
||||
error_log("Unsupported file extension: $extension");
|
||||
jsonError("Unsupported file type");
|
||||
exit;
|
||||
@@ -91,7 +109,7 @@ $publicPath = "/siro/auth/uploads/documents/" . $uniqueName;
|
||||
$imageUrl = rtrim(BASE_URL, '/') . $publicPath;
|
||||
|
||||
// ✅ نتيجة نهائية: فقط رابط الصورة وبعض البيانات المفيدة
|
||||
uploadLog("✅ Document upload succeeded. URL: $imageUrl");
|
||||
printSuccess([
|
||||
$imageUrl,
|
||||
|
||||
]);
|
||||
@@ -16,8 +16,9 @@ try {
|
||||
exit;
|
||||
}
|
||||
|
||||
/* ================== General Settings ================== */
|
||||
$PUBLIC_BASE = "https://intaleq.xyz/driver_docs";
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'api-syria.siromove.com';
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
|
||||
$PUBLIC_BASE = "$protocol://$host/siro/auth/uploads/documents";
|
||||
|
||||
/* ================== 1) Input Fields ================== */
|
||||
$raw_first_name = null;
|
||||
@@ -38,8 +39,12 @@ try {
|
||||
// vehicle_category_id, fuel_type_id
|
||||
|
||||
$docKeys = [
|
||||
'driver_license_front',
|
||||
'id_front',
|
||||
'id_back',
|
||||
'driver_license',
|
||||
'driver_license_back',
|
||||
'profile_picture',
|
||||
'criminal_record',
|
||||
'car_license_front',
|
||||
'car_license_back'
|
||||
];
|
||||
@@ -63,6 +68,7 @@ try {
|
||||
}
|
||||
|
||||
/* ================== 🟢 START PHONE FORMATTING LOGIC 🟢 ================== */
|
||||
$country = 'Syria'; // Default
|
||||
if (!empty($data['phone'])) {
|
||||
$phone = $data['phone'];
|
||||
|
||||
@@ -70,39 +76,46 @@ try {
|
||||
$phone = preg_replace('/[ \-\(\)\+]/', '', $phone);
|
||||
$phone = trim($phone);
|
||||
|
||||
// 2. توحيد البادئات الدولية
|
||||
if (strpos($phone, '00963') === 0) {
|
||||
$phone = substr($phone, 2);
|
||||
} elseif (strpos($phone, '0963') === 0) {
|
||||
$phone = substr($phone, 1);
|
||||
}
|
||||
if (strpos($phone, '962') === 0 || strpos($phone, '00962') === 0) {
|
||||
if (strpos($phone, '00962') === 0) $phone = substr($phone, 2);
|
||||
$country = 'Jordan';
|
||||
} elseif (strpos($phone, '20') === 0 || strpos($phone, '0020') === 0) {
|
||||
if (strpos($phone, '0020') === 0) $phone = substr($phone, 2);
|
||||
$country = 'Egypt';
|
||||
} else {
|
||||
// 2. توحيد البادئات الدولية (سوريا)
|
||||
if (strpos($phone, '00963') === 0) {
|
||||
$phone = substr($phone, 2);
|
||||
} elseif (strpos($phone, '0963') === 0) {
|
||||
$phone = substr($phone, 1);
|
||||
}
|
||||
|
||||
// 3. معالجة الحالات الخاصة بالصفر الزائد بعد الرمز الدولي
|
||||
if (strpos($phone, '96309') === 0) {
|
||||
$phone = '9639' . substr($phone, 5);
|
||||
}
|
||||
elseif (strpos($phone, '9630') === 0) {
|
||||
$phone = '9639' . substr($phone, 4);
|
||||
}
|
||||
// 3. معالجة الحالات الخاصة بالصفر الزائد بعد الرمز الدولي
|
||||
if (strpos($phone, '96309') === 0) {
|
||||
$phone = '9639' . substr($phone, 5);
|
||||
}
|
||||
elseif (strpos($phone, '9630') === 0) {
|
||||
$phone = '9639' . substr($phone, 4);
|
||||
}
|
||||
|
||||
// 4. معالجة الأرقام المحلية
|
||||
elseif (strpos($phone, '09') === 0) {
|
||||
$phone = '963' . substr($phone, 1);
|
||||
}
|
||||
elseif (strpos($phone, '9') === 0 && strlen($phone) == 9) {
|
||||
$phone = '963' . $phone;
|
||||
}
|
||||
elseif (strpos($phone, '0') === 0 && strlen($phone) == 10) {
|
||||
$phone = '963' . substr($phone, 1);
|
||||
}
|
||||
|
||||
// 5. التأكد من وجود 9 بعد الرمز الدولي
|
||||
if (strpos($phone, '963') === 0 && strlen($phone) > 3) {
|
||||
if (strpos($phone, '9639') !== 0) {
|
||||
$phone = '9639' . substr($phone, 3);
|
||||
// 4. معالجة الأرقام المحلية
|
||||
elseif (strpos($phone, '09') === 0) {
|
||||
$phone = '963' . substr($phone, 1);
|
||||
}
|
||||
elseif (strpos($phone, '9') === 0 && strlen($phone) == 9) {
|
||||
$phone = '963' . $phone;
|
||||
}
|
||||
elseif (strpos($phone, '0') === 0 && strlen($phone) == 10) {
|
||||
$phone = '963' . substr($phone, 1);
|
||||
}
|
||||
|
||||
// 5. التأكد من وجود 9 بعد الرمز الدولي
|
||||
if (strpos($phone, '963') === 0 && strlen($phone) > 3) {
|
||||
if (strpos($phone, '9639') !== 0) {
|
||||
$phone = '9639' . substr($phone, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data['phone'] = $phone;
|
||||
}
|
||||
/* ================== 🔴 END PHONE FORMATTING LOGIC 🔴 ================== */
|
||||
@@ -131,6 +144,7 @@ try {
|
||||
$docUrls = [];
|
||||
foreach ($docKeys as $k) {
|
||||
$u = filterRequest($k);
|
||||
if (($k === 'driver_license_back' || $k === 'criminal_record') && ($u === null || $u === '')) continue;
|
||||
if ($u === null || $u === '') {
|
||||
jsonError("Missing document URL: $k");
|
||||
exit;
|
||||
@@ -142,6 +156,149 @@ try {
|
||||
$docUrls[$k] = $u;
|
||||
}
|
||||
|
||||
/* ================== AI PROCESSING START ================== */
|
||||
$apiKey = getenv("GEMINI_API_KEY");
|
||||
if ($apiKey) {
|
||||
$promptBase = '
|
||||
You are a highly secure AI Assistant specialized in analyzing identification and driver documents.
|
||||
Country Context: ' . ($country ?? 'Syria') . '
|
||||
|
||||
### TASK
|
||||
We are providing you with multiple images representing a driver\'s documents (National ID, Driver License, Profile Picture, Criminal Record, and Car Registration).
|
||||
Extract all the required data accurately according to the exact schema provided, and perform a FACE MATCHING analysis.
|
||||
Since the driver may be from Jordan, Egypt, or Syria, the layout, fields, and distribution of information between the front and back of each card varies widely by country.
|
||||
Therefore, do NOT assume a specific field is on the front or the back of a card. You must scan all provided document images (e.g., both ID images, both license images) and intelligently locate the requested fields wherever they appear on the cards.
|
||||
|
||||
### RULES
|
||||
1. Convert any Eastern-Arabic digits (٠١٢٣٤٥٦٧٨٩) to Western digits (0-9).
|
||||
2. Dates must be formatted as ISO `YYYY-MM-DD`.
|
||||
3. Smart Extraction: Scan all provided document images without restriction. Do not fail the overall request if some optional fields (like governorate or address or issue dates) are missing or unreadable on the card. Simply set those specific fields to `null` in the JSON, but do NOT set the overall status to failure. Overall status should only be \'failure\' if there is a critical security/authenticity issue (e.g., face mismatch, fake/forged documents, or missing primary driver identity).
|
||||
4. FACE MATCHING (CRITICAL): Compare the face in the "Profile Picture" with the photos on the "National ID" and "Driver License".
|
||||
5. Ensure the Criminal/Non-Conviction record is valid and matches the driver\'s name.
|
||||
6. The `national_number` and `vin` (chassis) must contain Latin digits/characters only.
|
||||
7. Normalize color names (e.g. "أبيض" -> "White") and provide a matching Hex code (e.g. "#FFFFFF").
|
||||
8. Do NOT add markdown formatting around the output. Return ONLY raw JSON.
|
||||
|
||||
### REQUIRED JSON OUTPUT FORMAT
|
||||
{
|
||||
"status": "success|failure",
|
||||
"reason": "If failure, state the reason (e.g., Face mismatch, blurry, invalid record)",
|
||||
"face_match_confidence": "high|low",
|
||||
"driver": {
|
||||
"full_name": "", // Full name in Arabic
|
||||
"national_number": "", // National ID/National number (Latin digits)
|
||||
"dob": "YYYY-MM-DD", // Date of birth
|
||||
"address": "", // Full address
|
||||
"governorate": "", // Governorate/Site/City
|
||||
"gender": "Male|Female", // Gender
|
||||
"id_issue_date": "YYYY-MM-DD", // National ID issue date
|
||||
"license_issue_date": "YYYY-MM-DD", // Driver license issue date
|
||||
"license_expiry_date": "YYYY-MM-DD", // Driver license expiry date
|
||||
"license_number": "", // Driver license number
|
||||
"license_category": "", // License category (e.g., D1, B, Private, Public)
|
||||
"blood_type": "", // Blood type (e.g., A+, O-)
|
||||
"civil_registry": "", // Civil registry/Place of registration
|
||||
"birth_place": "" // Place of birth
|
||||
},
|
||||
"car": {
|
||||
"car_plate": "", // Full car plate (e.g., 155186 درعا)
|
||||
"owner": "", // Owner full name
|
||||
"vin": "", // Chassis/VIN number
|
||||
"color": "", // Color name
|
||||
"color_hex": "", // Color hex code (e.g., #FFFFFF)
|
||||
"car_issue_date": "YYYY-MM-DD", // Car registration issue date
|
||||
"inspection_date": "YYYY-MM-DD", // Car next inspection date
|
||||
"make": "", // Car Make (e.g., Hyundai)
|
||||
"model": "", // Car Model (e.g., H1)
|
||||
"year": "", // Manufacturing year (e.g., 2019)
|
||||
"fuel": "" // Fuel type (e.g., Petrol, Diesel, Electric)
|
||||
}
|
||||
}';
|
||||
|
||||
$contents = [
|
||||
["role" => "user", "parts" => [["text" => $promptBase]]]
|
||||
];
|
||||
|
||||
foreach ($docUrls as $key => $url) {
|
||||
$imgData = @file_get_contents($url);
|
||||
if ($imgData !== false) {
|
||||
$base64 = base64_encode($imgData);
|
||||
$ext = strtolower(pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
$mime = ($ext === 'png') ? 'image/png' : 'image/jpeg';
|
||||
$contents[0]["parts"][] = ["text" => "Image type: " . $key];
|
||||
$contents[0]["parts"][] = ["inlineData" => ["mimeType" => $mime, "data" => $base64]];
|
||||
}
|
||||
}
|
||||
|
||||
$apiURL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=$apiKey";
|
||||
$payload = ["contents" => $contents];
|
||||
|
||||
$ch = curl_init($apiURL);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response) {
|
||||
$aiData = json_decode($response, true);
|
||||
$textRaw = $aiData['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
||||
$textRaw = trim(preg_replace('/```json|```/', '', $textRaw));
|
||||
$json = json_decode($textRaw, true);
|
||||
|
||||
if ($json && isset($json['status']) && strtolower($json['status']) === 'failure') {
|
||||
jsonError("AI Verification Failed: " . ($json['reason'] ?? 'Unknown reason'));
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($json && isset($json['driver'])) {
|
||||
$ex = $json['driver'];
|
||||
if (!empty($ex['full_name'])) {
|
||||
$data['name_arabic'] = $ex['full_name'];
|
||||
$parts = explode(' ', trim($ex['full_name']));
|
||||
if (count($parts) >= 2) {
|
||||
$data['first_name'] = $parts[0];
|
||||
$data['last_name'] = implode(' ', array_slice($parts, 1));
|
||||
} else {
|
||||
$data['first_name'] = $ex['full_name'];
|
||||
$data['last_name'] = '';
|
||||
}
|
||||
}
|
||||
if (!empty($ex['national_number'])) $data['national_number'] = $ex['national_number'];
|
||||
if (!empty($ex['dob'])) {
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $ex['dob'])) {
|
||||
$data['birthdate'] = $ex['dob'];
|
||||
} elseif (preg_match('/^\d{4}$/', $ex['dob'])) {
|
||||
$data['birthdate'] = $ex['dob'] . '-01-01';
|
||||
}
|
||||
}
|
||||
if (!empty($ex['address'])) $data['address'] = $ex['address'];
|
||||
if (!empty($ex['governorate'])) $data['site'] = $ex['governorate'];
|
||||
if (!empty($ex['gender'])) $data['gender'] = $ex['gender'];
|
||||
if (!empty($ex['id_issue_date'])) $data['issue_date'] = $ex['id_issue_date'];
|
||||
if (!empty($ex['license_issue_date'])) $data['licenseIssueDate'] = $ex['license_issue_date'];
|
||||
if (!empty($ex['license_expiry_date'])) $data['expiry_date'] = $ex['license_expiry_date'];
|
||||
if (!empty($ex['license_category'])) $data['license_categories'] = $ex['license_category'];
|
||||
// Not mapped directly in basic DB schema but extracted: blood_type, civil_registry, birth_place
|
||||
}
|
||||
|
||||
if ($json && isset($json['car'])) {
|
||||
$ex = $json['car'];
|
||||
if (!empty($ex['car_plate'])) $car['car_plate'] = $ex['car_plate'];
|
||||
if (!empty($ex['make'])) $car['make'] = $ex['make'];
|
||||
if (!empty($ex['model'])) $car['model'] = $ex['model'];
|
||||
if (!empty($ex['year'])) $car['year'] = $ex['year'];
|
||||
if (!empty($ex['color'])) $car['color'] = $ex['color'];
|
||||
if (!empty($ex['color_hex'])) $car['color_hex'] = $ex['color_hex'];
|
||||
if (!empty($ex['vin'])) $car['vin'] = $ex['vin'];
|
||||
if (!empty($ex['owner'])) $car['owner'] = $ex['owner'];
|
||||
if (!empty($ex['fuel'])) $car['fuel'] = $ex['fuel'];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* ================== AI PROCESSING END ================== */
|
||||
|
||||
/* ================== 2) Generate default id/email ================== */
|
||||
if (empty($data['id'])) {
|
||||
$data['id'] = 'DRV' . date('YmdHis') . random_int(1000, 9999);
|
||||
@@ -320,6 +477,7 @@ $pwdHashed = password_hash($rawSecret, PASSWORD_DEFAULT);
|
||||
");
|
||||
|
||||
foreach ($docKeys as $k) {
|
||||
if (!isset($docUrls[$k])) continue;
|
||||
$url = $docUrls[$k];
|
||||
$name = basename(parse_url($url, PHP_URL_PATH) ?? '');
|
||||
if ($name === '') { $name = $k . '_' . time() . '.jpg'; }
|
||||
@@ -330,6 +488,18 @@ $pwdHashed = password_hash($rawSecret, PASSWORD_DEFAULT);
|
||||
':image_name' => $name,
|
||||
':link' => $url,
|
||||
]);
|
||||
|
||||
if ($k === 'profile_picture') {
|
||||
$insProfile = $con->prepare("
|
||||
INSERT INTO imageProfileCaptain (driverID, image_name, link)
|
||||
VALUES (:driverID, :image_name, :link)
|
||||
");
|
||||
$insProfile->execute([
|
||||
':driverID' => $driverID,
|
||||
':image_name' => $name,
|
||||
':link' => $url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================== 10) Commit ================== */
|
||||
|
||||
@@ -10,12 +10,17 @@ const MAX_FILE_MB = 5;
|
||||
const ALLOWED_MIMES = ['image/jpeg','image/png','image/webp']; // فقط صور
|
||||
const UPLOAD_ROOT = __DIR__ . "/../../private_uploads"; // مجلد خاص (غير عام)
|
||||
const SIGN_SECRET = getenv('SECRET_KEY_HMAC'); // غيّرها واقرأها من .env
|
||||
const PUBLIC_BASE = 'https://syria.intaleq.xyz/siro'; // الدومين العلني
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'api-syria.siromove.com';
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
|
||||
define('PUBLIC_BASE', "$protocol://$host/siro");
|
||||
const SIGNED_TTL_SEC = 172800; // 2 days = 60*60*24
|
||||
|
||||
// أنشئ مجلد الرفع إن لم يكن موجودًا
|
||||
if (!is_dir(UPLOAD_ROOT)) { @mkdir(UPLOAD_ROOT, 0700, true); }
|
||||
|
||||
// Log entry
|
||||
uploadLog("🚀 [uploadSyrianDocs.php] Document upload script started.");
|
||||
|
||||
// (اختياري) هيدرز أمان
|
||||
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
||||
$hmacHeader = $_SERVER['HTTP_X_HMAC_AUTH'] ?? '';
|
||||
@@ -26,7 +31,10 @@ $driverId = filterRequest('driver_id');
|
||||
$docType = filterRequest('doc_type');
|
||||
$purpose = filterRequest('purpose'); // اختياري
|
||||
|
||||
uploadLog("📥 Request params: driver_id=$driverId, doc_type=$docType");
|
||||
|
||||
if (empty($driverId) || empty($docType)) {
|
||||
uploadLog("❌ Missing driver_id or doc_type params.", 'ERROR');
|
||||
jsonError("driver_id and doc_type are required.");
|
||||
exit;
|
||||
}
|
||||
@@ -39,12 +47,26 @@ $allowedDocTypes = [
|
||||
'car_license_back',
|
||||
];
|
||||
if (!in_array($docType, $allowedDocTypes, true)) {
|
||||
uploadLog("❌ Invalid doc_type value: $docType", 'ERROR');
|
||||
jsonError("Invalid doc_type.");
|
||||
exit;
|
||||
}
|
||||
|
||||
// --------- التحقق من الملف ---------
|
||||
if (isset($_FILES['file'])) {
|
||||
uploadLog("$_FILES['file'] metadata", 'INFO', [
|
||||
'name' => $_FILES['file']['name'] ?? 'unknown',
|
||||
'type' => $_FILES['file']['type'] ?? 'unknown',
|
||||
'size' => $_FILES['file']['size'] ?? 0,
|
||||
'upload_error_code' => $_FILES['file']['error'] ?? UPLOAD_ERR_OK
|
||||
]);
|
||||
} else {
|
||||
uploadLog("No 'file' payload was sent in the request.", 'WARNING');
|
||||
}
|
||||
|
||||
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
$err = $_FILES['file']['error'] ?? 'missing_file';
|
||||
uploadLog("❌ File upload validation failed. Code: $err", 'ERROR');
|
||||
jsonError("No file uploaded or upload error.");
|
||||
exit;
|
||||
}
|
||||
@@ -116,6 +138,7 @@ $fileUrl = PUBLIC_BASE . "/secure_image.php"
|
||||
. "&signature={$signature}";
|
||||
|
||||
// --------- استجابة ---------
|
||||
uploadLog("✅ Document upload succeeded. URL: $fileUrl");
|
||||
printSuccess([
|
||||
"status" => "success",
|
||||
"success_file" => true,
|
||||
|
||||
Reference in New Issue
Block a user