diff --git a/app/api/download/route.ts b/app/api/download/route.ts new file mode 100644 index 0000000..95d8a37 --- /dev/null +++ b/app/api/download/route.ts @@ -0,0 +1,55 @@ +// app/api/download/route.ts +import { NextResponse } from 'next/server'; +import path from 'path'; +import fs from 'fs'; +import { RESOURCE_PATH } from '@/lib/constants'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const project = searchParams.get('project'); + const version = searchParams.get('version'); + const filename = searchParams.get('filename'); + + if (!project || !version || !filename) { + return NextResponse.json( + { error: 'Project, version, and filename parameters are required' }, + { status: 400 } + ); + } + + // 安全检查:确保 filename 不包含路径遍历字符 + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return NextResponse.json( + { error: 'Invalid filename' }, + { status: 400 } + ); + } + + const filePath = path.join(RESOURCE_PATH, project, version, 'apks', filename); + + try { + // 检查文件是否存在 + if (!fs.existsSync(filePath)) { + return NextResponse.json( + { error: 'File not found' }, + { status: 404 } + ); + } + + // 读取文件并流式返回 + const file = fs.readFileSync(filePath); + + return new NextResponse(file, { + headers: { + 'Content-Type': 'application/vnd.android.package-archive', + 'Content-Disposition': `attachment; filename="${filename}"`, + 'Content-Length': file.length.toString(), + }, + }); + } catch (error) { + return NextResponse.json( + { error: 'Failed to download file' }, + { status: 500 } + ); + } +}