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

5.5 KiB
Raw Blame History

F3 Simple Blog

Blog simple avec Fat-Free Framework.

Structure

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ésWeb::minify() via AssetController (GET /min/@file)
  • UploadWeb::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)
  • MarkdownMarkdown::instance()->convert() + reconstruction DOM en liste blanche
  • SlugsWeb::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
  • LoggingLog de F3 avec fallback file_put_contents
  • ORMDB\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 :

cp config.local.ini.example config.local.ini

Réglages minimums conseillés en production :

[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

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 :

php scripts/create-admin.php admin
# mot de passe : 10 caractères minimum

Déploiement avec Docker

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 :

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

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 :

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

docker compose up -d --build

Logs

  • Applicatifs : logs/app.log
  • PHP : logs/php-error.log
  • Apache (conteneur) : docker compose logs -f app