@nodefony/devkit 10.0.0-beta.1 → 10.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -12
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +2 -2
- package/dist/nodefony/controllers/DevkitController.js +2 -2
- package/dist/nodefony/controllers/McpController.js +71 -6
- package/dist/nodefony/service/DevkitService.js +2 -2
- package/dist/nodefony/src/card.js +1 -2
- package/dist/types/nodefony/controllers/McpController.d.ts +12 -1
- package/docs/index.md +63 -15
- package/package.json +9 -9
- package/skills/nodefony-add-realtime-channel/SKILL.md +66 -9
- package/skills/nodefony-add-service/SKILL.md +11 -0
- package/skills/nodefony-authentication/SKILL.md +234 -0
- package/skills/nodefony-browser/SKILL.md +45 -21
- package/skills/nodefony-browser/references/sondes.md +13 -0
- package/skills/nodefony-browser/scripts/audit.mjs +59 -10
- package/skills/nodefony-browser/scripts/inspect.mjs +122 -4
- package/skills/nodefony-browser/scripts/lib/browser.mjs +112 -6
- package/skills/nodefony-browser/scripts/lib/probes.mjs +166 -7
- package/skills/nodefony-dev/SKILL.md +56 -81
- package/skills/nodefony-dev/scripts/docs.mjs +21 -5
- package/skills/nodefony-devops/SKILL.md +16 -13
- package/skills/nodefony-devops/references/compose.md +8 -0
- package/skills/nodefony-devops/references/frontal.md +5 -1
- package/skills/nodefony-devops/references/image.md +1 -1
- package/skills/nodefony-devops/references/secrets.md +18 -4
- package/skills/nodefony-devops/references/variables.md +52 -19
- package/skills/nodefony-migrate-schema/SKILL.md +5 -2
- package/skills/nodefony-migrate-schema/references/verdicts.md +20 -19
- package/skills/nodefony-protect-route/SKILL.md +118 -17
- package/skills/nodefony-security-review/SKILL.md +127 -0
- package/skills/nodefony-security-review/scripts/review-diff.mjs +1090 -0
- package/skills/nodefony-troubleshoot/SKILL.md +151 -0
- package/skills/nodefony-troubleshoot/scripts/log-errors.mjs +465 -0
- package/skills/nodefony-upgrade/SKILL.md +130 -0
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
est-il fini", "je ne trouve rien sur ce sujet", "ce n'est pas documenté", "je touche au
|
|
15
15
|
frontend", "mon composant charge des données", "mon écran n'affiche rien".
|
|
16
16
|
metadata:
|
|
17
|
-
version: 1.
|
|
17
|
+
version: 1.3.0
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# nodefony-dev — développer dans cette application sans rien inventer
|
|
@@ -45,15 +45,19 @@ de son geste, et ceux-là ne se devinent pas — ils se paient.
|
|
|
45
45
|
| -------------------------------------------------------------------- | ------------------------------- |
|
|
46
46
|
| Une ressource : table, validation, service, REST et WebSocket | `nodefony-add-crud` |
|
|
47
47
|
| Une logique métier réutilisable, hors de tout controller | `nodefony-add-service` |
|
|
48
|
-
| Un flux temps réel, un canal, un abonnement
|
|
49
|
-
|
|
|
48
|
+
| Un flux temps réel (WebSocket ou SSE), un canal, un abonnement | `nodefony-add-realtime-channel` |
|
|
49
|
+
| Se connecter : /login, second facteur, retour après connexion, jeton | `nodefony-authentication` |
|
|
50
|
+
| Réserver une route ou une page à des rôles, ouvrir à un partenaire | `nodefony-protect-route` |
|
|
50
51
|
| Changer une entité déjà en base, ou déployer un schéma changé | `nodefony-migrate-schema` |
|
|
51
52
|
| Conclure quoi que ce soit d'un ÉCRAN — affichage, contraste, console | `nodefony-browser` |
|
|
53
|
+
| Quelque chose qui marchait ne marche plus : démarrage, 404, journal | `nodefony-troubleshoot` |
|
|
54
|
+
| Relire la sécurité de ton diff AVANT de commiter | `nodefony-security-review` |
|
|
55
|
+
| Monter Nodefony de version, ou une montée qui a laissé des rouges | `nodefony-upgrade` |
|
|
52
56
|
|
|
53
57
|
Aucune ligne ne correspond ? Alors seulement, continue ici. Et si ta tâche est EXACTEMENT celle
|
|
54
58
|
d'un spécialiste, va droit à lui : il n'y a pas à passer par cette page d'abord.
|
|
55
59
|
|
|
56
|
-
`ls .agents/skills/` liste ceux que TON projet a reçus ; `npx nodefony
|
|
60
|
+
`ls .agents/skills/` liste ceux que TON projet a reçus ; `npx nodefony upgrade` les remet à jour
|
|
57
61
|
après une montée de version. Ce sont des **pointeurs** : le contenu vit dans `node_modules` et
|
|
58
62
|
suit la version installée — les éditer ne servirait à rien.
|
|
59
63
|
|
|
@@ -152,7 +156,8 @@ npx nodefony symbols --module @nodefony/http # toute la surface exportée d'un
|
|
|
152
156
|
```
|
|
153
157
|
|
|
154
158
|
Le graphe symbolique de tout le framework est livré avec le paquet `nodefony` : la réponse ne
|
|
155
|
-
dépend ni d'un serveur, ni d'un build, ni de ta connexion.
|
|
159
|
+
dépend ni d'un serveur, ni d'un build, ni de ta connexion. Celui de TON code (`.ai/symbols.json`),
|
|
160
|
+
`npm run dev` le tient à jour — ou `npx nodefony symbols --generate` ; les deux sont lus ensemble. Va y chercher un symbole AVANT d'ouvrir
|
|
156
161
|
un `.d.ts` — et avant, surtout, d'inventer une signature.
|
|
157
162
|
|
|
158
163
|
**Si la commande te résiste, répare l'APPEL — ne te rabats pas sur les sources.** C'est le réflexe
|
|
@@ -171,25 +176,19 @@ ENSUITE. Tout le reste (déclarer un outil métier, le réserver à des scopes,
|
|
|
171
176
|
### L'environnement : ne devine JAMAIS, demande
|
|
172
177
|
|
|
173
178
|
```bash
|
|
174
|
-
npx nodefony env #
|
|
179
|
+
npx nodefony env # valeur EFFECTIVE de chaque variable, sa PROVENANCE
|
|
175
180
|
```
|
|
176
181
|
|
|
177
182
|
**Encadre toute modification de configuration par cette commande** : une fois AVANT, pour savoir ce
|
|
178
183
|
qui s'applique et d'où ça vient ; une fois APRÈS, pour prouver que ta valeur est celle qui gagne.
|
|
179
|
-
Lire
|
|
180
|
-
supposer, et une supposition fausse ne se voit qu'en production. La commande ne boote rien, donc
|
|
184
|
+
Lire `.env` toi-même donne un contenu ; ce que l'application voit dépend aussi du shell et de
|
|
185
|
+
`env.ts` — tu ne peux que le supposer, et une supposition fausse ne se voit qu'en production. La commande ne boote rien, donc
|
|
181
186
|
elle répond aussi quand l'application ne démarre plus.
|
|
182
187
|
|
|
183
|
-
**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
process.env > .env.<déploiement>.local > .env.<mode>.local > .env.local
|
|
188
|
-
> .env.<déploiement> > .env.<mode> > .env
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
`<mode>` = `NODE_ENV` · `<déploiement>` = `APP_ENV` (plus spécifique, donc plus fort). Les `*.local`
|
|
192
|
-
ne sont jamais committés : les secrets y vont, et nulle part ailleurs.
|
|
188
|
+
**Deux fichiers** : `.env` = valeurs du POSTE (secrets de dev compris), **jamais commité** ;
|
|
189
|
+
`.env.example` = notice commitée, **jamais chargée**. `process.env` > `.env`, rien d'autre. La
|
|
190
|
+
production n'a **aucun** fichier (orchestrateur ; réglages non secrets dans `nodefony.config.ts`).
|
|
191
|
+
Un module n'a pas de `.env`. N'écris JAMAIS `.env.local` ni `.env.<mode>` : refusés au démarrage (78).
|
|
193
192
|
|
|
194
193
|
| Forme | Ce que c'est | Où c'est déclaré |
|
|
195
194
|
| ------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------ |
|
|
@@ -240,6 +239,15 @@ npx nodefony stop <nom|chemin> # arrêter un AUTRE projet, sans change
|
|
|
240
239
|
npx nodefony production --detach --wait # boot réel en arrière-plan ; rend la main ports OUVERTS
|
|
241
240
|
```
|
|
242
241
|
|
|
242
|
+
**Déboguer** : en développement, les piles d'appels pointent vers les `.ts` de l'application (le
|
|
243
|
+
build de dev émet ses sourcemaps et le serveur les lit ; `npm run build`, lui, n'en émet pas — une
|
|
244
|
+
pile de production pointe vers `dist/`). Pour un point d'arrêt : `npm run debug` (= `nodefony debug` :
|
|
245
|
+
le mode développement, débogueur ouvert sur `127.0.0.1:9229`), puis `chrome://inspect` ou « Attach »
|
|
246
|
+
dans l'éditeur. `--inspect=<port>` change le port, `--inspect-brk` arrête avant le chargement de
|
|
247
|
+
l'application ; `nodefony development --inspect` fait la même chose. C'est le SERVEUR qui ouvre
|
|
248
|
+
l'inspecteur, et il le rouvre à chaque rechargement ; marche aussi avec `--no-watch`. ⚠️ Jamais `NODE_OPTIONS=--inspect` :
|
|
249
|
+
le premier process node de la chaîne (`npx`, `npm`) prend le port, et le serveur ne s'arrête jamais.
|
|
250
|
+
|
|
243
251
|
**Arrête ce que tu démarres.** Un serveur laissé derrière garde les ports : le run suivant échoue
|
|
244
252
|
sur une erreur qui ne parle jamais de lui (`EADDRINUSE`) — ou pire, un test interroge l'ANCIENNE
|
|
245
253
|
version du code. Et **jamais `… &`** : le processus reçoit SIGHUP et meurt ; tuer le PID du port ne
|
|
@@ -252,6 +260,13 @@ une table à part — et ce nom est ce que `stop` accepte. Donc « aucune instan
|
|
|
252
260
|
REFUSÉE, avec un code de sortie non nul et rien d'arrêté — **lis ce code**, un refus ressemble
|
|
253
261
|
sinon à un succès.
|
|
254
262
|
|
|
263
|
+
**Le plein écran n'est pas pour toi — et il est le DÉFAUT sous macOS et Linux.** Il fige la barre
|
|
264
|
+
d'état en bas, capte le clavier et la souris (molette, PgUp/PgDn, Ctrl+C devient une touche) :
|
|
265
|
+
c'est un écran pour un humain. Il s'active dès que le terminal répond à une sonde, et un
|
|
266
|
+
pseudo-terminal (tmux, un outil qui t'en fournit un) y répond. Nodefony ne devine jamais qu'un
|
|
267
|
+
agent est aux commandes : si tu lances `development` toi-même, ajoute **toujours** `--no-ui`
|
|
268
|
+
(ou pose `NF_DEV_UI=0`), ou `--output plain` pour une sortie sans séquences d'écran.
|
|
269
|
+
|
|
255
270
|
**Pour faire tourner une suite contre un serveur, prends `--no-watch`.** Le mode développement
|
|
256
271
|
relance le serveur dès qu'un fichier bouge : pendant un run, le redémarrage coupe les connexions
|
|
257
272
|
sous les tests, et le rouge qui en sort accuse le code alors que le fautif est le décor.
|
|
@@ -274,8 +289,9 @@ routes auth` ne rend que les routes dont le chemin, le nom, le contrôleur, l'ac
|
|
|
274
289
|
4. **Édite le moins possible**, et regroupe : toutes les modifications serveur d'une même
|
|
275
290
|
fonctionnalité, PUIS un seul cycle de reconstruction. Le frontend passe en HMR, zéro
|
|
276
291
|
redémarrage.
|
|
277
|
-
5. **Prouve.** `npm run verify` — une seule commande : types, style,
|
|
278
|
-
ordre. Elle s'arrête au premier rouge, et
|
|
292
|
+
5. **Prouve.** `npm run verify` — une seule commande : types, style, mise en forme,
|
|
293
|
+
`.env.example`, tests, build, câblage, dans cet ordre. Elle s'arrête au premier rouge, et
|
|
294
|
+
**ce rouge est ta tâche suivante**.
|
|
279
295
|
|
|
280
296
|
**Le point d'arrêt qu'on rate** : `npm test` seul ne prouve pas que ça compile — vitest
|
|
281
297
|
n'inspecte aucun type. Une application peut être verte et ne pas compiler.
|
|
@@ -295,50 +311,19 @@ ici — ils changent tous les trimestres ; raisonne en poids.
|
|
|
295
311
|
|
|
296
312
|
## 7. Les pièges du serveur
|
|
297
313
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
- **Une
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
générateur, qui, lui, déclare. `npx nodefony doctor` la NOMME.
|
|
306
|
-
- **Une clé de configuration mal orthographiée est retirée en silence.** `satisfies` sur le
|
|
307
|
-
fragment n'est pas décoratif : sans lui, la faute compile, puis la validation écarte la clé et
|
|
308
|
-
le module démarre sur son défaut. Personne ne le voit.
|
|
314
|
+
Ceux qu'on pose en ÉCRIVANT. Ceux qu'on rencontre quand quelque chose qui marchait ne marche plus
|
|
315
|
+
— démarrage qui échoue, route en 404, refus inattendu, journal en erreur, tests rouges d'un coup —
|
|
316
|
+
ont leur skill, qui part du symptôme : **`nodefony-troubleshoot`**.
|
|
317
|
+
|
|
318
|
+
- **Une clé de configuration mal orthographiée ARRÊTE le démarrage** (code 78), en la nommant.
|
|
319
|
+
`satisfies` sur un fragment extrait n'est pas décoratif pour autant : il te la signale à la
|
|
320
|
+
compilation, au lieu du prochain démarrage.
|
|
309
321
|
- **La config d'un module est FIGÉE après le démarrage** : l'écrire lève `TypeError`. Une variante
|
|
310
322
|
par requête = `overlayConfig()` (clés de la liste blanche du module), lue par `useConfig()`.
|
|
311
323
|
- **Tu lis une liste sans la BORNER.** Un `find` sans limite matérialise la table entière —
|
|
312
324
|
indolore sur les quelques lignes du poste de développement, fatal sur les dizaines de milliers
|
|
313
325
|
de la production. Le service d'une entité hérite `findPage({ limit: 25 })`.
|
|
314
326
|
|
|
315
|
-
- **Des dizaines de tests d'intégration rouges d'un coup** (`ECONNREFUSED`) : ils FRAPPENT un
|
|
316
|
-
serveur, ils ne le lancent pas — il est éteint. `npx nodefony status` d'abord ; en e2e, laisse
|
|
317
|
-
la commande gérer le cycle.
|
|
318
|
-
- **Ta route NEUVE répond 404 et le `dist/` est à jour** : elle n'est pas montée où tu crois. Le
|
|
319
|
-
chemin réel est le PRÉFIXE de son controller suivi du `path` de la route — une action
|
|
320
|
-
`path: "/widget"` dans un controller `@controller("/api")` répond sur `/api/widget`.
|
|
321
|
-
`npx nodefony inspect routes --json` donne le chemin MONTÉ.
|
|
322
|
-
- **TOUT répond 404, même les routes du gabarit** : un AUTRE serveur tient les ports — ou LE TIEN
|
|
323
|
-
a glissé, le port voulu étant pris. `npx nodefony status` montre les ports RÉELS, pas ceux que
|
|
324
|
-
tu as configurés, et NOMME le projet voisin.
|
|
325
|
-
- 🔴 **`nodefony <commande>` échoue là où `npm run dev` réussit, sur la MÊME application** : DEUX
|
|
326
|
-
paquets `nodefony` tournent dans le processus — un binaire lié ailleurs (installation globale,
|
|
327
|
-
`npm link`) exécute un noyau pendant que l'application importe le sien. Chaque copie a ses
|
|
328
|
-
propres classes, son contexte de requête et ses registres d'injection : un module construit à la
|
|
329
|
-
frontière perd son container SANS la moindre erreur — il figure dans la liste des modules et ne
|
|
330
|
-
fait rien. ⚠️ Les deux copies peuvent être la MÊME version ; deux fichiers distincts suffisent,
|
|
331
|
-
il n'y a aucune incohérence à repérer. `npm ls nodefony` liste les copies (« deduped » partout =
|
|
332
|
-
une seule) ; `NF_CLI_DEBUG=1 nodefony --version` dit quel CLI s'exécute.
|
|
333
|
-
- **`localhost` et `127.0.0.1` te jouent des tours** : ce sont deux ORIGINES distinctes — cookies,
|
|
334
|
-
cache et passkeys ne les partagent pas. Une seule origine en développement, partout, URL ouverte
|
|
335
|
-
comme callbacks.
|
|
336
|
-
|
|
337
|
-
- **Les routes authentifiées plafonnent quand le reste tient la charge** : le stockage de session
|
|
338
|
-
par défaut est SYNCHRONE — chaque reprise bloque la boucle d'événements. Compare une route
|
|
339
|
-
anonyme et une route authentifiée AVANT d'accuser TLS ou le pare-feu ; passe le stockage sur
|
|
340
|
-
redis pour la charge.
|
|
341
|
-
|
|
342
327
|
### Ce qui tue le processus, pas la requête
|
|
343
328
|
|
|
344
329
|
- 🔴 **Une promesse lancée sans `await` porte TOUJOURS un `.catch()`.** Audit, courriel,
|
|
@@ -352,24 +337,6 @@ Chacun a déjà coûté au moins une heure à quelqu'un. Les quatre premiers son
|
|
|
352
337
|
- **Un `PATCH` au corps vide se refuse en `400`.** Le laisser passer produit un `updateOne({})`,
|
|
353
338
|
qui finit en `500 « No values to set »` — une erreur serveur pour une faute de client.
|
|
354
339
|
|
|
355
|
-
### Quand ça ne va pas, et que le message ne suffit pas
|
|
356
|
-
|
|
357
|
-
- **Un test vert seul et rouge en suite accuse une RESSOURCE PARTAGÉE**, pas ton code : base ou
|
|
358
|
-
index réutilisé, port, store jamais purgé, assertion `count === N` qui suppose une table
|
|
359
|
-
vierge. Lance deux fichiers ENSEMBLE pour isoler la paire, puis cloisonne. **Ne sérialise
|
|
360
|
-
jamais la suite** pour faire passer le rouge : tu masques la cause et tu paies la lenteur à
|
|
361
|
-
chaque exécution.
|
|
362
|
-
- **Une suite lancée contre un serveur en `production` reçoit `404` partout** : les modules
|
|
363
|
-
`policy:"dev"` n'y sont pas. `NF_WITH_DEV_MODULES=1` déroge pour 30 minutes
|
|
364
|
-
(`NF_WITH_DEV_MODULES_TTL_MIN`, 4 h au plus), et le `CRITIC « arrêt automatique … dérogation »`
|
|
365
|
-
du journal est cette garde qui se referme — pas une panne.
|
|
366
|
-
- **Ton application a démarré AMPUTÉE et tout a l'air sain** — base injoignable, module écarté
|
|
367
|
-
par sa politique. Seul `npx nodefony doctor` le dit, en relisant `var/last-boot.json`.
|
|
368
|
-
⚠️ Ce bilan a un ÂGE : il décrit le **dernier démarrage**, et une commande console (`inspect`,
|
|
369
|
-
une commande de module) ne l'écrase pas. C'est voulu — lis la date avant d'en conclure.
|
|
370
|
-
- **L'application ne démarre plus et le superviseur avale la sortie** :
|
|
371
|
-
`NF_DEV_CHILD=1 npx nodefony development` lance l'enfant seul et montre le crash brut.
|
|
372
|
-
|
|
373
340
|
## 8. Données et fichiers — les façades qu'on ne recompose pas
|
|
374
341
|
|
|
375
342
|
- **Un cache ne remplace pas une base : ils se COMPLÈTENT.** Chaque adaptateur déclare les
|
|
@@ -445,11 +412,16 @@ Ne concerne qu'une application qui a un frontend. Sauf mention, **vaut pour les
|
|
|
445
412
|
**couleur seule**, `rel="noopener noreferrer"` sur les liens externes.
|
|
446
413
|
|
|
447
414
|
- **Une route d'API répond du HTML** : un repli SPA générique avale les routes voisines — le
|
|
448
|
-
premier motif qui correspond gagne.
|
|
449
|
-
|
|
415
|
+
premier motif qui correspond gagne. Déclare la route de la page monopage avec
|
|
416
|
+
`fallback: true` : elle ne sert alors que ce qu'aucune autre route ne sert, quel que soit
|
|
417
|
+
l'ordre. `npx nodefony inspect routes --json` montre l'ordre réel.
|
|
450
418
|
- **Des utilisateurs « déconnectés au hasard »** : le traitement global « 401 = session expirée »
|
|
451
|
-
frappe aussi
|
|
452
|
-
Exempte les
|
|
419
|
+
frappe aussi la connexion refusée (un mauvais mot de passe rend 401) — et détruit une session
|
|
420
|
+
valide. Exempte les routes d'authentification du traitement global. `me` ne renvoie jamais
|
|
421
|
+
401 : il répond toujours 200, `{ user: null }` sans session.
|
|
422
|
+
- **La connexion ne s'écrit pas en `fetch`** : `/login` est servie ; la liaison du front
|
|
423
|
+
(`useNodefonyLogin`…) porte `me()` et `logout()` — suis l'adresse que rend `logout()`, sinon
|
|
424
|
+
la session Keycloak reste ouverte. Détail : skill `nodefony-authentication`.
|
|
453
425
|
- **Ta page répond 200 et son script ne s'exécute pas** : la politique de contenu exige un `nonce`
|
|
454
426
|
sur les scripts, et le navigateur refuse un `<script>` en ligne qui n'en porte pas (« Refused to
|
|
455
427
|
execute inline script »). Un `curl` ne le voit JAMAIS, il ne lit que le corps. Signe le script
|
|
@@ -468,10 +440,13 @@ Ne concerne qu'une application qui a un frontend. Sauf mention, **vaut pour les
|
|
|
468
440
|
## 10. Avant de dire « fait »
|
|
469
441
|
|
|
470
442
|
```bash
|
|
471
|
-
npm run verify # types
|
|
443
|
+
npm run verify # types, style, mise en forme, .env.example, tests, build, câblage
|
|
472
444
|
npm run test:e2e # boot réel + HTTP/WS — le gate LENT, hors `verify`
|
|
473
445
|
```
|
|
474
446
|
|
|
447
|
+
Ton diff ajoute une route, une requête ou touche la sécurité ? Relis-le AVANT de commiter :
|
|
448
|
+
skill `nodefony-security-review` — un script y nomme, ligne par ligne, ce qu'un œil laisse passer.
|
|
449
|
+
|
|
475
450
|
Puis, en une phrase : **nomme ce que tu n'as PAS lancé.** Un vert ne couvre que le diff qui l'a
|
|
476
451
|
produit, et un banc sauté faute de son décor compte comme vert. Dire « e2e non lancé » coûte
|
|
477
452
|
cinq mots ; le taire coûte la confiance dans tout le reste.
|
|
@@ -21,10 +21,23 @@
|
|
|
21
21
|
* `@output` les pages classées, avec chemin, ligne et extrait — ou un refus qui DIT pourquoi
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
24
|
+
import { readFileSync, readdirSync, statSync, realpathSync } from "node:fs";
|
|
25
25
|
import path from "node:path";
|
|
26
26
|
import process from "node:process";
|
|
27
|
-
import {
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
import { findProjectRoot } from "nodefony/project-root";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Racine de l'APPLICATION vue depuis `cwd` — le dossier qui porte
|
|
32
|
+
* `nodefony.config.ts` —, ou `cwd` lui-même hors application. Un script lancé
|
|
33
|
+
* depuis `frontend/` ou `modules/blog/` travaille ainsi sur l'app entière.
|
|
34
|
+
* `nodefony/project-root` est le sous-chemin LÉGER du cœur (`node:` seul) :
|
|
35
|
+
* importer `nodefony` entier coûtait jusqu'à plusieurs secondes à froid.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} cwd - le dossier de lancement.
|
|
38
|
+
* @returns {string} la racine retenue.
|
|
39
|
+
*/
|
|
40
|
+
const appRoot = (cwd) => findProjectRoot(cwd) ?? cwd;
|
|
28
41
|
|
|
29
42
|
/** Nombre de pages rendues par défaut — au-delà, personne ne lit. */
|
|
30
43
|
const DEFAULT_LIMIT = 6;
|
|
@@ -55,14 +68,14 @@ const FLAGS = new Set([
|
|
|
55
68
|
* @returns les options lues.
|
|
56
69
|
* @throws Error quand un drapeau est inconnu ou qu'une valeur manque.
|
|
57
70
|
*/
|
|
58
|
-
export function parseArgs(argv) {
|
|
71
|
+
export function parseArgs(argv, cwd = process.cwd()) {
|
|
59
72
|
const opts = {
|
|
60
73
|
terms: [],
|
|
61
74
|
list: false,
|
|
62
75
|
open: /** @type {string | null} */ (null),
|
|
63
76
|
json: false,
|
|
64
77
|
limit: DEFAULT_LIMIT,
|
|
65
|
-
root:
|
|
78
|
+
root: appRoot(cwd),
|
|
66
79
|
help: false,
|
|
67
80
|
};
|
|
68
81
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -538,7 +551,10 @@ export function main() {
|
|
|
538
551
|
// confondrait deux scripts homonymes de deux skills.
|
|
539
552
|
if (
|
|
540
553
|
process.argv[1] !== undefined &&
|
|
541
|
-
|
|
554
|
+
// Chemins RÉELS des deux côtés : Node résout `import.meta.url` en suivant
|
|
555
|
+
// les liens, pas `argv[1]` — lancé par `/var/…` (macOS) ou un dossier lié,
|
|
556
|
+
// la comparaison brute échouait et le script ne faisait RIEN.
|
|
557
|
+
realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
542
558
|
) {
|
|
543
559
|
process.exitCode = main();
|
|
544
560
|
}
|
|
@@ -75,7 +75,8 @@ Ce que l'application ne peut pas deviner. Chacun de ces oublis produit une dégr
|
|
|
75
75
|
|
|
76
76
|
- **`NF__HTTP__TRUSTPROXY`** dès qu'un frontal termine le TLS. Sans lui, l'application constate un
|
|
77
77
|
schéma `http` et **un cookie `__Host-` n'est pas émis** : les sessions ne tiennent plus, sans le
|
|
78
|
-
moindre message
|
|
78
|
+
moindre message — et toute connexion par mot de passe répond `403`, le secret étant reçu
|
|
79
|
+
« en clair ». Détail et preuve → [`references/frontal.md`](references/frontal.md).
|
|
79
80
|
- **Deux volumes inscriptibles**, sur `/app/tmp` et `/app/var`, si tu scelles la racine.
|
|
80
81
|
- **Une fenêtre d'arrêt ≥ 30 s**, strictement supérieure au drain de 15 s.
|
|
81
82
|
- **Pas de limite processeur.** Une limite mémoire tue proprement et l'exemplaire redémarre ; une
|
|
@@ -106,18 +107,20 @@ Chaque ligne a la commande qui la **prouve**. Une case cochée sans commande est
|
|
|
106
107
|
|
|
107
108
|
Le tableau qu'on lit en urgence.
|
|
108
109
|
|
|
109
|
-
| Symptôme
|
|
110
|
-
|
|
|
111
|
-
| L'exemplaire redémarre en boucle, journaux muets
|
|
112
|
-
| Le conteneur sort en **137** à l'arrêt
|
|
113
|
-
| Des `5xx` **pendant** chaque déploiement
|
|
114
|
-
| Les sessions ne tiennent pas derrière le frontal
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
| La
|
|
110
|
+
| Symptôme | Cause | Correction |
|
|
111
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| L'exemplaire redémarre en boucle, journaux muets | La sonde de vivacité tombe avant la fin du démarrage | Sonde de démarrage sur `/readyz`, dimensionnée sur le **pire** cas |
|
|
113
|
+
| Le conteneur sort en **137** à l'arrêt | Il a été tué : la fenêtre d'arrêt est ≤ au drain | Fenêtre ≥ 30 s (Podman : `--stop-timeout 20`, son défaut est **10 s**) |
|
|
114
|
+
| Des `5xx` **pendant** chaque déploiement | Le répartiteur envoie encore du trafic quand le drain a commencé | Un délai avant drain, et un court arrêt avant terminaison |
|
|
115
|
+
| Les sessions ne tiennent pas derrière le frontal | Schéma constaté `http` → le cookie `__Host-` n'est pas émis | `NF__HTTP__TRUSTPROXY` |
|
|
116
|
+
| Toutes les connexions répondent `403` « doit passer par HTTPS » | Même cause : le mot de passe arrive sur une requête que l'application croit en clair | `NF__HTTP__TRUSTPROXY` — jamais `allowInsecureCredentials` |
|
|
117
|
+
| Sortie **78** au démarrage, une clé ou une variable nommée | Faute de configuration : clé inconnue (ou retirée par une mise à jour), valeur invalide, secret requis absent (`NF_CSRF_SECRET`, `NF_JWT_KEYSET`) | Lire la ligne qui la nomme ; `npx nodefony doctor --env production` annonce AVANT les variables requises (pas `NF_JWT_KEYSET`, exigée seulement si l'app émet des jetons) |
|
|
118
|
+
| Les journaux d'audit montrent tous la **même adresse** | L'adresse du client n'est pas propagée, ou le frontal n'est pas de confiance | `trustProxy` ; en Podman sans privilèges, **c'est irréparable** (§7) |
|
|
119
|
+
| `EACCES` au premier démarrage sur un volume | Le volume est né `root:root` : monté **par-dessus** un dossier dont l'image avait fixé le porteur | Volume nommé (il hérite du porteur) ou groupe de système de fichiers |
|
|
120
|
+
| La base refuse des connexions dès qu'on monte en exemplaires | Une base administrée les compte ; le pool n'est pas borné | Réduire les exemplaires — le plafond n'est pas encore réglable (§6) |
|
|
121
|
+
| La sonde répond `429` | Ce n'est **pas** la sonde : c'est la limitation de débit — donc l'appel n'a pas pris le raccourci | Interroger `/livez` et `/readyz` nus, sans en-tête ni préfixe ajouté |
|
|
122
|
+
| Un secret retiré du `Dockerfile` reste dans l'image | Une couche antérieure le contient encore ; l'effacer plus loin ne l'enlève pas | `npx nodefony image:check`, et reconstruire sans jamais l'avoir copié |
|
|
123
|
+
| La variable posée « ne fait rien » | Le nom est faux d'un caractère — une variable inconnue est **ignorée**, pas refusée | `npx nodefony doctor` → [`references/variables.md`](references/variables.md) |
|
|
121
124
|
|
|
122
125
|
## 6. Kubernetes — ce qui est rendu, et ce qui te reste
|
|
123
126
|
|
|
@@ -16,6 +16,7 @@ docker compose --profile edge up -d # la TOPOLOGIE DE PRODUCTION : front
|
|
|
16
16
|
docker compose --profile tools up -d # les explorateurs de données
|
|
17
17
|
docker compose --profile loki up -d # journalisation centralisée + tableaux de bord
|
|
18
18
|
docker compose --profile browser up -d # un navigateur piloté
|
|
19
|
+
docker compose --profile keycloak up -d keycloak # un fournisseur d'identité OpenID Connect, de DÉVELOPPEMENT
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
> Le profil `app` et le profil `edge` sont **exclusifs par nature** : le premier publie un port sur
|
|
@@ -35,6 +36,13 @@ docker compose --profile browser up -d # un navigateur piloté
|
|
|
35
36
|
| `redisinsight` | `tools` | explorateur du cache | `5540` |
|
|
36
37
|
| `loki` / `grafana` | `loki` | journaux et tableaux de bord | `3100`, `3000` |
|
|
37
38
|
| `browser` | `browser` | navigateur piloté (image **épinglée par empreinte**) | `3001` |
|
|
39
|
+
| `keycloak` | `keycloak` | `quay.io/keycloak/keycloak` (préréglage `complete`) | `8444` (8443 est au frontal) |
|
|
40
|
+
|
|
41
|
+
Le profil `keycloak` est un décor de **développement** (base embarquée, secrets publics) : realm
|
|
42
|
+
et thème viennent de `docker/keycloak/`, posés par le paquet `@nodefony/security` et tenus à jour
|
|
43
|
+
par `npx nodefony scaffold:sync --update`. Il exige `npx nodefony http:certificates` une fois
|
|
44
|
+
(Keycloak sert en https), et sous Linux natif `export KEYCLOAK_UID=$(id -u)` avant le `up`. Le
|
|
45
|
+
mode d'emploi est en commentaire au-dessus du service, dans `compose.yaml`.
|
|
38
46
|
|
|
39
47
|
**Les ports sont publiés sur la boucle locale**, pas sur `0.0.0.0`. Une base de développement
|
|
40
48
|
accessible depuis le réseau est une base de développement qui finit indexée.
|
|
@@ -34,7 +34,11 @@ Conséquence si tu ne le poses pas : le schéma constaté est `http`, donc :
|
|
|
34
34
|
sécurisée. Les sessions ne tiennent pas, **sans aucun message** ;
|
|
35
35
|
- l'adresse cliente enregistrée est celle du frontal, donc l'audit et toute limitation par adresse
|
|
36
36
|
portent sur une seule IP ;
|
|
37
|
-
- les redirections peuvent pointer sur `http
|
|
37
|
+
- les redirections peuvent pointer sur `http://` ;
|
|
38
|
+
- **toute connexion par mot de passe répond `403`** (« doit passer par HTTPS ») : en production,
|
|
39
|
+
un secret reçu sur une requête que l'application croit en clair est refusé avant toute
|
|
40
|
+
vérification. `security.allowInsecureCredentials` n'est PAS la correction — elle accepterait
|
|
41
|
+
vraiment le clair ; la correction est cette variable.
|
|
38
42
|
|
|
39
43
|
```yaml
|
|
40
44
|
NF__HTTP__TRUSTPROXY: uniquelocal # le frontal est sur un réseau privé
|
|
@@ -11,7 +11,7 @@ du frontal ne sort que si tu le demandes explicitement.
|
|
|
11
11
|
| ----------- | ------------------- | --------------------------------------------------------------------- | ----------------------------- |
|
|
12
12
|
| `build` | `node:24-alpine` | Installe, compile, élague les dépendances de développement | seulement son `/app` |
|
|
13
13
|
| `proxyconf` | hérite de `build` | **Dérive** la configuration nginx et rassemble les fichiers statiques | non — étage intermédiaire |
|
|
14
|
-
| `edge` | `nginx:1.
|
|
14
|
+
| `edge` | `nginx:1.30-alpine` | Le frontal, avec la configuration dérivée ci-dessus | non — cible séparée |
|
|
15
15
|
| _(final)_ | `node:24-alpine` | L'application en production | **oui** |
|
|
16
16
|
|
|
17
17
|
```bash
|
|
@@ -24,7 +24,7 @@ Il est rendu avec l'application, et il exclut quatre familles. Ne les retire pas
|
|
|
24
24
|
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
25
25
|
| `**/node_modules`, `**/dist` | Reconstruits **dans** l'image. Entrés depuis ta machine, ils masqueraient la construction et l'image partirait avec le code de la veille. |
|
|
26
26
|
| `var`, `logs`, `**/*.log`, `**/*.jsonl` | Écritures propres à une machine, sans valeur dans une image |
|
|
27
|
-
|
|
|
27
|
+
| `.env`, `.env.*` (sauf `.env.example`) | Les valeurs du poste, secrets de dev compris — jamais commitées, et pas davantage dans une image |
|
|
28
28
|
| **La matière cryptographique** | Clés privées, certificats |
|
|
29
29
|
|
|
30
30
|
> ⚠️ **Un motif de `.dockerignore` n'obéit pas aux règles du `.gitignore`.** Sans `**/`, il est
|
|
@@ -47,15 +47,29 @@ qu'aucun certificat auto-signé de ta machine ne descende en production.
|
|
|
47
47
|
| `NF_CSRF_SECRET` | Les jetons anti-CSRF | **oui** — sinon un jeton émis par un exemplaire est refusé par l'autre |
|
|
48
48
|
| `NF_TOTP_KEY` | Le chiffrement au repos des secrets de double authentification | **oui** — sinon les seconds facteurs deviennent illisibles |
|
|
49
49
|
| `NF_WEBHOOK_KEY` | Le chiffrement des signatures de webhooks | **oui** |
|
|
50
|
+
| `NF_JWT_KEYSET` | La clé qui signe les jetons (privée) | **oui** — sinon chaque exemplaire refuse les jetons des autres |
|
|
50
51
|
| `NF_ADMIN_PASSWORD` | Le compte d'administration | — |
|
|
51
52
|
|
|
52
53
|
> 🔴 **Le partage entre exemplaires n'est pas un détail de confort.** Un secret différent par
|
|
53
54
|
> exemplaire produit des échecs **intermittents** — une requête sur deux, selon le répartiteur.
|
|
54
55
|
> C'est le symptôme le plus coûteux à diagnostiquer, parce qu'il ressemble à un problème réseau.
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
**Lesquels arrêtent le démarrage en production** : `NF_CSRF_SECRET` toujours, `NF_JWT_KEYSET`
|
|
58
|
+
dès que l'application émet des jetons. Sans `NF_TOTP_KEY` ou `NF_WEBHOOK_KEY`, l'application
|
|
59
|
+
démarre mais la brique se désactive (double authentification, webhooks), avec un journal CRITIC.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx nodefony security:secrets --env # les secrets, en lignes CLÉ=valeur (--env-file, gestionnaire)
|
|
63
|
+
npx nodefony security:secrets --jwt-keyset # la valeur de NF_JWT_KEYSET — une ligne JSON, clé PRIVÉE
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Une valeur par ENVIRONNEMENT (staging ≠ production), générée une fois, rangée dans le
|
|
67
|
+
gestionnaire de secrets ; ne laisse traîner aucun fichier qui la contient.
|
|
68
|
+
|
|
69
|
+
`NF_ADMIN_PASSWORD` a un défaut en développement, aucun en production : sans lui, le premier
|
|
70
|
+
démarrage ne crée **aucun** compte d'administration — plutôt qu'un compte au mot de passe connu de
|
|
71
|
+
tout le monde. Il ne sert qu'à ce premier démarrage ; `npx nodefony security:user:add <id> --admin`
|
|
72
|
+
fait le même geste à la main.
|
|
59
73
|
|
|
60
74
|
## 5. Les monter, pas les poser en clair
|
|
61
75
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## 1. La commande qui répond, et à laquelle rien ne ment
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx nodefony env # la
|
|
8
|
+
npx nodefony env # la valeur EFFECTIVE de chaque variable, et sa PROVENANCE (.env ou process)
|
|
9
9
|
npx nodefony env --json # le même rapport, pour un script
|
|
10
10
|
```
|
|
11
11
|
|
|
@@ -19,8 +19,11 @@ npx nodefony inspect schema <module> # les clés qui EXISTENT, leur type,
|
|
|
19
19
|
npx nodefony doctor --env production # ce qui manquerait là-bas, depuis ici
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
> 🔴 **`inspect schema` avant d'écrire une clé.** Une clé inconnue
|
|
23
|
-
>
|
|
22
|
+
> 🔴 **`inspect schema` avant d'écrire une clé.** Une clé inconnue **arrête le démarrage**
|
|
23
|
+
> (code 78) en la nommant — y compris une clé qu'une version du framework a retirée, que tu
|
|
24
|
+
> découvres alors au premier déploiement après un `npm update`. Ce qui reste muet, c'est une
|
|
25
|
+
> **variable** mal orthographiée (`NF_PROT` au lieu de `NF_PORT`) : inconnue, donc ignorée, et
|
|
26
|
+
> seul `npx nodefony env` la montre.
|
|
24
27
|
|
|
25
28
|
## 2. Deux grammaires, et elles ne font pas la même chose
|
|
26
29
|
|
|
@@ -54,6 +57,27 @@ booléen, un nombre devient un nombre, `[…]` et `{…}` sont lus en JSON, une
|
|
|
54
57
|
devient un tableau, le reste reste une chaîne. C'est ce qui évite le piège classique où la chaîne
|
|
55
58
|
`"false"` est convertie en booléen **vrai**.
|
|
56
59
|
|
|
60
|
+
### `NF_<MODULE>_<RÉGLAGE>` — les noms courts des modules
|
|
61
|
+
|
|
62
|
+
Un module déclare sur un champ de son schéma le nom court qui le règle (`NF_REDIS_HOST`,
|
|
63
|
+
`NF_REALTIME_DRIVER`…). `npx nodefony env --example` les écrit dans `.env.example`, une section
|
|
64
|
+
par module. Trois règles, toutes au démarrage :
|
|
65
|
+
|
|
66
|
+
- La valeur est **validée par le schéma** : `NF_REDIS_PORT=abc` arrête le démarrage (78) en
|
|
67
|
+
nommant la VARIABLE, pas le seul champ.
|
|
68
|
+
- **Nom court ET `NF__…` sur le même champ** : refus du démarrage en production (78) ; en
|
|
69
|
+
développement, `NF__…` l'emporte avec un avertissement qui nomme les deux.
|
|
70
|
+
- Chaque nom court accepte `<NOM>_FILE` ; une variable vide vaut absente.
|
|
71
|
+
|
|
72
|
+
### Lire la notice `.env.example`
|
|
73
|
+
|
|
74
|
+
Chaque variable y porte ses métadonnées : `@required` / `@optional`, `@required=forEnv(production)`,
|
|
75
|
+
`@requiredWhen="…"` (exigée sous condition — `NF_JWT_KEYSET` : « en production, si l'application
|
|
76
|
+
émet des jetons »), `@sensitive`, `@default`, et **`@placement`**, qui dit où la poser en
|
|
77
|
+
production : `platform` (variables du déploiement), `secrets` (gestionnaire de secrets, ou
|
|
78
|
+
`<NOM>_FILE`), `workstation` (le poste seulement — ne la pose pas en production).
|
|
79
|
+
`npm run env:check` échoue (78) quand la notice a dérivé d'`env.ts`.
|
|
80
|
+
|
|
57
81
|
## 3. Le suffixe `_FILE` — les secrets montés
|
|
58
82
|
|
|
59
83
|
**Toute** variable du catalogue accepte un `_FILE`. Le contenu du fichier est lu, retour à la ligne
|
|
@@ -102,23 +126,32 @@ Les seules qui te concernent au déploiement. Le catalogue complet, c'est `npx n
|
|
|
102
126
|
|
|
103
127
|
### Données
|
|
104
128
|
|
|
105
|
-
| Variable | Rôle | Absente ⇒
|
|
106
|
-
| ----------------- | ---------------------- |
|
|
107
|
-
| `NF_DATABASE_URL` | L'URL de la base | **Repli sur SQLite local**, dans `var/` —
|
|
108
|
-
| `NF_REDIS_URL` | L'URL du cache partagé | Le cache n'est pas chargé
|
|
129
|
+
| Variable | Rôle | Absente ⇒ |
|
|
130
|
+
| ----------------- | ---------------------- | ------------------------------------------------- |
|
|
131
|
+
| `NF_DATABASE_URL` | L'URL de la base | **Repli sur SQLite local**, dans `var/` — annoncé |
|
|
132
|
+
| `NF_REDIS_URL` | L'URL du cache partagé | Le cache n'est pas chargé |
|
|
109
133
|
|
|
110
134
|
> Le repli SQLite est le piège d'exploitation classique : l'application **démarre**, sert des
|
|
111
|
-
> requêtes, écrit dans un fichier — et chaque exemplaire a sa propre base.
|
|
112
|
-
>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
135
|
+
> requêtes, écrit dans un fichier — et chaque exemplaire a sa propre base. Il est ANNONCÉ
|
|
136
|
+
> (avertissement au démarrage, point `DB_SQLITE_FALLBACK` dans `npx nodefony status --json`),
|
|
137
|
+
> mais rien n'échoue : lis le bilan du démarrage. Seule exception : si une entité est écrite pour
|
|
138
|
+
> un autre moteur (PostgreSQL, MySQL), le repli est **refusé** et le démarrage s'arrête.
|
|
139
|
+
|
|
140
|
+
### Secrets — ce qui se passe en production sans eux
|
|
141
|
+
|
|
142
|
+
| Variable | Ce qu'il protège | Absente en production ⇒ |
|
|
143
|
+
| ------------------- | ------------------------------------------------ | --------------------------------------------------- |
|
|
144
|
+
| `NF_CSRF_SECRET` | Les jetons anti-CSRF, partagés entre exemplaires | **Refus de démarrer** (78) |
|
|
145
|
+
| `NF_JWT_KEYSET` | La clé de signature des jetons, commune à tous | **Refus de démarrer** si l'app émet des jetons |
|
|
146
|
+
| `NF_TOTP_KEY` | Les secrets de double authentification au repos | Double authentification désactivée (journal CRITIC) |
|
|
147
|
+
| `NF_WEBHOOK_KEY` | Les signatures de webhooks | Webhooks désactivés (journal CRITIC) |
|
|
148
|
+
| `NF_ADMIN_PASSWORD` | Le compte d'administration, au PREMIER démarrage | Aucun compte créé ; inutile une fois le compte créé |
|
|
149
|
+
|
|
150
|
+
`NF_JWT_KEYSET` manque presque toujours au premier déploiement : sans elle, un exemplaire qui
|
|
151
|
+
émet des jetons **refuse de démarrer** en production (78) — et deux exemplaires à clés
|
|
152
|
+
différentes refuseraient chacun les jetons de l'autre. `npx nodefony security:secrets --jwt-keyset`
|
|
153
|
+
la produit (une ligne JSON) ; `--env` rend les autres secrets en lignes `CLÉ=valeur`, prêtes pour
|
|
154
|
+
`docker run --env-file` ou un gestionnaire de secrets.
|
|
122
155
|
|
|
123
156
|
Tous en `_FILE` en production. Détail et rotation → [`secrets.md`](secrets.md).
|
|
124
157
|
|
|
@@ -129,7 +162,7 @@ Tous en `_FILE` en production. Détail et rotation → [`secrets.md`](secrets.md
|
|
|
129
162
|
| `NF_POD_NAME` | Identité de l'exemplaire — **dérive l'identité d'origine du bus temps réel** |
|
|
130
163
|
| `NF_INSTANCE_ID` | Identifiant d'exemplaire |
|
|
131
164
|
| `NF_REALTIME_DRIVER` | Le transport du bus entre exemplaires |
|
|
132
|
-
| `NF_REALTIME_BACKPLANE_SECRET` | Scelle les enveloppes
|
|
165
|
+
| `NF_REALTIME_BACKPLANE_SECRET` | Scelle les enveloppes du bus — moins de 32 caractères arrête le démarrage |
|
|
133
166
|
| `NF_REALTIME_BACKPLANE_NAMESPACE` | Cloisonne deux applications qui partagent le même bus |
|
|
134
167
|
|
|
135
168
|
> 🔴 **Deux applications sur un même cache, sans espace de noms distinct, se parlent.** Le symptôme
|
|
@@ -259,10 +259,13 @@ neuve ne prouve RIEN de tout ça — elle est vide.
|
|
|
259
259
|
```bash
|
|
260
260
|
# sqlite : une copie du fichier. Le chemin par défaut du connecteur `default` est
|
|
261
261
|
# `var/databases/nodefony-drizzle.db` — `orm:migrate:status --json` le confirme.
|
|
262
|
-
|
|
262
|
+
# La copie va sous `tmp/scratch/` de l'application, pas dans le /tmp du système :
|
|
263
|
+
# c'est un résultat qu'on relit, et il doit disparaître avec le reste de `tmp/`.
|
|
264
|
+
mkdir -p tmp/scratch
|
|
265
|
+
cp var/databases/nodefony-drizzle.db tmp/scratch/essai.sqlite
|
|
263
266
|
# PostgreSQL : CREATE DATABASE app_essai TEMPLATE app; (ou une restauration de sauvegarde)
|
|
264
267
|
|
|
265
|
-
export NF_MIGRATE_DATABASE_URL="sqlite
|
|
268
|
+
export NF_MIGRATE_DATABASE_URL="sqlite:tmp/scratch/essai.sqlite"
|
|
266
269
|
npx nodefony orm:migrate --json
|
|
267
270
|
```
|
|
268
271
|
|