first-update

This commit is contained in:
2026-03-17 14:36:31 +08:00
parent 72f08aee7c
commit 4eddf05e79
516 changed files with 115270 additions and 1 deletions

View File

@@ -0,0 +1,109 @@
import { NextResponse } from 'next/server';
import { getImageDatasetById, updateImageDataset, deleteImageDataset } from '@/lib/db/imageDatasets';
import { getProjectPath } from '@/lib/db/base';
import fs from 'fs/promises';
import path from 'path';
// 获取单个数据集详情
export async function GET(request, { params }) {
try {
const { projectId, datasetId } = params;
const dataset = await getImageDatasetById(datasetId);
if (!dataset || dataset.projectId !== projectId) {
return NextResponse.json({ error: 'Dataset not found' }, { status: 404 });
}
// 获取项目路径
const projectPath = await getProjectPath(projectId);
// 读取图片 base64
let base64 = null;
try {
const imagePath = path.join(projectPath, 'images', dataset.imageName);
const imageBuffer = await fs.readFile(imagePath);
const base64Data = imageBuffer.toString('base64');
const ext = path.extname(dataset.imageName).toLowerCase();
const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg';
base64 = `data:${mimeType};base64,${base64Data}`;
} catch (error) {
console.error(`Failed to read image ${dataset.imageName}:`, error);
}
// 添加图片 base64
const datasetWithImage = {
...dataset,
base64
};
return NextResponse.json(datasetWithImage);
} catch (error) {
console.error('Failed to get dataset detail:', error);
return NextResponse.json({ error: error.message || 'Failed to get dataset detail' }, { status: 500 });
}
}
// 更新数据集
export async function PUT(request, { params }) {
try {
const { projectId, datasetId } = params;
const updates = await request.json();
// 验证数据集存在且属于该项目
const dataset = await getImageDatasetById(datasetId);
if (!dataset || dataset.projectId !== projectId) {
return NextResponse.json({ error: 'Dataset not found' }, { status: 404 });
}
// 更新数据集
const updated = await updateImageDataset(datasetId, updates);
// 获取项目路径
const projectPath = await getProjectPath(projectId);
// 读取图片 base64
let base64 = null;
try {
const imagePath = path.join(projectPath, 'images', updated.imageName);
const imageBuffer = await fs.readFile(imagePath);
const base64Data = imageBuffer.toString('base64');
const ext = path.extname(updated.imageName).toLowerCase();
const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg';
base64 = `data:${mimeType};base64,${base64Data}`;
} catch (error) {
console.error(`Failed to read image ${updated.imageName}:`, error);
}
// 添加图片 base64
const updatedWithImage = {
...updated,
base64
};
return NextResponse.json(updatedWithImage);
} catch (error) {
console.error('Failed to update dataset:', error);
return NextResponse.json({ error: error.message || 'Failed to update dataset' }, { status: 500 });
}
}
// 删除数据集
export async function DELETE(request, { params }) {
try {
const { projectId, datasetId } = params;
// 验证数据集存在且属于该项目
const dataset = await getImageDatasetById(datasetId);
if (!dataset || dataset.projectId !== projectId) {
return NextResponse.json({ error: 'Dataset not found' }, { status: 404 });
}
await deleteImageDataset(datasetId);
return NextResponse.json({ success: true });
} catch (error) {
console.error('Failed to delete dataset:', error);
return NextResponse.json({ error: error.message || 'Failed to delete dataset' }, { status: 500 });
}
}

View File

