a587736dd3
- Mobile responsiveness improvements across admin and public sections - Admin: bookings modal, open-day page, team page, layout polish - Added rate limiting, CSRF hardening, auth-edge improvements - Scroll reveal, floating contact, back-to-top, Yandex map fixes - Schedule filters refactor, team profile/info component updates - New useTrainerPhotos hook - Added class, team, master-class, and news images
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { verifyPassword, signToken, generateCsrfToken, COOKIE_NAME, CSRF_COOKIE_NAME } from "@/lib/auth";
|
|
import { checkRateLimit, getClientIp } from "@/lib/rateLimit";
|
|
|
|
export async function POST(request: NextRequest) {
|
|
const ip = getClientIp(request);
|
|
if (!checkRateLimit(ip, 5, 5 * 60_000)) {
|
|
return NextResponse.json(
|
|
{ error: "Слишком много попыток. Попробуйте через 5 минут." },
|
|
{ status: 429 }
|
|
);
|
|
}
|
|
|
|
const body = await request.json() as { password?: string };
|
|
|
|
if (!body.password || !verifyPassword(body.password)) {
|
|
return NextResponse.json({ error: "Неверный пароль" }, { status: 401 });
|
|
}
|
|
|
|
const token = signToken();
|
|
const csrfToken = generateCsrfToken();
|
|
const response = NextResponse.json({ ok: true });
|
|
|
|
response.cookies.set(COOKIE_NAME, token, {
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === "production",
|
|
sameSite: "lax",
|
|
path: "/",
|
|
maxAge: 60 * 60 * 24,
|
|
});
|
|
|
|
response.cookies.set(CSRF_COOKIE_NAME, csrfToken, {
|
|
httpOnly: false, // JS must read this to send as header
|
|
secure: process.env.NODE_ENV === "production",
|
|
sameSite: "lax", // Match auth cookie; strict breaks admin access from external links
|
|
path: "/",
|
|
maxAge: 60 * 60 * 24,
|
|
});
|
|
|
|
return response;
|
|
}
|