fix: mount referentiel ia token as docker secret
This commit is contained in:
@@ -61,6 +61,7 @@ services:
|
|||||||
# Passerelle centralisée : le jeton reste dans l'environnement serveur.
|
# Passerelle centralisée : le jeton reste dans l'environnement serveur.
|
||||||
REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-}
|
REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-}
|
||||||
REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}
|
REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}
|
||||||
|
REFERENTIEL_IA_TOKEN_FILE: /run/secrets/referentiel_ia_token
|
||||||
UPLOAD_DIR: /app/uploads
|
UPLOAD_DIR: /app/uploads
|
||||||
# Chemin unique utilisé par le serveur et vérifié au démarrage.
|
# Chemin unique utilisé par le serveur et vérifié au démarrage.
|
||||||
STORAGE_BASE_PATH: /app/storage
|
STORAGE_BASE_PATH: /app/storage
|
||||||
@@ -81,6 +82,10 @@ services:
|
|||||||
target: /app/storage
|
target: /app/storage
|
||||||
volume:
|
volume:
|
||||||
nocopy: true
|
nocopy: true
|
||||||
|
secrets:
|
||||||
|
- source: referentiel_ia_token
|
||||||
|
target: referentiel_ia_token
|
||||||
|
mode: 0400
|
||||||
# L’application doit utiliser eth0 sur le réseau de données ; l’ordre évite
|
# L’application doit utiliser eth0 sur le réseau de données ; l’ordre évite
|
||||||
# toute inversion d’interface lors d’une recréation Docker multi-réseaux.
|
# toute inversion d’interface lors d’une recréation Docker multi-réseaux.
|
||||||
networks:
|
networks:
|
||||||
@@ -110,6 +115,10 @@ volumes:
|
|||||||
external: true
|
external: true
|
||||||
name: ${STORAGE_VOLUME_NAME:-demat-facturation-dsi_demat_facturation_storage}
|
name: ${STORAGE_VOLUME_NAME:-demat-facturation-dsi_demat_facturation_storage}
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
referentiel_ia_token:
|
||||||
|
file: /opt/manus-secrets/demat-facturation-referentiel-ia-token
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
demat-facturation-internal:
|
demat-facturation-internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
|
/** Lit un secret monté par Docker, sans jamais exposer son contenu aux logs. */
|
||||||
|
export function readSecretFile(path: string | undefined): string {
|
||||||
|
if (!path) return "";
|
||||||
|
try {
|
||||||
|
return readFileSync(path, "utf8").trim();
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ENV = {
|
export const ENV = {
|
||||||
appId: process.env.VITE_APP_ID ?? "",
|
appId: process.env.VITE_APP_ID ?? "",
|
||||||
cookieSecret: process.env.JWT_SECRET ?? "",
|
cookieSecret: process.env.JWT_SECRET ?? "",
|
||||||
@@ -10,5 +22,5 @@ export const ENV = {
|
|||||||
mistralApiKey: process.env.MISTRAL_API_KEY ?? "",
|
mistralApiKey: process.env.MISTRAL_API_KEY ?? "",
|
||||||
geminiApiKey: process.env.GEMINI_API_KEY ?? "",
|
geminiApiKey: process.env.GEMINI_API_KEY ?? "",
|
||||||
referentielIaUrl: process.env.REFERENTIEL_IA_URL ?? "",
|
referentielIaUrl: process.env.REFERENTIEL_IA_URL ?? "",
|
||||||
referentielIaToken: process.env.REFERENTIEL_IA_TOKEN ?? "",
|
referentielIaToken: process.env.REFERENTIEL_IA_TOKEN ?? readSecretFile(process.env.REFERENTIEL_IA_TOKEN_FILE),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,5 +41,7 @@ describe("routage et persistance de déploiement", () => {
|
|||||||
|
|
||||||
expect(composeFile).toContain("REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-}");
|
expect(composeFile).toContain("REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-}");
|
||||||
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}");
|
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}");
|
||||||
|
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN_FILE: /run/secrets/referentiel_ia_token");
|
||||||
|
expect(composeFile).toContain("file: /opt/manus-secrets/demat-facturation-referentiel-ia-token");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
import {
|
import {
|
||||||
invokeReferentialIa,
|
invokeReferentialIa,
|
||||||
testReferentialIaConnection,
|
testReferentialIaConnection,
|
||||||
type ReferentielIaConnection,
|
type ReferentielIaConnection,
|
||||||
} from "./referentialIa";
|
} from "./referentialIa";
|
||||||
|
import { readSecretFile } from "./_core/env";
|
||||||
|
|
||||||
const connection: ReferentielIaConnection = {
|
const connection: ReferentielIaConnection = {
|
||||||
baseUrl: "https://referentiel-ia.recette.santinova-soft.org",
|
baseUrl: "https://referentiel-ia.recette.santinova-soft.org",
|
||||||
@@ -18,6 +22,18 @@ function jsonResponse(body: unknown, status = 200) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("Référentiel IA", () => {
|
describe("Référentiel IA", () => {
|
||||||
|
it("lit un jeton serveur depuis un fichier monté sans le journaliser", () => {
|
||||||
|
const directory = mkdtempSync(join(tmpdir(), "referentiel-ia-"));
|
||||||
|
const secretFile = join(directory, "token");
|
||||||
|
try {
|
||||||
|
writeFileSync(secretFile, " server-only-token\n", { mode: 0o600 });
|
||||||
|
expect(readSecretFile(secretFile)).toBe("server-only-token");
|
||||||
|
expect(readSecretFile(join(directory, "absent"))).toBe("");
|
||||||
|
} finally {
|
||||||
|
rmSync(directory, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("transmet une requête OpenAI-compatible à la passerelle sélectionnée", async () => {
|
it("transmet une requête OpenAI-compatible à la passerelle sélectionnée", async () => {
|
||||||
let receivedUrl = "";
|
let receivedUrl = "";
|
||||||
let receivedInit: RequestInit | undefined;
|
let receivedInit: RequestInit | undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user