false, 'err' => 'auth required']); exit; } if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET') { http_response_code(405); echo json_encode(['ok' => false, 'err' => 'GET only']); exit; } require_once __DIR__ . '/../partials/traffic.php'; try { $db = traffic_db(); $latestHitId = (int)$db->query('SELECT COALESCE(MAX(id), 0) FROM hits')->fetchColumn(); $hitCount = (int)$db->query('SELECT COUNT(*) FROM hits')->fetchColumn(); $summary = traffic_summary(); $analytics = traffic_analytics(); $top = traffic_top_pages(80); $recent = traffic_recent(150); // Normalize recent rows for the client (bool is_bot, formatted fields optional). $recentOut = []; foreach ($recent as $r) { $isBot = !empty($r['is_bot']) || traffic_visitor_is_bot((string)($r['visitor'] ?? '')); $recentOut[] = [ 'id' => (int)($r['id'] ?? 0), 'ts' => (int)($r['ts'] ?? 0), 'host' => (string)($r['host'] ?? ''), 'path' => (string)($r['path'] ?? ''), 'visitor' => (string)($r['visitor'] ?? ''), 'lang' => (string)($r['lang'] ?? ''), 'ref_host' => (string)($r['ref_host'] ?? ''), 'local_count' => (int)($r['local_count'] ?? 0), 'is_new' => !empty($r['is_new']) ? 1 : 0, 'is_bot' => $isBot ? 1 : 0, ]; } $topOut = []; foreach ($top as $p) { $topOut[] = [ 'page_key' => (string)($p['page_key'] ?? ''), 'host' => (string)($p['host'] ?? ''), 'path' => (string)($p['path'] ?? ''), 'hits' => (int)($p['hits'] ?? 0), 'unique_sessions' => (int)($p['unique_sessions'] ?? 0), 'bot_hits' => (int)($p['bot_hits'] ?? 0), 'last_local_count' => (int)($p['last_local_count'] ?? 0), 'last_visitor' => (string)($p['last_visitor'] ?? ''), 'last_hit' => (int)($p['last_hit'] ?? 0), ]; } // Fingerprint so the client can skip redraw when nothing changed. $rev = $latestHitId . ':' . $hitCount . ':' . (int)($summary['total_hits'] ?? 0) . ':' . (int)($summary['bot_hits'] ?? 0) . ':' . (int)($summary['today'] ?? 0) . ':' . (int)($summary['bot_today'] ?? 0); echo json_encode([ 'ok' => true, 'rev' => $rev, 'server_time' => time(), 'summary' => $summary, 'analytics' => $analytics, 'top' => $topOut, 'recent' => $recentOut, ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } catch (Throwable $e) { http_response_code(500); echo json_encode(['ok' => false, 'err' => 'store failed']); }