Files
blackheart-website/src/app/api/open-day-register/route.ts
diana.dolgolyova 9e0aa5b5dc fix: LOW priority — GPU hints, CSRF cleanup, redundant query removal, mobile perf
- Add will-change to .hero-glow-orb (filter, transform) and .team-card-glitter::before (background-position)
- Clear CSRF cookie on logout alongside auth cookie
- Add max array length (100) validation on team reorder endpoint
- Remove redundant isOpenDayClassBookedByPhone pre-check (DB UNIQUE constraint handles it)
- Extract Schedule grid layout calculation into useMemo
- Reduce HeroLogo sparkle animations on mobile (15 → 8 via hidden sm:block)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:28:35 +03:00

61 lines
2.1 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import {
addOpenDayBooking,
getPersonOpenDayBookings,
getOpenDayEvent,
} from "@/lib/db";
import { checkRateLimit, getClientIp } from "@/lib/rateLimit";
import { sanitizeName, sanitizePhone, sanitizeHandle } from "@/lib/validation";
export async function POST(request: NextRequest) {
const ip = getClientIp(request);
if (!checkRateLimit(ip, 10, 60_000)) {
return NextResponse.json(
{ error: "Слишком много запросов. Попробуйте через минуту." },
{ status: 429 }
);
}
try {
const body = await request.json();
const { classId, eventId, name, phone, instagram, telegram } = body;
if (!classId || !eventId) {
return NextResponse.json({ error: "classId and eventId are required" }, { status: 400 });
}
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 = addOpenDayBooking(classId, eventId, {
name: cleanName,
phone: cleanPhone,
instagram: sanitizeHandle(instagram),
telegram: sanitizeHandle(telegram),
});
// Return total bookings for this person (for discount calculation)
const totalBookings = getPersonOpenDayBookings(eventId, cleanPhone);
const event = getOpenDayEvent(eventId);
const pricePerClass = event && totalBookings >= event.discountThreshold
? event.discountPrice
: event?.pricePerClass ?? 30;
return NextResponse.json({ ok: true, id, totalBookings, pricePerClass });
} catch (e) {
const msg = e instanceof Error ? e.message : "Internal error";
if (msg.includes("UNIQUE")) {
return NextResponse.json({ error: "Вы уже записаны на это занятие" }, { status: 409 });
}
console.error("[open-day-register] POST error:", e);
return NextResponse.json({ error: "Internal error" }, { status: 500 });
}
}