import type { Database } from '@tursodatabase/database'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import sharp from 'sharp'; import { BackgroundRemovalError, removeBackgroundPreview } from './remove-background'; export const MANAGED_IMAGE_CATEGORIES = [ 'General', 'Interface', 'Gear', 'Weapons', 'Weapon Mods', 'Gear Mods', 'Talents', 'Brands', 'OS Protocols', 'Skill Chips', 'System' ] as const; export const MAX_MANAGED_IMAGE_BYTES = 5 * 1024 * 1024; const MAX_PIXELS = 24_000_000; const ID_PATTERN = /^[0-9a-f-]{36}$/i; const KEY_PATTERN = /^library-[0-9a-f-]{36}\.webp$/i; export class ManagedImageValidationError extends Error {} export function managedImageStorageDirectory() { return path.resolve(process.env.TDR_GAME_ICON_STORAGE_PATH || path.join(process.cwd(), 'data', 'game-icons')); } const storagePath = (key: string) => { if (!KEY_PATTERN.test(key)) throw new Error('Invalid managed image storage key'); return path.join(managedImageStorageDirectory(), key); }; export function managedImageUrl(id: string, updatedAt?: unknown) { if (!ID_PATTERN.test(id)) return ''; const version = typeof updatedAt === 'string' ? `?v=${encodeURIComponent(updatedAt)}` : ''; return `/images/library/${id}${version}`; } function validateSvg(input: Buffer) { const source = input.toString('utf8'); if (/<(?:script|foreignObject|image|iframe|object|embed)\b/i.test(source) || / MAX_MANAGED_IMAGE_BYTES) { throw new ManagedImageValidationError('Choose an image no larger than 5 MiB.'); } const input = Buffer.from(await file.arrayBuffer()); try { const looksLikeSvg = file.type === 'image/svg+xml' || input.subarray(0, 512).toString('utf8').toLowerCase().includes('