oa-ai/src/middleware.ts

73 lines
2.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
function decodeJwtPayload(token: string): Record<string, unknown> | 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<string, unknown> | 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).*)'] }