Update: 2026-07-07 04:57:50
This commit is contained in:
@@ -1,89 +0,0 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// create_tester_driver.php
|
||||
// Script to seed/register a pre-verified tester driver.
|
||||
// ============================================================
|
||||
|
||||
require_once __DIR__ . '/../../core/bootstrap.php';
|
||||
|
||||
$email = 'driver_tester@siromove.com';
|
||||
$phone = '+962790000002';
|
||||
$password = 'SiroDriver2026!';
|
||||
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
$encryptedEmail = $encryptionHelper->encryptData($email);
|
||||
$encryptedPhone = $encryptionHelper->encryptData($phone);
|
||||
$encryptedFirstName = $encryptionHelper->encryptData('Driver');
|
||||
$encryptedLastName = $encryptionHelper->encryptData('Tester');
|
||||
$encryptedGender = $encryptionHelper->encryptData('Male');
|
||||
$encryptedBirthdate = $encryptionHelper->encryptData('1990-01-01');
|
||||
$encryptedSite = $encryptionHelper->encryptData('Jordan');
|
||||
|
||||
try {
|
||||
$con = Database::get('main');
|
||||
|
||||
// 1. Check if driver exists
|
||||
$stmt = $con->prepare("SELECT id FROM driver WHERE email = :email LIMIT 1");
|
||||
$stmt->bindParam(':email', $encryptedEmail);
|
||||
$stmt->execute();
|
||||
$driver = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($driver) {
|
||||
$driverId = $driver['id'];
|
||||
$update = $con->prepare("UPDATE driver SET password = :password, phone = :phone WHERE id = :id");
|
||||
$update->bindParam(':password', $hashedPassword);
|
||||
$update->bindParam(':phone', $encryptedPhone);
|
||||
$update->bindParam(':id', $driverId);
|
||||
$update->execute();
|
||||
echo "Driver tester updated successfully.\n";
|
||||
} else {
|
||||
$driverId = bin2hex(random_bytes(10)); // 20 chars unique id
|
||||
$insert = $con->prepare("INSERT INTO driver (id, phone, email, password, gender, birthdate, site, first_name, last_name)
|
||||
VALUES (:id, :phone, :email, :password, :gender, :birthdate, :site, :first_name, :last_name)");
|
||||
$insert->bindParam(':id', $driverId);
|
||||
$insert->bindParam(':phone', $encryptedPhone);
|
||||
$insert->bindParam(':email', $encryptedEmail);
|
||||
$insert->bindParam(':password', $hashedPassword);
|
||||
$insert->bindParam(':gender', $encryptedGender);
|
||||
$insert->bindParam(':birthdate', $encryptedBirthdate);
|
||||
$insert->bindParam(':site', $encryptedSite);
|
||||
$insert->bindParam(':first_name', $encryptedFirstName);
|
||||
$insert->bindParam(':last_name', $encryptedLastName);
|
||||
$insert->execute();
|
||||
echo "Driver tester created successfully with ID: $driverId\n";
|
||||
}
|
||||
|
||||
// 2. Ensure phone_verification row exists
|
||||
$stmtPhone = $con->prepare("SELECT * FROM phone_verification WHERE phone_number = :phone LIMIT 1");
|
||||
$stmtPhone->bindParam(':phone', $encryptedPhone);
|
||||
$stmtPhone->execute();
|
||||
if ($stmtPhone->fetch()) {
|
||||
$updatePhone = $con->prepare("UPDATE phone_verification SET is_verified = 1 WHERE phone_number = :phone");
|
||||
$updatePhone->bindParam(':phone', $encryptedPhone);
|
||||
$updatePhone->execute();
|
||||
} else {
|
||||
$insertPhone = $con->prepare("INSERT INTO phone_verification (phone_number, is_verified) VALUES (:phone, 1)");
|
||||
$insertPhone->bindParam(':phone', $encryptedPhone);
|
||||
$insertPhone->execute();
|
||||
}
|
||||
|
||||
// 3. Ensure CarRegistration row exists
|
||||
$stmtCar = $con->prepare("SELECT * FROM CarRegistration WHERE driverID = :driverID LIMIT 1");
|
||||
$stmtCar->bindParam(':driverID', $driverId);
|
||||
$stmtCar->execute();
|
||||
if ($stmtCar->fetch()) {
|
||||
$updateCar = $con->prepare("UPDATE CarRegistration SET make = 'Toyota', model = 'Prius', year = '2020' WHERE driverID = :driverID");
|
||||
$updateCar->bindParam(':driverID', $driverId);
|
||||
$updateCar->execute();
|
||||
} else {
|
||||
$insertCar = $con->prepare("INSERT INTO CarRegistration (driverID, make, model, year) VALUES (:driverID, 'Toyota', 'Prius', '2020')");
|
||||
$insertCar->bindParam(':driverID', $driverId);
|
||||
$insertCar->execute();
|
||||
}
|
||||
|
||||
echo "Verification and Car Registration configured.\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$appPlatform = filterRequest("appPlatform");
|
||||
|
||||
|
||||
$sql = "SELECT
|
||||
*
|
||||
FROM
|
||||
`testApp`
|
||||
WHERE
|
||||
appPlatform = '$appPlatform'-- AND isTest = 0;";
|
||||
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
// Print the retrieved data
|
||||
// echo json_encode($result);
|
||||
jsonSuccess($data = $result);
|
||||
} else {
|
||||
// Print a failure message
|
||||
|
||||
jsonError($message = "No driver order data found");
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$appPlatform = filterRequest("appPlatform");
|
||||
|
||||
$sql = "UPDATE
|
||||
`testApp`
|
||||
SET
|
||||
`isTest` = '1'
|
||||
WHERE
|
||||
`testApp`.appPlatform = '$appPlatform';";
|
||||
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->execute();
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
// Print a success message
|
||||
jsonSuccess($message = "Test data updated successfully");
|
||||
} else {
|
||||
// Print a failure message
|
||||
jsonError($message = "Failed to update driver order data");
|
||||
}
|
||||
?>
|
||||
@@ -1,82 +0,0 @@
|
||||
-- ==========================================================
|
||||
-- Marketing Engine & Social Media Bot Combined Schema
|
||||
-- ==========================================================
|
||||
|
||||
-- 1. Original Social Media Tables (Facebook, Instagram)
|
||||
CREATE TABLE IF NOT EXISTS `social_accounts` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`platform` ENUM('facebook', 'instagram', 'tiktok', 'twitter', 'youtube') NOT NULL,
|
||||
`username` VARCHAR(100) NOT NULL,
|
||||
`status` ENUM('active', 'restricted', 'banned') DEFAULT 'active',
|
||||
`total_posts` INT DEFAULT 0,
|
||||
`total_comments` INT DEFAULT 0,
|
||||
`total_videos` INT DEFAULT 0,
|
||||
`proxy_ip` VARCHAR(100) NULL,
|
||||
`proxy_port` INT NULL,
|
||||
`proxy_username` VARCHAR(100) NULL,
|
||||
`proxy_password` VARCHAR(100) NULL,
|
||||
`last_active` DATETIME NULL,
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `social_tasks` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NULL,
|
||||
`platform` ENUM('facebook', 'instagram', 'tiktok', 'twitter', 'youtube') NOT NULL,
|
||||
`type` ENUM('join_group', 'read_posts', 'post_comment', 'share_link', 'upload_video', 'post_tweet') NOT NULL,
|
||||
`target_url` VARCHAR(500) NULL, -- URL of the group, post, or media download
|
||||
`prompt_context` TEXT NULL,
|
||||
`generated_comment` TEXT NULL,
|
||||
`status` ENUM('pending', 'in_progress', 'completed', 'failed') DEFAULT 'pending',
|
||||
`error_message` TEXT NULL,
|
||||
`scheduled_at` DATETIME NULL,
|
||||
`completed_at` DATETIME NULL,
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`account_id`) REFERENCES `social_accounts`(`id`) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `social_logs` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`task_id` INT NULL,
|
||||
`account_id` INT NULL,
|
||||
`log_level` ENUM('info', 'warning', 'error') DEFAULT 'info',
|
||||
`message` TEXT NOT NULL,
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`task_id`) REFERENCES `social_tasks`(`id`) ON DELETE SET NULL,
|
||||
FOREIGN KEY (`account_id`) REFERENCES `social_accounts`(`id`) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- 2. New Content Generation Pipeline Tables
|
||||
CREATE TABLE IF NOT EXISTS `content_pipeline` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`topic` VARCHAR(255) NOT NULL,
|
||||
`script_text` TEXT NULL,
|
||||
`voice_url` VARCHAR(500) NULL,
|
||||
`video_url` VARCHAR(500) NULL,
|
||||
`status` ENUM('pending', 'script_generated', 'voice_generated', 'video_rendered', 'published', 'failed') DEFAULT 'pending',
|
||||
`error_message` TEXT NULL,
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `api_quotas` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`service_name` ENUM('gemini', 'elevenlabs', 'creatomate', 'heygen') NOT NULL,
|
||||
`daily_usage` INT DEFAULT 0,
|
||||
`quota_limit` INT DEFAULT 10,
|
||||
`last_reset` DATE NOT NULL
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO `api_quotas` (`service_name`, `daily_usage`, `quota_limit`, `last_reset`) VALUES
|
||||
('gemini', 0, 100, CURDATE()),
|
||||
('elevenlabs', 0, 5, CURDATE()),
|
||||
('creatomate', 0, 2, CURDATE());
|
||||
|
||||
CREATE TABLE IF NOT EXISTS marketing_reports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
platform ENUM('facebook', 'instagram', 'tiktok', 'twitter', 'youtube', 'all') NOT NULL,
|
||||
report_html TEXT NOT NULL,
|
||||
is_weekly TINYINT(1) DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// get_all_driver_fingerprints.php
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// ⚠️ يُستخدم مرة واحدة فقط ثم يُحذف من السيرفر
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
require_once __DIR__ . '/../get_connect.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: https://siromove.com');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
|
||||
|
||||
// ── التحقق من admin_key ────────────────────────────────────────
|
||||
$adminKey = filterRequest('admin_key') ?? '';
|
||||
$expectedAdminKey = getenv('MIGRATION_ADMIN_KEY');
|
||||
|
||||
if (empty($adminKey) || !hash_equals($expectedAdminKey, $adminKey)) {
|
||||
http_response_code(403);
|
||||
exit(json_encode(['error' => 'Forbidden']));
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $con->prepare('
|
||||
SELECT captain_id, fingerPrint
|
||||
FROM driverToken
|
||||
WHERE fingerPrint IS NOT NULL
|
||||
AND fingerPrint != ""
|
||||
ORDER BY captain_id
|
||||
');
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'count' => count($rows),
|
||||
'data' => $rows,
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('❌ [get_all_driver_fingerprints] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Server error']);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// get_all_fingerprints.php — جلب كل البصمات للترحيل
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// ⚠️ يُستخدم مرة واحدة فقط ثم يُحذف من السيرفر
|
||||
// محمي بـ admin_key لمنع الوصول العشوائي
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
require_once __DIR__ . '/../get_connect.php';
|
||||
//include 'functions.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: https://siromove.com');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── التحقق من admin_key ────────────────────────────────────────
|
||||
$adminKey = filterRequest('admin_key') ?? '';
|
||||
$expectedAdminKey = getenv('MIGRATION_ADMIN_KEY'); // أضفه في .env
|
||||
|
||||
if (empty($adminKey) || !hash_equals($expectedAdminKey, $adminKey)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Forbidden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// جلب كل البصمات من جدول tokens
|
||||
// نجيب passengerID + fingerPrint فقط — لا نعطي بيانات حساسة أخرى
|
||||
$stmt = $con->prepare('
|
||||
SELECT passengerID, fingerPrint, "passenger" AS userType
|
||||
FROM tokens
|
||||
WHERE fingerPrint IS NOT NULL
|
||||
AND fingerPrint != ""
|
||||
ORDER BY passengerID
|
||||
');
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'count' => count($rows),
|
||||
'data' => $rows,
|
||||
]);
|
||||
http_response_code(200);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('❌ [get_all_fingerprints] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Server error']);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// update_driver_fingerprint_admin.php
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// ⚠️ يُستخدم فقط أثناء الترحيل ثم يُحذف
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
require_once __DIR__ . '/../get_connect.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: https://siromove.com');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
|
||||
|
||||
$adminKey = filterRequest('admin_key') ?? '';
|
||||
$expectedAdminKey = getenv('MIGRATION_ADMIN_KEY');
|
||||
|
||||
if (empty($adminKey) || !hash_equals($expectedAdminKey, $adminKey)) {
|
||||
http_response_code(403);
|
||||
exit(json_encode(['error' => 'Forbidden']));
|
||||
}
|
||||
|
||||
try {
|
||||
$captainId = filterRequest('captain_id') ?? '';
|
||||
$fingerprint = filterRequest('fingerprint') ?? '';
|
||||
|
||||
if (empty($captainId) || empty($fingerprint)) {
|
||||
http_response_code(400);
|
||||
exit(json_encode(['error' => 'Missing parameters']));
|
||||
}
|
||||
|
||||
$stmt = $con->prepare('
|
||||
UPDATE driverToken
|
||||
SET fingerPrint = :fp
|
||||
WHERE captain_id = :cid
|
||||
');
|
||||
$stmt->execute([':fp' => $fingerprint, ':cid' => $captainId]);
|
||||
|
||||
echo json_encode(['status' => 'success', 'affected' => $stmt->rowCount()]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('❌ [update_driver_fingerprint_admin] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Server error']);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// update_fingerprint_admin.php — تحديث بصمة واحدة (للترحيل)
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// ⚠️ يُستخدم فقط أثناء عملية الترحيل ثم يُحذف
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
require_once __DIR__ . '/../get_connect.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: https://siromove.com');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── التحقق من admin_key ────────────────────────────────────────
|
||||
$adminKey = filterRequest('admin_key') ?? '';
|
||||
$expectedAdminKey = getenv('MIGRATION_ADMIN_KEY');
|
||||
|
||||
if (empty($adminKey) || !hash_equals($expectedAdminKey, $adminKey)) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Forbidden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$passengerID = filterRequest('passengerID') ?? '';
|
||||
$fingerprint = filterRequest('fingerprint') ?? '';
|
||||
|
||||
if (empty($passengerID) || empty($fingerprint)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing parameters']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $con->prepare('
|
||||
UPDATE tokens
|
||||
SET fingerPrint = :fp
|
||||
WHERE passengerID = :pid
|
||||
');
|
||||
$stmt->execute([
|
||||
':fp' => $fingerprint,
|
||||
':pid' => $passengerID,
|
||||
]);
|
||||
|
||||
$affected = $stmt->rowCount();
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'affected' => $affected,
|
||||
]);
|
||||
http_response_code(200);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('❌ [update_fingerprint_admin] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Server error']);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
-- 1. Table for storing the unified 6-digit referral code for each user (Driver/Passenger)
|
||||
CREATE TABLE `user_referral_codes` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`user_id` VARCHAR(100) NOT NULL,
|
||||
`user_type` ENUM('driver', 'passenger') NOT NULL,
|
||||
`referral_code` VARCHAR(6) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_user_type_id` (`user_id`, `user_type`),
|
||||
UNIQUE KEY `idx_referral_code` (`referral_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- 2. Table for tracking the referral relationship and trip counts
|
||||
CREATE TABLE `unified_referrals` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`inviter_code` VARCHAR(6) NOT NULL,
|
||||
`invited_user_id` VARCHAR(100) NOT NULL,
|
||||
`invited_user_type` ENUM('driver', 'passenger') NOT NULL,
|
||||
`status` ENUM('registered', 'active', 'completed') NOT NULL DEFAULT 'registered',
|
||||
`trip_count` INT NOT NULL DEFAULT 0,
|
||||
`is_reward_claimed` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_invited_user` (`invited_user_id`, `invited_user_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- 3. Table for Driver Cash Claims (when a driver requests to receive their reward as manual cash)
|
||||
CREATE TABLE `driver_cash_claims` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`driver_id` VARCHAR(100) NOT NULL,
|
||||
`referral_id` INT NOT NULL,
|
||||
`amount_syp` INT NOT NULL,
|
||||
`status` ENUM('pending', 'paid') NOT NULL DEFAULT 'pending',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* passenger_socket.php
|
||||
* =====================
|
||||
* WebSocket Server للركاب — بورت 3030
|
||||
* Internal HTTP Server — بورت 3031
|
||||
*/
|
||||
|
||||
use Workerman\Worker;
|
||||
use PHPSocketIO\SocketIO;
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// نظام تسجيل الأحداث (Logging System)
|
||||
// ---------------------------------------------------------
|
||||
$LOG_FILE = __DIR__ . '/logs/socket_debug.log';
|
||||
|
||||
function socket_log($message, $data = null) {
|
||||
global $LOG_FILE;
|
||||
$date = date('Y-m-d H:i:s');
|
||||
$logMsg = "[$date] $message";
|
||||
if ($data !== null) {
|
||||
$logMsg .= " | DATA: " . (is_string($data) ? $data : json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
$logMsg .= PHP_EOL;
|
||||
|
||||
echo $logMsg; // للطباعة في الكونسول إذا كان يعمل في الـ Foreground
|
||||
@file_put_contents($LOG_FILE, $logMsg, FILE_APPEND); // الكتابة في الملف
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
socket_log("=== STARTING PASSENGER SOCKET SERVER ===");
|
||||
|
||||
$siteUser = get_current_user();
|
||||
$homeDir = "/home/$siteUser";
|
||||
if (!is_dir($homeDir)) {
|
||||
$homeDir = '/home/intaleq-rides'; // Fallback to original location
|
||||
}
|
||||
|
||||
$INTERNAL_KEY = trim((string) @file_get_contents(getenv('INTERNAL_SOCKET_KEY_PATH') ?: ($homeDir . '/.internal_socket_key')));
|
||||
|
||||
if (empty($INTERNAL_KEY)) {
|
||||
socket_log("[CRITICAL_ERROR] Internal key missing! Exiting.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$PORT = 3030;
|
||||
$INTERNAL_PORT = 3031;
|
||||
|
||||
$io = new SocketIO($PORT);
|
||||
|
||||
$io->on('workerStart', function () use ($io, $INTERNAL_KEY, $INTERNAL_PORT) {
|
||||
|
||||
$innerHttp = new Worker("http://0.0.0.0:$INTERNAL_PORT");
|
||||
|
||||
$innerHttp->onMessage = function ($connection, $request) use ($io, $INTERNAL_KEY) {
|
||||
|
||||
$headers = $request->header();
|
||||
$clientIp = $connection->getRemoteIp();
|
||||
|
||||
if (($headers['x-internal-key'] ?? '') !== $INTERNAL_KEY) {
|
||||
socket_log("[HTTP_ERROR] Unauthorized internal request from IP: $clientIp");
|
||||
$connection->send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
|
||||
$post = $request->post();
|
||||
$action = trim($post['action'] ?? '');
|
||||
|
||||
if ($action === 'update_ride_status') {
|
||||
|
||||
$passengerId = $post['passenger_id'] ?? null;
|
||||
$rawPayload = $post['payload'] ?? null;
|
||||
|
||||
if (!$passengerId || !$rawPayload) {
|
||||
socket_log("[HTTP_ERROR] Missing passenger_id or payload for action: update_ride_status", $post);
|
||||
$connection->send('Error: Missing passenger_id or payload');
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = is_string($rawPayload)
|
||||
? (json_decode($rawPayload, true) ?? $rawPayload)
|
||||
: $rawPayload;
|
||||
|
||||
socket_log("[HTTP_SUCCESS] Emitting 'ride_status_change' to Passenger #$passengerId", $payload);
|
||||
$io->to('passenger_' . $passengerId)->emit('ride_status_change', $payload);
|
||||
|
||||
$connection->send('OK');
|
||||
|
||||
} elseif ($action === 'update_driver_location') {
|
||||
|
||||
$passengerId = $post['passenger_id'] ?? null;
|
||||
$rawPayload = $post['payload'] ?? null;
|
||||
|
||||
if (!$passengerId || !$rawPayload) {
|
||||
socket_log("[HTTP_ERROR] Missing passenger_id or payload for action: update_driver_location", $post);
|
||||
$connection->send('Error: Missing passenger_id or payload');
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = is_string($rawPayload)
|
||||
? (json_decode($rawPayload, true) ?? $rawPayload)
|
||||
: $rawPayload;
|
||||
|
||||
socket_log("[HTTP_SUCCESS] Emitting 'driver_location_update' to Passenger #$passengerId", $payload);
|
||||
$io->to('passenger_' . $passengerId)->emit('driver_location_update', $payload);
|
||||
|
||||
$connection->send('OK');
|
||||
|
||||
} else {
|
||||
socket_log("[HTTP_WARNING] Unknown action received: $action", $post);
|
||||
$connection->send('Unknown action: ' . $action);
|
||||
}
|
||||
};
|
||||
|
||||
$innerHttp->listen();
|
||||
socket_log("[INFO] Internal HTTP started on port $INTERNAL_PORT");
|
||||
});
|
||||
|
||||
$io->on('connection', function ($socket) {
|
||||
|
||||
$query = $socket->handshake['query'] ?? [];
|
||||
$passengerId = $query['id'] ?? null;
|
||||
$clientIp = $socket->conn->remoteAddress ?? 'Unknown';
|
||||
|
||||
if (!$passengerId) {
|
||||
socket_log("[SOCKET_REJECTED] Connection rejected (No passenger ID) from IP: $clientIp");
|
||||
$socket->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
$socket->join('passenger_' . $passengerId);
|
||||
socket_log("[SOCKET_CONNECTED] Passenger Connected: #$passengerId (IP: $clientIp)");
|
||||
|
||||
$socket->on('heartbeat', function ($data) {
|
||||
// يمكن تفعيل السطر التالي للتأكد من النبضات إذا أردت دقة شديدة، لكنه قد يملأ ملف الـ log
|
||||
// socket_log("[SOCKET_HEARTBEAT] Received from Passenger #$passengerId");
|
||||
});
|
||||
|
||||
$socket->on('disconnect', function () use ($passengerId, $clientIp) {
|
||||
socket_log("[SOCKET_DISCONNECTED] Passenger Disconnected: #$passengerId (IP: $clientIp)");
|
||||
});
|
||||
});
|
||||
|
||||
Worker::runAll();
|
||||
@@ -1,28 +0,0 @@
|
||||
-- Migration: Add new columns to competitor_secret_formulas for enhanced analysis
|
||||
-- Run this once to upgrade the table schema
|
||||
|
||||
ALTER TABLE `competitor_secret_formulas`
|
||||
ADD COLUMN `tier` VARCHAR(20) DEFAULT 'standard' AFTER `country_code`,
|
||||
ADD COLUMN `min_fare` DECIMAL(8,3) DEFAULT 0 AFTER `price_per_min`,
|
||||
ADD COLUMN `rmse` DECIMAL(10,4) DEFAULT 0 AFTER `min_fare`,
|
||||
ADD COLUMN `r_squared` DECIMAL(10,4) DEFAULT 0 AFTER `rmse`,
|
||||
ADD COLUMN `surge_multiplier` DECIMAL(5,3) DEFAULT 1.0 AFTER `r_squared`,
|
||||
ADD COLUMN `peak_hours` VARCHAR(255) DEFAULT '[]' AFTER `surge_multiplier`;
|
||||
|
||||
-- Make the unique key include tier for multi-tier support
|
||||
ALTER TABLE `competitor_secret_formulas`
|
||||
DROP INDEX `idx_comp_country`,
|
||||
ADD UNIQUE KEY `idx_comp_country_tier` (`competitor_name`, `country_code`, `tier`);
|
||||
|
||||
-- New table for surge insights
|
||||
CREATE TABLE IF NOT EXISTS `competitor_surge_insights` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`competitor_name` VARCHAR(100) NOT NULL,
|
||||
`country_code` VARCHAR(5) NOT NULL,
|
||||
`surge_multiplier` DECIMAL(5,3) NOT NULL,
|
||||
`peak_start_hour` INT NOT NULL,
|
||||
`peak_end_hour` INT NOT NULL,
|
||||
`sample_count` INT NOT NULL,
|
||||
`detected_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `unique_surge` (`competitor_name`, `country_code`, `peak_start_hour`, `peak_end_hour`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// pricing_helper.php
|
||||
// يحتوي على دوال التسعير الموحدة
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* الحصول على سعر الكيلومتر حسب نوع السيارة من جدول أسعار الدولة
|
||||
*
|
||||
* @param string $carType نوع السيارة
|
||||
* @param array $kazanRow صف من جدول kazan
|
||||
* @return float
|
||||
*/
|
||||
function getPerKmRate(string $carType, array $kazanRow): float {
|
||||
$rateColumns = [
|
||||
'Comfort' => 'comfortPrice',
|
||||
'Speed' => 'speedPrice',
|
||||
'Lady' => 'ladyPrice',
|
||||
'Electric' => 'electricPrice',
|
||||
'Van' => 'vanPrice',
|
||||
'Delivery' => 'deliveryPrice',
|
||||
'Mishwar Vip' => 'mishwarVipPrice',
|
||||
'Fixed Price' => 'fixedPrice',
|
||||
'Awfar Car' => 'awfarPrice',
|
||||
];
|
||||
|
||||
$column = $rateColumns[$carType] ?? 'speedPrice';
|
||||
|
||||
// دعم التوافق مع الإصدارات القديمة (backward compatibility)
|
||||
$rate = floatval($kazanRow[$column] ?? 0);
|
||||
|
||||
if ($rate <= 0) {
|
||||
$oldColumnMap = [
|
||||
'Lady' => 'familyPrice',
|
||||
'Mishwar Vip' => 'freePrice',
|
||||
'Electric' => 'naturePrice',
|
||||
'Van' => 'heavyPrice',
|
||||
];
|
||||
$oldColumn = $oldColumnMap[$carType] ?? null;
|
||||
if ($oldColumn && isset($kazanRow[$oldColumn])) {
|
||||
$rate = floatval($kazanRow[$oldColumn]);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback أخير
|
||||
if ($rate <= 0) {
|
||||
$rate = floatval($kazanRow['speedPrice'] ?? 36);
|
||||
}
|
||||
|
||||
return $rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* الحصول على سعر الدقيقة حسب وقت اليوم من جدول kazan
|
||||
*
|
||||
* @param array $countryPricing صف من جدول kazan
|
||||
* @return float
|
||||
*/
|
||||
function getPerMinRate(array $countryPricing): float {
|
||||
$hour = (int)date('H');
|
||||
|
||||
// قراءة الأسعار من الأعمدة الجديدة للدقيقة
|
||||
$normalMinPrice = floatval($countryPricing['normalMinPrice'] ?? 0);
|
||||
$peakMinPrice = floatval($countryPricing['peakMinPrice'] ?? 0);
|
||||
$lateMinPrice = floatval($countryPricing['lateMinPrice'] ?? 0);
|
||||
|
||||
// دعم التوافق مع الإصدارات القديمة (latePrice, naturePrice)
|
||||
if ($lateMinPrice <= 0) $lateMinPrice = floatval($countryPricing['latePrice'] ?? 0);
|
||||
if ($normalMinPrice <= 0) $normalMinPrice = floatval($countryPricing['naturePrice'] ?? 0);
|
||||
|
||||
// Fallback: حساب سعر الدقيقة من speedPrice إذا كانت الأعمدة الجديدة فارغة
|
||||
if ($normalMinPrice <= 0) {
|
||||
$speedPrice = floatval($countryPricing['speedPrice'] ?? 36);
|
||||
$normalMinPrice = $speedPrice / 4;
|
||||
}
|
||||
if ($peakMinPrice <= 0) $peakMinPrice = $normalMinPrice * 1.15; // 15% زيادة
|
||||
if ($lateMinPrice <= 0) $lateMinPrice = $normalMinPrice * 1.25; // 25% زيادة
|
||||
|
||||
if ($hour >= 21 || $hour < 1) {
|
||||
return round($lateMinPrice, 2); // Late Night
|
||||
}
|
||||
if ($hour >= 14 && $hour <= 17) {
|
||||
return round($peakMinPrice, 2); // Peak
|
||||
}
|
||||
return round($normalMinPrice, 2); // Normal
|
||||
}
|
||||
|
||||
/**
|
||||
* تحديد رمز العملة حسب الدولة
|
||||
*
|
||||
* @param string $countryCode رمز الدولة (Syria, Egypt, Jordan, ...)
|
||||
* @return string رمز العملة (SYP, EGP, JOD, ...)
|
||||
*/
|
||||
function getCurrencyByCountry(string $countryCode): string {
|
||||
$currencies = [
|
||||
'Syria' => 'SYP',
|
||||
'Egypt' => 'EGP',
|
||||
'Jordan' => 'JOD',
|
||||
'Iraq' => 'IQD',
|
||||
'UAE' => 'AED',
|
||||
'Saudi Arabia' => 'SAR',
|
||||
'Qatar' => 'QAR',
|
||||
'Kuwait' => 'KWD',
|
||||
'Bahrain' => 'BHD',
|
||||
'Oman' => 'OMR',
|
||||
'Turkey' => 'TRY',
|
||||
'Lebanon' => 'LBP',
|
||||
'Palestine' => 'ILS',
|
||||
'Yemen' => 'YER',
|
||||
'Libya' => 'LYD',
|
||||
'Tunisia' => 'TND',
|
||||
'Algeria' => 'DZD',
|
||||
'Morocco' => 'MAD',
|
||||
'Sudan' => 'SDG',
|
||||
];
|
||||
|
||||
return $currencies[$countryCode] ?? 'SYP'; // افتراضي: ليرة سورية
|
||||
}
|
||||
?>
|
||||
@@ -1,117 +0,0 @@
|
||||
<?php
|
||||
// cancelRideFromDriver.php
|
||||
|
||||
// تأكد أن هذا الملف يحتوي على دوال الإشعارات (notifyPassengerOnRideServer)
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
require_once __DIR__ . '/streak_helper.php';
|
||||
|
||||
// 🚀 تسجيل بداية العملية
|
||||
error_log("🚀 [cancelRide.php] Request Started to Cancel Ride From Driver.");
|
||||
|
||||
$id = filterRequest("id"); // Ride ID
|
||||
|
||||
if (!$id) {
|
||||
error_log("❌ [cancelRide.php] Missing Ride ID.");
|
||||
jsonError("Missing ID");
|
||||
exit;
|
||||
}
|
||||
|
||||
// الحالة الجديدة (إلغاء نهائي من طرف السائق)
|
||||
$newStatus = "cancelRideFromDriver";
|
||||
|
||||
// الحالات المسموح بإلغاء الرحلة فيها فقط
|
||||
// نسمح بالإلغاء إذا كانت في وضع الانتظار أو القبول المبدئي
|
||||
$allowedStatuses = "'wait', 'waiting', 'Apply', 'accepted', 'arrive'";
|
||||
|
||||
try {
|
||||
// ---------------------------------------------------------
|
||||
// 1. التحديث على سيرفر التتبع (Remote DB)
|
||||
// ---------------------------------------------------------
|
||||
// نستخدم شرط الحالة لضمان عدم إلغاء رحلة بدأت بالفعل (Start/Begin)
|
||||
$sql = "UPDATE `ride`
|
||||
SET `status` = ?, `updated_at` = CURRENT_TIMESTAMP
|
||||
WHERE `id` = ?
|
||||
AND `status` IN ($allowedStatuses)";
|
||||
|
||||
// استخدام Prepared Statements للأمان
|
||||
$stmtRemote = $con_ride->prepare($sql);
|
||||
$stmtRemote->execute([$newStatus, $id]);
|
||||
|
||||
$count = $stmtRemote->rowCount();
|
||||
error_log("ℹ️ [cancelRide.php] Remote DB Rows Affected: $count");
|
||||
|
||||
// التحقق: هل تم التحديث؟
|
||||
if ($count > 0) {
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 2. التحديث على السيرفر المحلي (Local DB)
|
||||
// ---------------------------------------------------------
|
||||
// نبدأ معاملة لضمان تكامل البيانات
|
||||
if (isset($con)) {
|
||||
$con->beginTransaction();
|
||||
try {
|
||||
$stmtLocal = $con->prepare($sql);
|
||||
$stmtLocal->execute([$newStatus, $id]);
|
||||
|
||||
// تحديث جدول driver_orders أيضاً لتوحيد الحالة (اختياري ولكنه مفضل)
|
||||
$stmtDriverOrder = $con->prepare("UPDATE driver_orders SET status = ? WHERE order_id = ?");
|
||||
$stmtDriverOrder->execute([$newStatus, $id]);
|
||||
|
||||
// 🆕 تصفير التتابع لأن السائق ألغى الرحلة
|
||||
$stmtGetDriver = $con->prepare("SELECT driver_id FROM ride WHERE id = ?");
|
||||
$stmtGetDriver->execute([$id]);
|
||||
$driver_id_to_reset = $stmtGetDriver->fetchColumn() ?: $user_id;
|
||||
if ($driver_id_to_reset) {
|
||||
handleDriverStreak($con, $driver_id_to_reset, 'reset');
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
} catch (Exception $eLocal) {
|
||||
$con->rollBack();
|
||||
error_log("⚠️ Local DB Update Failed: " . $eLocal->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 3. 🔥 إشعار الراكب عبر السوكيت (القطعة المفقودة) 🔥
|
||||
// ---------------------------------------------------------
|
||||
|
||||
// أ. جلب معرف الراكب لإرسال الإشعار له
|
||||
// نستخدم connection الرحلات لضمان وجود البيانات
|
||||
$stmtPas = $con_ride->prepare("SELECT passenger_id FROM ride WHERE id = ?");
|
||||
$stmtPas->execute([$id]);
|
||||
$passenger_id = $stmtPas->fetchColumn();
|
||||
|
||||
if ($passenger_id) {
|
||||
$payload = [
|
||||
'ride_id' => $id,
|
||||
'status' => 'cancelled', // هذه الحالة يستقبلها الفلاتر ويغلق الواجهة
|
||||
'msg' => 'للأسف، قام السائق بإلغاء الرحلة.'
|
||||
];
|
||||
|
||||
// استدعاء الدالة المعرفة في functions.php/connect.php
|
||||
if (function_exists('notifyPassengerOnRideServer')) {
|
||||
notifyPassengerOnRideServer($passenger_id, $payload);
|
||||
error_log("📡 [cancelRide.php] Notification sent to Passenger ID: $passenger_id");
|
||||
} else {
|
||||
error_log("⚠️ [cancelRide.php] Function notifyPassengerOnRideServer not found!");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4. إنهاء العملية
|
||||
// ---------------------------------------------------------
|
||||
error_log("✅ [cancelRide.php] Ride cancelled successfully.");
|
||||
jsonSuccess(null, "Ride cancelled successfully");
|
||||
|
||||
} else {
|
||||
// الفشل يعني أن الرحلة غير موجودة أو حالتها لا تسمح بالإلغاء (مثلاً بدأت بالفعل)
|
||||
error_log("⚠️ [cancelRide.php] Failed. ID invalid OR Status not allowed (maybe started?).");
|
||||
jsonError("Cannot cancel ride. Status might be started or already completed.");
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("❌ [cancelRide.php] Database Error: " . $e->getMessage());
|
||||
jsonError("Database Error");
|
||||
}
|
||||
?>
|
||||
@@ -1,186 +0,0 @@
|
||||
<?php
|
||||
// نقوم بتضمين ملف الاتصال المعدل الذي يحتوي على $con (الرئيسي)
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
// تهيئة اتصال قاعدة بيانات الرحلات
|
||||
try {
|
||||
$con_ride = Database::get('ride');
|
||||
} catch (Exception $e) {
|
||||
error_log("[getRideOrderID] Failed to connect to Ride Database: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(["status" => "failure", "message" => "Database connection failed"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// استلام البيانات (يمكن استلام ID الرحلة أو ID الراكب)
|
||||
$passengerID = filterRequest("passengerID");
|
||||
$rideID = filterRequest("id"); // إضافة استقبال متغير رقم الرحلة
|
||||
|
||||
// التحقق من أن الراكب يطلب بيانات رحلته هو فقط (حماية IDOR)
|
||||
if (!empty($passengerID) && isset($user_id) && $passengerID != $user_id) {
|
||||
securityLog("IDOR attempt on getRideOrderID", ['requested' => $passengerID, 'user' => $user_id]);
|
||||
echo json_encode(["status" => "failure", "message" => "Unauthorized access"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// =================================================================
|
||||
// 1. الخطوة الأولى: تحديد استراتيجية البحث (بواسطة رقم الرحلة أو الراكب)
|
||||
// =================================================================
|
||||
|
||||
$sqlRide = "SELECT
|
||||
id,
|
||||
start_location,
|
||||
end_location,
|
||||
date,
|
||||
driver_id,
|
||||
passenger_id,
|
||||
price,
|
||||
status,
|
||||
created_at,
|
||||
DriverIsGoingToPassenger,
|
||||
rideTimeStart,
|
||||
rideTimeFinish,
|
||||
price_for_driver,
|
||||
distance
|
||||
FROM ride ";
|
||||
|
||||
// المنطق الجديد:
|
||||
// إذا تم إرسال rideID، نبحث عن الرحلة المحددة بدقة (تجنباً لأي تضارب)
|
||||
// إذا لم يتم إرساله، نبحث عن أحدث رحلة للراكب (للتتبع المباشر)
|
||||
if (!empty($rideID)) {
|
||||
$sqlRide .= "WHERE id = :rideID";
|
||||
} else {
|
||||
$sqlRide .= "WHERE passenger_id = :passengerID ORDER BY id DESC LIMIT 1";
|
||||
}
|
||||
|
||||
// نستخدم المتغير $con_ride (سيرفر الرحلات)
|
||||
$stmtRide = $con_ride->prepare($sqlRide);
|
||||
|
||||
// ربط المتغيرات حسب نوع البحث
|
||||
if (!empty($rideID)) {
|
||||
$stmtRide->bindParam(':rideID', $rideID);
|
||||
} else {
|
||||
$stmtRide->bindParam(':passengerID', $passengerID);
|
||||
}
|
||||
|
||||
$stmtRide->execute();
|
||||
|
||||
$rideData = $stmtRide->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// إذا لم يتم العثور على رحلة في سيرفر الرحلات، نوقف العملية
|
||||
if (!$rideData) {
|
||||
echo json_encode(["status" => "failure", "message" => "No ride found"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 2. الخطوة الثانية: جلب البيانات الثابتة (سائق، سيارة، تقييم) من السيرفر الرئيسي ($con)
|
||||
// نستخدم المعرفات التي حصلنا عليها من نتيجة الاستعلام الأول
|
||||
// =================================================================
|
||||
|
||||
$driverID = $rideData['driver_id'];
|
||||
$pID = $rideData['passenger_id']; // نأخذ معرف الراكب من الرحلة نفسها لضمان التطابق
|
||||
|
||||
// ملاحظة: استخدام :driverID_Sub في الاستعلام الفرعي لتجنب أخطاء PDO
|
||||
$sqlDetails = "SELECT
|
||||
passengers.first_name AS passengerName,
|
||||
passengers.last_name,
|
||||
|
||||
CarRegistration.make,
|
||||
CarRegistration.model,
|
||||
CarRegistration.car_plate,
|
||||
CarRegistration.year,
|
||||
CarRegistration.color,
|
||||
CarRegistration.color_hex,
|
||||
|
||||
driver.first_name AS driverName,
|
||||
driver.gender,
|
||||
driver.phone,
|
||||
|
||||
(
|
||||
SELECT ROUND(AVG(ratingDriver.rating), 2)
|
||||
FROM ratingDriver
|
||||
WHERE ratingDriver.driver_id = :driverID_Sub
|
||||
) AS ratingDriver,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM ratingDriver
|
||||
WHERE ratingDriver.driver_id = :driverID_Sub
|
||||
) AS ratingCount,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM ride
|
||||
WHERE ride.driver_id = :driverID_Sub
|
||||
AND ride.status IN ('Finished', 'finished')
|
||||
) AS completedRides,
|
||||
|
||||
driverToken.token AS token
|
||||
|
||||
FROM driver
|
||||
LEFT JOIN passengers ON passengers.id = :passengerID
|
||||
LEFT JOIN CarRegistration ON CarRegistration.driverID = driver.id
|
||||
LEFT JOIN driverToken ON driverToken.captain_id = driver.id
|
||||
WHERE driver.id = :driverID";
|
||||
|
||||
// نستخدم المتغير الأصلي $con للسيرفر الرئيسي
|
||||
$stmtDetails = $con->prepare($sqlDetails);
|
||||
|
||||
// نربط المتغيرات
|
||||
$stmtDetails->bindParam(':driverID', $driverID);
|
||||
$stmtDetails->bindParam(':driverID_Sub', $driverID);
|
||||
$stmtDetails->bindParam(':passengerID', $pID);
|
||||
|
||||
$stmtDetails->execute();
|
||||
|
||||
$detailsData = $stmtDetails->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// =================================================================
|
||||
// 3. الخطوة الثالثة: دمج البيانات وتجهيز الرد
|
||||
// =================================================================
|
||||
|
||||
$finalData = [];
|
||||
|
||||
if ($detailsData) {
|
||||
// دمج مصفوفة الرحلة (من سيرفر الرحلات) مع مصفوفة التفاصيل (من الرئيسي)
|
||||
$finalData = array_merge($rideData, $detailsData);
|
||||
} else {
|
||||
// في حال كانت الرحلة بدون سائق بعد، نكتفي ببيانات الرحلة
|
||||
$finalData = $rideData;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 4. فك التشفير (Decrypt)
|
||||
// =================================================================
|
||||
|
||||
if ($finalData) {
|
||||
$fieldsToDecrypt = ['driverName', 'gender', 'phone', 'car_plate', 'passengerName', 'last_name', 'token'];
|
||||
|
||||
foreach ($fieldsToDecrypt as $field) {
|
||||
if (!empty($finalData[$field])) {
|
||||
$finalData[$field] = $encryptionHelper->decryptData($finalData[$field]);
|
||||
}
|
||||
}
|
||||
$ratingValue = (float) ($finalData['ratingDriver'] ?: 5.0);
|
||||
$ratingCount = (int) ($finalData['ratingCount'] ?? 0);
|
||||
$completedRides = (int) ($finalData['completedRides'] ?? 0);
|
||||
if ($ratingValue >= 4.8 && $ratingCount >= 50 && $completedRides >= 100) {
|
||||
$finalData['driverTier'] = 'Professional driver';
|
||||
} elseif ($ratingValue >= 4.5 && $ratingCount >= 15 && $completedRides >= 30) {
|
||||
$finalData['driverTier'] = 'Trusted driver';
|
||||
} else {
|
||||
$finalData['driverTier'] = 'Verified driver';
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"data" => $finalData
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("API Error: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(["status" => "failure", "message" => "An internal server error occurred."]);
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user