Files
ai-scoring-application/app/api/upload/route.ts

141 lines
4.4 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 { writeFile, mkdir } from 'fs/promises';
import { ProjectService, ProjectFileService, CriteriaItemService } from '@/lib/services/database';
import {
getUploadAbsolutePath,
getUploadRelativePath,
getUploadDirPath,
generateUniqueFileName,
isValidFileType,
isValidFileSize
} from '@/lib/utils/file-path';
export async function POST(request: NextRequest) {
try {
const formData = await request.formData();
const projectTitle = formData.get('projectTitle') as string;
const projectDescription = formData.get('projectDescription') as string;
const file = formData.get('file') as File;
console.log('🚀 開始處理檔案上傳...');
console.log('📝 專案標題:', projectTitle);
console.log('📋 專案描述:', projectDescription);
console.log('📁 上傳文件:', file ? file.name : '無');
// 驗證必填欄位
if (!projectTitle?.trim()) {
return NextResponse.json(
{ success: false, error: '請填寫專案標題' },
{ status: 400 }
);
}
if (!file) {
return NextResponse.json(
{ success: false, error: '請上傳文件' },
{ status: 400 }
);
}
// 驗證檔案類型
if (!isValidFileType(file.type)) {
return NextResponse.json(
{ success: false, error: '不支援的檔案類型' },
{ status: 400 }
);
}
// 驗證檔案大小100MB
if (!isValidFileSize(file.size, 100)) {
return NextResponse.json(
{ success: false, error: '檔案大小超過 100MB 限制' },
{ status: 400 }
);
}
// 獲取預設評分標準模板
const templates = await CriteriaItemService.getAllTemplates();
if (!templates || templates.length === 0) {
return NextResponse.json(
{ success: false, error: '未找到評分標準,請先設定評分標準' },
{ status: 400 }
);
}
const templateId = templates[0].id;
console.log('📊 使用評分標準模板 ID:', templateId);
// 創建專案記錄
const projectData = {
user_id: 1, // 暫時使用固定用戶 ID
template_id: templateId,
title: projectTitle,
description: projectDescription || null,
status: 'uploading' as const, // 初始狀態為上傳中
analysis_started_at: null,
analysis_completed_at: null
};
console.log('💾 創建專案記錄...');
const projectResult = await ProjectService.create(projectData);
const projectId = (projectResult as any).insertId;
console.log('✅ 專案記錄創建成功ID:', projectId);
// 準備檔案儲存
const uploadDir = getUploadDirPath(projectId);
await mkdir(uploadDir, { recursive: true });
// 生成唯一檔案名稱
const uniqueFileName = generateUniqueFileName(file.name);
const filePath = getUploadAbsolutePath(projectId, uniqueFileName);
const relativeFilePath = getUploadRelativePath(projectId, uniqueFileName);
// 儲存檔案
console.log('💾 儲存檔案到:', filePath);
console.log('📁 相對路徑:', relativeFilePath);
const bytes = await file.arrayBuffer();
await writeFile(filePath, Buffer.from(bytes));
// 創建檔案記錄
const fileExtension = file.name.split('.').pop();
const fileData = {
project_id: projectId,
original_name: file.name,
file_name: uniqueFileName,
file_path: relativeFilePath, // 使用相對路徑
file_size: file.size,
file_type: fileExtension || '',
mime_type: file.type,
upload_status: 'completed' as const,
upload_progress: 100
};
console.log('💾 創建檔案記錄...');
await ProjectFileService.create(fileData);
console.log('✅ 檔案記錄創建成功');
// 更新專案狀態為 completed檔案上傳完成
console.log('🔄 更新專案狀態為 completed...');
await ProjectService.update(projectId, { status: 'completed' });
console.log('✅ 專案狀態更新完成');
return NextResponse.json({
success: true,
data: {
projectId,
fileName: file.name,
fileSize: file.size,
fileType: file.type
},
message: '檔案上傳成功'
});
} catch (error) {
console.error('❌ 檔案上傳失敗:', error);
return NextResponse.json(
{ success: false, error: '檔案上傳失敗,請稍後再試' },
{ status: 500 }
);
}
}