import { TRPCError } from "@trpc/server"; import { z } from "zod/v4"; import type { EmailsIdEmail } from "~/schemas/public/Emails"; import type { EventQueueUpdate } from "~/schemas/public/EventQueue"; import type { TestiEStringheStingaId } from "~/schemas/public/TestiEStringhe"; import { adminProcedure, createTRPCRouter, protectedProcedure, publicProcedure, } from "~/server/api/trpc"; import { addEventToQueue, clearUserEmailQueue, removeEventFromQueue, updateEvent, userEmailQueue, } from "~/server/controllers/event_queue.controller"; import { UtenteInteressatoAdAnnuncioEmail } from "~/server/controllers/servizio.controller"; import { db } from "~/server/db"; import { deleteAllUserEmails, deleteEmail, getEmails, } from "~/server/services/email.service"; import { type MailsTemplates, NewMail } from "~/server/services/mailer"; import { CONDIZIONI_DEFAULT } from "~/server/services/prezziario.service"; import { genPdfSchedaAnnuncioBase64 } from "~/server/services/puppeteer.service"; import { zAnnuncioId, zEventId, zOrdineId, zUserId, } from "~/server/utils/zod_types"; export const comunicazioniRouter = createTRPCRouter({ getEmails: protectedProcedure .input(z.object({ userId: zUserId })) .query(async ({ input }) => { return await getEmails({ userId: input.userId, }); }), deleteEmail: adminProcedure .input( z.object({ id_email: z.custom(), }), ) .mutation(async ({ input }) => { return await deleteEmail({ id_email: input.id_email, }); }), deleteAllUserEmails: adminProcedure .input(z.object({ userId: zUserId })) .mutation(async ({ input }) => { return await deleteAllUserEmails({ userId: input.userId, }); }), sendEmailContatto: publicProcedure .input( z.object({ cognome: z.string(), email: z.email(), messaggio: z.string(), nome: z.string(), telefono: z.string(), }), ) .mutation(async ({ input }) => { await NewMail({ template: { mailType: "contattoAdminEmail", props: { cognome: input.cognome, email: input.email, messaggio: input.messaggio, nome: input.nome, telefono: input.telefono, }, }, mail: { subject: `Richiesta di contatto da ${input.nome}`, to: "web@infoalloggi.it", }, }); return { success: true }; }), sendSchedaAnnuncioEmail: adminProcedure .input( z.object({ id: zAnnuncioId, codice: z.string(), numero: z.string(), nominativo: z.string(), indirizzo: z.string(), to: z.email(), }), ) .mutation(async ({ input }) => { try { const scheda = await genPdfSchedaAnnuncioBase64(input.id); await NewMail({ template: { mailType: "emailSchedaContatto", props: { nominativo: input.nominativo, telefono: input.numero, indirizzo: input.indirizzo, }, }, mail: { subject: `Scheda Annuncio ${input.codice} - Infoalloggi.it`, to: input.to, attachments: [ { filename: `scheda annuncio ${input.codice}.pdf`, content: scheda, encoding: "base64", contentType: "application/pdf", }, ], }, }); await NewMail({ template: { mailType: "generic", props: { title: `Ricevuta invio Scheda ${input.codice} a ${input.to}`, testo: `La scheda dell'annuncio ${input.codice} รจ stata inviata a ${input.to} con successo.`, }, }, mail: { subject: `Ricevuta invio Scheda ${input.codice} a ${input.to}`, to: "web@infoalloggi.it", }, }); } catch (e) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Errore nell'invio dell'email: ${(e as Error).message}`, cause: e instanceof Error ? e : undefined, }); } }), sendUtenteInteressatoAdAnnuncioEmail: publicProcedure .input( z.object({ codice: z.string(), email: z.string(), messaggio: z.string(), nome: z.string(), telefono: z.string(), }), ) .mutation(async ({ input }) => { return await UtenteInteressatoAdAnnuncioEmail({ ...input, }); }), sendRicevutaECondizioniEmail: adminProcedure .input( z.object({ ordineId: zOrdineId, }), ) .mutation(async ({ input }) => { try { const ordine = await db .selectFrom("ordini") .selectAll() .innerJoin("users", "users.id", "ordini.userid") .select(["users.email"]) .innerJoin("prezziario", "prezziario.idprezziario", "ordini.packid") .select(["prezziario.testo_condizioni"]) .where("ordine_id", "=", input.ordineId) .executeTakeFirstOrThrow(() => { throw new Error(`Ordine not found - ordineId: ${input.ordineId}`); }); await addEventToQueue({ data: { tipo: "email", data: { template: { mailType: "servizioAttivato", }, mail: { subject: "Pagamento Confermato - Acconto Infoalloggi.it", to: ordine.email, attachments: [ { filename: `condizioni_contrattuali_${ordine.created_at.toISOString()}.pdf`, generate: { type: "condizioni", stringId: (ordine.testo_condizioni || CONDIZIONI_DEFAULT) as TestiEStringheStingaId, }, encoding: "base64", contentType: "application/pdf", }, { filename: `ricevuta_cortesia_${ordine.ordine_id}.pdf`, generate: { type: "ricevuta_cortesia", ordineId: ordine.ordine_id, }, encoding: "base64", contentType: "application/pdf", }, ], }, userId: ordine.userid, }, }, lock_expires: new Date(Date.now() + 3000), // Lock for 30 sec }); } catch (e) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Errore nell'aggiunta dell'email alla coda: ${(e as Error).message}`, cause: e instanceof Error ? e : undefined, }); } }), getUserQueuedEmails: protectedProcedure .input(z.object({ userId: zUserId })) .query(async ({ input }) => { return await userEmailQueue(input.userId); }), addToUserEmailQueue: adminProcedure .input( z.object({ lock_expires: z.date(), userId: zUserId, template: z.custom(), subject: z.string(), }), ) .mutation(async ({ input }) => { try { const user = await db .selectFrom("users") .selectAll() .where("id", "=", input.userId) .executeTakeFirstOrThrow(() => { throw new Error(`User with id ${input.userId} not found`); }); await addEventToQueue({ data: { tipo: "email", data: { template: input.template, mail: { to: user.email, subject: input.subject, }, userId: input.userId, }, }, lock_expires: input.lock_expires, }); return { success: true }; } catch (error) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Errore nell'aggiunta dell'email alla coda: ${ error instanceof Error ? error.message : "Errore sconosciuto" }`, cause: error instanceof Error ? error : undefined, }); } }), removeFromUserEmailQueue: adminProcedure .input( z.object({ eventId: zEventId, }), ) .mutation(async ({ input }) => { return await removeEventFromQueue(input.eventId); }), clearUserEmailQueue: adminProcedure .input(z.object({ userId: zUserId })) .mutation(async ({ input }) => { return await clearUserEmailQueue(input.userId); }), updateEvent: adminProcedure .input( z.object({ eventId: zEventId, data: z.custom(), }), ) .mutation(async ({ input }) => { return await updateEvent(input.eventId, input.data); }), });