Commit Graph
56 Commits
Author SHA1 Message Date
Pontoporeia 554ba3ee8d fix(admin): stop logging out active long-form work; raise idle timeout to 4h
The admin idle timeout (30 min) was refreshed only by navigations and HTMX
requests. During long encoding sessions on an open form there are none, so
an actively-typing admin was logged out mid-work after ~30-45 min.

Add an activity-driven keepalive:
- /admin/session-keepalive.php: 204 when authenticated (refreshes
  admin_last_activity via AdminAuth::isAuthenticated()), 401 otherwise.
- admin-session-keepalive.js: marks activity only on real user input
  (pointer/keyboard/input/scroll/wheel/touch/focus) and pings at most once
  per 5 min while the tab is visible. A genuinely idle tab never pings, so
  the idle timeout still applies.

Raise the idle window 30 min -> 4 h: for a single-/few-admin back-office
whose main workflow is data entry, 30 min still kicked admins who stepped
away mid-form. With the keepalive in place, 4 h means "no interaction at
all", not "no navigation". Absolute timeout stays 12 h.

Also fix session ID rotation, which never fired: it used
`$absolute % IDLE_TIMEOUT_SECONDS === 0`, i.e. required a request to land
exactly on a multiple of the interval relative to login time. Replaced with
an explicit admin_last_rotation timestamp and a ROTATION_INTERVAL_SECONDS
(30 min) constant decoupled from the idle timeout, so raising the idle
window does not widen the fixation/replay window.

Refactor AdminAuth::enforceSessionTimeout() to return bool instead of
redirecting/exiting, so the keepalive endpoint can report 401 cleanly
rather than letting fetch follow a redirect to the login page.

Smoke test (just smoke-session-keepalive) covers activity refresh, 2 h idle
accepted, rotation firing, idle rejection+destruction, and unauthenticated
rejection. Docs updated.
2026-09-18 16:26:49 +02:00
Pontoporeia b1715b210d fix(deploy): restore www-data ownership after deploy-code to prevent HTTP 500 2026-09-18 16:26:49 +02:00
Pontoporeia 3f352b0d26 feat(admin): cleanup page — remove 'Fichiers temporaires' level, promote sections to h2 TOC entries 2026-09-18 16:26:49 +02:00
Pontoporeia 1ed69a2c1a refactor(justfile): de-dup deploy-code, move nginx/setup to deploy-nginx 2026-09-18 16:26:48 +02:00
Pontoporeia fc66b37801 feat(home): htmx lazy-load cover images
Replace the eager <img> on the home page with an htmx placeholder <figure>
that fetches a /cover-fragment endpoint when it scrolls into view
(hx-trigger="revealed"), so heavy cover bytes load only on demand.
Add spinner + settle-fade transition CSS, and load htmx.min.js on home.
2026-09-18 16:26:36 +02:00
Pontoporeia 541470b9bb feat(provision): idempotent setup for local dev and remote server 2026-09-18 16:26:36 +02:00
Pontoporeia 8accb88452 update: CSS per-page split and lightningcss unusedSymbols report
docs: record CSS template inventory + decide unusedSymbols-before-split ordering

feat(css): add content-source collector + dynamic-class safelist for unusedSymbols report

- scripts/css-content-sources.mjs: buildCorpus() gathers templates/public/src
  PHP + first-party js/app JS (vendor excluded), returns {corpus, sources,
  totalBytes, safelist, prefixes}
- Mined 22 exact runtime classes + 5 DB/state-derived suffix prefixes from
  status-badge.php, SystemController statusClass/logLineClass, and class=<?=?>
  ternaries
- docs/css-split-analysis.md notes content-corpus section
- td: #11 collect-content-sources done; feeds #12 report script

feat(css): unusedSymbols report script + just css-report recipe

- scripts/css-unused-report.mjs: per-bundle class/id extraction vs buildCorpus()
  corpus + safelists; measures reclaimable bytes via lightningcss transform
  unusedSymbols (report-only, no stripping to disk)
