Commit Graph

5 Commits

Author SHA1 Message Date
a95f157eb1 feat(billets): communiqué style + embed snapshot vignette + portable HTML archive (ref #851)
- Per-billet 'style' (default|communique). Communiqué permalink = the poster
  mockup (Space Grotesk/JetBrains Mono, ROUTE side-band, TRANSMISSION frame,
  slogans/footer); funky feed unchanged.
- Embed snapshot vignette: api/services/snapshot.py tries headless Chromium
  (guarded/optional), falls back to SSRF-guarded og:image, re-encoded via the
  media service. Captured off-loop on save (communiqué billets, cached per
  embed_url). Public render shows the vignette; click-to-load the live iframe.
- Portable single-HTML archive: GET /admin/billets/{id}/archive.html — self-
  contained (inlined CSS, system fonts, media+snapshot as base64 data-URIs,
  embed = snapshot linking to original, no iframe). Off-loop render.
- migration 0003 (style + embed_snapshot); models/repo/admin/main wired.
- CSP: style-src adds 'unsafe-inline' so the WAF-injected health-banner's
  dynamic <style> renders (was blocked → banner fell unstyled to page bottom);
  script-src stays 'self'. Google-Fonts relaxation page-scoped to communiqué.
- 118 tests (11 new: snapshot fallback/SSRF, communiqué render, archive self-containment).

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-14 07:57:54 +02:00
db74e02ffc feat(billets): image upload with zoomable vignette gallery + portable export
- 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>
2026-07-12 08:11:43 +02:00
ac6df22258 feat(billets): funky/party public webapp — inline media + link chips + quick-share
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>
2026-07-11 16:35:56 +02:00
5074d6589a feat(billets): step 5 — comments (moderated, anti-spam) + emoji reactions
Public write surface, CSRF-guarded (double-submit). Reactions: 6 closed emojis,
anonymous signed visitor cookie (only its hash stored), toggle via same-origin
fetch fragment swap with a no-JS POST+redirect fallback. Comments: honeypot +
signed min-think-time token + per-ip_hash rate-limit (5/h), pending-by-default
moderation with auto-approve for a returning (ip_hash,name) already vetted;
emails BLAKE2b-hashed, bodies escaped + autolinked rel=nofollow ugc. Admin
moderation queue (approve/reject -> event_log). 76 tests.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-11 15:41:28 +02:00
e9488cedf9 feat(billets): step 2 — public feed + permalinks + gitea revision store
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>
2026-07-11 15:02:17 +02:00