70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { getServerSession } from 'next-auth'
|
|
import { authOptions } from '@/lib/auth'
|
|
import { hasSystemAdminPermission } from '@/lib/department-permission'
|
|
|
|
const MAX_TEST_SIZE_BYTES = 5 * 1024 * 1024
|
|
const RATE_LIMIT_WINDOW_MS = 60 * 1000
|
|
const RATE_LIMIT_MAX_REQUESTS = 10
|
|
const requestCounts = new Map<string, { count: number; resetAt: number }>()
|
|
|
|
function consumeRateLimit(userId: string): number | null {
|
|
const now = Date.now()
|
|
const current = requestCounts.get(userId)
|
|
if (!current || current.resetAt <= now) {
|
|
requestCounts.set(userId, { count: 1, resetAt: now + RATE_LIMIT_WINDOW_MS })
|
|
return null
|
|
}
|
|
if (current.count >= RATE_LIMIT_MAX_REQUESTS) {
|
|
return Math.max(1, Math.ceil((current.resetAt - now) / 1000))
|
|
}
|
|
current.count += 1
|
|
return null
|
|
}
|
|
|
|
// 下载测速API
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions)
|
|
if (!session?.user) {
|
|
return NextResponse.json({ error: '请先登录' }, { status: 401 })
|
|
}
|
|
|
|
const user = session.user as any
|
|
if (!hasSystemAdminPermission(user.role, Boolean(user.isSuperAdmin))) {
|
|
return NextResponse.json({ error: '仅系统管理员可执行下载测速' }, { status: 403 })
|
|
}
|
|
|
|
const retryAfter = consumeRateLimit(String(user.id || 'unknown'))
|
|
if (retryAfter) {
|
|
return NextResponse.json(
|
|
{ error: '请求过于频繁,请稍后再试' },
|
|
{ status: 429, headers: { 'Retry-After': String(retryAfter) } },
|
|
)
|
|
}
|
|
|
|
const body = await request.json()
|
|
const size = body?.size === undefined ? 1024 * 1024 : Number(body.size)
|
|
if (!Number.isInteger(size) || size < 1 || size > MAX_TEST_SIZE_BYTES) {
|
|
return NextResponse.json(
|
|
{ error: `size 必须是 1 到 ${MAX_TEST_SIZE_BYTES} 之间的整数` },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
|
|
// 生成测试数据
|
|
const buffer = Buffer.alloc(size, 0)
|
|
|
|
return new NextResponse(buffer, {
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Length': size.toString(),
|
|
'Cache-Control': 'no-store',
|
|
}
|
|
})
|
|
} catch (error) {
|
|
console.error('下载测速失败:', error)
|
|
return NextResponse.json({ error: '下载测速失败' }, { status: 500 })
|
|
}
|
|
}
|