mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
docs: keep only reference documentation, archive one-offs
This commit is contained in:
+5
-3
@@ -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
@@ -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
@@ -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 |
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,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
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user