feat(infra): prêt au déploiement GitOps Portainer + Telegram optionnel (v0.32.0)

Compose de prod docker-compose.prod.yml (GitOps, sans env_file, réseau proxy
réel, certresolver letsencrypt) + runbook docs/deploy.md (Phase 1, users
chlova restreints Portainer/n8n). Surface Telegram rendue optionnelle pour un
déploiement UI-only ; garde assertHasSurface fail-closed. Typecheck + 78 tests
verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016w5jRe87MGdd6AMvXQcHNi
This commit is contained in:
Kantin-Petit
2026-06-23 11:25:30 +02:00
parent faa1e82301
commit d824d16eed
7 changed files with 379 additions and 21 deletions
+28 -1
View File
@@ -1,5 +1,10 @@
import { describe, it, expect } from "vitest";
import { loadConfig, assertReadOnlyPhase, redactedConfig } from "../src/config.js";
import {
loadConfig,
assertReadOnlyPhase,
assertHasSurface,
redactedConfig,
} from "../src/config.js";
const fullEnv = (): NodeJS.ProcessEnv => ({
OLLAMA_BASE_URL: "http://ollama:11434",
@@ -61,6 +66,28 @@ describe("verrou lecture seule Phase 1", () => {
});
});
describe("garde de surface (fail-closed)", () => {
it("Telegram seul suffit", () => {
expect(() => assertHasSurface(loadConfig(fullEnv()))).not.toThrow();
});
it("API/UI seule suffit (sans Telegram)", () => {
const env = fullEnv();
delete env.TELEGRAM_BOT_TOKEN;
env.CHLOVA_ADMIN_USER = "kantin";
env.CHLOVA_ADMIN_PASSWORD_HASH = "hash";
env.CHLOVA_TOTP_SECRET = "totp";
env.CHLOVA_JWT_SECRET = "jwt";
expect(() => assertHasSurface(loadConfig(env))).not.toThrow();
});
it("refuse de démarrer sans aucune surface", () => {
const env = fullEnv();
delete env.TELEGRAM_BOT_TOKEN;
expect(() => assertHasSurface(loadConfig(env))).toThrow(/surface/i);
});
});
describe("redactedConfig masque les secrets", () => {
it("ne révèle aucun secret", () => {
const red = redactedConfig(loadConfig(fullEnv()));