import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { getToken } from 'next-auth/jwt' // 允许的来源列表(内网和公网) - 支持通过环境变量配置,逗号分隔 const ALLOWED_ORIGINS = (() => { const fromEnv = process.env.ALLOWED_ORIGINS || '' const list = fromEnv.split(',').map(s => s.trim()).filter(Boolean) const defaults = ['http://localhost:3000', 'http://localhost:4320'] return Array.from(new Set([...defaults, ...list])) })() // 不需要许可证检查的路径 const LICENSE_EXEMPT_PATHS = [ '/activate', '/api/license', '/api/auth', '/login', '/_next', '/favicon.ico', ] export async function middleware(request: NextRequest) { const { pathname, origin } = request.nextUrl const requestOrigin = request.headers.get('origin') // 检查是否需要许可证验证 const needsLicenseCheck = !LICENSE_EXEMPT_PATHS.some(path => pathname.startsWith(path)) // 许可证检查(通过 cookie 判断是否已激活) if (needsLicenseCheck) { const licenseActivated = request.cookies.get('license_activated')?.value if (!licenseActivated) { // 首次访问,重定向到激活页面 // 注意:实际的许可证验证在服务端进行,这里只是快速检查 const url = new URL('/activate', request.url) return NextResponse.redirect(url) } } // 处理API路由的CORS if (pathname.startsWith('/api/')) { // 处理OPTIONS预检请求 if (request.method === 'OPTIONS') { return new NextResponse(null, { status: 204, headers: { 'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': requestOrigin || origin, 'Access-Control-Allow-Methods': 'GET,DELETE,PATCH,POST,PUT,OPTIONS', 'Access-Control-Allow-Headers': 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization, Cookie', 'Access-Control-Max-Age': '86400', }, }) } // 为所有API响应添加CORS头 const response = NextResponse.next() response.headers.set('Access-Control-Allow-Credentials', 'true') response.headers.set('Access-Control-Allow-Origin', requestOrigin || origin) response.headers.set('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT,OPTIONS') response.headers.set('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization, Cookie') return response } // 处理需要认证的页面 if (pathname === '/' || pathname.startsWith('/admin')) { const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET }) if (!token) { const url = new URL('/login', request.url) return NextResponse.redirect(url) } // 管理员页面需要管理员权限 const requiresAdmin = pathname.startsWith('/admin') && !pathname.startsWith('/admin/monitor') if (requiresAdmin) { if (token.role !== 'ADMIN') { const url = new URL('/', request.url) return NextResponse.redirect(url) } } } return NextResponse.next() } export const config = { matcher: ['/', '/admin/:path*', '/api/:path*'], }