import { Injectable } from "@nestjs/common"; import { EmailNotificationServicio, EmailNotificationStatus, EmailNotificationType, } from "@jorgecuadros/database"; import { PrismaService } from "../prisma/prisma.service"; import { AttemptStatus } from "./notification.types"; /** * The single writer for `email_notification_log`. * * Extracted out of `NotificationsService` so the renewal sweep can write the * same rows as the four bulk jobs without pulling that service (and its four * job pipelines) into `RenewalsModule`. Every outbound email the platform * sends goes through here, which is what makes /notificaciones' "Registro de * envíos" complete rather than per-feature. */ export interface NotificationLogEntry { notificationType: EmailNotificationType; servicio: EmailNotificationServicio; /** Defaults to now(). Pass it when the row must line up exactly with * another record of the same send (the renewal sweep pins it to * `RenewalNotice.sentAt`). */ sendDate?: Date; /** Type-dependent discriminator — see the `level` doc on the Prisma model. * 0/1 for ACCOUNT_STATUS, the generation for RENEWAL_NOTICE. */ level?: number | null; customerId: string | null; customerName: string; customerEmail: string; subject: string; bodySnapshot: string; bodyRequestUrl?: string; status: AttemptStatus; debug: boolean; providerMessageId?: string; providerResponse?: string; error?: string; } /** `providerResponse` is a VARCHAR(191); anything longer is a provider dump * we only need the head of. Errors go to the TEXT `error` column and get * the 4k cap the schema documents. */ const PROVIDER_RESPONSE_MAX = 180; const ERROR_MAX = 4096; @Injectable() export class NotificationLogService { constructor(private readonly prisma: PrismaService) {} async record(entry: NotificationLogEntry): Promise { await this.prisma.emailNotificationLog.create({ data: { notificationType: entry.notificationType, servicio: entry.servicio, ...(entry.sendDate && { sendDate: entry.sendDate }), level: entry.level ?? null, customerId: entry.customerId, customerName: entry.customerName, customerEmail: entry.customerEmail, subject: entry.subject, bodySnapshot: entry.bodySnapshot, bodyRequestUrl: entry.bodyRequestUrl ?? null, debug: entry.debug, providerMessageId: entry.providerMessageId ?? null, providerResponse: entry.providerResponse?.slice(0, PROVIDER_RESPONSE_MAX) ?? null, status: entry.status as EmailNotificationStatus, error: entry.error?.slice(0, ERROR_MAX) ?? null, }, }); } }