infoalloggi-monorepo/apps/infoalloggi/src/server/api/routers/interests.ts
Marco Pedone e5c0ca04ee feat: add PDF generation and email sending for printable announcements
- Updated package.json to include html2canvas-pro and jsPDF for PDF generation.
- Enhanced SchedaAnnuncioStampabile component to generate a PDF from HTML and send it via email.
- Implemented new API endpoint for sending the generated PDF as an email attachment.
- Refactored existing email sending logic across various controllers to use a unified template structure.
- Improved error handling and logging for email sending processes.
2025-11-19 17:18:28 +01:00

158 lines
4 KiB
TypeScript

import { TRPCError } from "@trpc/server";
import z from "zod/v4";
import { env } from "~/env";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { db } from "~/server/db";
import {
AddIntrest,
GetUserInterests,
HasUserInterest,
RemoveInterest,
} from "~/server/services/interests.service";
import { NewMail } from "~/server/services/mailer";
import { findUser_byId_MINI } from "~/server/services/user.service";
import { zAnnuncioId, zUserId } from "~/server/utils/zod_types";
import { withImages, withVideos } from "~/utils/kysely-helper";
export const intrestsRouter = createTRPCRouter({
addIntrest: protectedProcedure
.input(
z.object({
annuncioId: zAnnuncioId,
/**Optional */
userId: zUserId.optional(),
}),
)
.mutation(async ({ ctx, input }) => {
const userId = input.userId || ctx.session.id;
await AddIntrest({
annuncioId: input.annuncioId,
userId,
});
const annuncio = await db
.selectFrom("annunci")
.select(["id", "codice", "titolo_it"])
.where("id", "=", input.annuncioId)
.executeTakeFirst();
if (!annuncio) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Annuncio not found",
});
}
const user = await findUser_byId_MINI({
userId,
});
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
await NewMail({
template: {
mailType: "generic",
props: {
link: {
href: `${env.BASE_URL}/area-riservata/admin/user-view/ricerca/${userId}`,
label: "Visualizza utente",
},
testo: `L'utente ${user.username} (${user.email}) ha mostrato interesse per l'annuncio "${annuncio.titolo_it}" (Codice: ${annuncio.codice}).`,
title: "Nuovo interesse per un annuncio",
},
},
mail: {
subject: "Utente interessato ad un annuncio",
to: "web@infoalloggi.it",
},
userId,
});
return { success: true };
}),
getUserInterests: protectedProcedure
.input(
z.object({
userId: zUserId.optional(),
}),
)
.query(async ({ input, ctx }) => {
return await GetUserInterests(input.userId || ctx.session.id);
}),
getUserInterestsAnnunci: protectedProcedure
.input(
z.object({
userId: zUserId.optional(),
}),
)
.query(async ({ input, ctx }) => {
try {
const interests = await GetUserInterests(
input.userId || ctx.session.id,
);
if (!interests || interests.length === 0) {
return [];
}
const annunci = await db
.selectFrom("annunci")
.select((_eb) => [
"annunci.id",
"annunci.codice",
"annunci.comune",
"annunci.provincia",
"annunci.prezzo",
"annunci.consegna",
"annunci.numero_camere",
"annunci.mq",
"annunci.tipo",
"annunci.titolo_it",
"annunci.titolo_en",
"annunci.desc_en",
"annunci.desc_it",
"annunci.modificato_il",
"annunci.stato",
"annunci.external_videos",
"annunci.media_updated_at",
"annunci.homepage",
"annunci.web",
withImages(),
withVideos(),
])
.where("web", "=", true)
.where("stato", "!=", "Sospeso")
.where("annunci.id", "in", interests)
.execute();
return annunci;
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore durante il recupero degli annunci: ${(e as Error).message}`,
});
}
}),
hasUserInterest: protectedProcedure
.input(
z.object({
annuncioId: zAnnuncioId,
userId: zUserId.optional(),
}),
)
.query(async ({ ctx, input }) => {
return await HasUserInterest({
annuncioId: input.annuncioId,
userId: input.userId || ctx.session.id,
});
}),
removeIntrest: protectedProcedure
.input(
z.object({
annuncioId: zAnnuncioId,
userId: zUserId.optional(),
}),
)
.mutation(async ({ ctx, input }) => {
return await RemoveInterest({
annuncioId: input.annuncioId,
userId: input.userId || ctx.session.id,
});
}),
});