Add Nabeh integration: nabeh/ endpoints with NABEH_API_KEY auth
This commit is contained in:
252
backend/nabeh/query.php
Normal file
252
backend/nabeh/query.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
/**
|
||||
* Nabeh Integration — Unified Query API
|
||||
*
|
||||
* Called by Nabeh AI platform to query driver info, trips, stats, and trip details.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../core/bootstrap.php';
|
||||
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, X-API-Key');
|
||||
|
||||
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
|
||||
$expectedKey = getenv('NABEH_API_KEY') ?: '';
|
||||
|
||||
if (empty($apiKey) || $apiKey !== $expectedKey) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'Unauthorized: invalid API key']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = [];
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$raw = file_get_contents('php://input');
|
||||
$input = json_decode($raw, true) ?: [];
|
||||
} else {
|
||||
$input = $_GET;
|
||||
}
|
||||
|
||||
$queryType = $input['query_type'] ?? '';
|
||||
$phone = preg_replace('/[^0-9]/', '', $input['phone'] ?? '');
|
||||
$driverId = $input['driver_id'] ?? '';
|
||||
$tripId = $input['trip_id'] ?? '';
|
||||
$limit = min((int)($input['limit'] ?? 10), 50);
|
||||
|
||||
if (empty($queryType)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'query_type is required. Options: driver_info, driver_trips, driver_stats, trip_detail']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$validTypes = ['driver_info', 'driver_trips', 'driver_stats', 'trip_detail'];
|
||||
if (!in_array($queryType, $validTypes, true)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'Invalid query_type. Options: ' . implode(', ', $validTypes)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
global $encryptionHelper;
|
||||
$mainDb = Database::get('main');
|
||||
$rideDb = Database::get('ride');
|
||||
|
||||
// ========================================================================
|
||||
if ($queryType === 'driver_info') {
|
||||
if (empty($phone)) {
|
||||
jsonError('phone parameter is required');
|
||||
}
|
||||
|
||||
$encryptedPhone = $encryptionHelper->encryptData($phone);
|
||||
|
||||
$stmt = $mainDb->prepare("
|
||||
SELECT d.id, d.phone, d.first_name, d.last_name, d.name_arabic,
|
||||
d.status, d.created_at, d.birthdate, d.gender, d.site,
|
||||
cr.id as car_id, cr.make, cr.model, cr.year, cr.car_plate,
|
||||
cr.color, cr.color_hex, cr.fuel, cr.vin,
|
||||
cr.status as car_status, cr.expiration_date
|
||||
FROM driver d
|
||||
LEFT JOIN CarRegistration cr ON cr.driverID = d.id
|
||||
WHERE d.phone = :phone OR d.email LIKE :phoneLike
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([
|
||||
':phone' => $encryptedPhone,
|
||||
':phoneLike' => $phone . '%',
|
||||
]);
|
||||
$driver = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$driver) {
|
||||
echo json_encode(['status' => 'success', 'data' => null, 'message' => 'Driver not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$decrypt = function($val) use ($encryptionHelper) {
|
||||
return $val ? $encryptionHelper->decryptData($val) : $val;
|
||||
};
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'driver_id' => $driver['id'],
|
||||
'phone' => $decrypt($driver['phone']),
|
||||
'first_name' => $decrypt($driver['first_name']),
|
||||
'last_name' => $decrypt($driver['last_name']),
|
||||
'name_arabic' => $decrypt($driver['name_arabic']),
|
||||
'gender' => $decrypt($driver['gender']),
|
||||
'birthdate' => $driver['birthdate'],
|
||||
'status' => $driver['status'],
|
||||
'site' => $decrypt($driver['site']),
|
||||
'registered_at' => $driver['created_at'],
|
||||
'car' => $driver['car_id'] ? [
|
||||
'id' => $driver['car_id'],
|
||||
'make' => $driver['make'],
|
||||
'model' => $driver['model'],
|
||||
'year' => $driver['year'],
|
||||
'plate' => $driver['car_plate'],
|
||||
'color' => $driver['color'],
|
||||
'color_hex' => $driver['color_hex'],
|
||||
'fuel' => $driver['fuel'],
|
||||
'vin' => $decrypt($driver['vin']),
|
||||
'status' => $driver['car_status'],
|
||||
'expiration_date' => $driver['expiration_date'],
|
||||
] : null,
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
if ($queryType === 'driver_trips') {
|
||||
if (empty($driverId) && empty($phone)) {
|
||||
jsonError('driver_id or phone is required');
|
||||
}
|
||||
|
||||
if (empty($driverId) && !empty($phone)) {
|
||||
$encryptedPhone = $encryptionHelper->encryptData($phone);
|
||||
$stmt = $mainDb->prepare("SELECT id FROM driver WHERE phone = :phone LIMIT 1");
|
||||
$stmt->execute([':phone' => $encryptedPhone]);
|
||||
$driverRow = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$driverRow) {
|
||||
echo json_encode(['status' => 'success', 'data' => [], 'message' => 'Driver not found']);
|
||||
exit;
|
||||
}
|
||||
$driverId = $driverRow['id'];
|
||||
}
|
||||
|
||||
$stmt = $rideDb->prepare("
|
||||
SELECT id, start_location, end_location, date, time, endtime,
|
||||
price, price_for_driver, price_for_passenger,
|
||||
status, paymentMethod, carType, distance, created_at
|
||||
FROM ride
|
||||
WHERE driver_id = :driver_id
|
||||
ORDER BY created_at DESC
|
||||
LIMIT :lim
|
||||
");
|
||||
$stmt->bindValue(':driver_id', $driverId, PDO::PARAM_STR);
|
||||
$stmt->bindValue(':lim', $limit, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$trips = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => $trips,
|
||||
'count' => count($trips),
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
if ($queryType === 'driver_stats') {
|
||||
if (empty($driverId) && empty($phone)) {
|
||||
jsonError('driver_id or phone is required');
|
||||
}
|
||||
|
||||
if (empty($driverId) && !empty($phone)) {
|
||||
$encryptedPhone = $encryptionHelper->encryptData($phone);
|
||||
$stmt = $mainDb->prepare("SELECT id FROM driver WHERE phone = :phone LIMIT 1");
|
||||
$stmt->execute([':phone' => $encryptedPhone]);
|
||||
$driverRow = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$driverRow) {
|
||||
echo json_encode(['status' => 'success', 'data' => null, 'message' => 'Driver not found']);
|
||||
exit;
|
||||
}
|
||||
$driverId = $driverRow['id'];
|
||||
}
|
||||
|
||||
$stmt = $rideDb->prepare("
|
||||
SELECT
|
||||
COUNT(*) as total_trips,
|
||||
COALESCE(SUM(price_for_driver), 0) as total_earnings,
|
||||
COALESCE(SUM(price_for_passenger), 0) as total_collected,
|
||||
COALESCE(AVG(price_for_driver), 0) as avg_earning_per_trip,
|
||||
COALESCE(SUM(distance), 0) as total_distance,
|
||||
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_trips,
|
||||
COUNT(CASE WHEN status = 'cancelled' THEN 1 END) as cancelled_trips
|
||||
FROM ride
|
||||
WHERE driver_id = :driver_id
|
||||
");
|
||||
$stmt->execute([':driver_id' => $driverId]);
|
||||
$stats = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$driverStmt = $mainDb->prepare("SELECT status, created_at FROM driver WHERE id = :id LIMIT 1");
|
||||
$driverStmt->execute([':id' => $driverId]);
|
||||
$driverStatus = $driverStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'driver_id' => $driverId,
|
||||
'status' => $driverStatus['status'] ?? 'unknown',
|
||||
'registered_at' => $driverStatus['created_at'] ?? null,
|
||||
'stats' => [
|
||||
'total_trips' => (int)$stats['total_trips'],
|
||||
'completed_trips' => (int)$stats['completed_trips'],
|
||||
'cancelled_trips' => (int)$stats['cancelled_trips'],
|
||||
'total_earnings' => (float)$stats['total_earnings'],
|
||||
'total_collected' => (float)$stats['total_collected'],
|
||||
'avg_earning_per_trip' => (float)$stats['avg_earning_per_trip'],
|
||||
'total_distance_km' => (float)$stats['total_distance'],
|
||||
],
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
if ($queryType === 'trip_detail') {
|
||||
if (empty($tripId)) {
|
||||
jsonError('trip_id is required');
|
||||
}
|
||||
|
||||
$stmt = $rideDb->prepare("
|
||||
SELECT r.*,
|
||||
p.first_name as passenger_first_name,
|
||||
p.last_name as passenger_last_name,
|
||||
p.phone as passenger_phone
|
||||
FROM ride r
|
||||
LEFT JOIN driver p ON p.id = r.passenger_id
|
||||
WHERE r.id = :id
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([':id' => $tripId]);
|
||||
$trip = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$trip) {
|
||||
echo json_encode(['status' => 'success', 'data' => null, 'message' => 'Trip not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => $trip,
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
error_log("[Nabeh Query Error] " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'Internal server error']);
|
||||
}
|
||||
Reference in New Issue
Block a user