Files
giampy-dogservice/src/routes/admin/images/+page.server.ts
T
2026-04-20 13:20:43 +02:00

83 lines
3.2 KiB
TypeScript

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