- just css-report: rebuild CSS then run the report
- css-content-sources.mjs: add VENDOR_CLASS_PREFIXES (filepond--*, htmx-*)
- RESULT: 216,383B total, ~6.2KB (2.9%) reclaimable; FilePond/HTMX exclusion
  corrected inflated 26% (56KB) false-positive down to honest 2.9%
- docs/css-split-analysis.md findings table + TODO 12/13 done

docs(css): record go/no-go decision — split NO-GO, pruning conditional-go

- Decision analysis in docs/css-split-analysis.md
- ~6.2KB (2.9%) reclaimable of 216KB; base.min.css only 484B (2.3%)
- SPLIT NO-GO: base.css already well-used; parked u/w/x/y(/z) as deferred
- PRUNING conditional-go on hand-verifiable dead selers from source, never dist;
  re-run just css-report after each edit; keep needs-review + vendor-prefix cls
- td: task 14 done; split stream 41 tasks -> 2 pending / 32 done / 7 deferred

todo: defer CSS pruning stream (10/u/w/x/y/z), context updated
2026-09-18 16:26:36 +02:00
Pontoporeia 307988eb3c feat: enforce idle + absolute timeouts on admin session 2026-09-18 16:26:36 +02:00
Pontoporeia 30a16f9e9e fix: display db timestamps in Brussels time
(heure de dépôt was showing UTC)

- feat: add date_depot column (real TFE deposit date)
with CSV round-trip + Brussels→UTC sanitization
- fix: keep PHP default tz at UTC to preserve token/share-link
 expiry consistency; convert to Brussels only in db_datetime()
2026-09-18 16:26:36 +02:00
Pontoporeia e4b48867aa Add sso-diagnose.sh: verify all peertube-sso-incident claims into a log
- docs: record the open identity-forwarding question, auth contracts, and responsibility boundary
2026-08-24 11:36:02 +02:00
Pontoporeia f31addb6bc add password-reset smoke test + AdminAuth DI + OneTimeToken empty-context redeem fix 2026-08-24 11:36:02 +02:00
Pontoporeia fb5e856288 admin: backup logs via parameters.php, nextcloud secondary backup
- surface backup/cleanup cron logs + backup freshness status
- email xamxam@erg.be when SQLite backups go stale (backup watchdog)
- sync SQLite snapshots to Nextcloud WebDAV + remote-freshness watchdog
- precise retention pruning, manual sync in check recipe, and Nextcloud-sync docs
2026-08-24 11:34:57 +02:00
Pontoporeia d2cef85966 logs: standardise log filenames to xamxam-{service}-{date}.log 2026-08-24 11:34:34 +02:00
Pontoporeia 7b6d79c133 diag: invalid_grant is SSO auth-method mismatch, not bad creds
- feat: creds-test.sh gum probe for SMTP vs PeerTube auth + PeerTubeService::probeAuth()
- feat: app-token.sh gum probe for long-lived PeerTube app token (client_credentials)
- docs: add copy-paste proof commands to demonstrate the SSO break to admins
2026-08-24 11:33:34 +02:00
Pontoporeia 7938ab39c0 feat: add custom 404 page rendered via the site layout 2026-08-24 11:32:02 +02:00
Pontoporeia c1a05efcda fix: bypass /tmp tmpfs for file uploads by redirecting PHP upload_tmp_dir to storage partition 2026-07-10 15:42:07 +02:00
Pontoporeia 22a49f7cb6 fix: unify CSV import/export columns, fix import JS error, fix createThesis VALUES
- Fix createThesis SQL: extra ? placeholder in VALUES (27 values for 26 columns)
- Add createThesis integration tests to catch column/value mismatches
- Add CSV_COLUMNS as single source of truth in ExportController, deriving
  csvHeaders() and positional fallback from it
