states, and pulls data from the authenticated /api/v1/tenant/packages endpoint.
(resources/js/admin/pages/EventFormPage.tsx, resources/js/admin/api.ts)
- Harden tenant-admin auth flow: prevent PKCE state loss, scope out StrictMode double-processing, add SPA
routes for /event-admin/login and /event-admin/logout, and tighten token/session clearing semantics (resources/js/admin/auth/{context,tokens}.tsx, resources/js/admin/pages/{AuthCallbackPage,LogoutPage}.tsx,
resources/js/admin/router.tsx, routes/web.php)
113 lines
3.2 KiB
PHP
113 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Analytics;
|
|
|
|
use App\Models\EventJoinToken;
|
|
use App\Models\EventJoinTokenEvent;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
|
|
class JoinTokenAnalyticsRecorder
|
|
{
|
|
public function record(
|
|
?EventJoinToken $joinToken,
|
|
string $eventType,
|
|
Request $request,
|
|
array $context = [],
|
|
?string $providedToken = null,
|
|
?int $httpStatus = null
|
|
): void {
|
|
try {
|
|
EventJoinTokenEvent::create($this->buildPayload(
|
|
$joinToken,
|
|
$eventType,
|
|
$request,
|
|
$context,
|
|
$providedToken,
|
|
$httpStatus
|
|
));
|
|
} catch (\Throwable $exception) {
|
|
// Never block the main request if analytics fails
|
|
report($exception);
|
|
}
|
|
}
|
|
|
|
private function buildPayload(
|
|
?EventJoinToken $joinToken,
|
|
string $eventType,
|
|
Request $request,
|
|
array $context,
|
|
?string $providedToken,
|
|
?int $httpStatus
|
|
): array {
|
|
$route = $request->route();
|
|
$routeName = $route ? $route->getName() : null;
|
|
$deviceId = (string) $request->header('X-Device-Id', $request->input('device_id', ''));
|
|
$deviceId = $deviceId !== '' ? substr(preg_replace('/[^a-zA-Z0-9_-]/', '', $deviceId), 0, 64) : null;
|
|
|
|
$tokenHash = null;
|
|
$tokenPreview = null;
|
|
|
|
if ($joinToken) {
|
|
$tokenHash = $joinToken->token_hash ?? null;
|
|
$tokenPreview = $joinToken->token_preview ?? null;
|
|
} elseif ($providedToken) {
|
|
$tokenHash = hash('sha256', $providedToken);
|
|
$tokenPreview = $this->buildPreview($providedToken);
|
|
}
|
|
|
|
$eventId = $joinToken?->event_id;
|
|
$tenantId = null;
|
|
|
|
if ($joinToken && $joinToken->relationLoaded('event')) {
|
|
$tenantId = $joinToken->event?->tenant_id;
|
|
} elseif ($joinToken) {
|
|
$tenantId = $joinToken->event()->value('tenant_id');
|
|
}
|
|
|
|
return [
|
|
'event_join_token_id' => $joinToken?->getKey(),
|
|
'event_id' => $eventId,
|
|
'tenant_id' => $tenantId,
|
|
'token_hash' => $tokenHash,
|
|
'token_preview' => $tokenPreview,
|
|
'event_type' => $eventType,
|
|
'route' => $routeName,
|
|
'http_method' => $request->getMethod(),
|
|
'http_status' => $httpStatus,
|
|
'device_id' => $deviceId,
|
|
'ip_address' => $request->ip(),
|
|
'user_agent' => $this->shortenUserAgent($request->userAgent()),
|
|
'context' => $context ?: null,
|
|
'occurred_at' => now(),
|
|
];
|
|
}
|
|
|
|
private function shortenUserAgent(?string $userAgent): ?string
|
|
{
|
|
if ($userAgent === null) {
|
|
return null;
|
|
}
|
|
|
|
if (Str::length($userAgent) > 1024) {
|
|
return Str::substr($userAgent, 0, 1024);
|
|
}
|
|
|
|
return $userAgent;
|
|
}
|
|
|
|
private function buildPreview(string $token): string
|
|
{
|
|
$token = trim($token);
|
|
$length = Str::length($token);
|
|
|
|
if ($length <= 10) {
|
|
return $token;
|
|
}
|
|
|
|
return Str::substr($token, 0, 6).'…'.Str::substr($token, -4);
|
|
}
|
|
}
|
|
|