mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
- FileStorage : putObject/getObjectStream/removeObject, bucket créé au démarrage — MinIO confiné à son implémentation (règle ESLint intacte) - documents : upload multipart (PDF/JPG/PNG, 20 Mo max, rattachement appareil OU OT requis, permission d'édition sur la CIBLE), liste filtrable, téléchargement STREAMÉ par l'API (MinIO jamais exposé), suppression ; e2e : octets téléchargés identiques aux octets envoyés - analytics : GET /analytics/summary dérivé du réel — coûts/mois (mouvements + main-d'œuvre figés), pannes par organe (bilans codés), taux de préventif, durée moyenne de résolution, top équipements - générateur OpenAPI : query params, multipart, réponse binaire (71 ops) - CI : service MinIO (bitnami) sur les jobs api et e2e - test de régression du tri « Interventions récentes » rendu déterministe (positions absolues instables sous 12 suites parallèles) ; 58 tests, 6 runs complets consécutifs verts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import 'dotenv/config';
|
|
import { z } from 'zod';
|
|
|
|
const EnvSchema = z.object({
|
|
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
PORT: z.coerce.number().default(3000),
|
|
DATABASE_URL: z.string().min(1),
|
|
REDIS_URL: z.string().default('redis://localhost:6379'),
|
|
JWT_SECRET: z.string().min(16, 'JWT_SECRET : 16 caractères minimum'),
|
|
JWT_EXPIRES_IN: z.string().default('8h'),
|
|
MINIO_ENDPOINT: z.string().default('localhost'),
|
|
MINIO_PORT: z.coerce.number().default(9000),
|
|
MINIO_USE_SSL: z.string().optional(),
|
|
MINIO_ACCESS_KEY: z.string().default('siop'),
|
|
MINIO_SECRET_KEY: z.string().default('siop-minio'),
|
|
MINIO_BUCKET: z.string().default('siop2'),
|
|
});
|
|
|
|
export type Env = z.infer<typeof EnvSchema>;
|
|
|
|
/** Validée au démarrage — l'API refuse de booter sur une config invalide. */
|
|
export function loadEnv(): Env {
|
|
const parsed = EnvSchema.safeParse(process.env);
|
|
if (!parsed.success) {
|
|
throw new Error(
|
|
`Configuration invalide :\n${parsed.error.issues
|
|
.map((i) => ` - ${i.path.join('.')}: ${i.message}`)
|
|
.join('\n')}`,
|
|
);
|
|
}
|
|
return parsed.data;
|
|
}
|
|
|
|
/** Lues à l'appel (et non à l'import) pour rester testables. */
|
|
export function demoModeEnabled(): boolean {
|
|
return process.env.DEMO_MODE === 'true';
|
|
}
|
|
|
|
/**
|
|
* ADR-002, double verrou : DEMO_MODE=true en production exige l'aveu explicite
|
|
* DEMO_MODE_I_KNOW=true (réservé à l'instance de démonstration publique).
|
|
*/
|
|
export function assertDemoModeAllowed(): void {
|
|
if (
|
|
demoModeEnabled() &&
|
|
process.env.NODE_ENV === 'production' &&
|
|
process.env.DEMO_MODE_I_KNOW !== 'true'
|
|
) {
|
|
throw new Error(
|
|
'DEMO_MODE=true en production sans DEMO_MODE_I_KNOW=true — démarrage refusé (ADR-002).',
|
|
);
|
|
}
|
|
}
|