Complete port of all 163 API endpoints from Lucee/CFML to PHP 8.3. Shared helpers in api/helpers.php (DB, auth, request/response, security). PDO prepared statements throughout. Same JSON response shapes as CFML.
105 lines
4.9 KiB
PHP
105 lines
4.9 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../helpers.php';
|
|
runAuth();
|
|
|
|
$data = readJsonBody();
|
|
|
|
$hardwareId = trim($data['HardwareId'] ?? '');
|
|
if ($hardwareId === '') {
|
|
apiAbort(['OK' => false, 'ERROR' => 'missing_hardware_id', 'MESSAGE' => 'HardwareId is required']);
|
|
}
|
|
|
|
$bizId = (int) ($data['BusinessID'] ?? 0);
|
|
if ($bizId <= 0) apiAbort(['OK' => false, 'ERROR' => 'missing_business_id', 'MESSAGE' => 'BusinessID is required']);
|
|
|
|
$spId = (int) ($data['ServicePointID'] ?? 0);
|
|
if ($spId <= 0) apiAbort(['OK' => false, 'ERROR' => 'missing_servicepoint_id', 'MESSAGE' => 'ServicePointID is required']);
|
|
|
|
$beaconUUID = trim($data['UUID'] ?? '');
|
|
if ($beaconUUID === '') apiAbort(['OK' => false, 'ERROR' => 'missing_uuid', 'MESSAGE' => 'UUID is required']);
|
|
|
|
$major = (int) ($data['Major'] ?? 0);
|
|
$minor = (int) ($data['Minor'] ?? 0);
|
|
|
|
$txPower = isset($data['TxPower']) && is_numeric($data['TxPower']) ? (int) $data['TxPower'] : null;
|
|
$advInterval = isset($data['AdvertisingInterval']) && is_numeric($data['AdvertisingInterval']) ? (int) $data['AdvertisingInterval'] : null;
|
|
$firmwareVersion = trim($data['FirmwareVersion'] ?? '');
|
|
|
|
try {
|
|
// Verify business namespace
|
|
$qBiz = queryOne("
|
|
SELECT b.ID, b.BeaconShardID, b.BeaconMajor, bs.UUID AS ShardUUID
|
|
FROM Businesses b LEFT JOIN BeaconShards bs ON b.BeaconShardID = bs.ID
|
|
WHERE b.ID = ? LIMIT 1
|
|
", [$bizId]);
|
|
|
|
if (!$qBiz) apiAbort(['OK' => false, 'ERROR' => 'invalid_business', 'MESSAGE' => 'Business not found']);
|
|
|
|
if (strcasecmp($qBiz['ShardUUID'] ?? '', $beaconUUID) !== 0) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'uuid_mismatch', 'MESSAGE' => 'UUID does not match business\'s assigned shard',
|
|
'ExpectedUUID' => $qBiz['ShardUUID'], 'ProvidedUUID' => $beaconUUID]);
|
|
}
|
|
if ((int) $qBiz['BeaconMajor'] !== $major) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'major_mismatch', 'MESSAGE' => 'Major does not match business\'s assigned value',
|
|
'ExpectedMajor' => (int) $qBiz['BeaconMajor'], 'ProvidedMajor' => $major]);
|
|
}
|
|
|
|
// Verify service point
|
|
$qSP = queryOne("SELECT ID, BusinessID, BeaconMinor, Name FROM ServicePoints WHERE ID = ? LIMIT 1", [$spId]);
|
|
if (!$qSP) apiAbort(['OK' => false, 'ERROR' => 'invalid_servicepoint', 'MESSAGE' => 'Service point not found']);
|
|
if ((int) $qSP['BusinessID'] !== $bizId) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'servicepoint_business_mismatch', 'MESSAGE' => 'Service point does not belong to this business']);
|
|
}
|
|
|
|
if ($qSP['BeaconMinor'] === null) {
|
|
queryTimed("UPDATE ServicePoints SET BeaconMinor = ? WHERE ID = ?", [$minor, $spId]);
|
|
} elseif ((int) $qSP['BeaconMinor'] !== $minor) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'minor_mismatch', 'MESSAGE' => 'Minor does not match service point\'s assigned value',
|
|
'ExpectedMinor' => (int) $qSP['BeaconMinor'], 'ProvidedMinor' => $minor]);
|
|
}
|
|
|
|
// Upsert BeaconHardware
|
|
$qExisting = queryOne("SELECT ID, Status FROM BeaconHardware WHERE HardwareId = ? LIMIT 1", [$hardwareId]);
|
|
|
|
if ($qExisting) {
|
|
$setClauses = ["BusinessID = ?", "ServicePointID = ?", "ShardUUID = ?", "Major = ?", "Minor = ?", "Status = 'assigned'"];
|
|
$params = [$bizId, $spId, $beaconUUID, $major, $minor];
|
|
|
|
if ($txPower !== null) { $setClauses[] = "TxPower = ?"; $params[] = $txPower; }
|
|
if ($advInterval !== null) { $setClauses[] = "AdvertisingInterval = ?"; $params[] = $advInterval; }
|
|
if ($firmwareVersion !== '') { $setClauses[] = "FirmwareVersion = ?"; $params[] = $firmwareVersion; }
|
|
|
|
$setClauses[] = "UpdatedAt = NOW()";
|
|
$params[] = $hardwareId;
|
|
|
|
queryTimed("UPDATE BeaconHardware SET " . implode(', ', $setClauses) . " WHERE HardwareId = ?", $params);
|
|
$hwId = (int) $qExisting['ID'];
|
|
} else {
|
|
$cols = ['HardwareId', 'BusinessID', 'ServicePointID', 'ShardUUID', 'Major', 'Minor', 'Status'];
|
|
$vals = ['?', '?', '?', '?', '?', '?', "'assigned'"];
|
|
$params = [$hardwareId, $bizId, $spId, $beaconUUID, $major, $minor];
|
|
|
|
if ($txPower !== null) { $cols[] = 'TxPower'; $vals[] = '?'; $params[] = $txPower; }
|
|
if ($advInterval !== null) { $cols[] = 'AdvertisingInterval'; $vals[] = '?'; $params[] = $advInterval; }
|
|
if ($firmwareVersion !== '') { $cols[] = 'FirmwareVersion'; $vals[] = '?'; $params[] = $firmwareVersion; }
|
|
|
|
queryTimed("INSERT INTO BeaconHardware (" . implode(', ', $cols) . ") VALUES (" . implode(', ', $vals) . ")", $params);
|
|
$hwId = (int) lastInsertId();
|
|
}
|
|
|
|
jsonResponse([
|
|
'OK' => true,
|
|
'BeaconHardwareID' => $hwId,
|
|
'HardwareId' => $hardwareId,
|
|
'BusinessID' => $bizId,
|
|
'ServicePointID' => $spId,
|
|
'ServicePointName' => $qSP['Name'],
|
|
'UUID' => $beaconUUID,
|
|
'Major' => $major,
|
|
'Minor' => $minor,
|
|
'Status' => 'assigned',
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => $e->getMessage()]);
|
|
}
|