issue-ai/src/middleware.ts

59 lines
2.1 KiB
TypeScript
Raw 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 { NextRequest, NextResponse } from 'next/server'
import { verifyToken } from '@/lib/jwt'
function verifyApiKey(key: string): boolean {
// API Key 以环境变量形式存储,支持多个 Key逗号分隔
const allowedKeys = process.env.ALLOWED_API_KEYS || ''
if (!allowedKeys) return false
const keys = allowedKeys.split(',').map(k => k.trim())
return keys.includes(key)
}
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
if (pathname.startsWith('/login') || pathname === '/') return NextResponse.next()
if (pathname === '/api/auth/login') return NextResponse.next()
const authHeader = request.headers.get('authorization')
// API Key 认证Bearer ak_xxx 格式
if (authHeader?.startsWith('Bearer ak_')) {
const key = authHeader.slice(7)
if (verifyApiKey(key)) return NextResponse.next()
// 环境变量中未匹配API 路由仍放行route handler 可查询数据库二次验证)
if (pathname.startsWith('/api/')) return NextResponse.next()
}
// Cookie 认证
const token = request.cookies.get('session_issue')?.value
// 构建带 redirect 参数的登录 URL
function buildLoginRedirect() {
const loginUrl = new URL('/login', request.url)
const dest = pathname + (request.nextUrl.search || '')
loginUrl.searchParams.set('redirect', dest)
const response = NextResponse.redirect(loginUrl)
if (token) response.cookies.delete('session_issue')
return response
}
if (pathname.startsWith('/api/')) {
if (!token) return NextResponse.json({ error: '未登录' }, { status: 401 })
const valid = await verifyToken(token)
if (!valid) return NextResponse.json({ error: '会话已过期' }, { status: 401 })
return NextResponse.next()
}
if (!token) return buildLoginRedirect()
const valid = await verifyToken(token)
if (!valid) return buildLoginRedirect()
const response = NextResponse.next()
response.headers.set('x-original-pathname', pathname + (request.nextUrl.search || ''))
return response
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
}