Doc
This commit is contained in:
137
README.md
137
README.md
@@ -1,18 +1,19 @@
|
||||
# F3 Simple Blog
|
||||
|
||||
Blog simple construit autour de deux objectifs :
|
||||
Blog simple construit avec Fat-Free Framework et SQLite.
|
||||
|
||||
- **simplicité conceptuelle** ;
|
||||
- **fidélité à Fat-Free Framework**.
|
||||
Le projet vise un blog léger, lisible et facile à déployer, avec un petit back-office d’administration, une médiathèque locale et un rendu Markdown sécurisé.
|
||||
|
||||
Le rendu visuel est volontairement soigné, mais le backend reste petit et lisible :
|
||||
## Fonctionnalités
|
||||
|
||||
- **3 contrôleurs** : `SiteController`, `AuthController`, `AdminController`
|
||||
- **1 contrôleur de base** : `Controller`
|
||||
- **3 modèles** : `Post`, `Media`, `User`
|
||||
- **1 service dédié** : `MarkdownService`
|
||||
- **1 bootstrap F3**
|
||||
- **2 scripts CLI** : installation et création d'admin
|
||||
- listing public des articles avec pagination ;
|
||||
- page article ;
|
||||
- authentification admin ;
|
||||
- création, modification et suppression d’articles ;
|
||||
- médiathèque locale avec upload, texte alternatif, copie de la syntaxe Markdown et suppression ;
|
||||
- vignette de carte dérivée de la première image du contenu ;
|
||||
- rendu Markdown avec images locales `media:...` ;
|
||||
- stockage des images en **JPG/PNG bruts** sans transformation.
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -39,35 +40,67 @@ project/
|
||||
└── tmp/uploads/
|
||||
```
|
||||
|
||||
## Ce qui est utilisé côté F3
|
||||
## Architecture
|
||||
|
||||
- routes dans `config.ini`
|
||||
- `DB\SQL\Mapper` pour les trois tables
|
||||
- `Auth` pour la connexion
|
||||
- `Session` pour la session et le jeton CSRF de base
|
||||
- `Template` pour le rendu et le filtre `date_fr`
|
||||
- `Web::receive()` pour l’upload
|
||||
- `Markdown` pour le rendu des articles
|
||||
- `Web::slug()` pour les slugs
|
||||
Le backend s’appuie sur un petit noyau :
|
||||
|
||||
## Choix de simplicité
|
||||
- `SiteController` : pages publiques ;
|
||||
- `AuthController` : connexion / déconnexion ;
|
||||
- `AdminController` : back-office articles et médiathèque ;
|
||||
- `Controller` : rendu, session courante, flash, CSRF ;
|
||||
- `Post`, `Media`, `User` : modèles `DB\SQL\Mapper` ;
|
||||
- `MarkdownService` : compilation et sanitation du contenu Markdown.
|
||||
|
||||
- pas de namespaces ;
|
||||
- pas de couche repository ;
|
||||
- pas de système de migrations ;
|
||||
- pas de container DI ;
|
||||
- pas de logique de proxy avancée ;
|
||||
- un seul contrôleur admin pour les articles et la médiathèque ;
|
||||
- pas d’image de couverture dédiée : la première image du corps sert de vignette de carte.
|
||||
## Intégration F3
|
||||
|
||||
## Choix éditorial simplifié
|
||||
Le projet utilise directement les briques natives du framework :
|
||||
|
||||
Les articles n'ont plus de champ “image de couverture”. Les images vivent uniquement dans le Markdown, et la première image rendue dans `body_html` est réutilisée comme vignette dans les cartes d’article. Cela réduit le nombre de champs, supprime un cas métier entier, et garde un rendu visuel cohérent.
|
||||
- routes et aliases dans `app/config.ini` ;
|
||||
- `DB\SQL\Mapper` pour les tables principales ;
|
||||
- `Auth` pour la connexion ;
|
||||
- `Session` pour la session ;
|
||||
- `Template` pour le rendu ;
|
||||
- `Web::receive()` pour la réception des uploads ;
|
||||
- `Markdown` pour le parsing Markdown ;
|
||||
- `Web::slug()` pour les slugs.
|
||||
|
||||
## Simplifications supplémentaires
|
||||
## Contenu et médiathèque
|
||||
|
||||
- **zéro HTML brut** : le HTML saisi dans le Markdown est neutralisé avant le passage dans le parseur Markdown ; seuls les éléments générés par Markdown puis validés par le sanitizer sont rendus ;
|
||||
- **images stockées brutes** : l’upload accepte uniquement JPG et PNG, vérifie que le fichier est bien une image, puis le stocke tel quel sans réencodage ni transformation ; la dépendance à GD est supprimée.
|
||||
Les articles ne possèdent pas de champ “image de couverture”. Les images vivent dans le corps Markdown, et la première image rendue dans `body_html` sert de vignette dans les cartes d’article.
|
||||
|
||||
Les images du contenu utilisent la syntaxe :
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
La médiathèque :
|
||||
|
||||
- accepte uniquement **JPG** et **PNG** ;
|
||||
- vérifie que le fichier reçu est bien une image ;
|
||||
- conserve le fichier tel quel, sans réencodage ;
|
||||
- stocke en base le nom du fichier, le texte alternatif, la largeur, la hauteur et la date de création.
|
||||
|
||||
## Contrat Markdown
|
||||
|
||||
Le rendu Markdown suit les règles suivantes :
|
||||
|
||||
- le HTML brut saisi par l’auteur n’est pas rendu ;
|
||||
- les liens sont filtrés avant rendu ;
|
||||
- seules les images locales en `media:...` sont rendues ;
|
||||
- les images externes ne sont pas affichées ;
|
||||
- avec le parseur Markdown de F3, il faut laisser une ligne vide entre deux blocs (titre, liste, citation, image, code) pour un rendu fiable.
|
||||
|
||||
## Sécurité
|
||||
|
||||
Le projet inclut :
|
||||
|
||||
- session d’administration ;
|
||||
- jeton CSRF sur les formulaires ;
|
||||
- rotation du jeton CSRF après connexion et déconnexion ;
|
||||
- sanitation du HTML produit à partir du Markdown ;
|
||||
- validation des uploads image ;
|
||||
- cookies `httponly` et `samesite=Lax`.
|
||||
|
||||
## Pré-requis
|
||||
|
||||
@@ -75,7 +108,7 @@ Les articles n'ont plus de champ “image de couverture”. Les images vivent un
|
||||
|
||||
- PHP 8.3+
|
||||
- Composer
|
||||
- extensions : `pdo_sqlite`, `mbstring`, `intl`, `dom`
|
||||
- extensions PHP : `pdo_sqlite`, `mbstring`, `intl`, `dom`
|
||||
|
||||
### Déploiement Docker
|
||||
|
||||
@@ -91,7 +124,7 @@ php scripts/install.php
|
||||
php -S 127.0.0.1:8080 -t public
|
||||
```
|
||||
|
||||
Ouvre ensuite `http://127.0.0.1:8080`.
|
||||
Puis ouvrir `http://127.0.0.1:8080`.
|
||||
|
||||
Créer un compte admin :
|
||||
|
||||
@@ -111,18 +144,16 @@ app.env=prod
|
||||
app.timezone=Europe/Paris
|
||||
```
|
||||
|
||||
### Important sur le mode prod
|
||||
|
||||
Par défaut, `app/config.ini` laisse `app.env=dev`. Pour un vrai déploiement, il faut donc fournir un `config.local.ini` avec `app.env=prod`, sinon l’application gardera un comportement de développement.
|
||||
Le paramètre `app.env` doit être forcé à `prod` sur un déploiement réel.
|
||||
|
||||
## Déploiement Docker derrière Caddy
|
||||
|
||||
Le projet est cohérent pour un déploiement simple :
|
||||
Le projet est prévu pour un déploiement simple :
|
||||
|
||||
- Apache sert `public/` dans le conteneur ;
|
||||
- `compose.yaml` expose l’application sur `127.0.0.1:8888` par défaut ;
|
||||
- Caddy termine TLS et reverse-proxy vers cette cible ;
|
||||
- SQLite, les logs et les uploads sont montés sur des volumes hôtes.
|
||||
- SQLite, les logs et les uploads sont montés sur des volumes persistants.
|
||||
|
||||
Exemple :
|
||||
|
||||
@@ -132,9 +163,9 @@ cp config.local.ini.example config.local.ini
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Le `Caddyfile.example` donne une base de reverse proxy. Le point important est de **n’exposer publiquement que Caddy**. L’application Apache/PHP ne doit pas être accessible directement depuis Internet.
|
||||
Le `Caddyfile.example` fournit une base de reverse proxy. En production, il faut exposer publiquement **Caddy uniquement**. L’application Apache/PHP ne doit pas être accessible directement depuis Internet.
|
||||
|
||||
### Volumes persistants à sauvegarder
|
||||
## Sauvegarde
|
||||
|
||||
Pour sauvegarder le blog, il faut au minimum conserver :
|
||||
|
||||
@@ -143,23 +174,9 @@ Pour sauvegarder le blog, il faut au minimum conserver :
|
||||
|
||||
Les logs peuvent aussi être conservés si tu veux garder l’historique d’erreurs.
|
||||
|
||||
## Contrat Markdown
|
||||
## Limites assumées
|
||||
|
||||
Le projet assume un Markdown simple :
|
||||
|
||||
- titres, listes, citations, code, liens et images Markdown ;
|
||||
- pas de HTML brut auteur ;
|
||||
- les images du contenu utilisent la syntaxe `media:nom-de-fichier.ext` ;
|
||||
- les images externes ne sont pas rendues ;
|
||||
- avec le parseur Markdown de F3, laisse une ligne vide entre deux blocs pour un rendu fiable.
|
||||
|
||||
## Limite volontaire : pas de migrations
|
||||
|
||||
`scripts/install.php` crée les tables si elles n’existent pas. En revanche, il ne gère pas les évolutions de schéma. Si le modèle de données change dans le futur, il faudra soit repartir d’une base propre, soit appliquer une migration manuelle.
|
||||
|
||||
## Nettoyage de confort appliqué
|
||||
|
||||
- suppression d'une méthode utilitaire inutilisée dans `Media` ;
|
||||
- suppression d'un paramètre mort dans `Post::usesMedia()` ;
|
||||
- simplification de l'upload temporaire côté admin ;
|
||||
- détection `secure` des cookies un peu plus tolérante (`HTTPS`, `REQUEST_SCHEME`, `X-Forwarded-Proto`) sans réintroduire une grosse logique proxy.
|
||||
- base SQLite ;
|
||||
- instance applicative unique ;
|
||||
- pas de système de migrations automatique ;
|
||||
- en cas de changement de schéma, une migration manuelle ou une base recréée sera nécessaire.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
[globals]
|
||||
; Exemple local minimal. En production, préfère app.env=prod.
|
||||
app.env=dev
|
||||
app.timezone=Europe/Paris
|
||||
|
||||
Reference in New Issue
Block a user