chore: استيراد أولي من سيرو (ecfe7568) — بلا أي تعديل

نسخة كاملة من مستودع سيرو عند ecfe7568 لتكون أساس تطبيق «انطلق».
نُسخ المتعقَّب في git فقط (12,509 ملفاً / 302 م.ب) بـ git archive، لا
`cp -r` — فاستُثنيت تلقائياً مخلفات البناء (build · node_modules ·
.dart_tool · .gradle · Pods ≈ 10.7 غ.ب) وكل ما يستثنيه .gitignore.

هذا الكوميت **بلا أي تعديل عمداً** حتى يكون كل ما يليه فرقاً مقروءاً
مقابل سيرو الأصلي. سيرو نفسه لم يُمسّ.

⚠️ لا يبني بعد: `.env` و`lib/env/env.g.dart` غير متعقَّبين في سيرو (وهذا
صحيح — أسرار لكل مستأجر). كل تطبيق فلاتر هنا يحتاج .env خاصاً بانطلق ثم
توليد env.g.dart عبر build_runner. لا تُنسخ أسرار سيرو.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-27 05:10:29 +03:00
co-authored by Claude Opus 5
commit 92dc6b3641
3654 changed files with 896478 additions and 0 deletions
@@ -0,0 +1,167 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
// التطبيع عبر normalizePhone() الموحّدة في core/helpers.php (نفس المنطق سابقاً)
$phone = filterRequest('phone');
if (!$phone) {
error_log("[get_last_ride] Missing phone parameter");
jsonError("Phone is required");
exit;
}
// تطبيع الرقم أولاً
$raw = normalizePhone($phone);
// شَفِّر قبل الاستعلام
$enc_raw = $encryptionHelper->encryptData($raw);
try {
error_log("[get_last_ride] Searching phone normalized=$raw");
// 1) ابحث عن الراكب بالهاتف المشفّر
$selP = $con->prepare("
SELECT id, first_name, last_name, phone
FROM passengers
WHERE phone = :enc_raw OR (:bidx IS NOT NULL AND phone_bidx = :bidx)
LIMIT 1
");
$selP->execute(['enc_raw' => $enc_raw, 'bidx' => $pBidx]);
$passenger = $selP->fetch(PDO::FETCH_ASSOC);
// 2) ابحث عن السائق بالهاتف المشفّر
$selD = $con->prepare("
SELECT id AS driverID, first_name, last_name, phone
FROM driver
WHERE phone = :enc_raw OR (:bidx IS NOT NULL AND phone_bidx = :bidx)
LIMIT 1
");
$selD->execute(['enc_raw' => $enc_raw, 'bidx' => $dBidx]);
$driver = $selD->fetch(PDO::FETCH_ASSOC);
$userId = null;
$userType = null;
if ($passenger) {
$userId = $passenger['id'];
$userType = 'passenger';
error_log("[get_last_ride] Passenger found id=" . $userId);
}
if ($driver) {
$userId = $driver['driverID'];
$userType = 'driver';
error_log("[get_last_ride] Driver found id=" . $userId);
}
if (!$userId) {
error_log("[get_last_ride] User not found (phone=$raw)");
jsonError('Phone number not found in system');
exit;
}
// 3) تحديد حقل البحث في الرحلة
$userField = ($userType === 'driver') ? 'r.driver_id' : 'r.passenger_id';
// فلترة حسب الحالة إذا أُرسلت
$filterStatus = filterRequest('status');
$whereExtra = '';
$params = [':uid' => $userId];
if (!empty($filterStatus) && $filterStatus !== 'all') {
$whereExtra = "AND r.status = :filter_status";
$params[':filter_status'] = $filterStatus;
}
// 4) آخر 20 رحلة لهذا المستخدم
$rideStmt = $con->prepare("
SELECT
r.id,
r.start_location,
r.end_location,
r.date,
r.time,
r.endtime,
r.status,
r.paymentMethod,
r.carType,
r.price,
r.price_for_driver,
r.price_for_passenger,
r.distance,
r.driver_id,
r.passenger_id,
r.created_at,
r.updated_at,
r.DriverIsGoingToPassenger,
r.rideTimeStart,
r.rideTimeFinish,
d.first_name AS driver_first_name,
d.last_name AS driver_last_name,
d.phone AS d_phone,
p.first_name AS p_fname,
p.last_name AS p_lname,
p.phone AS p_phone
FROM ride r
LEFT JOIN driver d ON d.id = r.driver_id
LEFT JOIN passengers p ON p.id = r.passenger_id
WHERE $userField = :uid $whereExtra
ORDER BY r.created_at DESC, r.id DESC
LIMIT 20
");
$rideStmt->execute($params);
$rides = $rideStmt->fetchAll(PDO::FETCH_ASSOC);
// 5) فك تشفير الأسماء
if ($passenger) {
$passenger['first_name'] = $encryptionHelper->decryptData($passenger['first_name']);
$passenger['last_name'] = $encryptionHelper->decryptData($passenger['last_name']);
$passenger['phone'] = $encryptionHelper->decryptData($passenger['phone']);
}
if ($driver) {
$driver['first_name'] = $encryptionHelper->decryptData($driver['first_name']);
$driver['last_name'] = $encryptionHelper->decryptData($driver['last_name']);
$driver['phone'] = $encryptionHelper->decryptData($driver['phone']);
}
foreach ($rides as &$ride) {
if (!empty($ride['driver_first_name'])) {
$ride['driver_first_name'] = $encryptionHelper->decryptData($ride['driver_first_name']);
}
if (!empty($ride['driver_last_name'])) {
$ride['driver_last_name'] = $encryptionHelper->decryptData($ride['driver_last_name']);
}
if (!empty($ride['d_phone'])) {
$ride['d_phone'] = $encryptionHelper->decryptData($ride['d_phone']);
}
if (!empty($ride['p_fname'])) {
$ride['p_fname'] = $encryptionHelper->decryptData($ride['p_fname']);
}
if (!empty($ride['p_lname'])) {
$ride['p_lname'] = $encryptionHelper->decryptData($ride['p_lname']);
}
if (!empty($ride['p_phone'])) {
$ride['p_phone'] = $encryptionHelper->decryptData($ride['p_phone']);
}
}
unset($ride);
// 6) الرد
$response = [
'user_type' => $userType,
'user' => $userType === 'driver' ? $driver : $passenger,
'rides' => $rides
];
error_log("[get_last_ride] Success response for " . $userType . " id=" . $userId);
jsonSuccess($response);
} catch (Throwable $e) {
error_log("[get_last_ride] Exception: " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
@@ -0,0 +1,88 @@
<?php
require_once __DIR__ . '/../../connect.php';
$rideId = filterRequest('id');
$status = filterRequest('status');
$reason = filterRequest('reason'); // اختياري
if (empty($rideId) || empty($status)) {
jsonError("id and status are required");
exit;
}
/* whitelist للحالات المسموحة – تطابق حالات DB الفعلية */
$allowed = [
'New', 'waiting', 'wait', 'Apply', 'Applied',
'Arrived', 'arrived', 'Begin', 'Finished',
'Cancel', 'CancelFromDriver', 'CancelFromPassenger', 'TimeOut'
];
if (!in_array($status, $allowed, true)) {
jsonError("Invalid status");
exit;
}
try {
$con->beginTransaction();
// إن أردت ختم وقت النهاية تلقائيًا عند الإكمال
if ($status === 'Completed') {
$sql = "UPDATE ride
SET status = :st, rideTimeFinish = IFNULL(rideTimeFinish, NOW()), updated_at = CURRENT_TIMESTAMP
WHERE id = :id";
} else {
$sql = "UPDATE ride
SET status = :st, updated_at = CURRENT_TIMESTAMP
WHERE id = :id";
}
$stmt = $con->prepare($sql);
$ok = $stmt->execute(['st' => $status, 'id' => $rideId]);
if (!$ok || $stmt->rowCount() === 0) {
$con->rollBack();
jsonError("Ride not found or no change");
exit;
}
// أعِدّ بيانات الرحلة المحدّثة (للتحديث الفوري في الواجهة)
$fetch = $con->prepare("
SELECT
r.id,
r.start_location,
r.end_location,
r.date,
r.time,
r.endtime,
r.status,
r.paymentMethod,
r.carType,
r.price,
r.price_for_driver,
r.price_for_passenger,
r.distance,
r.driver_id,
r.passenger_id,
r.created_at,
r.updated_at,
r.DriverIsGoingToPassenger,
r.rideTimeStart,
r.rideTimeFinish,
d.first_name AS driver_first_name,
d.last_name AS driver_last_name
FROM ride r
LEFT JOIN driver d ON d.id = r.driver_id
WHERE r.id = :id
LIMIT 1
");
$fetch->execute(['id' => $rideId]);
$ride = $fetch->fetch(PDO::FETCH_ASSOC);
$con->commit();
jsonSuccess(['ride' => $ride, 'message' => 'Status updated']);
} catch (Throwable $e) {
if ($con->inTransaction()) $con->rollBack();
jsonError("An internal error occurred. Please try again later.");
}
@@ -0,0 +1,51 @@
<?php
// =================================================================
// ملف: get_driver_live_pos.php
// الوظيفة: جلب الموقع اللحظي لسائق محدد (بناءً على ID)
// =================================================================
require_once __DIR__ . '/../../connect.php'; // تأكد أن هذا الملف يحتوي على $con_tracking
header("Content-Type: application/json; charset=UTF-8");
try {
// 1. استقبال معرف السائق
$driver_id = filterRequest("driver_id");
if (!$driver_id) {
jsonError("driver_id is required");
exit;
}
// 2. الاستعلام من قاعدة بيانات التتبع (car_locations)
// نجلب أحدث إحداثيات تم تسجيلها لهذا السائق
$sql = "
SELECT
latitude,
longitude,
heading,
speed,
updated_at
FROM car_locations
WHERE driver_id = ?
ORDER BY updated_at DESC
LIMIT 1
";
$stmt = $con_tracking->prepare($sql);
$stmt->execute([$driver_id]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if ($data) {
jsonSuccess($data);
} else {
// السائق ليس له موقع مسجل (ربما لم يشغل التطبيق بعد)
jsonError("No location found for this driver");
}
} catch (PDOException $e) {
error_log("[get_driver_live_pos.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>
+118
View File
@@ -0,0 +1,118 @@
<?php
require_once __DIR__ . '/../../connect.php';
header('Content-Type: application/json; charset=utf-8');
try {
$statusFilter = filterRequest("status");
// القيم المتوقعة من التطبيق: 'All', 'Begin', 'New', 'Completed', 'Canceled'
if (!$statusFilter) $statusFilter = "Begin";
$params = [];
$whereClause = "";
// --- منطق ترجمة الحالات (Mapping Logic) - مصحح ليطابق حالات DB الفعلية ---
switch ($statusFilter) {
case 'All':
$whereClause = ""; // لا يوجد شرط، اجلب الكل
break;
// ملاحظة: قاعدة البيانات تحتوي عائلتين من الحالات — القديمة بصيغة
// CamelCase ('Finished','Begin','CancelFromPassenger') والجديدة التي
// يكتبها خط الرحلات الحالي بأحرف صغيرة ('completed','accepted',
// 'cancelled_by_passenger'). المقارنة تتم بـ LOWER() لتغطية الاثنتين.
case 'Pending':
// الرحلات المعلقة/الجديدة: بانتظار سائق
$whereClause = "WHERE LOWER(r.status) IN ('new','nothing','waiting','wait','pending','searching')";
break;
case 'Begin':
// الرحلات الجارية: من قبول السائق إلى بدء التشغيل
$whereClause = "WHERE LOWER(r.status) IN ('apply','applied','arrived','begin','accepted','started','claimed')";
break;
case 'Completed':
// الرحلات المكتملة
$whereClause = "WHERE LOWER(r.status) IN ('finished','completed')";
break;
case 'Canceled':
// جميع أنواع الإلغاء
$whereClause = "WHERE LOWER(r.status) IN (
'cancel','cancelfromdriver','cancelfromdriverafterapply','cancelfrompassenger',
'timeout','refused','cancelled_by_passenger','cancelled_by_driver',
'cancelled_no_driver_found'
)";
break;
default:
// في حال تم إرسال حالة محددة غير المذكورين
$whereClause = "WHERE LOWER(r.status) = LOWER(?)";
$params[] = $statusFilter;
break;
}
// --- الاستعلام ---
$sql = "
SELECT
r.*,
-- بيانات السائق
d.first_name as d_fname, d.last_name as d_lname, d.phone as d_phone, d.id as driver_real_id,
-- إحصائيات السائق (نحسب المكتمل والملغي بشكل أدق)
(SELECT COUNT(*) FROM ride WHERE driver_id = d.id AND status = 'Finished') as d_completed,
(SELECT COUNT(*) FROM ride WHERE driver_id = d.id AND status LIKE 'Cancel%') as d_canceled,
-- بيانات الراكب
p.first_name as p_fname, p.last_name as p_lname, p.phone as p_phone,
-- إحصائيات الراكب
(SELECT COUNT(*) FROM ride WHERE passenger_id = p.id AND status = 'Finished') as p_completed,
-- سبب الإلغاء
-- نحاول جلبه من جدول driver_orders (ملاحظات السائق)
-- نستخدم COALESCE لجلب 'لا يوجد سبب' إذا كانت القيمة فارغة
COALESCE(
(SELECT notes FROM driver_orders WHERE order_id = r.id LIMIT 1),
'لا يوجد سبب مسجل'
) as cancel_reason
FROM ride r
LEFT JOIN driver d ON r.driver_id = d.id
LEFT JOIN passengers p ON r.passenger_id = p.id
$whereClause
ORDER BY r.id DESC
LIMIT 100
";
$stmt = $con->prepare($sql);
$stmt->execute($params);
$rides = $stmt->fetchAll(PDO::FETCH_ASSOC);
$data = [];
foreach ($rides as $row) {
// فك التشفير
try { $row['d_fname'] = $encryptionHelper->decryptData($row['d_fname']); } catch(Exception $e){}
try { $row['d_lname'] = $encryptionHelper->decryptData($row['d_lname']); } catch(Exception $e){}
try { $row['d_phone'] = $encryptionHelper->decryptData($row['d_phone']); } catch(Exception $e){}
try { $row['p_fname'] = $encryptionHelper->decryptData($row['p_fname']); } catch(Exception $e){}
try { $row['p_lname'] = $encryptionHelper->decryptData($row['p_lname']); } catch(Exception $e){}
try { $row['p_phone'] = $encryptionHelper->decryptData($row['p_phone']); } catch(Exception $e){}
$row['driver_full_name'] = trim($row['d_fname'] . ' ' . $row['d_lname']);
$row['passenger_full_name'] = trim($row['p_fname'] . ' ' . $row['p_lname']);
if(empty($row['driver_full_name'])) $row['driver_full_name'] = "Unknown Driver";
if(empty($row['passenger_full_name'])) $row['passenger_full_name'] = "Unknown Passenger";
$data[] = $row;
}
jsonSuccess($data);
} catch (PDOException $e) {
error_log("[get_rides_by_status.php] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
}
?>
+158
View File
@@ -0,0 +1,158 @@
<?php
require_once __DIR__ . '/../../connect.php';
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized: Admin access required']);
exit;
}
// التطبيع عبر normalizePhone() الموحّدة في core/helpers.php (نفس المنطق سابقاً)
// 1. تسجيل بداية الطلب
$phone = filterRequest("phone");
error_log("[MONITOR_RIDE] ---------------- START REQUEST ----------------");
error_log("[MONITOR_RIDE] 1. Received Phone: " . $phone);
// تطبيع الرقم
$phone = normalizePhone($phone);
error_log("[MONITOR_RIDE] 1.5 Normalized Phone: " . $phone);
//------------------------------------------------------------------------
// 1) البحث عن الهاتف أولاً في جدول السائق ثم جدول الراكب
//------------------------------------------------------------------------
$encPhone = $encryptionHelper->encryptData($phone);
// فهرس البحث لكل جدول على حدة (النطاقات معزولة عمداً)
global $blindIndex;
$dBidx = $blindIndex ? $blindIndex->index('driver.phone', $phone) : null;
$pBidx = $blindIndex ? $blindIndex->index('passengers.phone', $phone) : null;
error_log("[MONITOR_RIDE] 2. Encrypted Phone: " . $encPhone);
// Check Driver Table
$driverQuery = $con->prepare("SELECT id AS driverID FROM driver WHERE phone = :phone OR (:bidx IS NOT NULL AND phone_bidx = :bidx) LIMIT 1");
$driverQuery->execute([':phone' => $encPhone, ':bidx' => $dBidx]);
$driver = $driverQuery->fetch(PDO::FETCH_ASSOC);
// Check Passenger Table
$customerQuery = $con->prepare("SELECT id AS customerID FROM passengers WHERE phone = :phone OR (:bidx IS NOT NULL AND phone_bidx = :bidx) LIMIT 1");
$customerQuery->execute([':phone' => $encPhone, ':bidx' => $pBidx]);
$customer = $customerQuery->fetch(PDO::FETCH_ASSOC);
// حدد نوع المستخدم
$userType = '';
$driverID = null;
$customerID = null;
if ($driver) {
$userType = 'driver';
$driverID = $driver['driverID'];
error_log("[MONITOR_RIDE] 3. User Found: Type = DRIVER, ID = " . $driverID);
} elseif ($customer) {
$userType = 'customer';
$customerID = $customer['customerID'];
error_log("[MONITOR_RIDE] 3. User Found: Type = CUSTOMER, ID = " . $customerID);
} else {
error_log("[MONITOR_RIDE] 3. FAILURE: Phone number not found in Driver or Passenger tables.");
jsonError("رقم الهاتف غير موجود في النظام.");
exit;
}
//------------------------------------------------------------------------
// 2) جلب آخر رحلة حالتها نشطة (Apply, Applied, Arrived, Begin)
//------------------------------------------------------------------------
$activeStatuses = "'Apply','Applied','Arrived','arrived','Begin'";
if ($userType == 'driver') {
error_log("[MONITOR_RIDE] 4. Searching for active ride for Driver ID: " . $driverID);
$rideQuery = $con->prepare("
SELECT * FROM ride
WHERE driver_id = :driverID AND status IN ($activeStatuses)
ORDER BY id DESC LIMIT 1
");
$rideQuery->execute([':driverID' => $driverID]);
} else {
error_log("[MONITOR_RIDE] 4. Searching for active ride for Customer ID: " . $customerID);
$rideQuery = $con->prepare("
SELECT * FROM ride
WHERE passenger_id = :customerID AND status IN ($activeStatuses)
ORDER BY id DESC LIMIT 1
");
$rideQuery->execute([':customerID' => $customerID]);
}
$ride = $rideQuery->fetch(PDO::FETCH_ASSOC);
if (!$ride) {
error_log("[MONITOR_RIDE] 4. FAILURE: No active ride found.");
jsonError("لا توجد رحلة نشطة لهذا المستخدم.");
exit;
} else {
error_log("[MONITOR_RIDE] 4. SUCCESS: Active Ride Found. Ride ID: " . $ride['id'] . " Status: " . $ride['status']);
}
//------------------------------------------------------------------------
// 3) جلب معلومات السائق من الرحلة
//------------------------------------------------------------------------
$rideDriverID = $ride['driverID'] ?? $ride['driver_id'];
error_log("[MONITOR_RIDE] 5. Fetching info for Driver ID from Ride: " . $rideDriverID);
$driverInfoQuery = $con->prepare("
SELECT id, first_name, last_name, phone
FROM driver
WHERE id = :driverID
LIMIT 1
");
$driverInfoQuery->execute([':driverID' => $rideDriverID]);
$driverInfo = $driverInfoQuery->fetch(PDO::FETCH_ASSOC);
if ($driverInfo) {
$driverInfo['phone'] = $encryptionHelper->decryptData($driverInfo['phone']);
$driverInfo['first_name'] = $encryptionHelper->decryptData($driverInfo['first_name']);
$driverInfo['last_name'] = $encryptionHelper->decryptData($driverInfo['last_name']);
$fullName = $driverInfo['first_name'] . " " . $driverInfo['last_name'];
$driverInfo['fullname'] = $fullName;
error_log("[MONITOR_RIDE] 5. Driver Info Found: " . $fullName);
} else {
error_log("[MONITOR_RIDE] 5. WARNING: Driver info not found for ID " . $rideDriverID);
}
//------------------------------------------------------------------------
// 4) جلب آخر موقع للسائق من جدول car_locations بشرط الحالة ON
//------------------------------------------------------------------------
error_log("[MONITOR_RIDE] 6. Querying Tracking DB for Driver ID: " . $rideDriverID);
$locationQuery = $con_tracking->prepare("
SELECT latitude, longitude, speed, heading, updated_at
FROM car_locations
WHERE driver_id = :driverID AND status = 'ON'
ORDER BY updated_at DESC LIMIT 1
");
$locationQuery->execute([':driverID' => $rideDriverID]);
$location = $locationQuery->fetch(PDO::FETCH_ASSOC);
if ($location) {
error_log("[MONITOR_RIDE] 6. Location Found: Lat=" . $location['latitude'] . " Lng=" . $location['longitude']);
} else {
error_log("[MONITOR_RIDE] 6. WARNING: No live location found.");
}
//------------------------------------------------------------------------
// 5) تجهيز البيانات للرد
//------------------------------------------------------------------------
$response = [
"ride_details" => $ride,
"driver_details" => $driverInfo,
"driver_location" => $location ?: "No live location"
];
error_log("[MONITOR_RIDE] 7. Sending Success Response.");
jsonSuccess($response);
error_log("[MONITOR_RIDE] 7. Sending Success Response.");
jsonSuccess($response);