- Add missing columns to export query + CSV: duration_pages, duration_minutes,
  has_annexes, license_custom, contact_visible, objet
- Add missing columns to import INSERT: license_id, license_custom, cc2r,
  exemplaire_baiu, exemplaire_erg, objet, contact_visible, duration_pages,
  duration_minutes, has_annexes
- Resolve license name → license_id during import
- Fix XamxamInitFilePonds: add file-upload-filepond.js to admin-entry.js
  so FilePond initialization code is available on the admin list page
- Add FilePond vendor CSS to admin.min.css bundle (import dialog styling)
- Generate .admin-file-hint from csvHeaders() instead of hardcoded stale list
- Use positional fallback from CSV_COLUMNS for import cell parsing
2026-07-10 11:56:27 +02:00
Pontoporeia 912b38583b Add --quiet flag to build.mjs for cleaner 'just dev' output 2026-07-08 13:50:03 +02:00
Pontoporeia 3e93150c76 justfile: standardise test recipes to lint-php/lint-css/lint-js/test + add fix recipe
- Removed ambiguous aliases: phpstan, cs-check, syntax
- Split lint-biome into lint-css and lint-js with correct paths
- Added lint meta-recipe and fix recipe (biome --unsafe + php-cs-fixer)
- Fixed FormBootstrap dead null check, CSS shorthand override bug
- Updated phpstan baseline, suppressed noDescendingSpecificity/noInnerDeclarations
- Applied ~74 biome auto-fixes across CSS/JS
2026-07-05 11:07:41 +02:00
Pontoporeia 6ecd3d4540 Fix biome lint errors: remove duplicate CSS properties, apply safe auto-fixes
CSS:
- Remove duplicate 'background' fallbacks in base.css, header.css, search.css
  (solid color declared before gradient — gradient always wins)
- Remove duplicate 'padding' in admin.css .admin-import-log

JS (biome --write safe fixes applied):
- function() → arrow functions in all IIFEs and callbacks
- forEach/callback → arrow functions
- evaluePtrn → parseInt(x, 10) in admin-contacts-form.js
- Cleaned label text in build.mjs lint step

