1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
<?php
/**
* Admin-only live traffic stats (JSON).
* Used by /admin.php?section=traffic to poll and redraw charts in realtime.
*
* Requires the shared admin session. Never stores or returns IPs.
*/
declare(strict_types=1);
require_once __DIR__ . '/../partials/session.php';
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: no-referrer');
header('X-Robots-Tag: noindex');
if (empty($_SESSION['admin'])) {
http_response_code(401);
echo json_encode(['ok' => 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']);
}
|