false, 'err' => 'origin not allowed']); exit; } } if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { if ($corsOk) { http_response_code(204); } else { http_response_code(400); } exit; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['ok' => false, 'err' => 'POST only']); exit; } require_once __DIR__ . '/../partials/proxy_helpers.php'; require_once __DIR__ . '/../partials/traffic.php'; // Rate limit abuse without logging the IP into the traffic DB. if (!proxy_rate_limit('hit_log', 60, 60)) { http_response_code(429); echo json_encode(['ok' => false, 'err' => 'rate limited']); exit; } $raw = file_get_contents('php://input'); $data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : null; if (!is_array($data)) { // Also accept form-urlencoded beacons $data = $_POST; } $clientHost = trim((string)($data['host'] ?? '')); $path = (string)($data['path'] ?? '/'); $localCount = (int)($data['local_count'] ?? 0); $isNew = !empty($data['unique']) || !empty($data['is_new']); $lang = trim((string)($data['lang'] ?? '')); $ref = trim((string)($data['ref'] ?? '')); // Resolve host without trusting arbitrary input. // Prefer CORS Origin (browser-set), then allowed client host, then request Host. $host = ''; if ($originHost !== '' && traffic_host_allowed($originHost)) { $host = $originHost; } elseif (traffic_host_allowed($clientHost)) { $host = $clientHost; } else { $rh = (string)($_SERVER['HTTP_HOST'] ?? ''); if (traffic_host_allowed($rh)) { $host = $rh; } } if ($host === '' || !traffic_host_allowed($host)) { http_response_code(400); echo json_encode(['ok' => false, 'err' => 'host not allowed']); exit; } $path = traffic_normalize_path($path); if ($path === '' || strlen($path) > 500) { http_response_code(400); echo json_encode(['ok' => false, 'err' => 'bad path']); exit; } // Skip noisy/internal paths even if a client tries to report them. if (preg_match('#^/(admin|api|partials|locales|tools|docs)(/|$)#i', $path)) { echo json_encode(['ok' => true, 'skipped' => true]); exit; } $uaHeader = (string)($_SERVER['HTTP_USER_AGENT'] ?? ''); $isBot = traffic_is_bot($uaHeader); $visitor = traffic_visitor_label($uaHeader); // e.g. "Firefox · Linux" or "Bot · Google" — not an IP $refHost = traffic_ref_host($ref !== '' ? $ref : (string)($_SERVER['HTTP_REFERER'] ?? '')); $lang = traffic_lang($lang !== '' ? $lang : (string)($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')); $localCount = max(0, min($localCount, 99999999)); try { $result = traffic_record_hit([ 'host' => $host, 'path' => $path, 'visitor' => $visitor, 'lang' => $lang, 'ref_host' => $refHost, 'local_count' => $localCount, // Bots never count as unique human sessions. 'is_new' => ($isNew && !$isBot) ? 1 : 0, 'is_bot' => $isBot ? 1 : 0, ]); echo json_encode([ 'ok' => true, 'page' => $result['page_key'], 'hits' => $result['hits'], 'uniq' => $result['unique_sessions'], 'bots' => $result['bot_hits'] ?? 0, 'bot' => !empty($result['is_bot']), ]); } catch (Throwable $e) { http_response_code(500); echo json_encode(['ok' => false, 'err' => 'store failed']); }