diff --git a/.gitignore b/.gitignore index efdd03a..3f26278 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,5 @@ coverage/ # td (TODO manager) local database .todo.sqlite +.todo.sqlite-shm +.todo.sqlite-wal diff --git a/docs/CSS.md b/docs/CSS.md index 0cdf277..54c2964 100644 --- a/docs/CSS.md +++ b/docs/CSS.md @@ -10,7 +10,7 @@ - **`typography.css`** — type scale - **`utilities.css`** — utility classes and spacing tokens - **`style.css`** — main public entry point (via `@import` chain) -- Page-specific: **`public.css`** (home), **`tfe.css`**, **`repertoire.css`**, **`content-page.css`** (about/licences), **`system.css`** (admin system dashboard), **`file-access.css`**, **`admin.css`** (admin section) +- Page-specific: **`public.css`** (home), **`tfe.css`**, **`repertoire.css`**, **`content-page.css`** (about/licences), **`system.css`** (admin system dashboard), **`file-access.css`**, **`not-found.css`** (404), **`admin.css`** (admin section) - Form: **`form-base.css`** + **`form-admin.css`** (plus FilePond vendor CSS) ## Build / bundling @@ -19,9 +19,11 @@ Source CSS is **bundled and minified** by `scripts/build-css.mjs` (lightningcss) into `app/public/assets/dist/*.min.css`: - `base.min.css` — resolves the `@import` chain in `style.css` into one file (eliminates ~17 sequential imports) -- `admin.min.css` — minified `admin.css` +- `admin.min.css` — minified `admin.css` (+ FilePond vendor CSS, for the CSV import dialog) - `form.min.css` — `form-base.css` + `form-admin.css` + FilePond vendor CSS -- plus individual `.min.css` for the page-specific files +- `form-base.min.css` — `form-base.css` standalone (partage pages without FilePond) +- `partage-form.min.css` — `form-base.css` + FilePond vendor CSS (partage pages with FilePond) +- plus individual `.min.css` for the page-specific files (`public`, `tfe`, `repertoire`, `content-page`, `not-found`, `system`, `file-access`) Run with `just build-css` / `just dev-build`. Never commit hand-edits into `dist/` — they are generated. diff --git a/docs/README.md b/docs/README.md index 8830975..cb4c3cf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,17 +1,16 @@ # Documentation index -This directory separates **current reference** docs, **proposals/plans**, and -**historical/archived** analysis (in [`archive/`](archive/)). +`docs/` holds **current reference** documentation describing how the system +actually works today. Everything else — proposals, plans, incident reports, +analyses, original requirements, one-off helpers — is in [`archive/`](archive/). -> **Note on naming:** XAMXAM was previously *Post-ERG* (and the code was once -> organised under `posterg-website/`, `apps/`, `front-backend/`, -> `/var/www/posterg/`). References to `posterg`, `apps/public/inc/header.php`, -> `posterg.db`, `just serve`, `php-live-reload`, `tests/run-tests.php`, etc. in -> historical docs reflect the old layout. The live paths are now `app/`, +> **Note on naming:** XAMXAM was previously *Post-ERG* (code once organised +> under `posterg-website/`, `apps/`, `front-backend/`, `/var/www/posterg/`). +> Historical docs may reference the old layout; the live paths are now `app/`, > `app/public/`, `app/storage/xamxam.db`, `just dev`, PHPUnit, and > `/var/www/xamxam/` on the server. -## Current reference (kept up to date) +## Reference docs | Doc | Contents | |-----|----------| @@ -26,50 +25,12 @@ This directory separates **current reference** docs, **proposals/plans**, and | [security.md](security.md) | Current security posture | | [file-uploads.md](file-uploads.md) | Upload surfaces, types, storage layout | | [CSS.md](CSS.md) | CSS architecture + build | -| [bookmarklet.md](bookmarklet.md) | Form auto-fill test helper | -## Proposals / plans (proposed, not all implemented) +## Archive -| Doc | Status | -|-----|--------| -| [ldap.md](ldap.md) | LDAP login — **not implemented** (merged plan + spec) | -| [monolog-plan.md](monolog-plan.md) | Single Monolog logger replacing AppLogger/AdminLogger/ErrorHandler/Audit — **plan** | -| [de-librairisation.md](de-librairisation.md) | Replace bespoke SMTP/Markdown/HTTP/crypto with libraries (partly done) | -| [refactoring.md](refactoring.md) | Older refactoring proposal | -| [ANALYSIS_STRUCTURE_REORG.md](ANALYSIS_STRUCTURE_REORG.md) | Proposed structure reorg | -| [ANALYSIS_INLINE_JS_CSS_MINIFY.md](ANALYSIS_INLINE_JS_CSS_MINIFY.md) | Inline JS/CSS/minify analysis (largely actioned by the build system) | -| [backup-plan.md](backup-plan.md) | Backup plan — largely implemented (see deployment.md) | -| [repertoire-mobile-propositions.md](repertoire-mobile-propositions.md) | Mobile repertoire UI proposals | -| [spec-sheet.md](spec-sheet.md) | Original requirements fiche technique (incl. client notes appendix) | - -## Active investigations (unresolved) - -| Doc | Status | -|-----|--------| -| [filepond-crash-analysis.md](filepond-crash-analysis.md) | FilePond upload crash — **unresolved**, root cause in vendor code (merged race-investigation appendix) | -| [peertube-sso-incident.md](peertube-sso-incident.md) | PeerTube `invalid_grant` diagnosis & ownership | -| [autosave-system.md](autosave-system.md) | Autosave architecture & HTMX migration assessment | - -## Historical / archived (in [`archive/`](archive/)) - -Kept for context — may reference the old codebase. Not maintained. - -| Doc | Contents | -|-----|----------| -| `vm-crash-incident.md` | VM crash root-cause (merged final report + reports + evidence + fix) | -| `CURRENT_ISSUES.md` | Issue log (2026-05-10) — many since resolved | -| `LIVRAISONS_PAR_MOIS.md` | Monthly delivery log grouped by functional family | -| `migration-history.md` | History of major structural migrations | -| `php-vs-flask.md` | Language choice decision | -| `orm-assessment.md` | ORM evaluation | -| `css.md` | Old Bulma-removal writeup (see CSS.md for current) | -| `system-setup.md` | PHP-extension inventory | -| `SETUP.md` | Earlier setup snapshot (see development.md) | -| `testing.md` | PHP testing best-practices writeup | -| `SMTP_550_POSTFIX_FIX.md` | SMTP troubleshooting record | -| `cms-migration-plan.html` | CMS migration plan | -| `Proposition procédure licences_V2.pdf` | Licence procedure proposal | -| `pi-session-2026-05-10T*.html` | Captured session log | +Non-documentation material (proposals, plans, incident reports, analyses, +requirements, one-off helpers) is preserved read-only in +[`archive/`](archive/) for context. It is not maintained. ## Related documentation elsewhere @@ -79,6 +40,5 @@ Kept for context — may reference the old codebase. Not maintained. --- -**Maintenance guidance:** when updating code, update the matching *current -reference* doc in the table above. Leave *archived* docs untouched (read-only -context). Move newly-written analysis into the appropriate section. +**Maintenance guidance:** when updating code, update the matching reference doc +above. Do not move analysis/proposals into `docs/` — put them in `archive/`. diff --git a/docs/ANALYSIS_INLINE_JS_CSS_MINIFY.md b/docs/archive/ANALYSIS_INLINE_JS_CSS_MINIFY.md similarity index 100% rename from docs/ANALYSIS_INLINE_JS_CSS_MINIFY.md rename to docs/archive/ANALYSIS_INLINE_JS_CSS_MINIFY.md diff --git a/docs/ANALYSIS_STRUCTURE_REORG.md b/docs/archive/ANALYSIS_STRUCTURE_REORG.md similarity index 100% rename from docs/ANALYSIS_STRUCTURE_REORG.md rename to docs/archive/ANALYSIS_STRUCTURE_REORG.md diff --git a/docs/TODO.md b/docs/archive/TODO.md similarity index 100% rename from docs/TODO.md rename to docs/archive/TODO.md diff --git a/docs/autosave-system.md b/docs/archive/autosave-system.md similarity index 100% rename from docs/autosave-system.md rename to docs/archive/autosave-system.md diff --git a/docs/backup-plan.md b/docs/archive/backup-plan.md similarity index 100% rename from docs/backup-plan.md rename to docs/archive/backup-plan.md diff --git a/docs/bookmarklet.md b/docs/archive/bookmarklet.md similarity index 100% rename from docs/bookmarklet.md rename to docs/archive/bookmarklet.md diff --git a/docs/css-split-analysis.md b/docs/archive/css-split-analysis.md similarity index 100% rename from docs/css-split-analysis.md rename to docs/archive/css-split-analysis.md diff --git a/docs/de-librairisation.md b/docs/archive/de-librairisation.md similarity index 100% rename from docs/de-librairisation.md rename to docs/archive/de-librairisation.md diff --git a/docs/filepond-crash-analysis.md b/docs/archive/filepond-crash-analysis.md similarity index 100% rename from docs/filepond-crash-analysis.md rename to docs/archive/filepond-crash-analysis.md diff --git a/docs/ldap.md b/docs/archive/ldap.md similarity index 100% rename from docs/ldap.md rename to docs/archive/ldap.md diff --git a/docs/monolog-plan.md b/docs/archive/monolog-plan.md similarity index 100% rename from docs/monolog-plan.md rename to docs/archive/monolog-plan.md diff --git a/docs/peertube-sso-incident.md b/docs/archive/peertube-sso-incident.md similarity index 100% rename from docs/peertube-sso-incident.md rename to docs/archive/peertube-sso-incident.md diff --git a/docs/refactoring.md b/docs/archive/refactoring.md similarity index 100% rename from docs/refactoring.md rename to docs/archive/refactoring.md diff --git a/docs/repertoire-mobile-propositions.md b/docs/archive/repertoire-mobile-propositions.md similarity index 100% rename from docs/repertoire-mobile-propositions.md rename to docs/archive/repertoire-mobile-propositions.md diff --git a/docs/spec-sheet.md b/docs/archive/spec-sheet.md similarity index 100% rename from docs/spec-sheet.md rename to docs/archive/spec-sheet.md diff --git a/docs/test-plan.md b/docs/archive/test-plan.md similarity index 100% rename from docs/test-plan.md rename to docs/archive/test-plan.md diff --git a/docs/database.md b/docs/database.md index 3357c4b..add586b 100644 --- a/docs/database.md +++ b/docs/database.md @@ -58,7 +58,7 @@ DB so it always reflects the applied set of migrations. ## Tables -31 tables. Grouped by purpose: +32 tables. Grouped by purpose: ### Core @@ -97,6 +97,7 @@ DB so it always reflects the applied set of migrations. | `file_access_tokens` | One-time access tokens | | `file_access_sessions` | Session tokens for granted access | | `file_access_audit` | Audit trail of access events | +| `one_time_tokens` | Generic single-use, expiring tokens (password reset, etc.); only SHA-256 hashes stored | ### Integrations / settings / content @@ -132,7 +133,8 @@ DB so it always reflects the applied set of migrations. | `objet` | TEXT | `'tfe'` default; other objets (`these`, `frart`) gated by site_settings | | `orientation_id` / `ap_program_id` / `finality_id` | INT FK | Lookup refs | | `synopsis` / `context_note` / `remarks` | TEXT | | -| `duration_pages`, `duration_minutes` | INT | Combined duration handled at app level | +| `duration_value` / `duration_unit` | REAL / TEXT | Normalised duration (`pages` or `minutes`) | +| `duration_pages`, `duration_minutes` | INT | Legacy per-unit duration fields (combined handling at app level) | | `has_annexes` | BOOLEAN | | | `access_type_id` | INT FK | Libre / Interne / Interdit | | `license_id` | INT FK | → `license_types` | @@ -141,7 +143,7 @@ DB so it always reflects the applied set of migrations. | `jury_points` | DECIMAL(4,2) | Grade / 20 | | `jury_note_added` | BOOLEAN | | | `contact_visible` | TEXT | `'public'`/`'internal'` control for author contact | -| `submitted_at`, `defense_date`, `published_at` | DATETIME | Lifecycle timestamps | +| `submitted_at`, `date_depot`, `defense_date`, `published_at` | DATETIME | Lifecycle timestamps (`date_depot` = real deposit date from CSV bulk import; Brussels-local parse → UTC storage) | | `is_published` | BOOLEAN | Public visibility flag | | `status` | TEXT | Default `'active'` | | `baiu_link` | TEXT | Institutional repo link | diff --git a/docs/deployment.md b/docs/deployment.md index b645b70..08f9f79 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -40,6 +40,22 @@ For a full initial rollout including backup + cleanup cron jobs: just deploy-all-first # deploy + deploy-backup + deploy-cleanup-cron ``` +### First-time `APP_KEY` on a brand-new server + +`deploy-env` uploads the **local** `app/.env` only when the remote has none. Do +**not** rely on a committed `.env` — generate a fresh key per environment and put +it on the server before first `deploy`: create `app/.env` locally (see +[development.md](development.md#appkey-and-the-appenv-file)) so `deploy-env` +uploads it, or create `/var/www/xamxam/.env` on the server directly: + +```bash +ssh xamxam 'sudo -u www-data bash -c "echo APP_KEY=\$(php -r \"echo base64_encode(random_bytes(32));\") > /var/www/xamxam/.env; chmod 640 /var/www/xamxam/.env; chown www-data:xamxam /var/www/xamxam/.env"' +``` + +If you ever rotate `APP_KEY`, re-encrypt the SMTP password with +`just reencrypt-password ` and push the new key via +`just deploy-env`. + --- ## Deploying diff --git a/docs/development.md b/docs/development.md index 33f9fac..f17c06f 100644 --- a/docs/development.md +++ b/docs/development.md @@ -29,6 +29,37 @@ just migrate # create/update the SQLite DB from schema + migration not the live-reload watcher. Prefer the explicit `composer install` + `npm ci` above. +### APP_KEY and the app/.env file + +The app derives DB/storage paths from the filesystem layout, so the public site +and admin panel run without any env file. But `Crypto` (decrypting the SMTP +password, PeerTube credentials, and Nextcloud WebDAV sync) requires an +`app/.env` containing an `APP_KEY`: + +``` +APP_KEY= +``` + +Generate one with: + +```bash +php -r 'echo "APP_KEY=" . base64_encode(random_bytes(32)) . PHP_EOL;' > app/.env +chmod 600 app/.env +``` + +`app/.env` must **never** be committed. If the file is missing, the public site +still runs but any path that reads encrypted credentials throws +`RuntimeException: APP_KEY not found`. On a fresh clone, create it before +relying on SMTP/PeerTube/Nextcloud features. + +### First admin login + +A brand-new DB seeds `site_settings.admin_password_hash` as an **empty string**, +which makes `AdminAuth::requireLogin()` a pass-through — i.e. `/admin/` starts +**unauthenticated** until a password is set. To secure it, open +`http://127.0.0.1:8000/admin/account` (or the production admin) and set the +admin password, which stores the bcrypt hash in `site_settings`. + ## Project Structure ``` @@ -136,7 +167,8 @@ just reset-db # rm DB + init-db just query # open an interactive SQLite shell just backup # SQL dump into app/storage/backup_.sql just backup-snapshot # WAL-safe hot backup + gzip into storage/backups/ -just cleanup-drafts [--no-dry-run] # remove orphaned drafts > 24h +just cleanup-drafts [--no-dry-run] # remove orphaned drafts > 7 days (default) + # OLDER_THAN_HOURS=24 to change the threshold ``` See [database.md](database.md) for the full schema reference. diff --git a/docs/environment.md b/docs/environment.md index ca2f229..7ede9c4 100644 --- a/docs/environment.md +++ b/docs/environment.md @@ -1,8 +1,8 @@ # Environment / runtime requirements The software stack and server environment required to run XAMXAM. This is the -machine-facing counterpart to the product spec in -[`spec-sheet.md`](spec-sheet.md). +machine-facing counterpart to the original product spec (fiche technique, +archived at [`spec-sheet.md`](archive/spec-sheet.md)). ## Production server (`xamxam`) @@ -120,4 +120,3 @@ Required on the **dev** machine (and `just` on deploy-from-local): - [`development.md`](development.md) — local dev workflow, structure, builds - [`deployment.md`](deployment.md) — server setup, deploy, backups, rollback - [`security.md`](security.md) — security posture, session GC tuning -- [`spec-sheet.md`](spec-sheet.md) — product/feature spec (fiche technique) diff --git a/docs/file-uploads.md b/docs/file-uploads.md index 4625f35..6c2e1a6 100644 --- a/docs/file-uploads.md +++ b/docs/file-uploads.md @@ -56,7 +56,7 @@ Each uploaded file is assigned a `file_type` that controls how it is displayed o |-----------|-----------|---------| | `.mp4` | `video/mp4` | `