- Implemented FormNewMail for creating new email entries in the queue. - Enhanced AdminPotenziali page layout for better responsiveness. - Added Coda component to manage user email queues, including adding, removing, and clearing emails. - Updated API routes for managing email queue operations, including adding, removing, and clearing user email queues. - Introduced new mail templates and integrated them into the mailer service. - Improved error handling in the email queue controller and service. - Added Zod validation for event IDs in the utility types.
306 lines
7.6 KiB
TypeScript
306 lines
7.6 KiB
TypeScript
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<EmailsIdEmail>(),
|
|
}),
|
|
)
|
|
.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<MailsTemplates>(),
|
|
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<EventQueueUpdate>(),
|
|
}),
|
|
)
|
|
.mutation(async ({ input }) => {
|
|
return await updateEvent(input.eventId, input.data);
|
|
}),
|
|
});
|