docs: verify and refactor documentation to match current codebase

This commit is contained in:
Pontoporeia
2026-08-24 11:31:38 +02:00
parent b2cdbd0174
commit e9747edce0
17 changed files with 1006 additions and 1442 deletions
+153 -156
View File
@@ -1,204 +1,201 @@
# Deployment
Server setup, deployment, and rollback procedures for Post-ERG.
Server setup, deployment, backups, and rollback procedures for XAMXAM.
---
## One-Time Server Setup
## Overview
Run before first deploy:
- Production host: `xamxam` (over SSH), app root `/var/www/xamxam/`
- Files are pushed with `rsync` — **there is no git repo on the remote**
- Web / FPM user: `www-data`, app group: `xamxam`
- The **DocumentRoot** is `app/public/`, but on the server the code lives flat
under `/var/www/xamxam/` (Composer autoload path adjusted to `src/`)
- SQLite database at `/var/www/xamxam/storage/xamxam.db`
Deployment is orchestrated through the `justfile` (`deploy` group).
---
## One-time server setup
```bash
just setup-server
ssh xamxam
sudo mkdir -p /var/www/xamxam
sudo chown www-data:xamxam /var/www/xamxam
sudo chmod 775 /var/www/xamxam
exit
```
This creates `/var/www/posterg/` with correct ownership/permissions:
- Owner: `www-data:posterg`
- Directories: **2775** (setgid — new files inherit `posterg` group)
- Files: **664**
- Database files: **660**
Then from local, deploy once and apply the nginx config + verify permissions:
> **Important:** After running `setup-server`, log out and back in on the server (or `newgrp posterg`) so group membership is active before deploying.
```bash
just deploy
just deploy-nginx
```
### Why setgid (2775)?
For a full initial rollout including backup + cleanup cron jobs:
rsync uses `--chown=www-data:posterg`. Both `padlock` and `www-data` must write to dirs. With `2775 + group=posterg`, new subdirs inherit the group automatically.
```bash
just deploy-all-first # deploy + deploy-backup + deploy-cleanup-cron
```
---
## Deploying
| Command | Purpose |
|---------|---------|
| `just deploy` | Full deploy: build + code + Composer deps + migrations + env + permissions check |
| `just deploy-code` | rsync app files + nginx config + permissions (no Composer, no migrations) |
| `just deploy-deps` | Sync composer.{json,lock} → server, then `composer install`/`dump-autoload` |
| `just deploy-migrate` | Run pending DB migrations on the server |
| `just deploy-env` | Upload `app/.env` (only if the remote `.env` is absent — never overwrites a key) |
| `just deploy-nginx` | Upload + apply + reload nginx config |
| `just deploy-db` | Push local `xamxam.db` → remote (**refuses** if a remote DB already exists) |
| `just deploy-verify-permissions` | Check ownership / permissions on the server |
> ℹ️ **First deploy?** After `just deploy`, run `just deploy-backup` to install
> the backup script + cron jobs.
### Environment file & re-encryption
`app/.env` holds secrets (e.g. `APP_KEY`). `deploy-env` will **not** overwrite a
remote `.env` that already has an `APP_KEY`. If you rotate `APP_KEY`, re-encrypt
the SMTP password and push the new key:
```bash
just deploy # Push all app files
just deploy-db # Push initial database (aborts if remote DB exists)
just deploy-nginx # Push + apply nginx config
just reencrypt-password <new_base64_key> # runs scripts/reencrypt-smtp-password.php on server
just deploy-env
```
### First-Time Deployment
---
Since we moved from `/var/www/html/` to `/var/www/posterg/`:
## Backups
1. **Setup server directory** (one time):
```bash
just setup-server
```
SQLite backups are taken with a WAL-safe hot backup (`sqlite3 .backup`) then
gzipped to `/var/backups/xamxam/` on the server.
2. **Deploy application**:
```bash
just deploy
```
Uploads to `/var/www/posterg/`, excludes tests/docs/vendor.
| Command | Purpose |
|---------|---------|
| `just deploy-backup` | Install backup script + cron jobs (one-shot) |
| `just deploy-backup-script` | Install `/usr/local/bin/backup-sqlite.sh` |
| `just deploy-backup-cron` | Install `/etc/cron.d/xamxam-backup` (hourly 30d + daily 90d) + dirs/log |
| `just deploy-check-backup-log` | Tail `/var/log/sqlite-backup.log` |
| `just deploy-list-backups` | List backups on the server |
| `just trigger-backup` | Run the backup script now |
| `just test-restore <path.gz>` | Fetch + decompress + verify a remote snapshot |
3. **Deploy nginx config**:
```bash
just deploy-nginx
ssh posterg
sudo bash /tmp/deploy-production.sh
sudo systemctl reload nginx
```
Retention: hourly backups kept 30 days, nightly (02:00) backups kept 90 days.
Backup files: `/var/backups/xamxam/db-<timestamp>.db.gz`.
4. **Verify**:
```bash
just server-status
curl -I https://posterg.erg.be/ # 200 ✓
curl -I https://posterg.erg.be/admin/ # 200 ✓
curl -I https://posterg.erg.be/storage/ # 404 ✓
```
Draft cleanup is handled by a separate cron (`/etc/cron.d/xamxam-cleanup`),
installed via `just deploy-cleanup-cron`, logging to
`/var/log/xamxam-cleanup.log`. Verify with `just deploy-check-cleanup-log`.
### Subsequent Deployments
---
## Permissions model
Managed/simulated by the justfile and verified by `deploy-verify-permissions`:
- Ownership: `www-data:xamxam`
- Directories: **2775** (setgid — new files inherit the `xamxam` group)
- Regular files: **664**
- `storage/xamxam.db` and other `*.db`: **660**
- `app/.env`: **640**
The nginx/`deploy-server.sh` step (`just deploy-nginx`, or
`sudo DEPLOY_USER=$USER bash /tmp/deploy-server.sh` via `just deploy-script`)
fixes permissions and installs the nginx config.
---
## Storage migration
If paths moved (e.g. legacy upload locations), a one-off migration script can
rewrite stored paths on the server:
```bash
just deploy-migrate-storage # apply
just deploy-migrate-storage --dry-run # dry-run only
```
---
## Rollback
Because there is no repo on the remote, rollback means restoring files and/or
restoring the database from a backup.
### Restore the database
```bash
ssh xamxam
sudo systemctl stop nginx
cp /var/backups/xamxam/db-<timestamp>.db.gz /tmp/restore.db.gz
gunzip -c /tmp/restore.db.gz > /var/www/xamxam/storage/xamxam.db
chown www-data:xamxam /var/www/xamxam/storage/xamxam.db
chmod 660 /var/www/xamxam/storage/xamxam.db
sudo systemctl start nginx
```
### Restore application files
Re-run `just deploy` from a good local state. In jj you can jump back to a
previous commit and redeploy:
```bash
jj log # find a known-good change
jj edit <previous-change-id> # work from that revision
just deploy
```
---
## Server Directory Structure
```
/var/www/posterg/ # Application root (private)
├── public/ # DocumentRoot (nginx points here)
│ ├── index.php
│ ├── search.php
│ ├── memoire.php
│ ├── admin/
│ └── assets/
├── includes/ # Templates (private)
├── config/ # Configuration (private)
├── storage/ # Database + uploads (private)
│ ├── posterg.db
│ └── theses/
├── src/ # PHP classes (private)
└── scripts/ # Admin tools (private)
```
**Nginx DocumentRoot:** `/var/www/posterg/public/`
Only `public/` is web-accessible. Everything else is physically private.
---
## Managing Admin Users
## Verify after deploy
```bash
ssh posterg "sudo bash /var/www/posterg/scripts/manage-admin-users.sh"
```
Interactive menu for adding/changing/deleting htpasswd entries at `/etc/nginx/.htpasswd-posterg`.
---
## Security Verification
After every deploy, verify private files are inaccessible:
```bash
curl -I https://posterg.erg.be/storage/test.db # Must 404
curl -I https://posterg.erg.be/config/bootstrap.php # Must 404
curl -I https://posterg.erg.be/src/Database.php # Must 404
curl -I https://xamxam.erg.be/ # expect 200
curl -I https://xamxam.erg.be/admin/ # expect 200
curl -I https://xamxam.erg.be/storage/ # expect 404 (blocked)
just deploy-verify-permissions # expect "All permissions OK"
```
---
## Troubleshooting
### rsync Permission Denied
```bash
just setup-server # Fixes directory permissions
# Then log out/in on server and retry
```
Manual fix:
```bash
ssh posterg
sudo chown -R www-data:posterg /var/www/posterg
sudo find /var/www/posterg -type d -exec chmod 2775 {} \;
sudo find /var/www/posterg -type f -exec chmod 664 {} \;
sudo chmod 660 /var/www/posterg/storage/*.db
```
### Nginx 403 Forbidden
```bash
ssh posterg
sudo find /var/www/posterg -type d -exec chmod 2775 {} \;
sudo find /var/www/posterg -type f -exec chmod 664 {} \;
sudo chmod 660 /var/www/posterg/storage/*.db
```
### Database Permission Error
```bash
ssh posterg
sudo chown www-data:posterg /var/www/posterg/storage/posterg.db
sudo chmod 660 /var/www/posterg/storage/posterg.db
```
### Nginx 500 / Site 404
Check nginx DocumentRoot:
```bash
ssh posterg "grep 'root ' /etc/nginx/sites-available/posterg"
# Should show: root /var/www/posterg/public;
```
### Admin 404
Nginx may still use old `/formulaire/` location. Update `nginx/posterg.conf` to use `/admin/`.
- **502 Bad Gateway**
```bash
sudo systemctl status php8.4-fpm
sudo systemctl restart php8.4-fpm
```
- **Nginx config error**
```bash
sudo nginx -t
```
- **`deploy-db` refuses to run**
The remote DB already exists. Remove it manually only if you intend to
overwrite production data.
- **Composer did not pick up new classes**
`deploy-deps` runs `composer dump-autoload` when the lock checksum is
unchanged; if that still fails, force a reinstall.
- **Backup/schedule not running**
Confirm the cron files are installed (`/etc/cron.d/xamxam-backup`,
`/etc/cron.d/xamxam-cleanup`) and the log files are writable by `www-data`.
---
## Rollback
If something goes wrong:
```bash
# Restore old nginx config
ssh posterg
sudo cp /etc/nginx/sites-available/posterg.backup /etc/nginx/sites-available/posterg
sudo systemctl reload nginx
# Or restore old site (if backed up)
sudo rm -rf /var/www/posterg
sudo mv /var/www/html.backup /var/www/html
```
With jj:
```bash
jj log
jj edit <previous-change-id>
```
---
## Commands Reference
## Selected commands reference
| Command | Purpose |
|---------|---------|
| `just setup-server` | Create `/var/www/posterg/` (first time only) |
| `just deploy` | Deploy application files |
| `just deploy-nginx` | Update nginx configuration |
| `just deploy-db` | Deploy database file |
| `just server-status` | Check server health |
| `just server-logs` | View server logs |
| `just deploy` | Full deploy (build + code + deps + migrate + env + perms) |
| `just deploy-nginx` | Apply nginx config |
| `just deploy-backup` | Install backup script + cron |
| `just deploy-cleanup-cron` | Install orphaned-draft cleanup cron |
| `just deploy-list-backups` | List server backups |
| `just trigger-backup` | Run backup now |
| `just reencrypt-password <key>` | Re-encrypt SMTP password after key rotation |
| `just test-restore <path.gz>` | Verify a snapshot |