Add Web Push warning notification backend
This commit is contained in:
71
lib/push-service.ts
Normal file
71
lib/push-service.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user