Less home code more F3
This commit is contained in:
106
README.md
106
README.md
@@ -1,6 +1,6 @@
|
||||
# F3 Simple Blog
|
||||
|
||||
Blog simple avec Fat-Free Framework, SQLite et une petite médiathèque d’images.
|
||||
Blog simple avec Fat-Free Framework, SQLite et une petite médiathèque d'images.
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -24,39 +24,26 @@ project/
|
||||
│ └── uploads/
|
||||
│ └── media/ # Images publiées (JPG conservé, PNG/WebP normalisés en PNG)
|
||||
├── scripts/
|
||||
│ ├── bootstrap.php # Autoload + bootstrap partagé par les scripts CLI
|
||||
│ ├── install.php # Initialisation idempotente de la base
|
||||
│ └── create-admin.php # Création d’un compte admin en CLI
|
||||
└── tmp/
|
||||
│ └── 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
|
||||
```
|
||||
|
||||
## Philosophie des dossiers runtime
|
||||
|
||||
Le projet sépare les données persistantes du runtime jetable :
|
||||
|
||||
- `tmp/` = runtime temporaire, recréable
|
||||
- `db/` = base SQLite persistante
|
||||
- `logs/` = logs persistants
|
||||
- `public/uploads/media/` = médias publiés et persistants
|
||||
|
||||
Autrement dit, `tmp/` peut être vidé sans perte métier. Les données à sauvegarder restent hors de `tmp/`.
|
||||
|
||||
## Fonctionnalités F3 utilisées
|
||||
|
||||
- **Routage nommé** — `config.ini [routes]`, filtre `alias` dans les templates, `reroute('@route')` dans les contrôleurs
|
||||
- **Cache HTTP / F3** — TTL appliqués dans les contrôleurs avec `expire()`
|
||||
- accueil : `300 s`
|
||||
- page article : `3600 s`
|
||||
- assets minifiés : `86400 s`
|
||||
- **Assets minifiés** — `Web::minify()` via `AssetController` (`GET /min/@file`)
|
||||
- **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()` + reconstruction DOM en liste blanche
|
||||
- **Slugs** — `Web::instance()->slug()`
|
||||
- **Session / CSRF** — `$f3->set('JAR', …)`, hooks `beforeRoute()` sur les contrôleurs protégés, jeton exposé via `@CSRF` puis recopié en session au rendu pour vérification lors du POST suivant
|
||||
- **ORM** — `DB\SQL\Mapper` : `paginate()`, `copyfrom()`, `cast()`, `find()`
|
||||
- **Erreurs** — gestion personnalisée en production via `ONERROR` + fallback HTML minimal sur erreur fatale
|
||||
- **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`).
|
||||
- **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.
|
||||
- **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.
|
||||
- **ORM** — `DB\SQL\Mapper` : `paginate()`, `copyfrom()`, `cast()`, `find()`.
|
||||
- **Erreurs** — gestion personnalisée en production via `ONERROR` + fallback HTML minimal sur erreur fatale.
|
||||
|
||||
## Prérequis
|
||||
|
||||
@@ -64,7 +51,7 @@ Autrement dit, `tmp/` peut être vidé sans perte métier. Les données à sauve
|
||||
|
||||
- PHP 8.3+
|
||||
- Composer
|
||||
- Extensions PHP : `pdo_sqlite`, `dom`, `gd`, `mbstring`, `intl`
|
||||
- Extensions PHP : `pdo_sqlite`, `gd`, `mbstring`, `intl`
|
||||
|
||||
### Déploiement Docker
|
||||
|
||||
@@ -73,9 +60,7 @@ Autrement dit, `tmp/` peut être vidé sans perte métier. Les données à sauve
|
||||
|
||||
## Configuration
|
||||
|
||||
Les paramètres par défaut sont dans `app/config.ini`.
|
||||
|
||||
Pour surcharger localement ou en production :
|
||||
Les paramètres par défaut sont dans `app/config.ini`. Pour surcharger localement ou en production :
|
||||
|
||||
```bash
|
||||
cp config.local.ini.example config.local.ini
|
||||
@@ -89,13 +74,6 @@ app.env=prod
|
||||
app.timezone=Europe/Paris
|
||||
```
|
||||
|
||||
Le fichier `config.local.ini` sert uniquement aux surcharges d’environnement. Les chemins runtime restent les mêmes partout :
|
||||
|
||||
- `tmp/cache/` pour le cache F3 et les assets minifiés
|
||||
- `tmp/uploads/` pour les fichiers temporaires d’upload
|
||||
|
||||
Les données persistantes restent hors de `tmp` : `db/`, `logs/`, `public/uploads/media/`.
|
||||
|
||||
## Développement local
|
||||
|
||||
```bash
|
||||
@@ -114,6 +92,8 @@ php scripts/create-admin.php admin
|
||||
# mot de passe : 10 caractères minimum
|
||||
```
|
||||
|
||||
`scripts/install.php` peut être relancé sans danger : il crée les tables si elles n'existent pas.
|
||||
|
||||
## Déploiement avec Docker
|
||||
|
||||
```bash
|
||||
@@ -122,9 +102,9 @@ cp config.local.ini.example config.local.ini
|
||||
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 réellement éphémère.
|
||||
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 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 alors sur les valeurs par défaut de `app/config.ini`.
|
||||
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`.
|
||||
|
||||
Le service écoute sur `http://127.0.0.1:8888`.
|
||||
|
||||
@@ -135,28 +115,27 @@ docker compose exec app php scripts/create-admin.php admin
|
||||
# mot de passe : 10 caractères minimum
|
||||
```
|
||||
|
||||
## Cache public et navigation
|
||||
## Cache public
|
||||
|
||||
Les pages publiques restent cacheables pour un visiteur anonyme :
|
||||
Les pages publiques sont cacheables pour un visiteur anonyme :
|
||||
|
||||
- `/` est servie avec un TTL de `300 s`
|
||||
- `/posts/@slug` est servie avec un TTL de `3600 s`
|
||||
- `/min/app.css` et `/min/app.js` sont servis avec un TTL de `86400 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 layout dépend de la session (navigation admin + formulaire de déconnexion avec CSRF). Le rendu est alors forcé en non-cacheable avec `expire(0)`.
|
||||
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 public lors des mutations d’articles : la fraîcheur dépend donc des TTL ci-dessus.
|
||||
Le projet ne fait pas d'invalidation explicite du cache lors des mutations d'articles : la fraîcheur dépend des TTL ci-dessus.
|
||||
|
||||
## Médias et limites d’upload
|
||||
## Médias et limites d'upload
|
||||
|
||||
- Formats acceptés à l’entrée : `JPG`, `PNG`, `WebP`
|
||||
- Taille max du fichier reçu : `10 Mo`
|
||||
- Dimensions max : `8000 × 8000 px`
|
||||
- Limite de surface : `40 mégapixels`
|
||||
- Sortie publiée : `JPG` pour les sources JPEG, `PNG` pour les sources PNG/WebP
|
||||
- Texte alternatif initial dérivé du nom de fichier d’origine
|
||||
- Formats acceptés à l'entrée : JPG, PNG, WebP.
|
||||
- Taille max du fichier reçu : 10 Mo.
|
||||
- Dimensions max : 8000 × 8000 px, 40 mégapixels.
|
||||
- Sortie publiée : JPG pour les sources JPEG, PNG pour les sources PNG/WebP.
|
||||
- Texte alternatif initial dérivé du nom de fichier d'origine.
|
||||
|
||||
La médiathèque admin est paginée et le picker dans l’éditeur charge seulement les `60` images les plus récentes pour éviter de charger toute la bibliothèque en mémoire à chaque formulaire.
|
||||
La médiathèque admin est paginée et le picker dans l'éditeur charge seulement les 60 images les plus récentes.
|
||||
|
||||
## Reverse proxy Caddy
|
||||
|
||||
@@ -180,13 +159,13 @@ blog.example.com {
|
||||
}
|
||||
```
|
||||
|
||||
Le fichier `Caddyfile.example` fournit en plus un jeu d’en-têtes de sécurité minimal.
|
||||
Le fichier `Caddyfile.example` fournit en plus un jeu d'en-têtes de sécurité minimal.
|
||||
|
||||
## Données à sauvegarder
|
||||
|
||||
- `db/` — base SQLite
|
||||
- `public/uploads/media/` — images
|
||||
- `logs/` — optionnel, utile pour diagnostic
|
||||
- `db/` — base SQLite.
|
||||
- `public/uploads/media/` — images.
|
||||
- `logs/` — optionnel, utile pour diagnostic.
|
||||
|
||||
## Mise à jour
|
||||
|
||||
@@ -196,10 +175,13 @@ docker compose up -d --build
|
||||
|
||||
## Logs
|
||||
|
||||
- PHP : `logs/php-error.log`
|
||||
- Apache / conteneur : `docker compose logs -f app`
|
||||
- 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é.
|
||||
- `scripts/install.php` peut être relancé sans danger : il crée les tables si elles n’existent pas.
|
||||
|
||||
Reference in New Issue
Block a user