Files

23 lines
1.3 KiB
TypeScript

import { extname } from "node:path";
import { logEvent } from "@/lib/observability";
const defaults = new Set(["image/jpeg", "image/png", "image/gif", "image/webp", "application/pdf", "text/plain", "text/markdown"]);
export async function validateUpload(file: File) {
const allowed = new Set((process.env.UPLOAD_ALLOWED_TYPES || "").split(",").map((item) => item.trim()).filter(Boolean));
const types = allowed.size ? allowed : defaults;
const max = Number(process.env.UPLOAD_MAX_BYTES || 10 * 1024 * 1024);
if (!types.has(file.type)) throw new Error(`不允許的附件類型:${file.type || extname(file.name) || "未知"}`);
if (file.size > max) throw new Error(`${file.name} exceeds upload limit`);
const scanner = process.env.VIRUS_SCAN_URL;
if (!scanner) return;
try {
const response = await fetch(scanner, { method: "POST", headers: { "content-type": file.type || "application/octet-stream", "x-filename": encodeURIComponent(file.name) }, body: await file.arrayBuffer(), signal: AbortSignal.timeout(15_000) });
const result = await response.json().catch(() => ({})) as { clean?: boolean };
if (!response.ok || result.clean !== true) throw new Error("附件未通過掃描");
} catch (error) {
logEvent("warn", "upload_scan_unavailable", { name: file.name });
if (process.env.VIRUS_SCAN_REQUIRED === "1") throw error;
}
}