Files
BarevClub/apps/landing/app/api/flats/request/route.ts
T
2026-10-25 22:55:29 +04:00

51 lines
1.9 KiB
TypeScript

import { crmFetch } from '../../../../lib/flats';
// Заявка на просмотр со страницы /ru/apartments → CRM (клиент «BarevClub» и сообщение в группу AREV).
/** Не больше 5 заявок с одного адреса за 10 минут — от случайных повторов и ботов */
const hits = new Map<string, number[]>();
function limited(ip: string) {
const now = Date.now();
const recent = (hits.get(ip) ?? []).filter((t) => now - t < 10 * 60_000);
recent.push(now);
hits.set(ip, recent);
if (hits.size > 5000) hits.clear();
return recent.length > 5;
}
const json = (body: object, status = 200) => Response.json(body, { status });
export async function POST(req: Request) {
const ip = (req.headers.get('x-forwarded-for') ?? '').split(',')[0]!.trim() || 'unknown';
if (limited(ip)) return json({ code: 'TOO_MANY' }, 429);
let body: Record<string, unknown>;
try {
body = (await req.json()) as Record<string, unknown>;
} catch {
return json({ code: 'VALIDATION' }, 400);
}
// скрытое поле: его заполняют только боты
if (typeof body.website === 'string' && body.website) return json({ ok: true });
const str = (v: unknown) => (typeof v === 'string' && v.trim() ? v.trim() : undefined);
const payload = {
leadId: Number(body.leadId),
day: str(body.day),
time: str(body.time),
name: str(body.name),
phone: str(body.phone),
telegram: str(body.telegram),
};
try {
const res = await crmFetch('/api/barev/requests', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(payload),
});
const data = (await res.json().catch(() => ({}))) as { code?: string };
if (!res.ok) return json({ code: data.code ?? 'FAILED' }, res.status === 401 ? 503 : res.status);
return json({ ok: true });
} catch {
return json({ code: 'FAILED' }, 502);
}
}