mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 19:51:17 +00:00
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
/**
|
|
* Kern-Helfer zum Erzeugen von In-App-Benachrichtigungen.
|
|
*
|
|
* Prinzipien:
|
|
* - Wirft NIE. Ein fehlgeschlagenes Insert (DB weg, o.ä.) darf niemals eine
|
|
* Buchung, Stornierung oder Kursabsage kippen. Alles in try/catch, Fehler
|
|
* landen nur im Log - gleiches Pattern wie der Mailversand.
|
|
* - Respektiert die pro-Business-Opt-outs aus Business.notificationSettings,
|
|
* wenn ein businessId übergeben wird. Ohne businessId (kein Business-Kontext)
|
|
* wird immer zugestellt.
|
|
* - Empfänger ist die Manager-ID des Business (kein Employee-Fanout im MVP).
|
|
* - Sendet zusätzlich Web-Push an alle Geräte des Empfängers (zweiter
|
|
* Zustellkanal für dieselbe Benachrichtigung, kein eigenes Opt-out - wer
|
|
* Push aktiviert hat, bekommt dieselben Typen wie in-app).
|
|
*/
|
|
|
|
import { prisma } from "@/src/lib/prisma";
|
|
import { sendPushToRecipient } from "@/src/lib/push/send-push";
|
|
import {
|
|
NOTIFICATION_TYPES,
|
|
resolveNotificationSettings,
|
|
type NotificationType,
|
|
} from "./notification-types";
|
|
|
|
interface CreateNotificationInput {
|
|
recipientId: string;
|
|
type: NotificationType;
|
|
title: string;
|
|
body: string;
|
|
linkUrl?: string;
|
|
}
|
|
|
|
/**
|
|
* Prüft, ob ein Benachrichtigungstyp für ein Business aktiv ist.
|
|
* Fällt bei fehlendem Business / fehlendem Key auf den Registry-Default zurück.
|
|
*/
|
|
export async function isNotificationEnabled(
|
|
businessId: string,
|
|
type: NotificationType
|
|
): Promise<boolean> {
|
|
try {
|
|
const business = await prisma.business.findUnique({
|
|
where: { id: businessId },
|
|
select: { notificationSettings: true },
|
|
});
|
|
if (!business) return NOTIFICATION_TYPES[type].defaultOn;
|
|
return resolveNotificationSettings(business.notificationSettings)[type];
|
|
} catch (error) {
|
|
console.error("isNotificationEnabled fehlgeschlagen:", error);
|
|
// Im Zweifel zustellen - eine verpasste Benachrichtigung ist schlimmer
|
|
// als eine, die trotz Opt-out durchrutscht.
|
|
return NOTIFICATION_TYPES[type].defaultOn;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Legt eine einzelne Benachrichtigung an. Gibt true zurück, wenn ein Datensatz
|
|
* geschrieben wurde, false wenn übersprungen (Opt-out) oder fehlgeschlagen.
|
|
*/
|
|
export async function createNotification(input: CreateNotificationInput): Promise<boolean> {
|
|
try {
|
|
await prisma.notification.create({
|
|
data: {
|
|
recipientId: input.recipientId,
|
|
type: input.type,
|
|
title: input.title,
|
|
body: input.body,
|
|
linkUrl: input.linkUrl ?? null,
|
|
},
|
|
});
|
|
|
|
// Fire-and-forget: Push-Fehler dürfen das erfolgreiche In-App-Insert
|
|
// oben nicht rückgängig machen oder den Aufrufer blockieren.
|
|
void sendPushToRecipient(input.recipientId, {
|
|
title: input.title,
|
|
body: input.body,
|
|
url: input.linkUrl,
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("createNotification fehlgeschlagen:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Legt eine Benachrichtigung für den Manager eines Business an - sofern der
|
|
* Typ für dieses Business nicht abgeschaltet wurde.
|
|
*
|
|
* Bewusst fire-and-forget-tauglich: Rückgabe wird von den Aufrufern (Webhook,
|
|
* Storno-Action, ...) nicht ausgewertet, der Aufruf hängt an einem
|
|
* .catch(console.error) und blockiert den Request nicht.
|
|
*/
|
|
export async function notifyBusinessManager(
|
|
businessId: string | null | undefined,
|
|
input: Omit<CreateNotificationInput, "recipientId">
|
|
): Promise<void> {
|
|
try {
|
|
if (!businessId) return;
|
|
|
|
const business = await prisma.business.findUnique({
|
|
where: { id: businessId },
|
|
select: { managerId: true, notificationSettings: true },
|
|
});
|
|
if (!business) return;
|
|
|
|
const enabled = resolveNotificationSettings(business.notificationSettings)[input.type];
|
|
if (!enabled) return;
|
|
|
|
await createNotification({ ...input, recipientId: business.managerId });
|
|
} catch (error) {
|
|
console.error("notifyBusinessManager fehlgeschlagen:", error);
|
|
}
|
|
}
|