Files
harmony-back/CLAUDE.md
T
admin 8e672633dc
Build release Docker image / Build Docker Images (push) Successful in 7m54s
0.13.14 : fix media uploads, move plugin secrets to env vars
Tous les uploads échouaient depuis mai 2026 sur « Credential access key
has length 5, should be 32 ».

config/env/production/plugins.ts surchargeait l'upload avec l'ancien
MinIO auto-hébergé (accessKeyId "admin", 5 caractères, endpoint
container.harmonylab.ovh aujourd'hui injoignable). Strapi fusionnant les
surcharges d'environnement en profondeur, la config effective en prod
n'était ni l'une ni l'autre : endpoint R2 hérité de config/plugins.ts,
mais identifiants MinIO, le provider lisant s3Options.credentials en
priorité sur les options à plat.

- surcharge production supprimée : une seule config plugins, les
  différences d'environnement passent par les variables d'environnement
- upload en forme s3Options attendue par @strapi/provider-upload-aws-s3 5.x
  (à plat, le provider n'accepte plus qu'au prix d'une dépréciation)
- secrets R2 et SMTP sortis du fichier vers env(), .env.example à jour
- CSP de production : les médias étaient autorisés depuis
  container.harmonylab.ovh et 192.168.0.211:9000, jamais depuis le domaine
  R2 — l'hôte est désormais dérivé de R2_PUBLIC_URL
- la surcharge production des middlewares réduisait strapi::body à sa forme
  par défaut, perdant includeUnparsed nécessaire au webhook Stripe

⚠️ Déploiement : poser R2_* et SMTP_PASSWORD dans Dokploy AVANT de
déployer. Les clés historiques restent dans l'historique git : rotation
R2 et ZeptoMail à faire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 18:44:58 +02:00

126 lines
6.6 KiB
Markdown

# Backend ChoralSync — Strapi
> Racine du repo backend. Vue d'ensemble front + back : `../CLAUDE.md`.
## Stack
- Strapi **5.8.1** (⚠️ API v5 : `documentId`, réponses aplaties — pas de `attributes` imbriqués comme en v4)
- Node **18 à 22** (`engines` du package.json ; v22 utilisée en dev) — gestionnaire : **yarn**
- TypeScript
- Base de données : sélectionnée par `DATABASE_CLIENT`**SQLite** par défaut en dev (better-sqlite3), **PostgreSQL** ou MySQL supportés (voir `config/database.ts`)
- Plugins actifs :
- Users & Permissions (rôles `Public`, `Authenticated`)
- Documentation (`@strapi/plugin-documentation`)
- Email : nodemailer via **ZeptoMail** (SMTP)
- Upload : **aws-s3** vers **Cloudflare R2** (bucket `choralsync`, servi par `container.choralsync.com`)
- `strapi-v5-plugin-populate-deep` (defaultDepth 3)
- Autres : Stripe (paiements, content-type `order`), Puppeteer (génération), cron jobs dans `config/cron-tasks.ts` (ex. récupération d'actualités GNews), templates d'emails dans `src/email-templates/`
## Structure
```
src/
├── api/ # 34 content-types (un dossier chacun)
│ └── <name>/
│ ├── content-types/<name>/schema.json
│ ├── controllers/
│ ├── routes/
│ └── services/
├── components/ # composants Strapi partagés
├── email-templates/ # templates des emails transactionnels
├── extensions/ # overrides : documentation, upload, users-permissions
└── admin/ # customisation admin
config/ # server, database, plugins, middlewares, cron-tasks, api
```
## Content-types
Vue par domaine (34 au total, liste exhaustive dans `src/api/`) :
| Domaine | Content-types |
|---|---|
| Chorale | `choral`, `choral-membership`, `choral-permission`, `permissions-template` |
| Social (offre gratuite) | `post`, `post-ownership`, `comment`, `activity`, `report` |
| Groupes | `group`, `group-membership` |
| Événements | `event`, `event-relationship` |
| Boards (kanban) | `board`, `board-list`, `board-card` |
| Messagerie | `channel`, `chat-conversation`, `chat-conversation-member`, `chat-message`, `conversation`, `direct-message`, `message` |
| Notifications | `notification`, `announcement`, `invite` |
| Paiement | `order` (Stripe — ⚠️ route custom `auth: false` dans `src/api/order/routes/order.ts`, webhook) |
| Contenu / divers | `page`, `legal-page`, `ad`, `contact`, `contact-meta`, `form-template`, `mails` |
<!-- Tableau détaillé (champs sensibles, accès par rôle) à compléter après export des permissions. -->
## Sécurité — règles NON NÉGOCIABLES
- Aucune route custom en `auth: false` sans justification écrite en commentaire
ET mention dans le CLAUDE.md parent. État actuel : 1 occurrence
(`src/api/order/routes/order.ts` — webhook Stripe).
- Tout controller custom qui lit/écrit des données utilisateur DOIT filtrer
par `ctx.state.user.id`. Jamais de `entityService.findMany` sans filtre
dans un contexte authentifié multi-utilisateurs.
- Toujours utiliser `sanitizeOutput` / les sanitizers Strapi avant de retourner
une entité depuis un controller custom (ne jamais renvoyer l'objet brut).
- `populate=*` interdit côté serveur comme côté client : populate explicite.
⚠️ Le plugin populate-deep (defaultDepth 3) est installé — ne pas en abuser,
il peut exposer des relations non prévues.
- Les permissions du plugin Users & Permissions sont versionnées dans
**`src/permissions-sync.ts`** (source de vérité) et synchronisées au
démarrage par le bootstrap (`src/index.ts`) — synchro ADDITIVE et
idempotente : les manquantes sont créées, les permissions en trop sont
seulement loguées (`[permissions-sync]`), jamais supprimées automatiquement.
Toute nouvelle route/action consommée par le front DOIT être ajoutée à ce
fichier (et jamais cochée uniquement dans l'admin, sinon elle sera signalée
comme non déclarée).
- Secrets uniquement en variables d'environnement. `.env` jamais commité ;
`.env.example` maintenu à jour. Depuis le 26/07/2026, `config/plugins.ts`
ne contient plus aucun secret (R2 et SMTP passent par `env()`).
⚠️ **Les clés historiques ont vécu en clair dans l'historique git** :
rotation R2 + ZeptoMail à faire, l'historique n'étant pas réécrit.
- ⚠️ **Pas de `config/env/<env>/plugins.ts`.** Strapi fusionne les surcharges
d'environnement en profondeur : une surcharge partielle de `providerOptions`
avait produit une config d'upload hybride (endpoint R2 de la base +
identifiants MinIO de la surcharge, prioritaires via `s3Options.credentials`)
et cassé tous les uploads de mai à juillet 2026. Les différences entre
environnements passent par les variables d'environnement, pas par un
second fichier. Même vigilance pour `config/env/production/middlewares.ts`,
qui doit répéter la config `strapi::body` (`includeUnparsed`) sous peine de
casser le webhook Stripe.
## Points d'audit récurrents
Checklist à dérouler lors d'une revue sécurité :
1. `grep -rn "auth: false" src/` — chaque occurrence doit être justifiée.
2. Rôle Public dans l'export des permissions : uniquement les find/findOne
réellement nécessaires au site public.
3. Controllers custom : filtrage par user, sanitization, gestion d'erreurs
(pas de stack trace renvoyée au client).
4. Lifecycles (`lifecycles.ts`) : pas de logique qui bypasse les permissions.
5. Champs privés : vérifier `"private": true` dans les schema.json pour les
champs sensibles (ils sont sinon exposés dans les réponses API).
6. Rate limiting / CORS dans `config/middlewares.ts` : origines explicites en prod.
7. API tokens : scope minimal, pas de token full-access utilisé par le front.
8. Webhook Stripe (`order`) : vérification de la signature
(`stripe.webhooks.constructEvent`).
## Commandes
```bash
yarn develop # dev + admin (strapi develop --debug)
yarn build
yarn strapi config:dump -f config/sync/permissions.json # export permissions
yarn strapi config:restore -f config/sync/permissions.json # import permissions
```
## Règles pour Claude
- Toute modification de schema.json = migration implicite : signaler l'impact
(données existantes, types front à mettre à jour dans `harmony-web/types/` et
`harmony-web/interfaces/`).
- Après modification d'une route/permission : mettre à jour le tableau des
content-types ici ET dans le CLAUDE.md parent.
- Ne jamais modifier directement la base de données.
- Strapi **5** : utiliser les `documentId` et le format de réponse v5
(pas de `attributes` imbriqués). Toujours vérifier package.json en cas de doute.