first commit
This commit is contained in:
177
README.md
Normal file
177
README.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# 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.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.
|
||||
Reference in New Issue
Block a user