244 lines
8.3 KiB
JavaScript
244 lines
8.3 KiB
JavaScript
import { NextResponse } from 'next/server';
|
||
import { getProject } from '@/lib/db/projects';
|
||
import path from 'path';
|
||
import { getProjectRoot, ensureDir } from '@/lib/db/base';
|
||
import { promises as fs } from 'fs';
|
||
import {
|
||
checkUploadFileInfoByMD5,
|
||
createUploadFileInfo,
|
||
delUploadFileInfoById,
|
||
getUploadFilesPagination
|
||
} from '@/lib/db/upload-files';
|
||
import { getFileMD5 } from '@/lib/util/file';
|
||
import { batchSaveTags } from '@/lib/db/tags';
|
||
import { getProjectChunks, getProjectTocByName } from '@/lib/file/text-splitter';
|
||
import { handleDomainTree } from '@/lib/util/domain-tree';
|
||
|
||
// Replace the deprecated config export with the new export syntax
|
||
export const dynamic = 'force-dynamic';
|
||
// This tells Next.js not to parse the request body automatically
|
||
export const bodyParser = false;
|
||
|
||
// 获取项目文件列表
|
||
export async function GET(request, { params }) {
|
||
try {
|
||
const { projectId } = params;
|
||
|
||
// 验证项目ID
|
||
if (!projectId) {
|
||
return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 });
|
||
}
|
||
const { searchParams } = new URL(request.url);
|
||
const page = parseInt(searchParams.get('page')) || 1;
|
||
const pageSize = parseInt(searchParams.get('pageSize')) || 10; // 每页10个文件,支持分页
|
||
const fileName = searchParams.get('fileName') || '';
|
||
const getAllIds = searchParams.get('getAllIds') === 'true'; // 新增:获取所有文件ID的标志
|
||
|
||
// 如果请求所有文件ID,直接返回ID列表
|
||
if (getAllIds) {
|
||
const allFiles = await getUploadFilesPagination(projectId, 1, 9999, fileName); // 获取所有文件
|
||
const allFileIds = allFiles.data?.map(file => String(file.id)) || [];
|
||
return NextResponse.json({ allFileIds });
|
||
}
|
||
// 获取文件列表
|
||
const files = await getUploadFilesPagination(projectId, page, pageSize, fileName);
|
||
|
||
return NextResponse.json(files);
|
||
} catch (error) {
|
||
console.error('Error obtaining file list:', String(error));
|
||
return NextResponse.json({ error: error.message || 'Error obtaining file list' }, { status: 500 });
|
||
}
|
||
}
|
||
|
||
// 删除文件
|
||
export async function DELETE(request, { params }) {
|
||
try {
|
||
const { projectId } = params;
|
||
const { searchParams } = new URL(request.url);
|
||
const fileId = searchParams.get('fileId');
|
||
const domainTreeAction = searchParams.get('domainTreeAction') || 'keep';
|
||
|
||
// 从请求体中获取模型信息和语言环境
|
||
const requestData = await request.json();
|
||
const model = requestData.model;
|
||
const language = requestData.language || 'en';
|
||
|
||
// 验证项目ID和文件名
|
||
if (!projectId) {
|
||
return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 });
|
||
}
|
||
|
||
if (!fileId) {
|
||
return NextResponse.json({ error: 'The file name cannot be empty' }, { status: 400 });
|
||
}
|
||
|
||
// 获取项目信息
|
||
const project = await getProject(projectId);
|
||
if (!project) {
|
||
return NextResponse.json({ error: 'The project does not exist' }, { status: 404 });
|
||
}
|
||
|
||
// 删除文件及其相关的文本块、问题和数据集
|
||
const { stats, fileName, fileInfo } = await delUploadFileInfoById(fileId);
|
||
const deleteToc = await getProjectTocByName(projectId, fileName);
|
||
try {
|
||
const projectRoot = await getProjectRoot();
|
||
const projectPath = path.join(projectRoot, projectId);
|
||
const tocDir = path.join(projectPath, 'toc');
|
||
const baseName = path.basename(fileInfo.fileName, path.extname(fileInfo.fileName));
|
||
const tocPath = path.join(tocDir, `${baseName}-toc.json`);
|
||
|
||
// 检查文件是否存在再删除
|
||
await fs.unlink(tocPath);
|
||
console.log(`成功删除 TOC 文件: ${tocPath}`);
|
||
} catch (error) {
|
||
console.error(`删除 TOC 文件失败:`, String(error));
|
||
// 即使 TOC 文件删除失败,不影响整体结果
|
||
}
|
||
|
||
// 如果选择了保持领域树不变,直接返回删除结果
|
||
if (domainTreeAction === 'keep') {
|
||
return NextResponse.json({
|
||
message: '文件删除成功',
|
||
stats: stats,
|
||
domainTreeAction: 'keep',
|
||
cascadeDelete: true
|
||
});
|
||
}
|
||
|
||
// 处理领域树更新
|
||
try {
|
||
// 获取项目的所有文件
|
||
const { chunks, toc } = await getProjectChunks(projectId);
|
||
|
||
// 如果不存在文本块,说明项目已经没有文件了
|
||
if (!chunks || chunks.length === 0) {
|
||
// 清空领域树
|
||
await batchSaveTags(projectId, []);
|
||
return NextResponse.json({
|
||
message: '文件删除成功,领域树已清空',
|
||
stats: stats,
|
||
domainTreeAction,
|
||
cascadeDelete: true
|
||
});
|
||
}
|
||
|
||
// 调用领域树处理模块
|
||
await handleDomainTree({
|
||
projectId,
|
||
action: domainTreeAction,
|
||
allToc: toc,
|
||
model,
|
||
language,
|
||
deleteToc,
|
||
project
|
||
});
|
||
} catch (error) {
|
||
console.error('Error updating domain tree after file deletion:', String(error));
|
||
// 即使领域树更新失败,也不影响文件删除的结果
|
||
}
|
||
|
||
return NextResponse.json({
|
||
message: '文件删除成功',
|
||
stats: stats,
|
||
domainTreeAction,
|
||
cascadeDelete: true
|
||
});
|
||
} catch (error) {
|
||
console.error('Error deleting file:', String(error));
|
||
return NextResponse.json({ error: error.message || 'Error deleting file' }, { status: 500 });
|
||
}
|
||
}
|
||
|
||
// 上传文件
|
||
export async function POST(request, { params }) {
|
||
console.log('File upload request processing, parameters:', params);
|
||
const { projectId } = params;
|
||
|
||
// 验证项目ID
|
||
if (!projectId) {
|
||
console.log('The project ID cannot be empty, returning 400 error');
|
||
return NextResponse.json({ error: 'The project ID cannot be empty' }, { status: 400 });
|
||
}
|
||
|
||
// 获取项目信息
|
||
const project = await getProject(projectId);
|
||
if (!project) {
|
||
console.log('The project does not exist, returning 404 error');
|
||
return NextResponse.json({ error: 'The project does not exist' }, { status: 404 });
|
||
}
|
||
console.log('Project information retrieved successfully:', project.name || project.id);
|
||
|
||
try {
|
||
console.log('Try using alternate methods for file upload...');
|
||
|
||
// 检查请求头中是否包含文件名
|
||
const encodedFileName = request.headers.get('x-file-name');
|
||
const fileName = encodedFileName ? decodeURIComponent(encodedFileName) : null;
|
||
console.log('Get file name from request header:', fileName);
|
||
|
||
if (!fileName) {
|
||
console.log('The request header does not contain a file name');
|
||
return NextResponse.json(
|
||
{ error: 'The request header does not contain a file name (x-file-name)' },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// 检查文件类型
|
||
if (!fileName.endsWith('.md') && !fileName.endsWith('.pdf')) {
|
||
return NextResponse.json({ error: 'Only Markdown files are supported' }, { status: 400 });
|
||
}
|
||
|
||
// 直接从请求体中读取二进制数据
|
||
const fileBuffer = Buffer.from(await request.arrayBuffer());
|
||
|
||
// 保存文件
|
||
const projectRoot = await getProjectRoot();
|
||
const projectPath = path.join(projectRoot, projectId);
|
||
const filesDir = path.join(projectPath, 'files');
|
||
|
||
await ensureDir(filesDir);
|
||
|
||
const filePath = path.join(filesDir, fileName);
|
||
await fs.writeFile(filePath, fileBuffer);
|
||
//获取文件大小
|
||
const stats = await fs.stat(filePath);
|
||
//获取文件md5
|
||
const md5 = await getFileMD5(filePath);
|
||
//获取文件扩展名
|
||
const ext = path.extname(filePath);
|
||
|
||
// let res = await checkUploadFileInfoByMD5(projectId, md5);
|
||
// if (res) {
|
||
// return NextResponse.json({ error: `【${fileName}】该文件已在此项目中存在` }, { status: 400 });
|
||
// }
|
||
|
||
let fileInfo = await createUploadFileInfo({
|
||
projectId,
|
||
fileName,
|
||
size: stats.size,
|
||
md5,
|
||
fileExt: ext,
|
||
path: filesDir
|
||
});
|
||
|
||
console.log('The file upload process is complete, and a successful response is returned');
|
||
return NextResponse.json({
|
||
message: 'File uploaded successfully',
|
||
fileName,
|
||
filePath,
|
||
fileId: fileInfo.id
|
||
});
|
||
} catch (error) {
|
||
console.error('Error processing file upload:', String(error));
|
||
console.error('Error stack:', error.stack);
|
||
return NextResponse.json(
|
||
{
|
||
error: 'File upload failed: ' + (error.message || 'Unknown error')
|
||
},
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|