Files
slim-blog/README.md
2026-03-16 16:02:01 +01:00

190 lines
9.7 KiB
Markdown

# Slim Blog
![PHP](https://img.shields.io/badge/PHP-8.4.1%2B-777BB4?logo=php&logoColor=white)
![Slim](https://img.shields.io/badge/Slim-4-74a045)
![PHPStan](https://img.shields.io/badge/PHPStan-niveau%208-blue)
![Tests](https://img.shields.io/badge/tests-442%20passing-brightgreen)
![Licence](https://img.shields.io/badge/licence-MIT-green)
Blog multi-utilisateurs modulaire développé avec Slim 4. Les domaines `Auth`, `Post`, `Category`, `Media`, `User`
et `Shared` portent une architecture DDD légère, lisible et réutilisable 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 par utilisateur
- **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](https://www.slimframework.com) |
| Base de données | [SQLite](https://sqlite.org) via PDO natif |
| Templates | [Twig](https://twig.symfony.com) |
| CSS | [Sass](https://sass-lang.com) (7-1, BEM) |
| Éditeur WYSIWYG | [Trumbowyg](https://alex-d.github.io/Trumbowyg/) |
| Emails | [PHPMailer](https://github.com/PHPMailer/PHPMailer) |
| Logging | [Monolog](https://github.com/Seldaek/monolog) |
| Sanitisation HTML | [HTMLPurifier](http://htmlpurifier.org) |
| Protection CSRF | [Slim CSRF](https://github.com/slimphp/Slim-Csrf) |
| Injection de dépendances | [PHP-DI](https://php-di.org) (autowiring) |
| Analyse statique | [PHPStan](https://phpstan.org) (niveau 8) |
## Développement
**Prérequis :** PHP 8.4.1+ avec `pdo_sqlite`, `intl`, `fileinfo`, `gd` (WebP), `xml` (dom), Composer, Node.js 18+
```bash
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
```
Le projet est encore en développement : l'historique des migrations a été simplifié en une baseline courte. Si vous aviez une ancienne base locale, supprimez `database/app.sqlite` puis reprovisionnez.
Pour surveiller les modifications SCSS et recompiler automatiquement en développement :
```bash
npm run watch
```
> `npm run watch` est 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 :
```bash
php -S localhost:8080 -t public -d upload_max_filesize=6M -d post_max_size=8M
```
> Si `upload_max_filesize` est 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
```bash
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=changeme123` est bloqué intentionnellement.
> `--build` est 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 :
```bash
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 :
```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](docs/GUIDE.md) — bases PHP, architecture en domaines, faire évoluer le projet
- [Architecture](docs/ARCHITECTURE.md) — référence concise : domaines, schéma BDD, routes, arborescence
- [Installation — développement](#développement) — prérequis, lancement local, upload, SCSS
- [Installation — production](#production) — Docker, reverse proxy, logs
- [Variables d'environnement](#variables-denvironnement)
- [Contribuer](CONTRIBUTING.md) — tests, conventions
## Licence
Le code source est distribué sous licence [MIT](LICENSE).
Le contenu du blog (articles publiés) est soumis à [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.fr).
## Provisioning
Le provisionnement (migrations + seed admin) s'exécute explicitement via `php bin/provision.php`.
- Développement local : exécuter `php bin/provision.php` après `cp .env.example .env`
- Docker / production : exécuter `docker compose exec app php bin/provision.php` après le demarrage du conteneur
Le runtime HTTP ne provisionne plus automatiquement la base. Si le schéma n'est pas présent, l'application echoue avec un message explicite demandant d'exécuter la commande de provisionnement.
Pour repartir d'un schéma frais en développement après 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 configuré), puis `php bin/provision.php`.