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 { addTestimonial, deleteTestimonial, getContent, updateTestimonial } from '$lib/server/content'; const UPLOAD_DIR = join(process.cwd(), 'static', 'img', 'uploads'); const MAX_BYTES = 4 * 1024 * 1024; const ALLOWED = new Set(['image/png', 'image/jpeg', 'image/webp']); export const load: PageServerLoad = () => { return { testimonials: getContent().testimonials }; }; function extFromType(type: string): string { if (type === 'image/png') return 'png'; if (type === 'image/webp') return 'webp'; return 'jpg'; } async function saveAvatar(file: File): Promise { if (!existsSync(UPLOAD_DIR)) mkdirSync(UPLOAD_DIR, { recursive: true }); const buf = Buffer.from(await file.arrayBuffer()); const ext = extFromType(file.type); const filename = `avatar-${randomUUID().slice(0, 8)}.${ext}`; writeFileSync(join(UPLOAD_DIR, filename), buf); return `/img/uploads/${filename}`; } function removePrevAvatar(src: string | undefined): void { if (!src?.startsWith('/img/uploads/')) return; const full = join(process.cwd(), 'static', src); try { if (existsSync(full)) unlinkSync(full); } catch { // ignore } } export const actions: Actions = { add: async ({ request }) => { const form = await request.formData(); const text = String(form.get('text') ?? '').trim(); const name = String(form.get('name') ?? '').trim(); const role = String(form.get('role') ?? '').trim(); const avatarAlt = String(form.get('avatarAlt') ?? '').trim() || name; const file = form.get('avatar'); if (!text || !name) return fail(400, { error: 'Testimonianza e nome sono obbligatori.' }); let avatarSrc = '/img/logo.png'; if (file instanceof File && file.size) { if (file.size > MAX_BYTES) return fail(400, { error: 'Avatar troppo grande (max 4 MB).' }); if (file.type && !ALLOWED.has(file.type)) return fail(400, { error: `Formato non supportato: ${file.type}.` }); avatarSrc = await saveAvatar(file); } const entry = addTestimonial({ text, name, role, avatarSrc, avatarAlt }); return { success: true, added: entry.id }; }, update: async ({ request }) => { const form = await request.formData(); const id = String(form.get('id') ?? ''); const text = String(form.get('text') ?? '').trim(); const name = String(form.get('name') ?? '').trim(); const role = String(form.get('role') ?? '').trim(); const avatarAlt = String(form.get('avatarAlt') ?? '').trim() || name; const file = form.get('avatar'); if (!id) return fail(400, { error: 'ID mancante.' }); if (!text || !name) return fail(400, { error: 'Testimonianza e nome sono obbligatori.' }); const current = getContent().testimonials.find((t) => t.id === id); let avatarSrc = current?.avatarSrc ?? '/img/logo.png'; if (file instanceof File && file.size) { if (file.size > MAX_BYTES) return fail(400, { error: 'Avatar troppo grande (max 4 MB).' }); if (file.type && !ALLOWED.has(file.type)) return fail(400, { error: `Formato non supportato: ${file.type}.` }); removePrevAvatar(current?.avatarSrc); avatarSrc = await saveAvatar(file); } const updated = updateTestimonial(id, { text, name, role, avatarSrc, avatarAlt }); if (!updated) return fail(404, { error: 'Testimonianza non trovata.' }); return { success: true, updated: id }; }, delete: async ({ request }) => { const form = await request.formData(); const id = String(form.get('id') ?? ''); if (!id) return fail(400, { error: 'ID mancante.' }); const current = getContent().testimonials.find((t) => t.id === id); removePrevAvatar(current?.avatarSrc); deleteTestimonial(id); return { success: true, deleted: id }; } };