- 新增许可证服务器 (license-server/) - 新增 Agent 部署模块 (flnxi-agent/) - 添加许可证验证和激活页面 - 添加 Harbor 镜像同步脚本 - 更新素材系统支持许可证验证
94 lines
3.3 KiB
TypeScript
94 lines
3.3 KiB
TypeScript
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*'],
|
|
}
|
|
|