Files
core/docs/DEV.md
T
hykocx 2e348a1608 feat(storage): add configurable storage access policies
Replace hardcoded `users/` path-based access control with a
declarative `storageAccessPolicies` system defined per module via
`defineModule()`.

- Add `storageAccessPolicies` field to `defineModule()` defaults with
  support for `owner` and `admin` policy types
- Expose `getAllStorageAccessPolicies()` from the modules/storage layer
- Refactor `handleGetFile` in `storage/api.js` to resolve access
  control dynamically from registered policies instead of hardcoded
  path checks
- Add `ZEN_STORAGE_ENDPOINT` env var and update `.env.example` to
  support S3-compatible backends (Cloudflare R2, Backblaze B2)
- Document the env/doc sync convention in `DEV.md`
2026-04-14 17:09:27 -04:00

4.0 KiB

Guide de développement

Ce document couvre les conventions de code, les règles de sécurité et la procédure de publication du package @zen/core.

Pour les conventions de rédaction : LANGUE.md et REDACTION.md.

Pour l'architecture partagée (modules, composants, icônes) : ARCHITECTURE.md.

Pour la procédure de publication du package : PUBLICATION.md.

Pour les conventions de commit : COMMITS.md.

Contexte projet : les utilisateurs finaux créent leur projet via npx @zen/start, qui génère automatiquement une structure Next.js avec le CMS déjà intégré. Lors d'une assistance ou d'une revue, partez du principe que le projet cible est déjà structuré de cette façon.


Standards de code

Principes généraux

Une fonction, une responsabilité. Si elle fait deux choses, c'est deux fonctions. Si elle ne tient pas sur un écran, la découper.

Le flux de contrôle se lit de haut en bas. Pas de récursion non bornée, pas de callbacks imbriqués à plus de deux niveaux. Quelqu'un qui lit le code pour la première fois doit pouvoir suivre l'exécution sans se perdre.

Les données entrantes sont suspectes. On valide en entrée de fonction. On ne suppose pas que l'appelant a fait le travail.

Les promesses ne s'ignorent pas. Chaque Promise est awaitée ou .catch()ée. Une promesse silencieuse qui échoue est un bug invisible.

La portée des variables est minimale. On déclare au plus près de l'usage. Pas de variables réutilisées pour deux rôles différents.

ESLint passe sans avertissement. Un warning ignoré aujourd'hui est un bug non détecté demain.

Les variables d'environnement et la documentation se mettent à jour avec le code. Toute variable ajoutée, renommée ou supprimée doit être reflétée dans .env.example. Toute décision architecturale ou convention nouvelle doit être documentée dans le fichier docs/ concerné. Le code et sa documentation vieillissent ensemble.


Build et configuration tsup

Règle des externals

Tout import de la forme @zen/core/* dans un fichier bundlé par tsup (typiquement src/modules/*/api.js, src/modules/*/actions.js, src/modules/*/crud.js) doit figurer dans la liste external du premier bloc de config dans tsup.config.js.

Pourquoi : tsup tente de résoudre ces imports au moment du build. Or les fichiers dist/ n'existent pas encore — le build échoue avec Could not resolve "@zen/core/...".

Règle : quand on crée ou refactorise un module src/core/*/index.js exposé via package.json exports, on ajoute immédiatement l'entrée correspondante dans external de tsup.config.js.

// tsup.config.js — external (premier bloc)
'@zen/core/api',       // ← à ajouter si src/core/api/index.js est un entry tsup
'@zen/core/database',
'@zen/core/storage',
// etc.

Sécurité

Données entrantes

Toute donnée externe est considérée malveillante par défaut : requêtes HTTP, données formulaire, réponses d'API tierce, contenu de fichier. On valide côté serveur. Le client ne contrôle rien de critique.

Base de données

On n'écrit jamais de SQL par concaténation de chaînes. Uniquement des requêtes paramétrées :

// ✓
await pool.query('SELECT * FROM users WHERE id = $1', [userId])

// ✗
await pool.query(`SELECT * FROM users WHERE id = '${userId}'`)

Secrets

Aucun token, clé API ou mot de passe dans le code. Tout passe par des variables d'environnement. Les clés Stripe, les tokens Resend et les credentials PostgreSQL ne sont jamais commités.

# .env.local — jamais dans git
DATABASE_URL=...
STRIPE_SECRET_KEY=...
RESEND_API_KEY=...

Erreurs exposées

Les messages d'erreur retournés à l'utilisateur ne contiennent pas de détails internes : pas de stack trace, pas de nom de table, pas de requête SQL. On log côté serveur, on renvoie un message générique côté client.