2025-08-04 17:45:44 +02:00
|
|
|
import type { Annunci, AnnunciId } from "~/schemas/public/Annunci";
|
|
|
|
|
import { TRPCError } from "@trpc/server";
|
2025-08-08 13:44:52 +02:00
|
|
|
import { createSrcset } from "~/server/services/imageServer";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { AnnuncioObjectWithImages } from "~/server/services/annunci.service";
|
|
|
|
|
import { zAnnuncioId } from "~/server/utils/zod_types";
|
2025-08-07 14:50:40 +02:00
|
|
|
import { z } from "zod/v4";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { db } from "~/server/db";
|
|
|
|
|
import type { ServizioServizioId } from "~/schemas/public/Servizio";
|
2025-08-08 13:30:21 +02:00
|
|
|
import { env } from "~/env.mjs";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
// const ratelimit = new RateLimiterHandler({
|
|
|
|
|
// windowSize: 10,
|
|
|
|
|
// maxRequests: 10,
|
|
|
|
|
// analytics: true,
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
export const getAnnunciListHandler = async (): Promise<
|
|
|
|
|
Pick<
|
|
|
|
|
Annunci,
|
|
|
|
|
"id" | "codice" | "titolo_it" | "comune" | "tipo" | "locatore" | "stato"
|
|
|
|
|
>[]
|
|
|
|
|
> => {
|
|
|
|
|
try {
|
|
|
|
|
return await db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.select([
|
|
|
|
|
"id",
|
|
|
|
|
"codice",
|
|
|
|
|
"titolo_it",
|
|
|
|
|
"comune",
|
|
|
|
|
"tipo",
|
|
|
|
|
"locatore",
|
|
|
|
|
"stato",
|
|
|
|
|
])
|
|
|
|
|
.execute();
|
|
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getAllRaw",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getCodici_AnnunciHandler = async (): Promise<string[]> => {
|
|
|
|
|
try {
|
|
|
|
|
const codici = await db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.select("codice")
|
|
|
|
|
.where("annunci.web", "=", true)
|
|
|
|
|
.where("stato", "!=", "Sospeso")
|
|
|
|
|
.execute();
|
|
|
|
|
if (!codici) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return codici.map((c) => c.codice);
|
|
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getCodici",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getAnnunciByCod = async ({
|
|
|
|
|
cod,
|
|
|
|
|
}: {
|
|
|
|
|
cod: string;
|
2025-08-08 11:34:33 +02:00
|
|
|
}): Promise<Annunci | null> => {
|
2025-08-04 17:45:44 +02:00
|
|
|
try {
|
|
|
|
|
const annuncio = await db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.selectAll()
|
|
|
|
|
.where("annunci.web", "=", true)
|
|
|
|
|
.where("codice", "=", cod)
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
if (!annuncio) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "NOT_FOUND",
|
|
|
|
|
message: "Annuncio non trovato",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 11:34:33 +02:00
|
|
|
return AnnuncioObjectWithImages<typeof annuncio>(annuncio);
|
2025-08-08 13:30:21 +02:00
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getAnnuncio",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
export const getAnnunciMetaByCod = async ({ cod }: { cod: string }) => {
|
|
|
|
|
try {
|
|
|
|
|
const annuncio = await db
|
|
|
|
|
.selectFrom("annunci")
|
2025-08-08 13:44:52 +02:00
|
|
|
.select(["titolo_it", "desc_it", "og_url"])
|
2025-08-08 13:30:21 +02:00
|
|
|
.where("annunci.web", "=", true)
|
|
|
|
|
.where("codice", "=", cod)
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
if (!annuncio) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "NOT_FOUND",
|
|
|
|
|
message: "Annuncio non trovato",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2025-08-08 13:44:52 +02:00
|
|
|
ogImage: annuncio.og_url || "",
|
2025-08-08 13:30:21 +02:00
|
|
|
title: annuncio.titolo_it || "",
|
|
|
|
|
description: annuncio.desc_it || "",
|
|
|
|
|
ogUrl: env.NEXT_PUBLIC_BASE_URL + "/annuncio/" + cod,
|
|
|
|
|
};
|
2025-08-04 17:45:44 +02:00
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getAnnuncio",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getAnnunciById = async ({
|
|
|
|
|
id,
|
|
|
|
|
}: {
|
|
|
|
|
id: AnnunciId;
|
2025-08-08 11:34:33 +02:00
|
|
|
}): Promise<Annunci | null> => {
|
2025-08-04 17:45:44 +02:00
|
|
|
try {
|
|
|
|
|
const annuncio = await db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.selectAll()
|
|
|
|
|
.where("id", "=", id)
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
if (!annuncio) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "NOT_FOUND",
|
|
|
|
|
message: "Annuncio non trovato",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 11:34:33 +02:00
|
|
|
return AnnuncioObjectWithImages<typeof annuncio>(annuncio);
|
2025-08-04 17:45:44 +02:00
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getAnnuncioById",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AnnuncioRicercaWPosition = AnnuncioRicerca & {
|
|
|
|
|
lat_secondario: string | null;
|
|
|
|
|
lon_secondario: string | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const get_AnnunciPositionsHandler = async ({
|
|
|
|
|
tipologia,
|
|
|
|
|
comune,
|
|
|
|
|
consegna,
|
|
|
|
|
}: {
|
|
|
|
|
tipologia?: string;
|
|
|
|
|
comune?: string;
|
|
|
|
|
consegna?: number;
|
|
|
|
|
}): Promise<AnnuncioRicercaWPosition[]> => {
|
|
|
|
|
try {
|
|
|
|
|
let query = db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.select([
|
|
|
|
|
"id",
|
|
|
|
|
"codice",
|
|
|
|
|
"comune",
|
|
|
|
|
"provincia",
|
|
|
|
|
"prezzo",
|
|
|
|
|
"consegna",
|
|
|
|
|
"numero_camere",
|
|
|
|
|
"mq",
|
|
|
|
|
"tipo",
|
|
|
|
|
"titolo_it",
|
|
|
|
|
"titolo_en",
|
|
|
|
|
"url_immagini",
|
|
|
|
|
"modificato_il",
|
|
|
|
|
"stato",
|
|
|
|
|
"lon_secondario",
|
|
|
|
|
"lat_secondario",
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
.where("web", "=", true)
|
|
|
|
|
.where("stato", "!=", "Sospeso")
|
|
|
|
|
.orderBy("id", "asc");
|
|
|
|
|
|
|
|
|
|
if (tipologia && tipologia !== "") {
|
|
|
|
|
query = query.where("tipo", "=", tipologia);
|
|
|
|
|
}
|
|
|
|
|
if (comune && comune !== "") {
|
|
|
|
|
query = query.where("comune", "like", comune);
|
|
|
|
|
}
|
|
|
|
|
if (consegna) {
|
|
|
|
|
query = query.where("consegna", "=", consegna);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const annunci = await query.execute();
|
|
|
|
|
if (!annunci) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
for (const annuncio of annunci) {
|
|
|
|
|
annuncio.url_immagini = annuncio.url_immagini
|
|
|
|
|
? createSrcset(annuncio.url_immagini)
|
|
|
|
|
: [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return annunci;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getAnnunciPositions: " + (e as Error).message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AnnuncioRicerca = Pick<
|
|
|
|
|
Annunci,
|
|
|
|
|
| "id"
|
|
|
|
|
| "codice"
|
|
|
|
|
| "comune"
|
|
|
|
|
| "provincia"
|
|
|
|
|
| "prezzo"
|
|
|
|
|
| "consegna"
|
|
|
|
|
| "numero_camere"
|
|
|
|
|
| "mq"
|
|
|
|
|
| "tipo"
|
|
|
|
|
| "titolo_it"
|
|
|
|
|
| "titolo_en"
|
|
|
|
|
| "url_immagini"
|
|
|
|
|
| "modificato_il"
|
|
|
|
|
| "stato"
|
|
|
|
|
>;
|
|
|
|
|
|
|
|
|
|
export const getCursor_AnnunciHandler = async ({
|
2025-08-08 16:55:02 +02:00
|
|
|
page = 0,
|
|
|
|
|
pageSize,
|
|
|
|
|
tipologia,
|
|
|
|
|
comune,
|
|
|
|
|
consegna,
|
|
|
|
|
sort,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-08 16:55:02 +02:00
|
|
|
page?: number;
|
|
|
|
|
pageSize: number;
|
|
|
|
|
tipologia?: string;
|
|
|
|
|
comune?: string;
|
|
|
|
|
consegna?: number;
|
|
|
|
|
sort?: "Recenti" | "Prezzo" | "Consegna" | "Mq";
|
|
|
|
|
}) => {
|
2025-08-04 17:45:44 +02:00
|
|
|
try {
|
|
|
|
|
let query = db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.select([
|
|
|
|
|
"id",
|
|
|
|
|
"codice",
|
|
|
|
|
"comune",
|
|
|
|
|
"provincia",
|
|
|
|
|
"prezzo",
|
|
|
|
|
"consegna",
|
|
|
|
|
"numero_camere",
|
|
|
|
|
"mq",
|
|
|
|
|
"tipo",
|
|
|
|
|
"titolo_it",
|
|
|
|
|
"titolo_en",
|
|
|
|
|
"url_immagini",
|
|
|
|
|
"modificato_il",
|
|
|
|
|
"stato",
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
.where("web", "=", true)
|
|
|
|
|
.where("stato", "!=", "Sospeso");
|
|
|
|
|
|
2025-08-08 16:55:02 +02:00
|
|
|
if (tipologia && tipologia !== "") {
|
|
|
|
|
query = query.where("tipo", "=", tipologia);
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
2025-08-08 16:55:02 +02:00
|
|
|
if (comune && comune !== "") {
|
|
|
|
|
query = query.where("comune", "like", comune);
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
2025-08-08 16:55:02 +02:00
|
|
|
if (consegna) {
|
|
|
|
|
query = query.where("consegna", "=", consegna);
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
2025-08-08 16:55:02 +02:00
|
|
|
if (sort) {
|
|
|
|
|
switch (sort) {
|
2025-08-04 17:45:44 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
query = query.orderBy("id", "asc");
|
|
|
|
|
|
2025-08-08 16:55:02 +02:00
|
|
|
const annunci = await query
|
|
|
|
|
.limit(pageSize + 1)
|
|
|
|
|
.offset(page * pageSize)
|
|
|
|
|
.execute();
|
|
|
|
|
const hasMore = annunci.length > pageSize;
|
|
|
|
|
if (annunci.length > pageSize) {
|
|
|
|
|
annunci.pop(); // Remove the last item if it exceeds the page size
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const annuncio of annunci) {
|
|
|
|
|
annuncio.url_immagini = annuncio.url_immagini
|
|
|
|
|
? createSrcset(annuncio.url_immagini)
|
|
|
|
|
: [];
|
|
|
|
|
}
|
2025-08-08 16:55:02 +02:00
|
|
|
|
2025-08-04 17:45:44 +02:00
|
|
|
return {
|
|
|
|
|
annunci,
|
2025-08-08 16:55:02 +02:00
|
|
|
hasMore,
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getWithCursor",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getOptions_AnnunciHandler = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const comuni = await db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.select(["comune", "tipo", "consegna"])
|
|
|
|
|
.where("web", "=", true)
|
|
|
|
|
.where("stato", "!=", "Sospeso")
|
|
|
|
|
.distinct()
|
|
|
|
|
.execute();
|
|
|
|
|
const results: { comune: string; tipo: string; consegna: number }[] = [];
|
|
|
|
|
|
|
|
|
|
for (const comune of comuni) {
|
|
|
|
|
if (comune.comune && comune.tipo && comune.consegna) {
|
|
|
|
|
results.push({
|
|
|
|
|
comune: comune.comune,
|
|
|
|
|
tipo: comune.tipo,
|
|
|
|
|
consegna: comune.consegna,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore query getComuni",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const EdiAnnuncioSchema = z.object({
|
|
|
|
|
annuncioId: zAnnuncioId,
|
|
|
|
|
titolo_it: z.string().nonempty("Inserisci un titolo valido"),
|
|
|
|
|
desc_it: z.string().nonempty("Inserisci una descrizione valida"),
|
|
|
|
|
titolo_en: z.string().nonempty("Inserisci un titolo valido"),
|
|
|
|
|
desc_en: z.string().nonempty("Inserisci una descrizione valida"),
|
|
|
|
|
status: z.string().nonempty("Seleziona uno stato"),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
type EditAnnuncioFormValues = z.infer<typeof EdiAnnuncioSchema>;
|
|
|
|
|
|
|
|
|
|
export const editAnnuncioHandler = async ({
|
|
|
|
|
annuncioId,
|
|
|
|
|
titolo_it,
|
|
|
|
|
desc_it,
|
|
|
|
|
titolo_en,
|
|
|
|
|
desc_en,
|
|
|
|
|
status,
|
|
|
|
|
}: EditAnnuncioFormValues) => {
|
|
|
|
|
try {
|
|
|
|
|
await db
|
|
|
|
|
.updateTable("annunci")
|
|
|
|
|
.set({
|
|
|
|
|
titolo_it: titolo_it,
|
|
|
|
|
desc_it: desc_it,
|
|
|
|
|
titolo_en: titolo_en,
|
|
|
|
|
desc_en: desc_en,
|
|
|
|
|
stato: status,
|
|
|
|
|
})
|
|
|
|
|
.where("id", "=", annuncioId)
|
|
|
|
|
.execute();
|
|
|
|
|
return true;
|
|
|
|
|
} catch {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore interno",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getProprietarioDataHandler = async ({
|
|
|
|
|
annuncioId,
|
|
|
|
|
servizioId,
|
|
|
|
|
}: {
|
|
|
|
|
annuncioId: AnnunciId;
|
|
|
|
|
servizioId: ServizioServizioId;
|
|
|
|
|
}) => {
|
|
|
|
|
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) {
|
|
|
|
|
return { propData: null, opened_at: null };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const propData = await db
|
|
|
|
|
.selectFrom("annunci")
|
|
|
|
|
.select([
|
|
|
|
|
"codice",
|
|
|
|
|
"comune",
|
|
|
|
|
"indirizzo",
|
|
|
|
|
"civico",
|
|
|
|
|
"provincia",
|
|
|
|
|
"regione",
|
|
|
|
|
"cap",
|
|
|
|
|
"lat",
|
|
|
|
|
"lon",
|
|
|
|
|
"numero",
|
|
|
|
|
"locatore",
|
|
|
|
|
])
|
|
|
|
|
.where("id", "=", annuncioId)
|
|
|
|
|
.executeTakeFirstOrThrow();
|
|
|
|
|
|
|
|
|
|
return { propData, opened_at: opened_at.open_contatti_at };
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: "Errore recupero dati proprietario: " + (e as Error).message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|