Apps, web, i18n, UI, partners, payouts, backoffice, bariki, prod infra
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
45d7512d2b
commit
3489e0b816
319 files changed
+45483
-121
No files matched your search
@@ -0,0 +1,50 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user