secubox-deb/packages/secubox-billets/pyproject.toml
CyberMind-FR 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

35 lines
1.2 KiB
TOML

# SPDX-License-Identifier: LicenseRef-CMSD-1.0
[project]
name = "secubox-billets"
version = "0.1.0"
description = "billets — micro-blog gateway inter-médias sociaux (SecuBox module)"
requires-python = ">=3.11"
authors = [{ name = "Gérald Kerma", email = "devel@cybermind.fr" }]
dependencies = [
"fastapi==0.115.6",
"uvicorn==0.34.0",
"pydantic==2.10.4",
"aiosqlite==0.20.0",
"jinja2==3.1.5",
"argon2-cffi==23.1.0",
"nh3==0.2.20", # HTML sanitizer (ammonia/rust) — embed + comment safety
"httpx==0.28.1", # oEmbed / OpenGraph fetches (with SSRF guard)
"pyotp==2.9.0", # optional author TOTP
"itsdangerous==2.2.0", # signed cookies / timestamp tokens / CSRF
]
[project.optional-dependencies]
dev = ["pytest==8.3.4", "pytest-asyncio==0.25.0", "anyio==4.7.0"]
# OPTIONAL: real headless-Chromium embed screenshots. Without it, the embed
# snapshot vignette falls back to the page's og:image (see services/snapshot.py).
# After install, fetch the browser binary: `playwright install chromium`.
snapshot = ["playwright==1.58.0"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"