import webPush, { type PushSubscription } from "web-push"; import type { Language } from "@/lib/i18n"; import { formatProvinceName } from "@/lib/provinces"; import type { WeatherWarning } from "@/types/imgw"; import type { WarningPushSubscription } from "@/types/notifications"; const VAPID_PUBLIC_KEY = process.env.WEB_PUSH_VAPID_PUBLIC_KEY; const VAPID_PRIVATE_KEY = process.env.WEB_PUSH_VAPID_PRIVATE_KEY; const VAPID_SUBJECT = process.env.WEB_PUSH_VAPID_SUBJECT ?? "mailto:alerts@wtr.local"; let configured = false; export function getVapidPublicKey() { return VAPID_PUBLIC_KEY ?? null; } export function isWebPushConfigured() { return Boolean(VAPID_PUBLIC_KEY && VAPID_PRIVATE_KEY); } function ensureWebPushConfigured() { if (!VAPID_PUBLIC_KEY || !VAPID_PRIVATE_KEY) throw new Error("Web Push VAPID keys are not configured."); if (!configured) { webPush.setVapidDetails(VAPID_SUBJECT, VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY); configured = true; } } function getWarningLevelLabel(warning: WeatherWarning, language: Language) { if (warning.level === null) return language === "pl" ? "stopień nieokreślony" : "level not specified"; return language === "pl" ? `stopień ${warning.level}` : `level ${warning.level}`; } function formatWarningValidity(warning: WeatherWarning, language: Language) { if (!warning.validFrom && !warning.validTo) return null; const locale = language === "pl" ? "pl-PL" : "en-GB"; const formatter = new Intl.DateTimeFormat(locale, { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit", }); const from = warning.validFrom ? formatter.format(new Date(warning.validFrom)) : null; const to = warning.validTo ? formatter.format(new Date(warning.validTo)) : null; if (from && to) return language === "pl" ? `od ${from} do ${to}` : `from ${from} to ${to}`; if (from) return language === "pl" ? `od ${from}` : `from ${from}`; return language === "pl" ? `do ${to}` : `until ${to}`; } function buildWarningPayload(preference: WarningPushSubscription, warning: WeatherWarning) { const province = formatProvinceName(preference.province, preference.language); const title = warning.title || (preference.language === "pl" ? "Ostrzeżenie meteorologiczne" : "Weather warning"); const validity = formatWarningValidity(warning, preference.language); const level = getWarningLevelLabel(warning, preference.language); const bodyParts = preference.language === "pl" ? [`${province}: ${level}`, validity, warning.probability !== null ? `prawdopodobieństwo ${warning.probability}%` : null] : [`${province}: ${level}`, validity, warning.probability !== null ? `${warning.probability}% probability` : null]; return { title: `IMGW: ${title}`, body: bodyParts.filter(Boolean).join(" · "), url: "/warnings", warningId: warning.id, }; } export async function sendWarningNotification(preference: WarningPushSubscription, warning: WeatherWarning) { ensureWebPushConfigured(); const payload = JSON.stringify(buildWarningPayload(preference, warning)); await webPush.sendNotification(preference.subscription as PushSubscription, payload); } export async function sendTestNotification(preference: WarningPushSubscription) { ensureWebPushConfigured(); const province = formatProvinceName(preference.province, preference.language); const payload = JSON.stringify({ title: preference.language === "pl" ? "wtr.: test powiadomień IMGW" : "wtr.: IMGW notification test", body: preference.language === "pl" ? `Powiadomienia są aktywne dla województwa: ${province}.` : `Notifications are active for: ${province}.`, url: "/settings", }); await webPush.sendNotification(preference.subscription as PushSubscription, payload); }