// lib/api/routes/icon_sets.js import { Router } from 'express'; import multer from 'multer'; import { requireOwner } from '../cap.js'; import { asyncWrap, errorMiddleware } from '../errors.js'; import { softAuth } from '../soft_auth.js'; import * as sets from '../../icons/sets.js'; import { processFile, unpackZip, fetchUrl, isZip } from '../../icons/ingest.js'; export const router = Router(); router.use(softAuth); const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 6 * 1024 * 1024, files: 50 } }); // GET /api/icon-sets — list sets + their icons (open; can't send bearer). router.get('/', asyncWrap(async (_req, res) => res.json(await sets.listSets()))); // GET /api/icon-sets/:set/:file — serve one icon. router.get('/:set/:file', asyncWrap(async (req, res) => { let buf; try { buf = await sets.readIcon(req.params.set, req.params.file); } catch (e) { return res.status(e.message === 'bad_slug' ? 400 : 404).end(); } const ct = req.params.file.endsWith('.svg') ? 'image/svg+xml' : 'image/png'; res.set('Content-Type', ct).set('Cache-Control', 'public, max-age=86400').send(buf); })); // POST /api/icon-sets/:set — owner upload: multipart files (incl .zip) and/or { url }. router.post('/:set', requireOwner, upload.array('files'), asyncWrap(async (req, res) => { const set = req.params.set; const items = []; // [{name, buffer}] for (const f of req.files || []) { if (isZip(f.buffer)) items.push(...unpackZip(f.buffer)); else items.push(processFile({ name: f.originalname, buffer: f.buffer })); } if (req.body?.url) { const { buffer } = await fetchUrl(req.body.url); if (isZip(buffer)) items.push(...unpackZip(buffer)); else { const name = new URL(req.body.url).pathname.split('/').pop() || 'icon.png'; items.push(processFile({ name, buffer })); } } if (!items.length) return res.status(400).json({ error: { code: 'no_icons' } }); for (const it of items) await sets.writeIcon(set, it.name, it.buffer); res.json((await sets.listSets()).find(s => s.set === set) || { set, icons: [] }); })); // DELETE /api/icon-sets/:set — owner remove an uploaded set. router.delete('/:set', requireOwner, asyncWrap(async (req, res) => { try { await sets.deleteSet(req.params.set); } catch (e) { return res.status(e.message === 'reserved_set' ? 409 : 400).json({ error: { code: e.message } }); } res.json({ ok: true }); })); router.use(errorMiddleware);