Remaining warnings are intentional: !important overrides, descending
specificity (admin.css cascade), noUnusedVariables (functions exported
to window/onclick), useTemplate style preference.
2026-06-24 13:57:00 +02:00
Pontoporeia 20fe4b6c8c Add biome + rolldown + lightningcss build pipeline for JS/CSS bundling & minification
- package.json with biome, rolldown, lightningcss devDependencies  
- biome.json: add CSS formatter support
- scripts/build-css.mjs: lightningcss resolves @import chain, bundles/minifies CSS  
- scripts/build-js.mjs: rolldown per-entry JS bundling (no code splitting)
- scripts/build.mjs: orchestrator for both CSS + JS
- scripts/check-build.mjs: staleness checker for CI/deploy guard
- justfile: add build, build-css, build-js, build-install, build-check recipes
- justfile: deploy recipe now runs build before deploy-code
- head.php + form-page.php: use dist/base.min.css instead of style.css
- All controllers + FormBootstrap: reference dist/*.min.{css,js}
- admin footer: load admin.min.js for all admin pages
- repertoire: use public.min.js instead of individual app JS files
- Fix stray '}' syntax error in admin.css line 305
- .gitignore: add app/public/assets/dist/
2026-06-24 13:09:50 +02:00
Pontoporeia 03c9c3566f Add SQLite indexes for contenus page language/tag queries + WIP: Peertube orphans, dialogs, contact decoupling, context note, finality types 2026-06-21 16:36:34 +02:00
Pontoporeia d588ae004d Reintroduce TFE duration metadata: DB columns, form fields, controllers, views, and migration
Add 'unsafe-eval' to CSP script-src directives (htmx requires Function())
2026-06-15 15:56:52 +02:00
Pontoporeia 00fed5f0e3 Add periodic cleanup of orphaned drafts: cleanup job, just command, deploy cron 2026-06-11 13:05:34 +02:00
Pontoporeia defc919cd0 cleanup modal: list stale files to remove; storage restructure: documents/ → {objet}/ 2026-05-19 23:58:51 +02:00
Pontoporeia 31ccbd195b add syntaqlite SQL validation to migrate.sh before applying schema.sql 2026-05-19 00:08:06 +02:00
Pontoporeia cb6394e119 add incremental migration runner to deploy recipe — execute whole SQL files (not semicolon-split), catch 'no such column' for idempotent re-runs, merge into migrate.sh 2026-05-19 00:08:06 +02:00
Pontoporeia c1960d224b fix deploy: multiple deploy recipe fixes — upload xamxam.conf before deploy-server.sh, sudo rsync for chown, migrate.sh via sqlite3, chmod WAL/SHM sidecar files, deploy-verify-permissions awk fix, .env sudo perms 2026-05-19 00:08:06 +02:00
Pontoporeia 28ef35dce5 fix: make schema.sql fully idempotent — add IF NOT EXISTS to all CREATE INDEX, CREATE TRIGGER, and CREATE VIEW statements 2026-05-19 00:08:06 +02:00
Pontoporeia 973444bdbb feat(backup): deploy cron-based SQLite backups to production
- Create deploy/xamxam-backup.cron with hourly (30d) and daily (90d) jobs
- Add just recipes for deploying backup infrastructure:
  - deploy-backup-script: upload backup-sqlite.sh to /usr/local/bin
  - deploy-backup-cron: install cron.d file, create /var/backups/xamxam + log
  - deploy-backup: one-shot convenience (script + cron)
  - deploy-check-backup-log: tail the backup log
  - deploy-list-backups: ls remote backup directory
  - trigger-backup: manually invoke backup on server
  - test-restore: scp, gunzip, verify a remote snapshot
- Add reminder to run deploy-backup after first deploy
- Replace 'Contenu (Markdown)' label with 'Syntax Markdown' link (cheatsheet)
2026-05-19 00:08:06 +02:00
Pontoporeia 72f7192156 feat(deploy): add deploy-verify-permissions recipe + upload/run deploy-server.sh before verification + run migrations in deploy 2026-05-19 00:08:06 +02:00
Pontoporeia 926659087f feat: implement SQLite backup & data integrity plan (Phases 2-4) 2026-05-19 00:08:06 +02:00
Pontoporeia 8db7b6e9eb feat: FilePond production hardening — extension-based validation, server-side size limits (2GB), annexe validation, drop accept attributes, FilePond file styling 2026-05-19 00:08:05 +02:00
Pontoporeia 95fcbc919a Remove required from all admin add/edit form inputs
- Skip required-field validation for orientation/ap/finality/licence/jury in admin add+edit
2026-05-13 17:59:13 +02:00
Pontoporeia ca5983075d feat: admin audit logging across all admin actions
- AdminLogger: JSON-lines → /var/log/xamxam.log (prod) / storage/logs/admin.log (dev)
  + best-effort DB mirror to admin_audit_log table
- DB: admin_audit_log table, share_links.is_archived column
- ShareLink: archive() replaces delete(), toggleActive() returns new state,
  listActive()/listArchived() split, validateLink blocks archived slugs
- All action handlers wired: publish, unpublish, visibility, delete, csv/db export,
  tfe add/edit, tags, pages, apropos, form-help, access-request, maintenance,
  settings (formulaire toggles, objet types, smtp update), smtp-test
- acces.php: archive button replaces delete; collapsible archived links section
- setup-server.sh: provision /var/log/xamxam.log (www-data:xamxam 640)
2026-05-05 11:04:52 +02:00
Pontoporeia cb883ab33f fix: deploy-server.sh migrates posterg.db → xamxam.db and cleans legacy nginx configs 2026-05-05 11:04:52 +02:00
Pontoporeia ab51bf3a66 fix: deploy-server.sh cleans up legacy posterg configs and prunes old xamxam backups 2026-05-05 11:04:52 +02:00
Pontoporeia 68e30abb56 fix: remove Post-ERG branding → XAMXAM; drop legacy posterg nginx symlink in deploy script; rename posterg.db → xamxam.db 2026-05-05 11:04:52 +02:00
Pontoporeia c949cf9481 rename posterg → xamxam throughout: nginx conf, scripts, PHP source, docs 2026-05-05 11:04:52 +02:00
Pontoporeia 18a02a0018 deploy: rename deploy path from /var/www/posterg to /var/www/xamxam 2026-04-28 22:21:09 +02:00
Théophile Gervreau-Mercier 7e26351f4b refactor: remove test.db, use only posterg.db for all environments
- Simplified Database.php determineDatabasePath to always use posterg.db
- Removed test.db auto-detection based on php_sapi_name
- Removed test.db targets from justfile (migrate-test removed)
- Removed CreateTestDatabase.php fixture script
- Updated migrate.sh to only init posterg.db
- Updated setup-dev.sh to init posterg.db
- Updated run-tests.php (removed DB_ENV=test env var)
- Updated deploy-db to use posterg.db
- Removed test.db file

refactor: remove empty fixtures directory
2026-04-27 18:07:20 +02:00
Pontoporeia dbaabaf8a0 merge all migrations into schema.sql 2026-04-24 23:03:49 +02:00
Pontoporeia 75f808bee4 feat: extract MediaController, wire into Dispatcher, delete media.php 2026-04-20 12:32:00 +02:00
Pontoporeia 6f04514aa2 fix: add structural guard for migration 008 in migrate.sh 2026-04-15 14:24:44 +02:00
Théophile Gervreau-Mercier 5c5054d744 Investigating VM crash 2026-04-13 11:12:12 +02:00
Pontoporeia b45e6c50cc fix: admin CSP allow inline scripts
script-src 'self' 'unsafe-inline' added to admin Content-Security-Policy.
default-src 'self' was blocking OverType editor init block and
the dev live-reload poller. Admin section is auth-gated so
unsafe-inline is acceptable.
2026-04-08 14:14:37 +02:00
Pontoporeia e6960f0c9c fix: RateLimit permission denied — code + deploy scripts
RateLimit.php:
- Silence mkdir() with @ operator
- Guard file_put_contents with is_writable() check (graceful degrade)

scripts/deploy-server.sh + setup-server.sh:
- mkdir -p storage/cache/rate_limit on every deploy
- chown www-data:posterg + chmod 2775 on storage/cache/
  so php-fpm can always write rate limit files
2026-04-06 16:45:14 +02:00
Pontoporeia 756ddb5765 fix: RateLimit graceful degradation on permission denied
Silence mkdir() with @ operator; guard file_put_contents with
is_writable() check. When storage/cache/rate_limit is not writable
by php-fpm, requests are allowed through instead of throwing
warnings that flood the nginx error log.
2026-04-06 16:40:55 +02:00
Pontoporeia a88e5562f8 fix(config): auto-route test.db locally, posterg.db on production
- config.php: getDatabasePath() detects php built-in CLI server
  (php_sapi_name() === 'cli-server') and routes to test.db; all
  other SAPIs (nginx/fpm) get posterg.db. DB_ENV env-var still
  overrides either way.

- migrate.sh: auto-initialise the target DB from storage/schema.sql
  when the file is absent or has no tables yet. Existing DBs with
  data are left completely untouched (table_count check, no re-run
  of schema on populated DB). Idempotent: safe to run repeatedly.

- justfile: serve still calls migrate (which now handles init too),
  no DB_ENV prefix needed since sapi detection handles routing.
2026-04-01 15:55:12 +02:00
Pontoporeia 72d48c49c3 feat(db): auto-migrate both DBs on serve via scripts/migrate.sh 2026-04-01 15:55:12 +02:00