import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' function decodeJwtPayload(token: string): Record | null { try { const parts = token.split('.') if (parts.length !== 3) return null let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/') while (payload.length % 4) payload += '=' return JSON.parse(atob(payload)) } catch { return null } } function isValidPayload(payload: Record | null): boolean { if (!payload) return false return !(payload.exp && (payload.exp as number) < Math.floor(Date.now() / 1000)) } function noCache(response: NextResponse) { response.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate') return response } export function middleware(request: NextRequest) { const { pathname } = request.nextUrl // 登录页:已登录用户自动跳转首页 if (pathname === '/login') { const token = request.cookies.get('tlyq_session')?.value const payload = token ? decodeJwtPayload(token) : null if (isValidPayload(payload)) { return NextResponse.redirect(new URL('/', request.url)) } return NextResponse.next() } // 设置密码/API 路径放行(API 路由自行验证) if (pathname === '/setup-password' || pathname.startsWith('/api/auth/') || pathname.startsWith('/api/admin/')) { return NextResponse.next() } // /admin 管理页面需要认证 if (pathname.startsWith('/admin')) { const token = request.cookies.get('tlyq_session')?.value const payload = token ? decodeJwtPayload(token) : null if (!isValidPayload(payload)) { return NextResponse.redirect(new URL('/login', request.url)) } return noCache(NextResponse.next()) } // 静态资源放行(已有路径哈希,允许缓存) if (pathname.startsWith('/_next/') || pathname === '/favicon.ico') { return NextResponse.next() } const token = request.cookies.get('tlyq_session')?.value const payload = token ? decodeJwtPayload(token) : null if (isValidPayload(payload)) { const response = NextResponse.next() response.cookies.set('session', JSON.stringify({ username: payload!.username }), { httpOnly: true, sameSite: 'lax', path: '/', }) return noCache(response) } return noCache(NextResponse.redirect(new URL('/login', request.url))) } export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|public).*)'] }