- Extract shared sanitization to src/lib/validation.ts, apply to all 3 registration routes (#2) - Replace key={index} with stable keys in About and News (#4) - Add 5-min in-memory content cache in content.ts, invalidate on admin section save (#6) - Refactor Schedule from 8 useState calls to useReducer — single dispatch, fewer re-renders (#8) - Remove Hero scroll indicator, add auto-scroll to next section on wheel/swipe Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
42 lines
1.4 KiB
TypeScript
42 lines
1.4 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { addGroupBooking } from "@/lib/db";
|
|
import { checkRateLimit, getClientIp } from "@/lib/rateLimit";
|
|
import { sanitizeName, sanitizePhone, sanitizeHandle, sanitizeText } from "@/lib/validation";
|
|
|
|
export async function POST(request: NextRequest) {
|
|
const ip = getClientIp(request);
|
|
if (!checkRateLimit(ip, 5, 60_000)) {
|
|
return NextResponse.json(
|
|
{ error: "Слишком много запросов. Попробуйте через минуту." },
|
|
{ status: 429 }
|
|
);
|
|
}
|
|
|
|
try {
|
|
const body = await request.json();
|
|
const { name, phone, groupInfo, instagram, telegram } = body;
|
|
|
|
const cleanName = sanitizeName(name);
|
|
if (!cleanName) {
|
|
return NextResponse.json({ error: "Имя обязательно" }, { status: 400 });
|
|
}
|
|
|
|
const cleanPhone = sanitizePhone(phone);
|
|
if (!cleanPhone) {
|
|
return NextResponse.json({ error: "Телефон обязателен" }, { status: 400 });
|
|
}
|
|
|
|
const id = addGroupBooking(
|
|
cleanName,
|
|
cleanPhone,
|
|
sanitizeText(groupInfo),
|
|
sanitizeHandle(instagram),
|
|
sanitizeHandle(telegram)
|
|
);
|
|
return NextResponse.json({ ok: true, id });
|
|
} catch (err) {
|
|
console.error("[group-booking] POST error:", err);
|
|
return NextResponse.json({ error: "Internal error" }, { status: 500 });
|
|
}
|
|
}
|