# 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.