import { eq } from "drizzle-orm"; import { db } from "@/lib/db"; import logger from "@/lib/logger"; import { notifications, users } from "./schema"; import { sendPush } from "./push"; type NotifyPayload = { title: string; body: string; url?: string; channels?: ("push" | "inapp" | "ntfy")[]; }; export async function notify(userId: string, payload: NotifyPayload) { const channels = payload.channels ?? ["push", "inapp"]; const [user] = await db .select({ notifPush: users.notifPush, notifInApp: users.notifInApp, notifNtfy: users.notifNtfy, }) .from(users) .where(eq(users.id, userId)) .limit(1); if (!user) return; const pushEnabled = process.env["VAPID_PUBLIC_KEY"] && process.env["VAPID_PRIVATE_KEY"]; if (channels.includes("push") && user.notifPush && pushEnabled) { await sendPush(userId, payload).catch((err) => logger.error({ err }, "push channel failed")); } if (channels.includes("inapp") && user.notifInApp) { await db.insert(notifications).values({ userId, title: payload.title, body: payload.body, url: payload.url, }); } if (channels.includes("ntfy") && user.notifNtfy) { const ntfyUrl = process.env["NTFY_URL"]; const ntfyTopic = process.env["NTFY_TOPIC"]; if (ntfyUrl && ntfyTopic) { await fetch(`${ntfyUrl}/${ntfyTopic}`, { method: "POST", headers: { Title: payload.title, "Content-Type": "text/plain" }, body: payload.body, }).catch((err) => logger.error({ err }, "ntfy delivery failed")); } } }