2026-03-27 23:04:15 +01:00
Doc
2026-03-27 22:40:44 +01:00
2026-03-27 14:43:08 +01:00
2026-03-27 22:30:10 +01:00
2026-03-27 14:43:08 +01:00
2026-03-27 14:43:08 +01:00
2026-03-27 14:43:08 +01:00
2026-03-27 22:30:10 +01:00
2026-03-27 22:50:42 +01:00
Doc
2026-03-27 22:40:44 +01:00
2026-03-27 20:14:11 +01:00
2026-03-27 22:30:10 +01:00
2026-03-27 22:56:24 +01:00
2026-03-27 14:43:08 +01:00
2026-03-27 22:50:42 +01:00

F3 Simple Blog

Blog simple avec Fat-Free Framework, SQLite et une petite médiathèque dimages.

Structure

project/
├── config.local.ini         # Surcharges locales (gitignored)
├── app/
│   ├── config.ini           # Configuration F3 (globals + routes)
│   ├── bootstrap.php        # Initialisation (config, DB, session, erreurs)
│   ├── Controllers/
│   ├── Helpers/             # Fonctions utilitaires
│   ├── Models/              # DB\SQL\Mapper (Post, Media, User)
│   ├── Services/            # MarkdownService
│   └── Views/
├── db/
│   └── app.sqlite           # Base SQLite persistante
├── logs/
│   └── php-error.log        # Log PHP configuré au runtime
├── public/
│   ├── assets/              # Sources CSS/JS servies via /min/@file
│   └── uploads/
│       └── media/           # Images publiées (JPG conservé, PNG/WebP normalisés en PNG)
├── scripts/
│   ├── install.php          # Initialisation idempotente de la base
│   └── create-admin.php     # Création dun compte admin en CLI
└── tmp/
    ├── 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é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 / 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
  • ORMDB\SQL\Mapper : paginate(), copyfrom(), cast(), find()
  • Erreurs — gestion personnalisée en production via ONERROR + fallback HTML minimal sur erreur fatale

Prérequis

Développement local

  • PHP 8.3+
  • Composer
  • Extensions PHP : pdo_sqlite, dom, gd, mbstring, intl

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 denvironnement. 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 dupload

Les données persistantes restent hors de tmp : db/, logs/, public/uploads/media/.

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 quil reste réellement éphémère.

Le fichier config.local.ini est monté en lecture seule. Si le fichier hôte nexiste pas, Docker peut créer un répertoire à la place ; lentrypoint le supprime et lapplication retombe alors sur 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 restent 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

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).

Le projet ne fait pas dinvalidation explicite du cache public lors des mutations darticles : la fraîcheur dépend donc des TTL ci-dessus.

Médias et limites dupload

  • Formats acceptés à lentré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 dorigine

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.

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
}

Le fichier Caddyfile.example fournit en plus un jeu den-têtes de sécurité minimal.

Données à sauvegarder

  • db/ — base SQLite
  • public/uploads/media/ — images
  • logs/ — optionnel, utile pour diagnostic

Mise à jour

docker compose up -d --build

Logs

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

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 nexistent pas.
Description
No description provided
Readme 301 KiB
Languages
PHP 48.4%
HTML 20.6%
CSS 19.7%
JavaScript 7.1%
Dockerfile 2.6%
Other 1.6%