mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 19:51:17 +00:00
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
/**
|
|
* Versand von Web-Push-Benachrichtigungen an einen Empfänger (alle seine
|
|
* Geräte). Analog zu sendMail: wirft NIE, ein Push-Fehler darf keinen
|
|
* aufrufenden Vorgang (Buchung, Storno, ...) kippen.
|
|
*/
|
|
|
|
import { WebPushError } from "web-push";
|
|
import { prisma } from "@/src/lib/prisma";
|
|
import { getWebPush, isPushConfigured } from "./web-push";
|
|
|
|
export interface PushPayload {
|
|
title: string;
|
|
body: string;
|
|
/** Ziel-URL, die beim Klick auf die Benachrichtigung geöffnet wird. */
|
|
url?: string;
|
|
}
|
|
|
|
/**
|
|
* Sendet eine Push-Benachrichtigung an alle registrierten Geräte eines
|
|
* Empfängers (Manager-, Employee- oder User-ID). Tote Subscriptions (Browser
|
|
* hat sie serverseitig verworfen, HTTP 404/410) werden dabei aus der DB
|
|
* entfernt.
|
|
*/
|
|
export async function sendPushToRecipient(
|
|
recipientId: string,
|
|
payload: PushPayload
|
|
): Promise<void> {
|
|
if (!isPushConfigured()) return;
|
|
|
|
try {
|
|
const subscriptions = await prisma.pushSubscription.findMany({
|
|
where: { recipientId },
|
|
});
|
|
if (subscriptions.length === 0) return;
|
|
|
|
const webpush = getWebPush();
|
|
const body = JSON.stringify(payload);
|
|
|
|
const results = await Promise.allSettled(
|
|
subscriptions.map((sub) =>
|
|
webpush.sendNotification(
|
|
{
|
|
endpoint: sub.endpoint,
|
|
keys: { p256dh: sub.p256dh, auth: sub.auth },
|
|
},
|
|
body
|
|
)
|
|
)
|
|
);
|
|
|
|
const deadEndpoints: string[] = [];
|
|
results.forEach((result, i) => {
|
|
if (result.status === "rejected") {
|
|
const error = result.reason;
|
|
// 404/410 = Subscription existiert clientseitig nicht mehr
|
|
// (Browser-Daten gelöscht, App deinstalliert, ...) - dauerhaft tot.
|
|
if (
|
|
error instanceof WebPushError &&
|
|
(error.statusCode === 404 || error.statusCode === 410)
|
|
) {
|
|
deadEndpoints.push(subscriptions[i].endpoint);
|
|
} else {
|
|
console.error("sendPushToRecipient: Zustellung fehlgeschlagen:", error);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (deadEndpoints.length > 0) {
|
|
await prisma.pushSubscription
|
|
.deleteMany({ where: { endpoint: { in: deadEndpoints } } })
|
|
.catch((error) => console.error("sendPushToRecipient: Aufräumen fehlgeschlagen:", error));
|
|
}
|
|
} catch (error) {
|
|
console.error("sendPushToRecipient fehlgeschlagen:", error);
|
|
}
|
|
}
|