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; } }