/** * Port-logo upload service. * * Layer 1 of the design (`docs/superpowers/specs/2026-05-12-pdf-stack-overhaul-design.md`): * sharp normalization + svgo sanitization + atomic system_settings upsert + * audit logging. Single entry point for the admin upload endpoint and the * branding-preview endpoint. Returns processed bytes + the file row, plus a * collected list of warnings the UI surfaces in the preview swatch. */ import { and, eq } from 'drizzle-orm'; import sharp from 'sharp'; import { optimize as svgoOptimize } from 'svgo'; import { db } from '@/lib/db'; import { files } from '@/lib/db/schema/documents'; import { systemSettings } from '@/lib/db/schema/system'; import { ports } from '@/lib/db/schema/ports'; import { createAuditLog, type AuditMeta } from '@/lib/audit'; import { ValidationError } from '@/lib/errors'; import { getStorageBackend } from '@/lib/storage'; import { generateStorageKey } from '@/lib/services/storage'; import { logger } from '@/lib/logger'; import { PORT_LOGO_SETTING_KEY } from '@/lib/pdf/brand-kit/logo'; const MAX_RAW_BYTES = 5 * 1024 * 1024; const MAX_FINAL_BYTES = 1 * 1024 * 1024; const MIN_DIMENSION = 200; const TARGET_LONG_EDGE = 1200; const SVG_RASTER_DENSITY = 300; const SUPPORTED_INPUT_FORMATS = new Set([ 'png', 'jpeg', 'jpg', 'webp', 'svg', 'heic', 'heif', 'avif', ]); export type LogoWarning = | 'trimmed' | 'resized' | 'no-alpha' | 'jpeg-source' | 'svg-rasterized' | 'heic-converted' | 'webp-converted'; export interface LogoCrop { /** Crop coordinates in raw-image pixel units. */ x: number; y: number; width: number; height: number; } export interface ProcessLogoResult { pngBuffer: Buffer; warnings: LogoWarning[]; originalFormat: string; originalDimensions: { width: number; height: number }; finalDimensions: { width: number; height: number }; finalBytes: number; } function sanitizeSvg(rawSvg: string): string { // preset-default strips