first commit

This commit is contained in:
julien
2026-03-16 01:47:07 +01:00
commit 8f7e61bda0
185 changed files with 27731 additions and 0 deletions

177
README.md Normal file
View File

@@ -0,0 +1,177 @@
# Slim Blog
![PHP](https://img.shields.io/badge/PHP-8.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-355%20passing-brightgreen)
![Licence](https://img.shields.io/badge/licence-MIT-green)
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.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 -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
```
> 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`).
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` |
| `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) peut etre execute explicitement via `php bin/provision.php`.
En developpement, il reste activable automatiquement via `APP_AUTO_PROVISION=true`.
En production, il est recommande de le lancer separement du runtime HTTP.