@@ -0,0 +1,85 @@
import { NextResponse } from 'next/server';
import { getImageDatasetsForExport } from '@/lib/db/imageDatasets';
import archiver from 'archiver';
import { getProjectPath } from '@/lib/db/base';
import path from 'path';
import fs from 'fs';
/**
* 导出图片文件压缩包
*/
export async function GET(request, { params }) {
try {
const { projectId } = params;
const { searchParams } = new URL(request.url);
const confirmedOnly = searchParams.get('confirmedOnly') === 'true';
// 验证项目ID
if (!projectId) {
return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 });
}
// 获取数据集(用于确定需要哪些图片)
const datasets = await getImageDatasetsForExport(projectId, confirmedOnly);
if (!datasets || datasets.length === 0) {
return NextResponse.json({ error: 'No data to export' }, { status: 404 });
}
// 获取所有需要的图片名称
const imageNames = new Set(datasets.map(d => d.imageName).filter(Boolean));
if (imageNames.size === 0) {
return NextResponse.json({ error: 'No images to export' }, { status: 404 });
}
// 创建压缩包
const archive = archiver('zip', {
zlib: { level: 9 }
});
// 设置响应头
const dateStr = new Date().toISOString().slice(0, 10);
const filename = `images-${projectId}-${dateStr}.zip`;
// 添加图片文件到压缩包
const projectPath = await getProjectPath(projectId);
const imageDir = path.join(projectPath, 'images');
if (!fs.existsSync(imageDir)) {
return NextResponse.json({ error: 'Image directory not found' }, { status: 404 });
}
let addedCount = 0;
for (const imageName of imageNames) {
const imagePath = path.join(imageDir, imageName);
if (fs.existsSync(imagePath)) {
archive.file(imagePath, { name: imageName });
addedCount++;
}
}
if (addedCount === 0) {
return NextResponse.json({ error: 'No image files found' }, { status: 404 });
}
// 完成压缩
archive.finalize();
// 返回流式响应
return new NextResponse(archive, {
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${filename}"`
}
});
} catch (error) {
console.error('Failed to export images:', String(error));
return NextResponse.json(
{
error: error.message || 'Failed to export images'
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,32 @@
import { NextResponse } from 'next/server';
import { getImageDatasetsForExport } from '@/lib/db/imageDatasets';
/**
* 导出图像数据集
*/
export async function POST(request, { params }) {
try {
const { projectId } = params;
const body = await request.json();
// 验证项目ID
if (!projectId) {
return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 });
}
const confirmedOnly = body.confirmedOnly || false;
// 获取数据集
const datasets = await getImageDatasetsForExport(projectId, confirmedOnly);
return NextResponse.json(datasets);
} catch (error) {
console.error('Failed to export image datasets:', String(error));
return NextResponse.json(
{
error: error.message || 'Failed to export image datasets'
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,72 @@
import { NextResponse } from 'next/server';
import { getImageDatasetsByProject } from '@/lib/db/imageDatasets';
import { getProjectPath } from '@/lib/db/base';
import fs from 'fs/promises';
import path from 'path';
// 获取图片数据集列表
export async function GET(request, { params }) {
try {
const { projectId } = params;
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page')) || 1;
const pageSize = parseInt(searchParams.get('pageSize')) || 20;
const search = searchParams.get('search') || '';
const confirmed = searchParams.get('confirmed');
const minScore = searchParams.get('minScore');
const maxScore = searchParams.get('maxScore');
// 构建筛选条件
const filters = {};
if (search) {
filters.search = search;
}
if (confirmed !== null && confirmed !== undefined) {
filters.confirmed = confirmed === 'true';
}
if (minScore) {
filters.minScore = parseInt(minScore);
}
if (maxScore) {
filters.maxScore = parseInt(maxScore);
}
const result = await getImageDatasetsByProject(projectId, page, pageSize, filters);
// 获取项目路径
const projectPath = await getProjectPath(projectId);
// 为每个数据集添加图片 base64
const datasetsWithImages = await Promise.all(
result.data.map(async dataset => {
try {
const imagePath = path.join(projectPath, 'images', dataset.imageName);
const imageBuffer = await fs.readFile(imagePath);
const base64 = imageBuffer.toString('base64');
const ext = path.extname(dataset.imageName).toLowerCase();
const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg';
return {
...dataset,
base64: `data:${mimeType};base64,${base64}`
};
} catch (error) {
console.error(`Failed to read image ${dataset.imageName}:`, error);
return {
...dataset,
base64: null
};
}
})
);
return NextResponse.json({
data: datasetsWithImages,
total: result.total
});
} catch (error) {
console.error('Failed to get image datasets:', error);
return NextResponse.json({ error: error.message || 'Failed to get image datasets' }, { status: 500 });
}
}

View File

@@ -0,0 +1,37 @@
import { NextResponse } from 'next/server';
import { getImageDatasetsTagsByProject } from '@/lib/db/imageDatasets';
// 获取项目中所有已使用的标签
export async function GET(request, { params }) {
try {
const { projectId } = params;
// 获取项目的所有数据集
const datasets = await getImageDatasetsTagsByProject(projectId);
console.log('datasets', datasets);
// 提取所有标签
const tagsSet = new Set();
datasets.forEach(dataset => {
if (dataset.tags) {
try {
const tags = JSON.parse(dataset.tags);
if (Array.isArray(tags)) {
tags.forEach(tag => tagsSet.add(tag));
}
} catch (e) {
// 忽略解析错误
}
}
});
// 转换为数组并排序
const tags = Array.from(tagsSet).sort();
return NextResponse.json({ tags });
} catch (error) {
console.error('Failed to get tags:', error);
return NextResponse.json({ error: error.message || 'Failed to get tags' }, { status: 500 });
}
}