infoalloggi-monorepo/apps/infoalloggi/src/server/controllers/annunci.controller.ts

477 lines
9.9 KiB
TypeScript
Raw Normal View History

2025-08-28 18:27:07 +02:00
import { TRPCError } from "@trpc/server";
import { env } from "~/env";
import { getStorageUrl } from "~/lib/storage_utils";
import type {
2025-08-28 18:27:07 +02:00
Annunci,
AnnunciId,
AnnunciUpdate,
} from "~/schemas/public/Annunci";
import type { ImagesRefs } from "~/schemas/public/ImagesRefs";
2025-08-04 17:45:44 +02:00
import type { ServizioServizioId } from "~/schemas/public/Servizio";
import type { VideosRefs } from "~/schemas/public/VideosRefs";
2025-08-28 18:27:07 +02:00
import { db } from "~/server/db";
import { withImages, withVideos } from "~/utils/kysely-helper";
import { revalidate } from "../utils/revalidationHelper";
2025-08-04 17:45:44 +02:00
// const ratelimit = new RateLimiterHandler({
// windowSize: 10,
// maxRequests: 10,
// analytics: true,
// });
export type AnnunciWithMedia = Annunci & {
tipo: string;
images: Pick<ImagesRefs, "img" | "thumb">[];
videos: Pick<VideosRefs, "video" | "thumb">[];
};
2025-08-04 17:45:44 +02:00
export const getAnnunciListHandler = async (): Promise<
2025-08-28 18:27:07 +02:00
Pick<
Annunci,
"id" | "codice" | "titolo_it" | "comune" | "tipo" | "locatore" | "stato"
>[]
2025-08-04 17:45:44 +02:00
> => {
2025-08-28 18:27:07 +02:00
try {
return await db
.selectFrom("annunci")
.select([
"id",
"codice",
"titolo_it",
"comune",
"tipo",
"locatore",
"stato",
])
.execute();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getAllRaw: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export const getCodici_AnnunciHandler = async (): Promise<string[]> => {
2025-08-28 18:27:07 +02:00
try {
const codici = await db
.selectFrom("annunci")
.select("codice")
.where("annunci.web", "=", true)
.where("stato", "!=", "Sospeso")
.execute();
if (!codici) {
return [];
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return codici.map((c) => c.codice);
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getCodici: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export const getAnnunciByCod = async ({
2025-08-28 18:27:07 +02:00
cod,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
cod: string;
}): Promise<AnnunciWithMedia | null> => {
2025-08-28 18:27:07 +02:00
try {
const annuncio = await db
.selectFrom("annunci")
.selectAll()
.select((_eb) => [withImages(), withVideos()])
2025-08-28 18:27:07 +02:00
.where("annunci.web", "=", true)
.where("tipo", "is not", null)
2025-08-28 18:27:07 +02:00
.where("codice", "=", cod)
.executeTakeFirst();
if (!annuncio || annuncio.tipo === null) {
2025-08-28 18:27:07 +02:00
return null;
}
2025-08-04 17:45:44 +02:00
return annuncio as AnnunciWithMedia;
2025-08-28 18:27:07 +02:00
} catch (e) {
throw new TRPCError({
2025-08-29 16:18:32 +02:00
cause: (e as Error).cause,
2025-08-28 18:27:07 +02:00
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getAnnunciByCod: ${(e as Error).message}`,
});
}
};
export const getAnnunciMetaByCod = async ({ cod }: { cod: string }) => {
2025-08-28 18:27:07 +02:00
try {
const annuncio = await db
.selectFrom("annunci")
.select((_eb) => [
"titolo_it",
"desc_it",
withImages(),
"media_updated_at",
])
2025-08-28 18:27:07 +02:00
.where("annunci.web", "=", true)
.where("codice", "=", cod)
.executeTakeFirst();
if (!annuncio) {
return null;
}
const ogImage =
annuncio.images && annuncio.images.length > 0 && annuncio.images[0]
? getStorageUrl({
storageId: annuncio.images[0].img,
params: {
cacheKey: annuncio.media_updated_at?.toISOString(),
media: "image",
},
host: env.BASE_URL,
})
: `${env.BASE_URL}/og.jpg`;
2025-08-28 18:27:07 +02:00
return {
description: annuncio.desc_it || "",
ogImage,
ogUrl: `${env.BASE_URL}/annuncio/${cod}`,
2025-08-29 16:18:32 +02:00
title: annuncio.titolo_it || "",
2025-08-28 18:27:07 +02:00
};
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getAnnunciMetaByCod: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export const getAnnunciById_rawImgUrls = async ({
2025-08-28 18:27:07 +02:00
id,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
id: AnnunciId;
}): Promise<AnnunciWithMedia | null> => {
2025-08-28 18:27:07 +02:00
try {
const annuncio = await db
.selectFrom("annunci")
.selectAll()
.select((_eb) => [withImages(), withVideos()])
2025-08-28 18:27:07 +02:00
.where("id", "=", id)
.where("tipo", "is not", null)
2025-08-28 18:27:07 +02:00
.executeTakeFirst();
if (!annuncio || annuncio.tipo === null) {
2025-08-28 18:27:07 +02:00
throw new TRPCError({
code: "NOT_FOUND",
message: "Annuncio non trovato",
});
}
2025-08-04 17:45:44 +02:00
return annuncio as AnnunciWithMedia;
2025-08-28 18:27:07 +02:00
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getAnnuncioById : ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export type AnnuncioRicercaWPosition = AnnuncioRicerca & {
2025-08-28 18:27:07 +02:00
lat_secondario: string | null;
lon_secondario: string | null;
2025-08-04 17:45:44 +02:00
};
export const get_AnnunciPositionsHandler = async ({
2025-08-28 18:27:07 +02:00
tipologia,
comune,
consegna,
minBudget,
maxBudget,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
tipologia?: string;
comune?: string;
consegna?: number[];
minBudget?: number;
maxBudget?: number;
2025-08-04 17:45:44 +02:00
}): Promise<AnnuncioRicercaWPosition[]> => {
2025-08-28 18:27:07 +02:00
try {
let query = db
.selectFrom("annunci")
.select((_eb) => [
2025-08-28 18:27:07 +02:00
"id",
"codice",
"comune",
"provincia",
"prezzo",
"consegna",
"numero_camere",
"mq",
"tipo",
"titolo_it",
"titolo_en",
"desc_en",
"desc_it",
"web",
2025-08-28 18:27:07 +02:00
"modificato_il",
"stato",
"external_videos",
2025-08-28 18:27:07 +02:00
"lon_secondario",
"lat_secondario",
"media_updated_at",
"homepage",
withImages(),
withVideos(),
2025-08-28 18:27:07 +02:00
])
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
.where("web", "=", true)
.where("stato", "!=", "Sospeso")
.orderBy("id", "asc");
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (tipologia && tipologia !== "") {
query = query.where("tipo", "=", tipologia);
}
if (comune && comune !== "") {
query = query.where("comune", "like", comune);
}
if (consegna) {
query = query.where("consegna", "in", consegna);
2025-08-28 18:27:07 +02:00
}
if (minBudget) {
query = query.where("prezzo", ">=", minBudget);
}
if (maxBudget) {
query = query.where("prezzo", "<=", maxBudget);
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const annunci = await query.execute();
if (!annunci) {
return [];
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return annunci;
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getAnnunciPositions: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export type AnnuncioRicerca = Pick<
2025-08-28 18:27:07 +02:00
Annunci,
| "id"
| "codice"
| "comune"
| "provincia"
| "prezzo"
| "consegna"
| "numero_camere"
| "mq"
| "tipo"
| "titolo_it"
| "titolo_en"
| "desc_en"
| "desc_it"
2025-08-28 18:27:07 +02:00
| "modificato_il"
| "stato"
| "external_videos"
| "media_updated_at"
| "homepage"
| "web"
> & {
images: Pick<ImagesRefs, "img" | "thumb">[];
videos: Pick<VideosRefs, "video" | "thumb">[];
};
2025-08-04 17:45:44 +02:00
export const getAnnunciRicerca = async ({
2025-08-28 18:27:07 +02:00
tipologia,
comune,
consegna,
sort,
minBudget,
maxBudget,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
tipologia?: string;
comune?: string;
consegna?: number[];
2025-08-28 18:27:07 +02:00
sort?: "Recenti" | "Prezzo" | "Consegna" | "Mq";
minBudget?: number;
maxBudget?: number;
}): Promise<AnnuncioRicerca[]> => {
2025-08-28 18:27:07 +02:00
try {
let query = db
.selectFrom("annunci")
.select((_eb) => [
2025-08-28 18:27:07 +02:00
"id",
"codice",
"comune",
"provincia",
"prezzo",
"consegna",
"numero_camere",
"mq",
"tipo",
"titolo_it",
"titolo_en",
"desc_en",
"desc_it",
"web",
2025-08-28 18:27:07 +02:00
"modificato_il",
"stato",
"external_videos",
"media_updated_at",
"homepage",
withImages(),
withVideos(),
2025-08-28 18:27:07 +02:00
])
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
.where("web", "=", true)
.where("stato", "!=", "Sospeso");
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (tipologia && tipologia !== "") {
query = query.where("tipo", "=", tipologia);
}
if (comune && comune !== "") {
query = query.where("comune", "like", comune);
}
if (consegna) {
query = query.where("consegna", "in", consegna);
2025-08-28 18:27:07 +02:00
}
if (minBudget) {
query = query.where("prezzo", ">=", minBudget);
}
if (maxBudget) {
query = query.where("prezzo", "<=", maxBudget);
}
switch (sort) {
case "Recenti":
query = query.orderBy("modificato_il", "desc");
break;
case "Prezzo":
query = query.orderBy("prezzo", "asc");
break;
case "Consegna":
query = query.orderBy("consegna", "asc");
break;
case "Mq":
query = query.orderBy("mq", "asc");
break;
case undefined:
break;
2025-08-28 18:27:07 +02:00
}
2025-08-28 18:27:07 +02:00
query = query.orderBy("id", "asc");
2025-08-04 17:45:44 +02:00
return await query.execute();
2025-08-28 18:27:07 +02:00
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query ricercaAnnunci: ${(e as Error).message}`,
2025-08-28 18:27:07 +02:00
});
}
2025-08-04 17:45:44 +02:00
};
export const getOptions_AnnunciHandler = async () => {
2025-08-28 18:27:07 +02:00
try {
const comuni = await db
.selectFrom("annunci")
.select(["comune", "tipo", "consegna", "prezzo"])
2025-08-28 18:27:07 +02:00
.where("web", "=", true)
.where("stato", "!=", "Sospeso")
.distinct()
.execute();
const results: {
comune: string;
tipo: string;
consegna: number;
prezzo: number;
}[] = [];
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
for (const comune of comuni) {
if (comune.comune && comune.tipo && comune.consegna && comune.prezzo) {
2025-08-28 18:27:07 +02:00
results.push({
comune: comune.comune,
consegna: comune.consegna,
2025-08-29 16:18:32 +02:00
tipo: comune.tipo,
prezzo: comune.prezzo,
2025-08-28 18:27:07 +02:00
});
}
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return results;
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore query getComuni: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export const editAnnuncioHandler = async ({
2025-08-28 18:27:07 +02:00
annuncioId,
data,
}: {
2025-08-28 18:27:07 +02:00
annuncioId: AnnunciId;
data: AnnunciUpdate;
}) => {
2025-08-28 18:27:07 +02:00
try {
const annuncio = await db
2025-08-28 18:27:07 +02:00
.updateTable("annunci")
.set({
...data,
modificato_il: new Date(),
2025-08-28 18:27:07 +02:00
})
.where("id", "=", annuncioId)
.returning("codice")
.executeTakeFirstOrThrow();
await revalidate(`/annuncio/${annuncio.codice}`);
2025-08-28 18:27:07 +02:00
return true;
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore interno: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export const getProprietarioDataHandler = async ({
2025-08-28 18:27:07 +02:00
annuncioId,
servizioId,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
annuncioId: AnnunciId;
servizioId: ServizioServizioId;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
try {
const opened_at = await db
.selectFrom("servizio_annunci")
.select("open_contatti_at")
.where("servizio_id", "=", servizioId)
.where("annunci_id", "=", annuncioId)
.executeTakeFirst();
if (!opened_at || !opened_at.open_contatti_at) {
2025-08-29 16:18:32 +02:00
return { opened_at: null, propData: null };
2025-08-28 18:27:07 +02:00
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const propData = await db
.selectFrom("annunci")
.select([
"codice",
"comune",
"indirizzo",
"civico",
"provincia",
"regione",
"cap",
"lat",
"lon",
"numero",
"locatore",
])
.where("id", "=", annuncioId)
.executeTakeFirstOrThrow();
2025-08-04 17:45:44 +02:00
2025-08-29 16:18:32 +02:00
return { opened_at: opened_at.open_contatti_at, propData };
2025-08-28 18:27:07 +02:00
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore recupero dati proprietario: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};