Files
blackheart-website/src/app/api/auth/login/route.ts
T
diana.dolgolyova a587736dd3 feat: mobile UX, admin polish, rate limiting, and media assets
- 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
2026-04-10 18:42:54 +03:00

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;
}