docs: keep only reference documentation, archive one-offs

This commit is contained in:
Pontoporeia committed 2026-09-18 16:26:36 +02:00
1 parent 8accb88452
commit d5c1dfb23d
27 files changed
+108 -78

No files matched your search

+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/`.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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.