188 lines
9.5 KiB
Markdown
188 lines
9.5 KiB
Markdown
# 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](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
|
|
```
|
|
|
|
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'execute explicitement via `php bin/provision.php`.
|
|
|
|
- Developpement local : executer `php bin/provision.php` apres `cp .env.example .env`
|
|
- Docker / production : executer `docker compose exec app php bin/provision.php` apres 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`.
|