refactor: Business logic and dependencies updates

- 核心服务代码更新 (db, server, auth, proxy)
- Agent 相关模块更新 (mindspace, experience)
- 前端组件和 hooks 更新
- 数据库 schema 更新
- 依赖版本更新
This commit is contained in:
john
2026-06-27 08:25:02 +08:00
parent f1220a7905
commit 25f8223253
20 changed files with 1458 additions and 116 deletions
+52 -1
View File
@@ -32,7 +32,7 @@ const ALLOWED_EXTENSIONS = new Map([
['.html', 'text/html'],
['.htm', 'text/html'],
]);
const MAX_IMAGE_UPLOAD_BYTES = 1536 * 1024;
const MAX_IMAGE_UPLOAD_BYTES = 2 * 1024 * 1024;
const PUBLIC_TEMP_IMAGE_DIR = '.tmp-images';
const PUBLIC_IMAGE_EXTENSIONS = new Map([
@@ -65,6 +65,54 @@ function expectedMimeType(filename) {
return ALLOWED_EXTENSIONS.get(path.extname(filename).toLowerCase()) ?? null;
}
function extractImageDimensions(buffer, mimeType) {
if (mimeType === 'image/png' && buffer.length >= 24) {
const width = buffer.readUInt32BE(16);
const height = buffer.readUInt32BE(20);
return { width, height };
}
if (mimeType === 'image/jpeg' && buffer.length >= 2) {
let offset = 2;
while (offset < buffer.length - 9) {
if (buffer[offset] !== 0xff) break;
const marker = buffer[offset + 1];
if (marker === 0xd9) break;
if (marker >= 0xd0 && marker <= 0xd8) {
offset += 2;
continue;
}
const segmentLength = buffer.readUInt16BE(offset + 2);
if (marker === 0xc0 || marker === 0xc1 || marker === 0xc2) {
const height = buffer.readUInt16BE(offset + 5);
const width = buffer.readUInt16BE(offset + 7);
return { width, height };
}
offset += segmentLength + 2;
}
}
if (mimeType === 'image/webp' && buffer.length >= 30) {
const width = buffer.readUInt32LE(24) + 1;
const height = buffer.readUInt32LE(28) + 1;
return { width, height };
}
return null;
}
function validateImagePixels(buffer, mimeType) {
if (!mimeType?.startsWith('image/')) return null;
const dims = extractImageDimensions(buffer, mimeType);
if (!dims) return { ok: true };
const { width, height } = dims;
const megapixels = (width * height) / 1_000_000;
if (megapixels > 25) {
throw Object.assign(
new Error(`图片像素超过限制(${megapixels.toFixed(1)}MP > 25MP`),
{ code: 'image_pixel_exceeded' }
);
}
return { ok: true, width, height };
}
function detectMimeType(buffer, filename) {
const head = buffer.subarray(0, 256).toString('utf8').trimStart().toLowerCase();
if (head.startsWith('<!doctype html') || head.startsWith('<html')) return 'text/html';
@@ -386,6 +434,9 @@ export function createAssetService(pool, options = {}) {
if (detectedMimeType.startsWith('image/') && buffer.length > MAX_IMAGE_UPLOAD_BYTES) {
throw Object.assign(new Error('图片文件超过单文件大小限制'), { code: 'file_too_large' });
}
if (detectedMimeType.startsWith('image/')) {
validateImagePixels(buffer, detectedMimeType);
}
const target = absoluteStoragePath(upload.temporary_storage_key);
await fs.mkdir(path.dirname(target), { recursive: true });