docs: keep only reference documentation, archive one-offs

This commit is contained in:
Pontoporeia
2026-09-18 16:26:36 +02:00
parent 8accb88452
commit d5c1dfb23d
27 changed files with 108 additions and 78 deletions
+2
View File
@@ -72,3 +72,5 @@ coverage/
# td (TODO manager) local database
.todo.sqlite
.todo.sqlite-shm
.todo.sqlite-wal
+5 -3
View File
@@ -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.
+13 -53
View File
@@ -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/`.
+5 -3
View File
@@ -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 |
+16
View File
@@ -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 <new_base64_key>` and push the new key via
`just deploy-env`.
---
## Deploying
+33 -1
View File
@@ -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=<base64-encoded 32 random bytes>
```
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_<timestamp>.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.
+2 -3
View File
@@ -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)
+26 -11
View File
@@ -56,7 +56,7 @@ Each uploaded file is assigned a `file_type` that controls how it is displayed o
|-----------|-----------|---------|
| `.mp4` | `video/mp4` | `<video>` |
| `.webm` | `video/webm` | `<video>` |
| `.mov` | `video/quicktime` | `<video>` (served as `video/mp4`) |
| `.mov` | `video/quicktime` | `<video>` (served with Range support) |
| `.ogv` | `video/ogg` | `<video>` |
#### Audio
@@ -153,22 +153,37 @@ Labels are stored in `thesis_files.display_label`. If blank, the field falls bac
## Storage layout
Files are stored outside the webroot in `app/storage/`.
Files are stored outside the webroot in `app/storage/`. Each thesis gets its own
folder keyed by object type, year, author slug and a title slug:
```
app/storage/
├── covers/
│ └── <random-hex>.jpg # cover images
└── theses/
└── <year>/
└── <YEAR>_<AUTHOR_SLUG>/
└── <AUTHOR_SLUG>_<sanitized-filename>.<ext>
├── {objet}/ # tfe | these | frart (plus legacy theses/, documents/)
│ └── <year>/
│ └── <YEAR>_<AUTHOR_SLUG>_<TITLE_SLUG>/ # e.g. 2025_EMMA_RENARD_REHABILITATION_..._VERS_UN
│ ├── <PREFIX>_TFE_01.pdf # main files: _TFE_<NN>.<ext>
│ ├── ..._TFE_02.jpg
│ ├── ..._ANNEXE_01.pdf # annexes: _ANNEXE_<NN>.<ext>
│ ├── ..._COUVERTURE.png # cover image (_COUVERTURE.<ext>)
│ ├── ..._NOTE_INTENTION.pdf # note d'intention (_NOTE_INTENTION.pdf)
│ └── ..._TFE_03.vtt # captions share the _TFE_ series
└── covers/ # legacy dir — no longer written to
```
Where `<PREFIX>` = `<YEAR>_<AUTHOR_SLUG>_<TITLE_SLUG>` (the folder name), and the
object type `{objet}` ∈ `tfe`, `these`, `frart` (older/imported rows may live
under legacy `theses/` or `documents/`).
- Author slug: uppercase ASCII, spaces → underscores, accents stripped (e.g. `EMMA_RENARD`).
- Filename: same normalisation applied to the original filename.
- If a folder `<YEAR>_<AUTHOR_SLUG>` already exists a numeric suffix is appended (`_1`, `_2`, …).
- If a filename already exists in the folder a numeric suffix is appended before the extension.
- Title slug: accented chars → ASCII base, other non-alphanumerics → underscores (uppercase).
- The cover and note d'intention are stored **inside** the thesis folder using the
`_COUVERTURE` / `_NOTE_INTENTION` suffixes, recorded as `file_type='cover'` /
`'note_intention'` rows — not in a separate `covers/` directory.
- TFE files (and captions) are numbered `_TFE_<NN>` (`_TFE_01`, …); annexes use
`_ANNEXE_<NN>` so several files of the same kind coexist in the folder.
- If a folder `<PREFIX>` already exists a numeric suffix is appended to the folder
(`_1`, `_2`, …); if a filename already exists in the folder a numeric suffix is
appended before the extension.
Files are never served directly from disk. All access goes through `MediaController` (`/media?path=…`), which enforces:
- Path traversal prevention (character whitelist + `realpath()` jail)
+2 -1
View File
@@ -2,7 +2,8 @@
XAMXAM pushes its latest SQLite snapshot to a Nextcloud folder so that a
server/disk failure doesn't take the backup history with it. This is the
implementation of *Phase 5 — Remote Sync* from [backup-plan.md](backup-plan.md).
implementation of *Phase 5 — Remote Sync* from the archived
[`backup-plan.md`](archive/backup-plan.md).
## What it does
+1
View File
@@ -28,6 +28,7 @@ Single-value filters (`collectSearchParams()`):
- `finality` — finality type
- `format` — format
- `keyword` — tag/keyword
- `language` — language
Results are paginated (`limit = 20` default); the **search bar**
(`app/templates/partials/search-bar.php`) submits a GET form to `/search`.
+3 -3
View File
@@ -29,8 +29,8 @@ Current security posture for XAMXAM.
so `session.gc_maxlifetime` must be ≥ the 12 h absolute timeout or PHP would
reap active sessions early.
- nginx `auth_basic` layer has been removed; the PHP session layer is the only
gate. (LDAP-based login is a proposed future enhancement — see
[`ldap.md`](ldap.md). It is **not** implemented.)
gate. (LDAP-based login is a proposed future enhancement — **not**
implemented. See [`ldap.md`](archive/ldap.md).)
### PHP-FPM session GC configuration
@@ -130,7 +130,7 @@ spoofing.
## Areas to keep monitored
- Tightening the public CSP (`frame-ancestors 'none'` vs `'self'` on embed
routes) is an active topic — see `TODO.md`.
routes) is an active topic — tracked in the repo-root `TODO.md`.
- Sensitive file downloads and their expiry/token handling are worth periodic
review as usage grows.