- Upload images (png/jpeg/webp/gif ≤5 MiB) alongside the text of a billet:
multi-image mini-gallery. New api/services/media.py validates + FULLY
re-encodes every upload via Pillow (drops EXIF/GPS, neutralises polyglots;
SVG refused), producing a cleaned original + a ≤480px thumbnail.
- Storage under BILLETS_MEDIA_DIR (/var/lib/secubox/billets/media), served by
nginx /media/ alias (in-process StaticFiles fallback). img-src 'self' already
covers it — no third party, no CSP relaxation.
- Public: vignette grid in feed + permalink, pure-JS zoomable lightbox with
◀▶ keyboard/arrow navigation (graceful degradation: links open the full
image with JS off). First image drives og:image / twitter:image.
- Admin editor: multipart file input + existing-media thumbnails with delete.
New media table (migration 0002, ON DELETE CASCADE), repo CRUD, media delete
route; billet delete removes files. All Pillow work off the event loop
(asyncio.to_thread).
- Portable backup: GET /admin/export.sbxsite emits every billet + media inlined
as base64 (single re-importable file).
- nginx client_max_body_size 6m; requirements pillow==11.1.0. 104 tests (11 new).
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
Public feed + permalink redesigned funky/party (theme-aware gradients, emoji
accents, playful cards). Embeds now render INLINE in the feed too (responsive
16:9-ish media, CSP frame-src augmented for self-hosted hosts). Inline embedded
reference links shown as 🌐 chips. Prominent emoji quick-share row (Bluesky/X/
WhatsApp/Telegram/Reddit/LinkedIn/email + Mastodon/copy via billets.js) on every
billet in the feed and permalink (replaces the hidden Republier details).
Asset URLs versioned (?v=2) to bust the sbxwaf media cache. 90 tests.
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
Public read surface: keyset (cursor) pagination on (published_at,id), permalinks
with view counting, restricted-markdown -> nh3-sanitized HTML (raw HTML escaped,
no live script), theme-aware CSS, h-entry microformats, strict CSP + security
headers. Plus per-user request: a git-backed revision store (services/revisions)
versioning each billet's content+style into a Gitea repo (front-matter .md,
best-effort push, full git-log history). 31 tests pass.
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>