Files
coruna-lab/app/Http/Controllers/PageHitController.php
T
2026-08-30 23:06:24 +08:00

85 lines
3.0 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Channel;
use App\Models\PageVisit;
use App\Support\UserAgentParser;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
class PageHitController extends Controller
{
private const GIF_1X1 = "GIF89a\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
public function __invoke(Request $request): Response
{
$channelId = trim((string) $request->query('c', $request->input('c', '')));
$clientUid = trim((string) $request->query('u', $request->input('u', '')));
$newCode = Channel::normalizeNewChannelId($channelId);
if ($newCode !== null) {
$channelId = $newCode;
} else {
$channelId = strtolower($channelId);
if (! preg_match('/^[0-9a-f]{32}$/', $channelId)) {
return $this->pixel();
}
}
if (! preg_match('/^[0-9a-fA-F-]{8,64}$/', $clientUid) && ! preg_match('/^tmp_[0-9a-f]+$/i', $clientUid)) {
return $this->pixel();
}
$ip = PageVisit::normalizeIp((string) $request->ip());
$domain = PageVisit::normalizeDomain($request->getHost());
$uid = PageVisit::visitorUid($domain ?? $request->getHost(), $ip);
$debounceKey = 'page_hit:'.$channelId.':'.$uid;
if (! Cache::add($debounceKey, 1, now()->addSeconds(8))) {
return $this->pixel();
}
$ua = substr((string) $request->userAgent(), 0, 512);
$parsed = UserAgentParser::parse($ua);
$referer = $this->referer($request);
PageVisit::recordLanding([
'channel_id' => $channelId,
'client_uid' => $uid,
'user_agent' => $ua !== '' ? $ua : null,
'os' => $parsed['os'],
'os_version' => $parsed['os_version'] !== '' ? $parsed['os_version'] : null,
'browser' => $parsed['browser'],
'browser_version' => $parsed['browser_version'] !== '' ? $parsed['browser_version'] : null,
'ip' => $ip !== '' ? $ip : null,
'domain' => $domain,
'referer' => $referer,
], PageVisit::chainFromIosVersion($parsed['os'], $parsed['os_version']));
return $this->pixel();
}
private function referer(Request $request): ?string
{
$referer = trim((string) $request->query('r', $request->input('r', '')));
if ($referer === '') {
$referer = trim((string) $request->headers->get('referer', ''));
}
if ($referer === '') {
return null;
}
return substr($referer, 0, 512);
}
private function pixel(): Response
{
return response(self::GIF_1X1, 200, [
'Content-Type' => 'image/gif',
'Content-Length' => (string) strlen(self::GIF_1X1),
'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
'Pragma' => 'no-cache',
]);
}
}