Files
f3-simple-blog/README.md
2026-03-27 20:14:11 +01:00

185 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# F3 Simple Blog
Blog simple avec Fat-Free Framework.
## Structure
```text
project/
├── config.local.ini # Surcharges locales (gitignored)
├── app/
│ ├── config.ini # Routes et variables F3
│ ├── bootstrap.php # Initialisation (DB, session, cache, erreurs)
│ ├── Controllers/
│ ├── Helpers/ # Fonctions utilitaires (App.php, Error.php)
│ ├── Models/ # DB\SQL\Mapper (Post, Media, User)
│ ├── Services/ # MarkdownService
│ └── Views/
├── db/
│ └── app.sqlite
├── logs/
│ ├── app.log
│ └── php-error.log
├── public/
│ ├── assets/ # Sources CSS/JS (servis minifiés via /min/@file)
│ └── uploads/
│ └── media/ # Images publiées (JPG conservé, PNG/WebP normalisés en PNG)
└── tmp/
├── cache/ # Cache F3 (pages publiques + 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 + serveur** — TTL déclarés directement dans `[routes]`, `Cache::reset('.url')` à la mutation
- **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** — `$f3->set('JAR', …)`, hooks `beforeRoute()` sur les contrôleurs protégés, token CSRF créé seulement sur les vues qui contiennent des formulaires
- **Logging** — `Log` de F3 avec fallback `file_put_contents`
- **ORM** — `DB\SQL\Mapper` : `paginate()`, `copyfrom()`, `cast()`, `find()`
## Prérequis
### Développement local
- PHP 8.3+
- Composer
- Extensions PHP : `pdo_sqlite`, `dom`, `gd`, `mbstring`
### Déploiement Docker
- Docker
- Docker Compose
## Configuration
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
```
Réglages minimums conseillés en production :
```ini
[globals]
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
## Développement local
```bash
composer install
cp config.local.ini.example config.local.ini
php scripts/install.php
php -S 127.0.0.1:8080 -t public
```
Ouvre ensuite `http://127.0.0.1:8080`.
Créer un compte admin :
```bash
php scripts/create-admin.php admin
# mot de passe : 10 caractères minimum
```
## Déploiement avec Docker
```bash
cp config.local.ini.example config.local.ini
# édite config.local.ini (app.env=prod, app.timezone, etc.)
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.
Si `config.local.ini` n'existe pas, le conteneur démarre avec les valeurs par défaut de `app/config.ini`.
Le service écoute sur `http://127.0.0.1:8888`.
Créer un compte admin :
```bash
docker compose exec app php scripts/create-admin.php admin
# mot de passe : 10 caractères minimum
```
## Cache public et navigation
Les pages publiques (`/` et `/posts/@slug`) restent cacheables parce que leur rendu n'accède ni à la session, ni au token CSRF. La navigation publique affiche donc un lien statique vers la connexion / l'administration, tandis que les vues d'administration restent session-aware.
## 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
La médiathèque admin est paginée et le picker dans l'éditeur charge seulement les images les plus récentes pour éviter de charger toute la bibliothèque en mémoire à chaque formulaire.
## Reverse proxy Caddy
### Caddy sur le même hôte
```caddy
blog.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8888
}
```
### Caddy dans Docker
Si Caddy tourne aussi dans Docker, place-le sur le même réseau que `app` et cible directement le service :
```caddy
blog.example.com {
encode zstd gzip
reverse_proxy app:80
}
```
## Données à sauvegarder
- `db/` — base SQLite
- `public/uploads/media/` — images
- `logs/` — optionnel
- `tmp/` — non persistant, recréable
## Mise à jour
```bash
docker compose up -d --build
```
## Logs
- Applicatifs : `logs/app.log`
- PHP : `logs/php-error.log`
- Apache (conteneur) : `docker compose logs -f app`