owltide/server/web-push.ts

72 lines
2.2 KiB
TypeScript
Raw Normal View History

/*
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
SPDX-License-Identifier: AGPL-3.0-or-later
*/
import * as fs from "node:fs/promises";
import type { H3Event } from "h3";
import webPush from "web-push";
import { readSubscriptions, writeSubscriptions } from "~/server/database";
let cachedVapidDetails: {
subject: string,
publicKey: string,
privateKey: string,
} | undefined;
async function useVapidDetails(event: H3Event) {
if (cachedVapidDetails) {
return cachedVapidDetails;
}
const runtimeConfig = useRuntimeConfig(event);
if (!runtimeConfig.vapidSubject) throw new Error("NUXT_VAPID_SUBJECT not set.")
if (!runtimeConfig.public.vapidPublicKey) throw new Error("NUXT_PUBLIC_VAPID_PUBLIC_KEY not set.")
if (!runtimeConfig.vapidPrivateKeyFile) throw new Error("NUXT_VAPID_PRIVATE_KEY_FILE not set.")
return cachedVapidDetails = {
subject: runtimeConfig.vapidSubject,
publicKey: runtimeConfig.public.vapidPublicKey,
privateKey: await fs.readFile(runtimeConfig.vapidPrivateKeyFile, "utf-8"),
}
}
export async function sendPush(event: H3Event, title: string, body: string) {
const vapidDetails = await useVapidDetails(event);
const payload = JSON.stringify({ title, body });
const subscriptions = await readSubscriptions();
console.log(`Sending "${payload}" to ${subscriptions.length} subscribers`);
const removeIndexes = [];
for (let index = 0; index < subscriptions.length; index += 1) {
const subscription = subscriptions[index];
if (subscription.type !== "push")
continue;
try {
await webPush.sendNotification(
subscription.push as webPush.PushSubscription,
payload,
{
TTL: 3600,
urgency: "high",
vapidDetails,
}
)
} catch (err: any) {
if (err?.statusCode === 410) {
removeIndexes.push(index);
} else {
console.error("Received error sending push notice:", err.message, err?.statusCode)
console.error(err);
}
}
}
if (removeIndexes.length) {
console.log(`Removing indexes ${removeIndexes} from subscriptions`)
removeIndexes.reverse();
for (const index of removeIndexes) {
subscriptions.splice(index, 1);
}
await writeSubscriptions(subscriptions);
}
console.log("Push notices sent");
}