magricambi/src/lib/price-search/n8n.ts

80 lines
2.2 KiB
TypeScript
Raw Normal View History

2026-06-07 18:34:46 +02:00
import {
DEFAULT_DISCLAIMER,
isPriceSearchResponse,
parseN8nResponseBody,
} from "@/src/lib/price-search/normalize";
import type { PriceSearchRequest, PriceSearchResponse } from "@/src/types/priceSearch";
function getWebhookConfig() {
const url =
process.env.N8N_PRICE_SEARCH_WEBHOOK_URL?.trim() ??
process.env.N8N_PRICE_WEBHOOK_URL?.trim();
const secret = process.env.N8N_PRICE_WEBHOOK_SECRET?.trim();
const timeoutMs = Number(process.env.N8N_PRICE_WEBHOOK_TIMEOUT_MS ?? 45_000);
if (!url || !secret) {
throw new Error("Ricerca prezzi n8n non configurata (URL o secret mancanti)");
}
return { url, secret, timeoutMs: Number.isFinite(timeoutMs) ? timeoutMs : 45_000 };
}
export async function searchPriceOffersViaN8n(
input: PriceSearchRequest,
meta?: { userId?: string; userName?: string },
): Promise<PriceSearchResponse> {
const { url, secret, timeoutMs } = getWebhookConfig();
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Webhook-Secret": secret,
},
body: JSON.stringify({
...input,
meta: {
source: "magricambi",
userId: meta?.userId ?? null,
userName: meta?.userName ?? null,
requestedAt: new Date().toISOString(),
},
}),
signal: controller.signal,
cache: "no-store",
});
const text = await res.text();
const data = parseN8nResponseBody(text, res.status);
if (!res.ok) {
const err =
data && typeof data === "object" && "error" in data
? String((data as { error: unknown }).error)
: `Errore n8n (HTTP ${res.status})`;
throw new Error(err);
}
if (!isPriceSearchResponse(data)) {
throw new Error(
"Formato risposta n8n non conforme. Verifica il nodo Respond OK del workflow.",
);
}
return data;
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
throw new Error("Timeout ricerca prezzi: riprova tra poco");
}
throw error;
} finally {
clearTimeout(timer);
}
}
export { DEFAULT_DISCLAIMER };