9.5 KiB
Slim Blog
Blog multi-utilisateurs modulaire développé avec Slim 4. Les domaines Auth, Category, Media, User
et Shared sont indépendants du domaine métier et réutilisables sans modification pour d'autres
projets (boutique, portfolio…).
Fonctionnalités
- Articles — création, édition, suppression avec éditeur WYSIWYG, slugs stables
- Catégories — filtrage sur la page d'accueil et dans l'interface admin
- Médias — upload WebP avec déduplication SHA-256
- Recherche — full-text FTS5 cumulable avec le filtre catégorie
- Comptes — trois rôles (
user,editor,admin), réinitialisation de mot de passe par email - RSS — flux 2.0 des 20 derniers articles (
/rss.xml) - Protection brute-force — verrouillage par IP après trop de tentatives échouées sur la connexion et la réinitialisation de mot de passe
Stack
| Rôle | Librairie |
|---|---|
| Framework HTTP | Slim 4 |
| Base de données | SQLite via PDO natif |
| Templates | Twig |
| CSS | Sass (7-1, BEM) |
| Éditeur WYSIWYG | Trumbowyg |
| Emails | PHPMailer |
| Logging | Monolog |
| Sanitisation HTML | HTMLPurifier |
| Protection CSRF | Slim CSRF |
| Injection de dépendances | PHP-DI (autowiring) |
| Analyse statique | PHPStan (niveau 8) |
Développement
Prérequis : PHP 8.4.1+ avec pdo_sqlite, intl, fileinfo, gd (WebP), xml (dom), Composer, Node.js 18+
git clone https://git.netig.net/netig/slim-blog
cd slim-blog
composer install
npm install && npm run build
cp .env.example .env
php bin/provision.php
php -S localhost:8080 -t public
Pour surveiller les modifications SCSS et recompiler automatiquement en développement :
npm run watch
npm run watchest réservé au développement local. En production, le CSS est compilé une fois lors du build Docker.
Pour tester l'upload de fichiers, augmenter les limites PHP :
php -S localhost:8080 -t public -d upload_max_filesize=6M -d post_max_size=8M
Si
upload_max_filesizeest trop basse, PHP abandonne l'intégralité du corps POST — fichier et tokens CSRF — ce qui provoque une erreur générique sans message explicite.
Production
Prérequis : Docker, Docker Compose
git clone https://git.netig.net/netig/slim-blog
cd slim-blog
cp .env.example .env
# Définir APP_ENV=production, APP_URL, ADMIN_PASSWORD et la configuration SMTP
docker compose up -d --build
docker compose exec app php bin/provision.php
Le démarrage en production avec
ADMIN_PASSWORD=changeme123est bloqué intentionnellement.
--buildest superflu sur une machine vierge mais garantit une image à jour dans tous les cas.
Note sur le .env : docker-compose.yml monte le fichier .env physiquement dans le conteneur
(- ./.env:/var/www/app/.env:ro). Ce mount est nécessaire car phpdotenv lit un fichier sur le
disque via file_get_contents — env_file seul ne suffit pas.
Au premier démarrage, l'entrypoint initialise automatiquement ./data/ sur l'hôte :
data/
├── public/ # assets compilés, index.php — servis par Nginx
│ └── media/ # uploads utilisateurs — persistés entre redéploiements
├── database/ # migrations + app.sqlite
└── var/ # cache Twig/HTMLPurifier, logs
Pour mettre à jour le site après un changement de code :
docker compose build
docker compose up -d
Logs Docker
Les erreurs PHP remontent dans docker compose logs grâce à error_log = /dev/stderr dans
docker/php/php.ini. Sans ce réglage, les erreurs des workers FPM ne sont pas transmises au
process principal et n'apparaissent pas dans les logs Docker.
Durcissement HTTP
docker/php/php.ini désactive l'en-tête X-Powered-By (expose_php = Off) et renomme le cookie de session de PHPSESSID en sid pour ne pas exposer la stack technique.
docker/nginx/default.conf ajoute quatre en-têtes de sécurité sur toutes les réponses :
| En-tête | Valeur | Protection |
|---|---|---|
X-Frame-Options |
SAMEORIGIN |
Clickjacking |
X-Content-Type-Options |
nosniff |
Sniffing MIME |
Referrer-Policy |
strict-origin-when-cross-origin |
Fuite d'URL |
Permissions-Policy |
camera=(), microphone=(), geolocation=() |
APIs navigateur |
Derrière un reverse proxy (Caddy, Nginx…)
Nginx écoute sur 127.0.0.1:8888 (câblé dans docker-compose.yml).
Configurer aussi TRUSTED_PROXIES si vous déployez l'application derrière un autre proxy que le Nginx Docker fourni. En stack Docker par défaut, docker-compose.yml force TRUSTED_PROXIES=* côté conteneur PHP-FPM pour faire confiance au Nginx interne.
Exemple de configuration Caddy :
https://blog.exemple.com {
header Strict-Transport-Security max-age=31536000;
reverse_proxy localhost:8888
}
Variables d'environnement
| Variable | Description | Exemple |
|---|---|---|
APP_ENV |
development ou production |
production |
APP_URL |
URL de base (liens emails, flux RSS) — inclure le port en développement | http://localhost:8080 |
APP_NAME |
Nom du blog (flux RSS, emails) | Slim Blog |
TIMEZONE |
Fuseau horaire PHP | Europe/Paris |
TRUSTED_PROXIES |
Proxies autorisés à fournir X-Forwarded-For / X-Forwarded-Proto |
127.0.0.1,::1 ou * |
ADMIN_USERNAME |
Nom d'utilisateur du compte admin | admin |
ADMIN_EMAIL |
Email du compte admin | admin@example.com |
ADMIN_PASSWORD |
Mot de passe admin (obligatoire en production) | (à changer) |
MAIL_HOST |
Serveur SMTP | smtp.example.com |
MAIL_PORT |
Port SMTP (587 TLS, 465 SSL) |
587 |
MAIL_USERNAME |
Identifiant SMTP | noreply@example.com |
MAIL_PASSWORD |
Mot de passe SMTP | (à renseigner) |
MAIL_ENCRYPTION |
tls ou ssl |
tls |
MAIL_FROM |
Adresse expéditeur | noreply@example.com |
MAIL_FROM_NAME |
Nom expéditeur | Slim Blog |
UPLOAD_MAX_SIZE |
Taille max upload en octets | 5242880 |
Documentation
- Guide technique — bases PHP, architecture en domaines, faire évoluer le projet
- Architecture — référence concise : domaines, schéma BDD, routes, arborescence
- Installation — développement — prérequis, lancement local, upload, SCSS
- Installation — production — Docker, reverse proxy, logs
- Variables d'environnement
- Contribuer — tests, conventions
Licence
Le code source est distribué sous licence MIT. Le contenu du blog (articles publiés) est soumis à CC BY-SA 4.0.
Provisioning
Le provisionnement (migrations + seed admin) s'execute explicitement via php bin/provision.php.
- Developpement local : executer
php bin/provision.phpaprescp .env.example .env - Docker / production : executer
docker compose exec app php bin/provision.phpapres le demarrage du conteneur
Le runtime HTTP ne provisionne plus automatiquement la base. Si le schema n'est pas present, l'application echoue avec un message explicite demandant d'executer la commande de provisionnement.
Pour repartir d'un schema frais en developpement apres un nettoyage de l'historique des migrations, supprimez d'abord la base SQLite locale puis relancez le provisionnement : rm -f database/app.sqlite (ou votre fichier SQLite configure), puis php bin/provision.php.