SPB Git

Repository Public

notify.ts 1.7 KB 32 lines typescript

1import { j, sql, type Tx } from "../db";2import { nowMs } from "../env";34type Q = Tx | typeof sql;56export type NotificationKind =7  | "request_received" | "booking_confirmed" | "request_declined" | "reschedule_declined" | "proposal" | "booking_cancelled"8  | "booking_rescheduled" | "request_expired" | "reminder"9  | "admin_new_request" | "admin_new_booking" | "admin_visitor_cancelled" | "admin_request_withdrawn" | "admin_reschedule_request" | "admin_proposal_accepted"10  | "test";1112export interface EnqueueInput {13  userId: string; kind: NotificationKind; dedupKey: string; to: string; locale: string;14  requestId?: string | null; bookingId?: string | null; runAt?: Date; payload?: Record<string, unknown>;15}1617/**18 * Outbox: the job is written in the same transaction as the change it announces, so a notification exists if and19 * only if the change was committed. `dedup_key` makes a second enqueue of the same event a no-op.20 */21export async function enqueue(q: Q, n: EnqueueInput): Promise<void> {22  await q`INSERT INTO notification_jobs (user_id, kind, dedup_key, to_email, locale, request_id, booking_id, run_at, payload)23          VALUES (${n.userId}, ${n.kind}, ${n.dedupKey}, ${n.to}, ${n.locale === "en" ? "en" : "fr"}, ${n.requestId ?? null}, ${n.bookingId ?? null}, ${n.runAt ?? new Date(nowMs())}, ${j(n.payload ?? {})})24          ON CONFLICT (dedup_key) DO NOTHING`;25}2627/** Where the owner receives alerts: the address set in the settings, else the account's e-mail. */28export async function ownerEmail(q: Q, userId: string): Promise<string | null> {29  const [r] = await q<{ email: string | null }[]>`SELECT COALESCE(s.notify_email, u.email)::text AS email FROM users u LEFT JOIN settings s ON s.user_id = u.id WHERE u.id = ${userId}`;30  return r?.email ?? null;31}32
Files main
Menu

Theme