Add Nabeh integration: nabeh/ endpoints with NABEH_API_KEY auth

This commit is contained in:
Hamza-Ayed
2026-06-17 18:22:45 +03:00
parent c2c4ed22e3
commit b67417eb98
5 changed files with 733 additions and 0 deletions

252
backend/nabeh/query.php Normal file
View 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']);
}