267 lines
8.5 KiB
PHP
267 lines
8.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controllers;
|
|
|
|
use Core\Request;
|
|
use Core\Response;
|
|
use Core\Database;
|
|
use PDO;
|
|
|
|
class GatewayController
|
|
{
|
|
private string $configuredAppKey;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->configuredAppKey = getenv('GATEWAY_APP_KEY') ?: 'uruk_gateway_secret_2026';
|
|
}
|
|
|
|
/**
|
|
* Authenticate gateway device app_key
|
|
*/
|
|
private function authenticate(string $appKey): bool
|
|
{
|
|
return hash_equals($this->configuredAppKey, $appKey);
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/gateway/pending-call or /pending-call.php
|
|
*/
|
|
public function pendingCall(Request $request): void
|
|
{
|
|
$deviceId = (string)$request->get('device_id');
|
|
$appKey = (string)$request->get('app_key');
|
|
|
|
if (!$this->authenticate($appKey) || empty($deviceId)) {
|
|
Response::json([
|
|
'task_id' => null,
|
|
'phone' => null,
|
|
'caller_id' => null,
|
|
'otp' => null,
|
|
'timeout_seconds' => null,
|
|
'error' => 'Unauthorized or missing device_id'
|
|
], 401);
|
|
return;
|
|
}
|
|
|
|
$pdo = Database::getConnection();
|
|
|
|
// Update device heartbeat
|
|
$this->heartbeat($pdo, $deviceId);
|
|
|
|
// Fetch oldest pending FLASH_CALL task
|
|
$stmt = $pdo->prepare('
|
|
SELECT id, target_phone, otp_code, timeout_seconds
|
|
FROM gateway_tasks
|
|
WHERE task_type = "FLASH_CALL"
|
|
AND (assigned_device_id = :dev OR assigned_device_id IS NULL)
|
|
AND status = "PENDING"
|
|
AND created_at >= NOW() - INTERVAL 60 SECOND
|
|
ORDER BY id ASC
|
|
LIMIT 1
|
|
');
|
|
$stmt->execute([':dev' => $deviceId]);
|
|
$task = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($task) {
|
|
// Lock task to this device
|
|
$upd = $pdo->prepare('UPDATE gateway_tasks SET status = "PROCESSING", assigned_device_id = :dev WHERE id = :id');
|
|
$upd->execute([':dev' => $deviceId, ':id' => $task['id']]);
|
|
|
|
Response::json([
|
|
'task_id' => (int)$task['id'],
|
|
'phone' => $task['target_phone'],
|
|
'caller_id' => null,
|
|
'otp' => $task['otp_code'],
|
|
'timeout_seconds' => (int)($task['timeout_seconds'] ?: 25),
|
|
]);
|
|
return;
|
|
}
|
|
|
|
Response::json([
|
|
'task_id' => null,
|
|
'phone' => null,
|
|
'caller_id' => null,
|
|
'otp' => null,
|
|
'timeout_seconds' => null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/gateway/pending-sms or /pending-sms.php
|
|
*/
|
|
public function pendingSms(Request $request): void
|
|
{
|
|
$deviceId = (string)$request->get('device_id');
|
|
$appKey = (string)$request->get('app_key');
|
|
|
|
if (!$this->authenticate($appKey) || empty($deviceId)) {
|
|
Response::json([
|
|
'task_id' => null,
|
|
'phone' => null,
|
|
'caller_id' => null,
|
|
'otp' => null,
|
|
'timeout_seconds' => null,
|
|
'error' => 'Unauthorized or missing device_id'
|
|
], 401);
|
|
return;
|
|
}
|
|
|
|
$pdo = Database::getConnection();
|
|
$this->heartbeat($pdo, $deviceId);
|
|
|
|
$stmt = $pdo->prepare('
|
|
SELECT id, target_phone, otp_code, timeout_seconds
|
|
FROM gateway_tasks
|
|
WHERE task_type = "SMS"
|
|
AND (assigned_device_id = :dev OR assigned_device_id IS NULL)
|
|
AND status = "PENDING"
|
|
AND created_at >= NOW() - INTERVAL 120 SECOND
|
|
ORDER BY id ASC
|
|
LIMIT 1
|
|
');
|
|
$stmt->execute([':dev' => $deviceId]);
|
|
$task = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($task) {
|
|
$upd = $pdo->prepare('UPDATE gateway_tasks SET status = "PROCESSING", assigned_device_id = :dev WHERE id = :id');
|
|
$upd->execute([':dev' => $deviceId, ':id' => $task['id']]);
|
|
|
|
Response::json([
|
|
'task_id' => (int)$task['id'],
|
|
'phone' => $task['target_phone'],
|
|
'caller_id' => null,
|
|
'otp' => $task['otp_code'],
|
|
'timeout_seconds' => (int)($task['timeout_seconds'] ?: 30),
|
|
]);
|
|
return;
|
|
}
|
|
|
|
Response::json([
|
|
'task_id' => null,
|
|
'phone' => null,
|
|
'caller_id' => null,
|
|
'otp' => null,
|
|
'timeout_seconds' => null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/gateway/call-done or /call-done.php
|
|
*/
|
|
public function callDone(Request $request): void
|
|
{
|
|
$taskId = (int)$request->get('task_id');
|
|
$deviceId = (string)$request->get('device_id');
|
|
$appKey = (string)$request->get('app_key');
|
|
$result = (string)$request->get('result');
|
|
|
|
if (!$this->authenticate($appKey)) {
|
|
Response::json(['success' => false, 'message' => 'Unauthorized'], 401);
|
|
return;
|
|
}
|
|
|
|
$pdo = Database::getConnection();
|
|
$newStatus = (strtoupper($result) === 'SUCCESS' || strtoupper($result) === 'DONE') ? 'COMPLETED' : 'FAILED';
|
|
|
|
$stmt = $pdo->prepare('
|
|
UPDATE gateway_tasks
|
|
SET status = :st, completed_at = NOW()
|
|
WHERE id = :id AND assigned_device_id = :dev
|
|
');
|
|
$stmt->execute([':st' => $newStatus, ':id' => $taskId, ':dev' => $deviceId]);
|
|
|
|
Response::json([
|
|
'success' => true,
|
|
'message' => "Call task {$taskId} recorded as {$newStatus}.",
|
|
'device_id' => $deviceId,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/gateway/sms-done or /sms-done.php
|
|
*/
|
|
public function smsDone(Request $request): void
|
|
{
|
|
$taskId = (int)$request->get('task_id');
|
|
$deviceId = (string)$request->get('device_id');
|
|
$appKey = (string)$request->get('app_key');
|
|
$result = (string)$request->get('result');
|
|
|
|
if (!$this->authenticate($appKey)) {
|
|
Response::json(['success' => false, 'message' => 'Unauthorized'], 401);
|
|
return;
|
|
}
|
|
|
|
$pdo = Database::getConnection();
|
|
$newStatus = (strtoupper($result) === 'SUCCESS' || strtoupper($result) === 'SENT') ? 'COMPLETED' : 'FAILED';
|
|
|
|
$stmt = $pdo->prepare('
|
|
UPDATE gateway_tasks
|
|
SET status = :st, completed_at = NOW()
|
|
WHERE id = :id AND assigned_device_id = :dev
|
|
');
|
|
$stmt->execute([':st' => $newStatus, ':id' => $taskId, ':dev' => $deviceId]);
|
|
|
|
Response::json([
|
|
'success' => true,
|
|
'message' => "SMS task {$taskId} recorded as {$newStatus}.",
|
|
'device_id' => $deviceId,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/gateway/register-device or /register-device.php
|
|
*/
|
|
public function registerDevice(Request $request): void
|
|
{
|
|
$deviceId = (string)$request->get('device_id');
|
|
$phoneNumber = (string)$request->get('phone_number');
|
|
$simSlot = (int)$request->get('sim_slot', 1);
|
|
$appKey = (string)$request->get('app_key');
|
|
|
|
if (!$this->authenticate($appKey) || empty($deviceId)) {
|
|
Response::json(['success' => false, 'message' => 'Unauthorized or missing device_id'], 401);
|
|
return;
|
|
}
|
|
|
|
$pdo = Database::getConnection();
|
|
$stmt = $pdo->prepare('
|
|
INSERT INTO gateway_devices (device_id, phone_number, sim_slot, status, last_heartbeat)
|
|
VALUES (:did, :phone, :slot, "ACTIVE", NOW())
|
|
ON DUPLICATE KEY UPDATE
|
|
phone_number = VALUES(phone_number),
|
|
sim_slot = VALUES(sim_slot),
|
|
status = "ACTIVE",
|
|
last_heartbeat = NOW()
|
|
');
|
|
$stmt->execute([
|
|
':did' => $deviceId,
|
|
':phone' => $phoneNumber,
|
|
':slot' => $simSlot,
|
|
]);
|
|
|
|
Response::json([
|
|
'success' => true,
|
|
'message' => 'Gateway device registered and active.',
|
|
'device_id' => $deviceId,
|
|
]);
|
|
}
|
|
|
|
private function heartbeat(PDO $pdo, string $deviceId): void
|
|
{
|
|
try {
|
|
$stmt = $pdo->prepare('
|
|
INSERT INTO gateway_devices (device_id, status, last_heartbeat)
|
|
VALUES (:did, "ACTIVE", NOW())
|
|
ON DUPLICATE KEY UPDATE status = "ACTIVE", last_heartbeat = NOW()
|
|
');
|
|
$stmt->execute([':did' => $deviceId]);
|
|
} catch (\Throwable $e) {
|
|
// Ignore heartbeat failures if table schema is pending
|
|
}
|
|
}
|
|
}
|