5.2 KiB
F3 Simple Blog
Blog simple construit avec Fat-Free Framework et SQLite.
Le projet vise un blog léger, lisible et facile à déployer, avec un petit back-office d’administration, une médiathèque locale et un rendu Markdown sécurisé.
Fonctionnalités
- listing public des articles avec pagination ;
- page article ;
- authentification admin ;
- création, modification et suppression d’articles ;
- médiathèque locale avec upload, texte alternatif, copie de la syntaxe Markdown et suppression ;
- vignette de carte dérivée de la première image du contenu ;
- rendu Markdown avec images locales
media:...; - stockage des images en JPG/PNG bruts sans transformation.
Structure
project/
├── app/
│ ├── bootstrap.php
│ ├── config.ini
│ ├── helpers.php
│ ├── Controllers/
│ ├── Models/
│ ├── Services/
│ └── Views/
├── db/
├── logs/
├── public/
│ ├── assets/
│ └── uploads/media/
├── docker/
├── scripts/
│ ├── bootstrap.php
│ ├── install.php
│ └── create-admin.php
└── tmp/uploads/
Architecture
Le backend s’appuie sur un petit noyau :
SiteController: pages publiques ;AuthController: connexion / déconnexion ;AdminController: back-office articles et médiathèque ;Controller: rendu, session courante, flash, CSRF ;Post,Media,User: modèlesDB\SQL\Mapper;MarkdownService: compilation et sanitation du contenu Markdown.
Intégration F3
Le projet utilise directement les briques natives du framework :
- routes et aliases dans
app/config.ini; DB\SQL\Mapperpour les tables principales ;Authpour la connexion ;Sessionpour la session ;Templatepour le rendu ;Web::receive()pour la réception des uploads ;Markdownpour le parsing Markdown ;Web::slug()pour les slugs.
Contenu et médiathèque
Les articles ne possèdent pas de champ “image de couverture”. Les images vivent dans le corps Markdown, et la première image rendue dans body_html sert de vignette dans les cartes d’article.
Les images du contenu utilisent la syntaxe :

La médiathèque :
- accepte uniquement JPG et PNG ;
- vérifie que le fichier reçu est bien une image ;
- conserve le fichier tel quel, sans réencodage ;
- stocke en base le nom du fichier, le texte alternatif, la largeur, la hauteur et la date de création.
Contrat Markdown
Le rendu Markdown suit les règles suivantes :
- le HTML brut saisi par l’auteur n’est pas rendu ;
- les liens sont filtrés avant rendu ;
- seules les images locales en
media:...sont rendues ; - les images externes ne sont pas affichées ;
- avec le parseur Markdown de F3, il faut laisser une ligne vide entre deux blocs (titre, liste, citation, image, code) pour un rendu fiable.
Sécurité
Le projet inclut :
- session d’administration ;
- jeton CSRF sur les formulaires ;
- rotation du jeton CSRF après connexion et déconnexion ;
- sanitation du HTML produit à partir du Markdown ;
- validation des uploads image ;
- cookies
httponlyetsamesite=Lax.
Pré-requis
Développement local
- PHP 8.3+
- Composer
- extensions PHP :
pdo_sqlite,mbstring,intl,dom
Déploiement Docker
- Docker
- Docker Compose
Démarrage local
composer install
cp config.local.ini.example config.local.ini
php scripts/install.php
php -S 127.0.0.1:8080 -t public
Puis ouvrir http://127.0.0.1:8080.
Créer un compte admin :
php scripts/create-admin.php admin
Configuration
Le fichier app/config.ini contient les valeurs par défaut. Tu peux les surcharger dans config.local.ini.
Exemple minimal en production :
[globals]
app.env=prod
app.timezone=Europe/Paris
Le paramètre app.env doit être forcé à prod sur un déploiement réel.
Déploiement Docker derrière Caddy
Le projet est prévu pour un déploiement simple :
- Apache sert
public/dans le conteneur ; compose.yamlexpose l’application sur127.0.0.1:8888par défaut ;- Caddy termine TLS et reverse-proxy vers cette cible ;
- SQLite, les logs et les uploads sont montés sur des volumes persistants.
Exemple :
cp config.local.ini.example config.local.ini
# édite config.local.ini : app.env=prod
docker compose up -d --build
Créer un compte admin dans le conteneur :
docker compose exec app php scripts/create-admin.php admin
Le script te demandera ensuite le mot de passe du compte.
Le Caddyfile.example fournit une base de reverse proxy. En production, il faut exposer publiquement Caddy uniquement. L’application Apache/PHP ne doit pas être accessible directement depuis Internet.
Sauvegarde
Pour sauvegarder le blog, il faut au minimum conserver :
db/app.sqlitepublic/uploads/media/
Les logs peuvent aussi être conservés si tu veux garder l’historique d’erreurs.
Limites assumées
- base SQLite ;
- instance applicative unique ;
- pas de système de migrations automatique ;
- en cas de changement de schéma, une migration manuelle ou une base recréée sera nécessaire.