Add Nabeh integration: nabeh/ endpoints with NABEH_API_KEY auth
This commit is contained in:
164
backend/nabeh/upload_document.php
Normal file
164
backend/nabeh/upload_document.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* Nabeh Integration — Document Upload
|
||||
*
|
||||
* Called by Nabeh AI platform to upload driver documents to Siro's private storage.
|
||||
* Returns a signed URL valid for 48 hours.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../core/bootstrap.php';
|
||||
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, X-API-Key');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
// Rate limiting
|
||||
$rateLimitFile = __DIR__ . '/../logs/nabeh_upload_rate_' . md5($_SERVER['REMOTE_ADDR'] ?? 'unknown') . '.lock';
|
||||
$rateLimitWindow = 60;
|
||||
$rateLimitMax = 10;
|
||||
|
||||
$nowTime = time();
|
||||
$attempts = [];
|
||||
if (file_exists($rateLimitFile)) {
|
||||
$attempts = json_decode(file_get_contents($rateLimitFile), true) ?: [];
|
||||
$attempts = array_filter($attempts, fn($t) => $t > ($nowTime - $rateLimitWindow));
|
||||
}
|
||||
if (count($attempts) >= $rateLimitMax) {
|
||||
http_response_code(429);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'Too many requests. Try again later.']);
|
||||
exit;
|
||||
}
|
||||
$attempts[] = $nowTime;
|
||||
file_put_contents($rateLimitFile, json_encode($attempts), LOCK_EX);
|
||||
|
||||
const MAX_FILE_MB = 5;
|
||||
const ALLOWED_MIMES = ['image/jpeg', 'image/png', 'image/webp'];
|
||||
const UPLOAD_ROOT = __DIR__ . '/../private_uploads';
|
||||
const SIGNED_TTL_SEC = 172800;
|
||||
|
||||
$signSecret = getenv('SECRET_KEY_HMAC') ?: '';
|
||||
if (empty($signSecret)) {
|
||||
uploadLog('[Nabeh Upload] SECRET_KEY_HMAC not configured', 'ERROR');
|
||||
http_response_code(500);
|
||||
echo json_encode(['status' => 'failure', 'message' => 'Server configuration error']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$host = getenv('APP_DOMAIN') ?: 'api-syria.siromove.com';
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||
define('PUBLIC_BASE', "$protocol://$host/siro");
|
||||
|
||||
if (!is_dir(UPLOAD_ROOT)) {
|
||||
@mkdir(UPLOAD_ROOT, 0700, true);
|
||||
}
|
||||
|
||||
uploadLog("[Nabeh Upload] Document upload started");
|
||||
|
||||
$allowedDocTypes = [
|
||||
'id_front', 'id_back',
|
||||
'driver_license_front', 'driver_license_back',
|
||||
'car_license_front', 'car_license_back',
|
||||
'criminal_record', 'profile_picture',
|
||||
];
|
||||
|
||||
$driverId = $_POST['driver_id'] ?? '';
|
||||
$docType = $_POST['doc_type'] ?? '';
|
||||
|
||||
if (empty($driverId) || empty($docType)) {
|
||||
jsonError('driver_id and doc_type are required.');
|
||||
}
|
||||
|
||||
$driverIdSafe = preg_replace('/[^A-Za-z0-9_\-]/', '_', $driverId);
|
||||
|
||||
if (!in_array($docType, $allowedDocTypes, true)) {
|
||||
jsonError("Invalid doc_type. Allowed: " . implode(', ', $allowedDocTypes));
|
||||
}
|
||||
|
||||
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
$errCode = $_FILES['file']['error'] ?? 'missing_file';
|
||||
uploadLog("[Nabeh Upload] File upload error. Code: $errCode", 'ERROR');
|
||||
jsonError('No file uploaded or upload error.');
|
||||
}
|
||||
|
||||
$tmpPath = $_FILES['file']['tmp_name'];
|
||||
$size = filesize($tmpPath);
|
||||
if ($size === false || $size <= 0) {
|
||||
jsonError('Invalid file size.');
|
||||
}
|
||||
if ($size > MAX_FILE_MB * 1024 * 1024) {
|
||||
jsonError('File too large. Max ' . MAX_FILE_MB . ' MB.');
|
||||
}
|
||||
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
$mime = $finfo->file($tmpPath) ?: 'application/octet-stream';
|
||||
if (!in_array($mime, ALLOWED_MIMES, true)) {
|
||||
jsonError("Unsupported file type: $mime");
|
||||
}
|
||||
|
||||
$extMap = [
|
||||
'image/jpeg' => '.jpg',
|
||||
'image/png' => '.png',
|
||||
'image/webp' => '.webp',
|
||||
];
|
||||
$ext = $extMap[$mime];
|
||||
|
||||
$h = hash('sha1', $driverIdSafe);
|
||||
$subdir = substr($h, 0, 2) . '/' . substr($h, 2, 2);
|
||||
$destDir = UPLOAD_ROOT . '/' . $subdir;
|
||||
if (!is_dir($destDir)) { @mkdir($destDir, 0700, true); }
|
||||
|
||||
$serverName = "{$driverIdSafe}__{$docType}{$ext}";
|
||||
$destPath = $destDir . '/' . $serverName;
|
||||
|
||||
$resolvedDest = realpath($destPath) ?: $destPath;
|
||||
$resolvedRoot = realpath(UPLOAD_ROOT) ?: UPLOAD_ROOT;
|
||||
|
||||
if (is_file($destPath) && str_starts_with($resolvedDest, $resolvedRoot)) {
|
||||
@unlink($destPath);
|
||||
}
|
||||
|
||||
if (!move_uploaded_file($tmpPath, $destPath)) {
|
||||
jsonError('Failed to save the uploaded file.');
|
||||
}
|
||||
@chmod($destPath, 0600);
|
||||
|
||||
$extShort = ltrim($ext, '.');
|
||||
$expires = time() + SIGNED_TTL_SEC;
|
||||
$message = $driverIdSafe . ':' . $docType . ':' . $extShort . ':' . $expires;
|
||||
$signature = hash_hmac('sha256', $message, $signSecret);
|
||||
|
||||
$fileUrl = PUBLIC_BASE . '/secure_image.php'
|
||||
. '?driver_id=' . urlencode($driverIdSafe)
|
||||
. '&doc_type=' . urlencode($docType)
|
||||
. '&ext=' . urlencode($extShort)
|
||||
. '&expires=' . $expires
|
||||
. '&signature=' . urlencode($signature);
|
||||
|
||||
uploadLog("[Nabeh Upload] Document uploaded successfully. Type: $docType, Size: $size");
|
||||
|
||||
printSuccess([
|
||||
'status' => 'success',
|
||||
'success_file' => true,
|
||||
'file_url' => $fileUrl,
|
||||
'file_name' => $serverName,
|
||||
'driver_id' => $driverIdSafe,
|
||||
'doc_type' => $docType,
|
||||
'mime_type' => $mime,
|
||||
'size_bytes' => $size,
|
||||
'expires_at' => date('c', $expires),
|
||||
]);
|
||||
Reference in New Issue
Block a user