Add guest push notifications and queue alerts

This commit is contained in:
Codex Agent
2025-11-12 20:38:49 +01:00
parent 2c412e3764
commit 574aa47ce7
34 changed files with 1806 additions and 74 deletions

View File

@@ -19,6 +19,7 @@ use App\Services\EventTasksCacheService;
use App\Services\GuestNotificationService;
use App\Services\Packages\PackageLimitEvaluator;
use App\Services\Packages\PackageUsageTracker;
use App\Services\PushSubscriptionService;
use App\Services\Storage\EventStorageManager;
use App\Support\ApiError;
use App\Support\ImageHelper;
@@ -49,6 +50,7 @@ class EventPublicController extends BaseController
private readonly PackageUsageTracker $packageUsageTracker,
private readonly EventTasksCacheService $eventTasksCache,
private readonly GuestNotificationService $guestNotificationService,
private readonly PushSubscriptionService $pushSubscriptions,
) {}
/**
@@ -1666,6 +1668,69 @@ class EventPublicController extends BaseController
->header('Vary', 'X-Device-Id, Accept-Language');
}
public function registerPushSubscription(Request $request, string $token)
{
$result = $this->resolvePublishedEvent($request, $token, ['id']);
if ($result instanceof JsonResponse) {
return $result;
}
[$eventRecord] = $result;
$validated = $request->validate([
'endpoint' => ['required', 'url', 'max:500'],
'keys.p256dh' => ['required', 'string', 'max:255'],
'keys.auth' => ['required', 'string', 'max:255'],
'expiration_time' => ['nullable'],
'content_encoding' => ['nullable', 'string', 'max:32'],
]);
$event = Event::findOrFail($eventRecord->id);
$guestIdentifier = $this->resolveNotificationIdentifier($request);
$deviceId = $this->resolveDeviceIdentifier($request);
$payload = [
'endpoint' => $validated['endpoint'],
'keys' => [
'p256dh' => $validated['keys']['p256dh'],
'auth' => $validated['keys']['auth'],
],
'expiration_time' => $validated['expiration_time'] ?? null,
'content_encoding' => $validated['content_encoding'] ?? null,
'language' => $request->getPreferredLanguage() ?? $request->headers->get('Accept-Language'),
'user_agent' => (string) $request->userAgent(),
];
$subscription = $this->pushSubscriptions->register($event, $guestIdentifier, $deviceId, $payload);
return response()->json([
'id' => $subscription->id,
'status' => $subscription->status,
], Response::HTTP_CREATED);
}
public function destroyPushSubscription(Request $request, string $token)
{
$result = $this->resolvePublishedEvent($request, $token, ['id']);
if ($result instanceof JsonResponse) {
return $result;
}
[$eventRecord] = $result;
$validated = $request->validate([
'endpoint' => ['required', 'url', 'max:500'],
]);
$event = Event::findOrFail($eventRecord->id);
$revoked = $this->pushSubscriptions->revoke($event, $validated['endpoint']);
return response()->json([
'status' => $revoked ? 'revoked' : 'not_found',
]);
}
public function markNotificationRead(Request $request, string $token, GuestNotification $notification)
{
return $this->handleNotificationAction($request, $token, $notification, 'read');
@@ -1831,10 +1896,16 @@ class EventPublicController extends BaseController
return $identifier;
}
$deviceId = (string) $request->headers->get('X-Device-Id', '');
$deviceId = substr(preg_replace('/[^A-Za-z0-9 _\-]/', '', $deviceId) ?? '', 0, 120);
return $this->resolveDeviceIdentifier($request);
}
return $deviceId !== '' ? $deviceId : 'anonymous';
private function resolveDeviceIdentifier(Request $request): string
{
$deviceId = (string) $request->headers->get('X-Device-Id', '');
$normalized = preg_replace('/[^A-Za-z0-9 _\-]/', '', $deviceId) ?? '';
$normalized = trim(substr($normalized, 0, 120));
return $normalized !== '' ? $normalized : 'anonymous';
}
public function stats(Request $request, string $token)
@@ -2216,8 +2287,7 @@ class EventPublicController extends BaseController
$eventPackage = $this->packageLimitEvaluator
->resolveEventPackageForPhotoUpload($tenantModel, $eventId, $eventModel);
$deviceId = (string) $request->header('X-Device-Id', 'anon');
$deviceId = substr(preg_replace('/[^a-zA-Z0-9_-]/', '', $deviceId), 0, 64) ?: 'anon';
$deviceId = $this->resolveDeviceIdentifier($request);
// Per-device cap per event (MVP: 50)
$deviceCount = DB::table('photos')->where('event_id', $eventId)->where('guest_name', $deviceId)->count();