mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
"use server";
|
|
|
|
import { prisma } from "@/src/lib/prisma";
|
|
import { requireAuthWithData, isManager, isEmployee } from "@/src/lib/auth/getUser";
|
|
import type { NotificationType } from "../lib/notification-types";
|
|
|
|
const MAX_ITEMS = 20;
|
|
|
|
export interface NotificationDTO {
|
|
id: string;
|
|
type: NotificationType;
|
|
title: string;
|
|
body: string;
|
|
linkUrl: string | null;
|
|
readAt: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface GetNotificationsResult {
|
|
items: NotificationDTO[];
|
|
unreadCount: number;
|
|
}
|
|
|
|
const EMPTY: GetNotificationsResult = { items: [], unreadCount: 0 };
|
|
|
|
/**
|
|
* Lädt die letzten Benachrichtigungen des eingeloggten Nutzers plus die
|
|
* Anzahl ungelesener. Nur die Studio-Seite (Manager/Employee) hat ein
|
|
* Notification-Center - für die User-Rolle (oder ohne Session) wird bewusst
|
|
* ein leeres Ergebnis zurückgegeben, damit die Bell-Komponente ohne
|
|
* Sonderfall gerendert werden kann.
|
|
*/
|
|
export async function getNotifications(): Promise<GetNotificationsResult> {
|
|
let userData;
|
|
try {
|
|
userData = await requireAuthWithData();
|
|
} catch {
|
|
return EMPTY;
|
|
}
|
|
|
|
if (!isManager(userData) && !isEmployee(userData)) return EMPTY;
|
|
|
|
try {
|
|
const [items, unreadCount] = await Promise.all([
|
|
prisma.notification.findMany({
|
|
where: { recipientId: userData.id },
|
|
orderBy: { createdAt: "desc" },
|
|
take: MAX_ITEMS,
|
|
}),
|
|
prisma.notification.count({
|
|
where: { recipientId: userData.id, readAt: null },
|
|
}),
|
|
]);
|
|
|
|
return {
|
|
items: items.map((n) => ({
|
|
id: n.id,
|
|
type: n.type as NotificationType,
|
|
title: n.title,
|
|
body: n.body,
|
|
linkUrl: n.linkUrl,
|
|
readAt: n.readAt?.toISOString() ?? null,
|
|
createdAt: n.createdAt.toISOString(),
|
|
})),
|
|
unreadCount,
|
|
};
|
|
} catch (error) {
|
|
console.error("getNotifications fehlgeschlagen:", error);
|
|
return EMPTY;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Markiert eine einzelne Benachrichtigung als gelesen. Die recipientId steht
|
|
* in der WHERE-Bedingung (updateMany), damit niemand fremde Benachrichtigungen
|
|
* manipulieren kann - ein Treffer 0 ist kein Fehler.
|
|
*/
|
|
export async function markNotificationRead(id: string): Promise<{ success: boolean }> {
|
|
let userData;
|
|
try {
|
|
userData = await requireAuthWithData();
|
|
} catch {
|
|
return { success: false };
|
|
}
|
|
if (!isManager(userData) && !isEmployee(userData)) return { success: false };
|
|
|
|
try {
|
|
await prisma.notification.updateMany({
|
|
where: { id, recipientId: userData.id, readAt: null },
|
|
data: { readAt: new Date() },
|
|
});
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error("markNotificationRead fehlgeschlagen:", error);
|
|
return { success: false };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Markiert alle ungelesenen Benachrichtigungen des Nutzers als gelesen.
|
|
*/
|
|
export async function markAllNotificationsRead(): Promise<{ success: boolean }> {
|
|
let userData;
|
|
try {
|
|
userData = await requireAuthWithData();
|
|
} catch {
|
|
return { success: false };
|
|
}
|
|
if (!isManager(userData) && !isEmployee(userData)) return { success: false };
|
|
|
|
try {
|
|
await prisma.notification.updateMany({
|
|
where: { recipientId: userData.id, readAt: null },
|
|
data: { readAt: new Date() },
|
|
});
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error("markAllNotificationsRead fehlgeschlagen:", error);
|
|
return { success: false };
|
|
}
|
|
}
|