import { NextRequest, NextResponse } from "next/server"; import { cookies } from "next/headers"; import { checkAuth, createSession, SESSION_KEY } from "@/lib/auth"; import { registerFailedAttempt, clearAttempts, isLocked } from "@/lib/rate-limit"; const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || "asui2026"; const SESSION_MAX_AGE = 60 * 60 * 24 * 7; // 7 天 /** 取客户端 IP 作为限流 key,兼容代理转发头。 */ function clientKey(request: NextRequest): string { const fwd = request.headers.get("x-forwarded-for"); const ip = fwd ? fwd.split(",")[0].trim() : request.headers.get("x-real-ip") || "unknown"; return `login:${ip}`; } export async function POST(request: NextRequest) { const key = clientKey(request); const lock = isLocked(key); if (lock.locked) { return NextResponse.json( { error: `尝试次数过多,请 ${Math.ceil(lock.retryAfterSec / 60)} 分钟后再试` }, { status: 429 } ); } let body: { password?: string }; try { body = await request.json(); } catch { return NextResponse.json({ error: "请求格式错误" }, { status: 400 }); } if (typeof body.password !== "string" || body.password !== ADMIN_PASSWORD) { const result = registerFailedAttempt(key); if (result.locked) { return NextResponse.json( { error: "密码错误次数过多,账户已锁定 15 分钟" }, { status: 429 } ); } return NextResponse.json({ error: "密码错误" }, { status: 401 }); } clearAttempts(key); const token = await createSession(SESSION_MAX_AGE); const cookieStore = await cookies(); cookieStore.set(SESSION_KEY, token, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", maxAge: SESSION_MAX_AGE, path: "/", }); return NextResponse.json({ ok: true }); } export async function DELETE() { const cookieStore = await cookies(); cookieStore.delete(SESSION_KEY); return NextResponse.json({ ok: true }); } export async function GET() { return NextResponse.json({ authenticated: await checkAuth() }); }