Less home code more F3
This commit is contained in:
66
README.md
66
README.md
@@ -11,7 +11,7 @@ project/
|
||||
│ ├── config.ini # Configuration F3 (globals + routes)
|
||||
│ ├── bootstrap.php # Initialisation (config, DB, session, erreurs)
|
||||
│ ├── Controllers/
|
||||
│ ├── Helpers/ # Fonctions utilitaires
|
||||
│ ├── Helpers/ # Fonctions utilitaires ciblées
|
||||
│ ├── Models/ # DB\SQL\Mapper (Post, Media, User)
|
||||
│ ├── Services/ # MarkdownService
|
||||
│ └── Views/
|
||||
@@ -20,7 +20,7 @@ project/
|
||||
├── logs/
|
||||
│ └── php-error.log # Log PHP configuré au runtime
|
||||
├── public/
|
||||
│ ├── assets/ # Sources CSS/JS servies via /min/@file
|
||||
│ ├── assets/ # Assets statiques servis directement
|
||||
│ └── uploads/
|
||||
│ └── media/ # Images publiées (JPG conservé, PNG/WebP normalisés en PNG)
|
||||
├── scripts/
|
||||
@@ -28,7 +28,6 @@ project/
|
||||
│ ├── install.php # Initialisation idempotente de la base
|
||||
│ └── create-admin.php # Création d'un compte admin en CLI
|
||||
└── tmp/ # Runtime temporaire, recréable sans perte métier
|
||||
├── cache/ # Cache F3 + assets minifiés
|
||||
└── uploads/ # Transit Web::receive(), nettoyé après chaque upload
|
||||
```
|
||||
|
||||
@@ -36,12 +35,12 @@ project/
|
||||
|
||||
- **Routage nommé** — `config.ini [routes]`, filtre `alias` dans les templates, `reroute('@route')` dans les contrôleurs.
|
||||
- **Cache HTTP** — TTL par contrôleur via `expire()`, forcé à `0` quand un utilisateur est connecté.
|
||||
- **Assets minifiés** — `Web::minify()` via `AssetController` (`GET /min/@file`).
|
||||
- **Assets statiques** — servis directement depuis `public/assets/`, laissés au serveur HTTP / reverse proxy.
|
||||
- **Upload** — `Web::receive()` avec contrôle de taille, puis validation MIME/dimensions côté modèle.
|
||||
- **Images** — normalisation des médias via GD (JPG conservé, PNG/WebP convertis en PNG pour préserver la transparence).
|
||||
- **Markdown** — `Markdown::instance()->convert()` + `strip_tags` et résolution des images média.
|
||||
- **Images** — normalisation des médias via `Image`, lecture/écriture via `Base::read()` / `Base::write()`.
|
||||
- **Markdown** — `Markdown::instance()->convert()` suivi d'un assainissement DOM ciblé et de la résolution des images média.
|
||||
- **Slugs** — `Web::instance()->slug()`.
|
||||
- **Session / CSRF** — `$f3->set('JAR', …)`, hooks `beforeRoute()` sur les contrôleurs protégés, jeton `@CSRF` recopié en session au rendu et vérifié au POST suivant.
|
||||
- **Session / CSRF** — `$f3->set('JAR', …)`, `new Session(null, 'CSRF')`, hooks `beforeRoute()` sur les contrôleurs protégés, et petit pool de jetons côté session pour éviter les collisions multi-onglets.
|
||||
- **ORM** — `DB\SQL\Mapper` : `paginate()`, `copyfrom()`, `cast()`, `find()`.
|
||||
- **Erreurs** — gestion personnalisée en production via `ONERROR` + fallback HTML minimal sur erreur fatale.
|
||||
|
||||
@@ -51,7 +50,7 @@ project/
|
||||
|
||||
- PHP 8.3+
|
||||
- Composer
|
||||
- Extensions PHP : `pdo_sqlite`, `gd`, `mbstring`, `intl`
|
||||
- Extensions PHP : `pdo_sqlite`, `gd`, `mbstring`, `intl`, `dom`
|
||||
|
||||
### Déploiement Docker
|
||||
|
||||
@@ -74,6 +73,25 @@ app.env=prod
|
||||
app.timezone=Europe/Paris
|
||||
```
|
||||
|
||||
### Proxies de confiance
|
||||
|
||||
L'application ne délègue pas la sécurité des cookies à Apache : elle détermine le schéma de la requête pour marquer la session en `Secure`.
|
||||
|
||||
La valeur par défaut couvre les cas les plus courants :
|
||||
|
||||
- `127.0.0.1,::1` — Caddy sur le même hôte ;
|
||||
- `172.16.0.0/12` — réseaux Docker IPv4 standard, y compris un Caddy conteneurisé sur le même réseau.
|
||||
|
||||
Si ton proxy tourne sur un autre sous-réseau, surcharge `app.trusted_proxies` dans `config.local.ini`.
|
||||
|
||||
```ini
|
||||
[globals]
|
||||
app.trusted_proxies=127.0.0.1,::1,172.16.0.0/12
|
||||
# ou, avec une liste F3 : app.trusted_proxies[]=10.0.0.42
|
||||
```
|
||||
|
||||
Seuls ces proxies sont autorisés à influencer `X-Forwarded-Proto` / `Forwarded`.
|
||||
|
||||
## Développement local
|
||||
|
||||
```bash
|
||||
@@ -98,11 +116,16 @@ php scripts/create-admin.php admin
|
||||
|
||||
```bash
|
||||
cp config.local.ini.example config.local.ini
|
||||
# édite config.local.ini (app.env=prod, app.timezone, etc.)
|
||||
# édite config.local.ini (app.env=prod, app.timezone, trusted proxies si besoin)
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Docker ne monte que les dossiers persistants (`db/`, `logs/`, `public/uploads/media/`) et laisse `tmp/` dans le conteneur pour qu'il reste éphémère.
|
||||
Le déploiement Docker est cohérent avec le projet :
|
||||
|
||||
- l'image embarque les extensions réellement utilisées (`pdo_sqlite`, `gd`, `mbstring`, `intl`, `dom`) ;
|
||||
- `scripts/install.php` s'exécute au démarrage pour initialiser SQLite de façon idempotente ;
|
||||
- seuls les répertoires métier/persistants sont montés (`db/`, `logs/`, `public/uploads/media/`) ;
|
||||
- `tmp/` reste dans le conteneur et sert uniquement au runtime F3 (transit d'upload, fichiers temporaires).
|
||||
|
||||
Le fichier `config.local.ini` est monté en lecture seule. Si le fichier hôte n'existe pas, Docker peut créer un répertoire à la place ; l'entrypoint le supprime et l'application retombe sur les valeurs par défaut de `app/config.ini`.
|
||||
|
||||
@@ -117,15 +140,14 @@ docker compose exec app php scripts/create-admin.php admin
|
||||
|
||||
## Cache public
|
||||
|
||||
Les pages publiques sont cacheables pour un visiteur anonyme :
|
||||
Les pages publiques restent cacheables pour un visiteur anonyme :
|
||||
|
||||
- `/` : TTL de 300 s.
|
||||
- `/` : TTL de 300 s ;
|
||||
- `/posts/@slug` : TTL de 3600 s.
|
||||
- `/min/app.css` et `/min/app.js` : TTL de 86400 s.
|
||||
|
||||
Quand un utilisateur est connecté, le rendu est forcé en non-cacheable avec `expire(0)` pour ne pas servir du contenu admin via un cache intermédiaire.
|
||||
|
||||
Le projet ne fait pas d'invalidation explicite du cache lors des mutations d'articles : la fraîcheur dépend des TTL ci-dessus.
|
||||
Les assets statiques (`public/assets/`) sont servis directement ; leur éventuel cache HTTP relève du serveur web ou du reverse proxy.
|
||||
|
||||
## Médias et limites d'upload
|
||||
|
||||
@@ -159,8 +181,19 @@ blog.example.com {
|
||||
}
|
||||
```
|
||||
|
||||
Dans la plupart des cas, la valeur par défaut de `app.trusted_proxies` suffit aussi pour ce mode ; surcharge-la seulement si ton réseau Docker utilise un autre sous-réseau.
|
||||
|
||||
Comme F3 lit aussi `X-Forwarded-For` pour déterminer `IP`, le conteneur applicatif ne doit pas être exposé directement à Internet : Caddy doit rester l'unique point d'entrée public et réécrire les en-têtes `X-Forwarded-*`.
|
||||
|
||||
Le fichier `Caddyfile.example` fournit en plus un jeu d'en-têtes de sécurité minimal.
|
||||
|
||||
## Répartition des responsabilités avec Caddy
|
||||
|
||||
Le projet n'essaie pas de dupliquer ce que le reverse proxy gère naturellement :
|
||||
|
||||
- **Caddy** — TLS, compression, en-têtes de sécurité, terminaison HTTPS, reverse proxy.
|
||||
- **Application / PHP-F3** — sessions, CSRF, cookies, validation métier, rendu HTML et cache HTTP des pages dynamiques.
|
||||
|
||||
## Données à sauvegarder
|
||||
|
||||
- `db/` — base SQLite.
|
||||
@@ -178,10 +211,7 @@ docker compose up -d --build
|
||||
- PHP : `logs/php-error.log`.
|
||||
- Apache / conteneur : `docker compose logs -f app`.
|
||||
|
||||
## Limitations connues
|
||||
|
||||
- **CSRF et multi-onglets** — F3 expose un jeton CSRF unique via `@CSRF`. Le projet le recopie en session au rendu et le vérifie au POST suivant. Si l'admin ouvre deux onglets, le token du premier est écrasé par celui du second. La soumission du premier formulaire échouera avec « Jeton CSRF invalide ». Solution de contournement : travailler dans un seul onglet à la fois. F3 ne fournit pas de mécanisme de pool de tokens.
|
||||
|
||||
## Notes
|
||||
|
||||
- Les dates sont stockées en UTC (`gmdate`) puis formatées côté affichage avec le fuseau configuré.
|
||||
- Le pipeline Markdown n'autorise que les éléments utiles au rendu éditorial et remappe systématiquement les images vers la médiathèque locale.
|
||||
|
||||
Reference in New Issue
Block a user