'method_not_allowed']); exit; } $raw = file_get_contents('php://input') ?: ''; $req = json_decode($raw, true); if (!is_array($req)) { http_response_code(400); echo json_encode(['error' => 'invalid_json']); exit; } $heartbeat = $req['heartbeat'] ?? []; if (!is_array($heartbeat)) { $heartbeat = []; } $type = (string)($heartbeat['type'] ?? 'generic'); $srvEpoch = time(); $srvTz = date('O'); $clientIp = (string)($_SERVER['HTTP_X_FORWARDED_FOR'] ?? ($_SERVER['REMOTE_ADDR'] ?? '')); $resp = [ 'heartbeat' => [ 'type' => $type, 'server_date' => $srvEpoch, 'server_tz' => $srvTz, ], ]; if ($type === 'ntp') { $clientDate = (int)($heartbeat['date'] ?? 0); $clientTz = (string)($heartbeat['tz'] ?? ''); $timeSource = trim((string)($heartbeat['time_source'] ?? 'device')); if ($timeSource === '') { $timeSource = 'device'; } $correction = $clientDate > 0 ? ($srvEpoch - $clientDate) : null; $resp['heartbeat']['client_date'] = $clientDate; $resp['heartbeat']['client_tz'] = $clientTz; $resp['heartbeat']['time_source'] = $timeSource; $resp['heartbeat']['correction_seconds'] = $correction; $resp['heartbeat']['ntp_correction_s'] = $correction; $resp['heartbeat']['enabled'] = true; } if ($type === 'ip') { $resp['heartbeat']['external_ip'] = $clientIp; } if ($type === 'ra') { require_once __DIR__ . '/../../src/RemoteAccessRepository.php'; $ra = RemoteAccessRepository::handleDeviceHeartbeat($heartbeat, $clientIp); $resp['heartbeat'] = array_merge($resp['heartbeat'], $ra); } echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);