106 lines
4.5 KiB
TypeScript
106 lines
4.5 KiB
TypeScript
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";
|
|
import type { WeatherBrief } from "@/lib/weather-brief";
|
|
|
|
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);
|
|
}
|
|
|
|
export async function sendMorningBriefNotification(preference: WarningPushSubscription, brief: WeatherBrief) {
|
|
ensureWebPushConfigured();
|
|
const payload = JSON.stringify({
|
|
title: preference.language === "pl" ? "wtr.: brief dnia" : "wtr.: daily brief",
|
|
body: brief.pushBody,
|
|
url: "/",
|
|
});
|
|
await webPush.sendNotification(preference.subscription as PushSubscription, payload);
|
|
}
|
|
|
|
export async function sendTomorrowBriefNotification(preference: WarningPushSubscription, brief: WeatherBrief) {
|
|
ensureWebPushConfigured();
|
|
const payload = JSON.stringify({
|
|
title: preference.language === "pl" ? "wtr.: jutro" : "wtr.: tomorrow",
|
|
body: brief.pushBody,
|
|
url: "/",
|
|
});
|
|
await webPush.sendNotification(preference.subscription as PushSubscription, payload);
|
|
}
|