import type { Actions, PageServerLoad } from './$types'; import { fail } from '@sveltejs/kit'; import { writeFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs'; import { join } from 'node:path'; import { randomUUID } from 'node:crypto'; import { getContent, resetImage, setImage } from '$lib/server/content'; import { DEFAULT_IMAGES } from '$lib/content/defaults'; const UPLOAD_DIR = join(process.cwd(), 'static', 'img', 'uploads'); const MAX_BYTES = 8 * 1024 * 1024; // 8 MB const ALLOWED = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif', 'image/svg+xml']); const LOCKED_SLOTS = new Set(['cima_logo']); export const load: PageServerLoad = () => { const content = getContent(); const images: typeof content.images = {}; for (const [id, img] of Object.entries(content.images)) { if (LOCKED_SLOTS.has(id)) continue; images[id] = img; } return { images }; }; function extFromType(type: string, fallbackName: string): string { if (type === 'image/png') return 'png'; if (type === 'image/jpeg') return 'jpg'; if (type === 'image/webp') return 'webp'; if (type === 'image/gif') return 'gif'; if (type === 'image/svg+xml') return 'svg'; const m = /\.([a-zA-Z0-9]+)$/.exec(fallbackName); return m ? m[1].toLowerCase() : 'bin'; } export const actions: Actions = { upload: async ({ request }) => { if (!existsSync(UPLOAD_DIR)) mkdirSync(UPLOAD_DIR, { recursive: true }); const form = await request.formData(); const slot = String(form.get('slot') ?? ''); const alt = String(form.get('alt') ?? '').trim(); const file = form.get('file'); if (!slot || !(slot in DEFAULT_IMAGES) || LOCKED_SLOTS.has(slot)) return fail(400, { error: 'Slot immagine non valido.' }); if (!(file instanceof File) || !file.size) { // alt-only update if (alt) { setImage(slot, { alt }); return { success: true, slot, altOnly: true }; } return fail(400, { error: 'Nessun file caricato.' }); } if (file.size > MAX_BYTES) return fail(400, { error: 'File troppo grande (max 8 MB).' }); if (file.type && !ALLOWED.has(file.type)) return fail(400, { error: `Tipo file non supportato: ${file.type}.` }); const buf = Buffer.from(await file.arrayBuffer()); const ext = extFromType(file.type, file.name); const filename = `${slot}-${randomUUID().slice(0, 8)}.${ext}`; const fullPath = join(UPLOAD_DIR, filename); writeFileSync(fullPath, buf); const publicPath = `/img/uploads/${filename}`; const next = setImage(slot, { src: publicPath, alt: alt || undefined }); return { success: true, slot, src: next.src }; }, reset: async ({ request }) => { const form = await request.formData(); const slot = String(form.get('slot') ?? ''); if (!slot || !(slot in DEFAULT_IMAGES) || LOCKED_SLOTS.has(slot)) return fail(400, { error: 'Slot non valido.' }); const current = getContent().images[slot]; if (current?.src?.startsWith('/img/uploads/')) { const fullPath = join(process.cwd(), 'static', current.src); try { if (existsSync(fullPath)) unlinkSync(fullPath); } catch { // ignore } } resetImage(slot); return { success: true, slot, reset: true }; } };