feat(upload): 支持 Agent 本地图片安全上传
This commit is contained in:
@@ -4,7 +4,7 @@ import { notesRepository } from '../repositories/notesRepository.js';
|
||||
import { imagesRepository } from '../repositories/imagesRepository.js';
|
||||
import { annotationsRepository } from '../repositories/annotationsRepository.js';
|
||||
import { database, databaseDialect, withTransaction, type QueryContext } from '../database.js';
|
||||
import { storeExternalImageUrl, storeUploadedFile } from '../storage.js';
|
||||
import { StorageImportError, storeExternalImageUrl, storeUploadedFile } from '../storage.js';
|
||||
import { recalculateCollectionStatus } from './collectionsService.js';
|
||||
import { ensureProjectCompatibilityCollection, recalculateProjectReviewStatus } from './projectsService.js';
|
||||
|
||||
@@ -15,13 +15,34 @@ export interface UrlRound { title: string; description: string; tags: string[];
|
||||
type StoredImage = { url: string; width: number; height: number; storageProvider: 'local' | 'tencent_cos' | 'external'; storageKey: string };
|
||||
type PreparedRound = { title: string; description: string; tags: string[]; images: StoredImage[] };
|
||||
|
||||
async function readImageSize(filePath: string): Promise<{ width: number; height: number }> {
|
||||
try { const meta = await sharp(filePath).metadata(); return { width: meta.width ?? 0, height: meta.height ?? 0 }; }
|
||||
catch { return { width: 0, height: 0 }; }
|
||||
}
|
||||
|
||||
async function prepareFiles(files: UploadedFile[]): Promise<StoredImage[]> {
|
||||
return Promise.all(files.map(async (file) => ({ ...(await readImageSize(file.path)), ...(await storeUploadedFile(file)) })));
|
||||
const supported = new Map([
|
||||
['jpeg', { extension: '.jpg', contentType: 'image/jpeg' }],
|
||||
['png', { extension: '.png', contentType: 'image/png' }],
|
||||
['gif', { extension: '.gif', contentType: 'image/gif' }],
|
||||
['webp', { extension: '.webp', contentType: 'image/webp' }],
|
||||
['avif', { extension: '.avif', contentType: 'image/avif' }],
|
||||
]);
|
||||
const inspected = await Promise.all(files.map(async (file) => {
|
||||
try {
|
||||
const metadata = await sharp(file.path).metadata();
|
||||
const detected = metadata.format ? supported.get(metadata.format) : undefined;
|
||||
if (!detected || !metadata.width || !metadata.height) throw new Error('invalid image');
|
||||
return { file, width: metadata.width, height: metadata.height, ...detected };
|
||||
} catch {
|
||||
throw new StorageImportError(422, `上传文件不是有效或受支持的图片: ${file.originalname || file.filename}`);
|
||||
}
|
||||
}));
|
||||
const prepared: StoredImage[] = [];
|
||||
for (const item of inspected) {
|
||||
const stored = await storeUploadedFile({
|
||||
...item.file,
|
||||
originalname: `upload${item.extension}`,
|
||||
mimetype: item.contentType,
|
||||
});
|
||||
prepared.push({ width: item.width, height: item.height, ...stored });
|
||||
}
|
||||
return prepared;
|
||||
}
|
||||
|
||||
async function prepareExternalImages(images: string[]): Promise<StoredImage[]> {
|
||||
|
||||
Reference in New Issue
Block a user