import "server-only"; import { and, asc, eq, gt, isNull, lte, sql } from "drizzle-orm"; import { getDb } from "@/db"; import { guides, users } from "@/db/schema"; import type { OutboxEventLike } from "@/lib/events/invalidation"; import { createNotifications } from "@/lib/notifications/repository"; export async function sendGuidePublicationNotifications(event: OutboxEventLike) { const publishedAt = typeof event.payload.publishedAt === "string" ? new Date(event.payload.publishedAt) : null; if (event.eventType !== "guide.published" || event.topic !== "notifications:guide" || !publishedAt || Number.isNaN(publishedAt.getTime())) { throw new Error("Invalid guide publication notification event"); } const db = getDb(); const [guide] = await db.select({ name: guides.name, slug: guides.slug }).from(guides) .where(and(eq(guides.id, event.aggregateId), eq(guides.isPublic, true), isNull(guides.trashedAt))).limit(1); if (!guide) return; let cursor: string | undefined; while (true) { const recipients: { id: string }[] = await db.select({ id: users.id }).from(users) .where(and(sql`${users.banned} is not true`, lte(users.createdAt, publishedAt), cursor === undefined ? undefined : gt(users.id, cursor))) .orderBy(asc(users.id)).limit(100); if (!recipients.length) return; await createNotifications(recipients.map(user => ({ userId: user.id, url: `/${guide.slug}` })), { eventKey: `guide:${event.aggregateId}:published`, kind: "guide_published", title: "เผยแพร่ Guide ใหม่", body: `อ่าน Guide ${guide.name} ได้แล้ว`, }); cursor = recipients[recipients.length - 1].id; } }