json(['publicKey' => $this->vapidPublicKey]); } /** * POST /api/push/subscribe * Enregistre un abonnement push. * * { * "endpoint": "https://...", * "keys": { "auth": "...", "p256dh": "..." }, * "coastalPointId": "uuid" // optionnel * } */ #[Route('/subscribe', name: 'subscribe', methods: ['POST'])] public function subscribe(Request $request): JsonResponse { $limiter = $this->apiPushLimiter->create($request->getClientIp() ?? 'unknown'); if (!$limiter->consume()->isAccepted()) { return $this->json(['error' => 'Too many requests'], 429); } if (strlen($request->getContent()) > 4096) { return $this->json(['error' => 'Request body too large'], 413); } $data = json_decode($request->getContent(), true); if (!isset($data['endpoint'], $data['keys']['auth'], $data['keys']['p256dh'])) { return $this->json(['error' => 'Missing required fields'], 422); } if (!filter_var($data['endpoint'], FILTER_VALIDATE_URL)) { return $this->json(['error' => 'Invalid endpoint URL'], 422); } // Upsert : si l'endpoint existe déjà, on met à jour $repo = $this->em->getRepository(PushSubscription::class); $sub = $repo->findOneBy(['endpoint' => $data['endpoint']]) ?? new PushSubscription(); $sub->setEndpoint($data['endpoint']); $sub->setAuthToken($data['keys']['auth']); $sub->setP256dhKey($data['keys']['p256dh']); if (!empty($data['coastalPointId'])) { $spot = $this->em->getRepository(CoastalPoint::class)->find($data['coastalPointId']); $sub->setCoastalPoint($spot); } $this->em->persist($sub); $this->em->flush(); return $this->json(['id' => (string) $sub->getId()], 201); } /** * DELETE /api/push/subscribe * Supprime un abonnement push. */ #[Route('/subscribe', name: 'unsubscribe', methods: ['DELETE'])] public function unsubscribe(Request $request): JsonResponse { $limiter = $this->apiPushLimiter->create($request->getClientIp() ?? 'unknown'); if (!$limiter->consume()->isAccepted()) { return $this->json(['error' => 'Too many requests'], 429); } $data = json_decode($request->getContent(), true); if (empty($data['endpoint'])) { return $this->json(['error' => 'Missing endpoint'], 422); } $sub = $this->em->getRepository(PushSubscription::class) ->findOneBy(['endpoint' => $data['endpoint']]); if ($sub !== null) { $this->em->remove($sub); $this->em->flush(); } return $this->json(null, 204); } }