import { z } from "zod"; import prisma from "~/server/utils/prisma"; const ENV = useRuntimeConfig(); function requireApiKey(event) { const headers = getRequestHeaders(event); const provided = headers["x-api-key"] || headers["X-API-Key"]; const expected = ENV.notificationApiKey; if (!expected) { throw createError({ statusCode: 500, statusMessage: "Notification API is not configured (missing NUXT_NOTIFICATION_API_KEY environment variable)", }); } if (!provided || provided !== expected) { throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); } } const listSchema = z.object({ page: z .string() .optional() .transform((v) => (v ? parseInt(v) : 1)) .default("1"), limit: z .string() .optional() .transform((v) => (v ? parseInt(v) : 20)) .default("20"), status: z.string().optional(), priority: z.string().optional(), category: z.string().optional(), search: z.string().optional(), sortBy: z.string().default("created_at"), sortOrder: z.enum(["asc", "desc"]).default("desc"), }); export default defineEventHandler(async (event) => { try { requireApiKey(event); const params = listSchema.parse(getQuery(event) || {}); const where = {}; if (params.status) where.status = params.status; if (params.priority) where.priority = params.priority; if (params.category) where.notification_categories = { value: params.category }; if (params.search) { where.OR = [ { title: { contains: params.search } }, { email_subject: { contains: params.search } }, { push_title: { contains: params.search } }, ]; } const total = await prisma.notifications.count({ where }); const notifications = await prisma.notifications.findMany({ where, select: { id: true, title: true, priority: true, status: true, delivery_type: true, scheduled_at: true, created_at: true, notification_categories: { select: { name: true } }, notification_channels: { select: { channel_type: true } }, notification_recipients: { select: { status: true } }, }, orderBy: { [params.sortBy]: params.sortOrder }, skip: (params.page - 1) * params.limit, take: params.limit, }); const items = notifications.map((n) => { const totalRecipients = n.notification_recipients.length; const delivered = n.notification_recipients.filter( (r) => r.status === "delivered" ).length; const successRate = totalRecipients > 0 ? Math.round((delivered / totalRecipients) * 100) : 0; return { id: n.id, title: n.title, category: n.notification_categories?.name || "Uncategorized", channels: n.notification_channels.map((c) => c.channel_type), priority: n.priority, status: n.status, recipients: totalRecipients, successRate, createdAt: n.created_at, }; }); const totalPages = Math.ceil(total / params.limit); return { success: true, data: { notifications: items, pagination: { page: params.page, totalPages, totalItems: total, hasMore: params.page < totalPages, }, }, }; } catch (error) { if (error instanceof z.ZodError) { throw createError({ statusCode: 400, statusMessage: "Invalid query parameters", data: error.errors, }); } if (error.statusCode) throw error; throw createError({ statusCode: 500, statusMessage: "Failed to fetch notifications", data: { error: error.message }, }); } });