export async function sendNotification(webhookUrl: string | null, message: string) { if (!webhookUrl) return; try { const response = await fetch(webhookUrl, { method: "POST", headers: { "Content-Type": "application/json", "User-Agent": "CatSittingPlanner/1.0" }, // Works for Discord (content) and generic Telegram Webhook bridges (text) body: JSON.stringify({ content: message, text: message }), }); if (!response.ok) { console.error(`[Notification] Webhook failed with status ${response.status}`); } } catch (error) { console.error("Failed to send notification:", error); } } import { sendPushNotification } from "./push"; import prisma from "@/lib/prisma"; export async function sendPlanNotification(planId: string, message: string, webhookUrl?: string | null) { // Parallelize sending const promises: Promise[] = []; if (webhookUrl) { promises.push(sendNotification(webhookUrl, message)); } try { const subscriptions = await prisma.pushSubscription.findMany({ where: { planId } }); if (subscriptions.length > 0) { const payload = { title: "Cat Sitting Planner", body: message, url: `/` // We could pass specific URL if needed, but for now root is okay or dashboard? // The service worker opens the URL. // Ideally, we want to open `/dashboard/[planId]`. }; // Refine URL in payload // We need 'lang'. We don't have it here easily unless passed. // But we can guess or just link to root and let redirect handle it? // Or just link to context. // Let's rely on SW opening `/`. subscriptions.forEach(sub => { promises.push((async () => { const res = await sendPushNotification(sub, payload); if (!res.success && (res.statusCode === 410 || res.statusCode === 404)) { await prisma.pushSubscription.delete({ where: { id: sub.id } }); } })()); }); } } catch (e) { console.error("Failed to fetch/send push subscriptions", e); } await Promise.allSettled(promises); }