import { NextResponse } from 'next/server'; import { normalizeEmail,validPassword, trustedOrigin,accountCookie } from './account-crypto'; import { requestPasswordReset,resetPassword,rateLimit,registerAccount,resendVerification,verifyAccount,loginAccount,currentAccount,logoutAccount } from './account'; export const runtime = 'nodejs'; const headers = {'Cache-Control':'private, no-store','Referrer-Policy':'no-referrer'}; const json = (body: object,status=200) => NextResponse.json(body,{status,headers}); type Context = {params: Promise<{action:string}>}; export async function GET(request: Request,context: Context) { if ((await context.params).action !== 'session') return json({error:'不存在'},404); try { return json({user:await currentAccount(request)}); } catch { return json({error:'账户服务暂不可用'},503); } } export async function POST(request: Request,context: Context) { if (!trustedOrigin(request)) return json({error:'请求来源不受信任'},403); const {action} = await context.params; if (!['register','resend','verify','login','logout','forgot','reset'].includes(action)) return json({error:'不存在'},404); try { // Revocation remains available even when login or mail limits are exhausted. if (action === 'logout') { await logoutAccount(request); const r=json({ok:true});r.headers.set('Set-Cookie',accountCookie('',true));return r; } // Use a global cap by default: forwarding headers are untrusted unless the deployment explicitly enables a trusted ingress. const ip = process.env.ACCOUNT_TRUST_PROXY === 'true' ? request.headers.get('x-forwarded-for')?.split(',')[0].trim() || 'unknown' : 'global'; if (!await rateLimit('request:'+ip,process.env.ACCOUNT_TRUST_PROXY === 'true' ? 60 : 300)) return json({error:'请求过于频繁,请稍后再试'},429); if (Number(request.headers.get('content-length') || 0)>4096) return json({error:'请求过大'},413); const text=await request.text(); if(text.length>4096)return json({error:'请求过大'},413); let body;try{body=JSON.parse(text);}catch{return json({error:'请求格式错误'},400);} if (!body || typeof body!=='object') return json({error:'请求格式错误'},400); if (action === 'verify') { if(typeof body.token!=='string'||! /^[a-f0-9]{64}$/.test(body.token))return json({error:'验证链接无效或已过期'},400); return await verifyAccount(body.token) ? json({ok:true,message:'邮箱已验证,请登录'}) : json({error:'验证链接无效或已过期'},400); } if(action==='reset') { if(typeof body.token!=='string'||!/^[a-f0-9]{64}$/.test(body.token)||!validPassword(body.password))return json({error:'重设链接无效,或密码不符合要求'},400); if(!await rateLimit('reset:'+body.token,5))return json({error:'请求过于频繁,请稍后再试'},429); if(!await resetPassword(body.token,body.password))return json({error:'重设链接无效或已过期,请重新申请'},400); const r=json({ok:true,message:'密码已更新,请使用新密码登录'});r.headers.set('Set-Cookie',accountCookie('',true));return r; } if(action==='forgot') { const email=normalizeEmail(body.email); if(!email)return json({error:'请输入有效邮箱'},400); if(!await rateLimit('forgot:'+email,3))return json({error:'请求过于频繁,请 15 分钟后再试'},429); await requestPasswordReset(email); return json({ok:true,message:'若该邮箱对应已验证账户,重设链接将发送至邮箱,有效期 30 分钟。'}); } const email=normalizeEmail(body.email); if(!email||!validPassword(body.password))return json({error:'请输入有效邮箱及 12–256 字节密码'},400); if(!await rateLimit(action+':'+email,action==='login'?10:3))return json({error:'请求过于频繁,请 15 分钟后再试'},429); if(action==='register')await registerAccount(email,body.password); if(action==='resend')await resendVerification(email,body.password); if(action==='login') { const token=await loginAccount(email,body.password); if(!token)return json({error:'邮箱或密码错误,或邮箱尚未验证'},401); const r=json({ok:true});r.headers.set('Set-Cookie',accountCookie(token));return r; } return json({ok:true,message:'若账户符合条件,验证邮件已发送。请检查收件箱和垃圾邮件。'}); }catch{return json({error:'账户服务暂不可用,请稍后重试'},503);} }