feat: normaliser les URL production par entité
All checks were successful
Validation Dashboard / Tests backend et build frontend (push) Successful in 58s

This commit is contained in:
Santinova Deployment
2026-10-24 17:06:49 +00:00
parent 3c000c37af
commit 9fd5dbb0df
17 changed files with 84 additions and 79 deletions

View File

@@ -32,8 +32,8 @@ Le dashboard et le portail applicatif utilisent exclusivement le manifeste plac
"activeConfigurationPath": "/api/ai/active-configuration"
},
"urls": {
"recette": "https://contacts.recette.santinova-soft.org",
"prod": "https://contacts.santinova-soft.org"
"recette": "https://contacts-itinova.recette.santinova-soft.org",
"prod": "https://contacts-itinova.santinova-soft.org"
}
}
```

View File

@@ -1,43 +1,34 @@
# Stage 1: Build frontend
# Stage 1: Build frontend from Git-tracked sources
FROM node:20-alpine AS frontend-builder
WORKDIR /app/frontend
# Copy frontend package files
COPY frontend/package.json frontend/pnpm-lock.yaml* ./
# Copy frontend package files and source from the versioned src tree.
COPY src/frontend/package.json src/frontend/pnpm-lock.yaml* ./
RUN npm install
# Copy frontend source
COPY frontend/ ./
# Build frontend
COPY src/frontend/ ./
RUN npm run build
# Stage 2: Production
# Stage 2: Production runtime
FROM node:20-alpine
WORKDIR /app
# Install docker CLI for docker compose commands
# Docker controls are required by the dashboard runtime.
RUN apk add --no-cache docker-cli docker-cli-compose git curl unzip bash openssh-client
# Configure git global identity to avoid "Author identity unknown" errors
RUN git config --global user.email "admin@santinova-soft.org" && \
git config --global user.name "Manus Dashboard"
# Copy backend
COPY backend/package.json ./backend/
# Copy backend dependencies and source from the versioned src tree.
COPY src/backend/package.json src/backend/package-lock.json ./backend/
WORKDIR /app/backend
RUN npm install --production
RUN npm ci --omit=dev
COPY src/backend/ ./
COPY backend/ ./
# Copy built frontend
# Copy the frontend built from the same Git revision.
WORKDIR /app
COPY --from=frontend-builder /app/frontend/dist ./frontend/dist
WORKDIR /app/backend
EXPOSE 3001
CMD ["node", "src/index.js"]

View File

@@ -28,5 +28,5 @@ docker compose build --no-cache && docker compose up -d
## Accès
- URL : https://dashboard.santinova-soft.org
- URL : https://dashboard-santinova.santinova-soft.org
- Login : adminItinova / Itinova69!

View File

@@ -3,8 +3,8 @@
"name": "Dashboard Production",
"category": "INFRA",
"urls": {
"recette": "https://dashboard.recette.santinova-soft.org",
"prod": "https://dashboard.santinova-soft.org"
"recette": "https://dashboard-santinova.recette.santinova-soft.org",
"prod": "https://dashboard-santinova.santinova-soft.org"
},
"containerName": "manus-dashboard",
"image": "images/dashboard.png",

View File

@@ -36,7 +36,7 @@ services:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.manus-dashboard.rule=Host(`dashboard.santinova-soft.org`)"
- "traefik.http.routers.manus-dashboard.rule=Host(`dashboard.santinova-soft.org`) || Host(`dashboard-santinova.santinova-soft.org`)"
- "traefik.http.routers.manus-dashboard.entrypoints=websecure"
- "traefik.http.routers.manus-dashboard.tls=true"
- "traefik.http.routers.manus-dashboard.tls.certresolver=letsencrypt"

View File

@@ -4,7 +4,7 @@ set -Eeuo pipefail
readonly DASHBOARD_APP_DIR="${DASHBOARD_APP_DIR:-/opt/manus-deploy/apps/manus-dashboard}"
readonly DASHBOARD_ENV_FILE="${DASHBOARD_ENV_FILE:-${DASHBOARD_APP_DIR}/.env}"
readonly REFERENTIEL_CONTAINER="${REFERENTIEL_CONTAINER:-referentiel-ia}"
readonly REFERENTIEL_URL="${IA_REFERENTIEL_URL:-https://referentiel-ia.recette.santinova-soft.org}"
readonly REFERENTIEL_URL="${IA_REFERENTIEL_URL:-https://referentiel-ia-santinova.recette.santinova-soft.org}"
backup="$(mktemp /tmp/manus-dashboard-env-backup.XXXXXX)"
temporary="$(mktemp /tmp/manus-dashboard-env-next.XXXXXX)"

View File

@@ -12,7 +12,7 @@ from pathlib import Path
GITEA_URL = os.environ.get("GITEA_URL", "https://git.recette.santinova-soft.org").rstrip("/")
GITEA_USER = os.environ.get("GITEA_USERNAME", "manus-admin")
DASHBOARD_URL = os.environ.get("DASHBOARD_URL", "https://dashboard.recette.santinova-soft.org").rstrip("/")
DASHBOARD_URL = os.environ.get("DASHBOARD_URL", "https://dashboard-santinova.recette.santinova-soft.org").rstrip("/")
DASHBOARD_APP_DIR = Path(os.environ.get("DASHBOARD_APP_DIR", "/opt/manus-deploy/apps/manus-dashboard"))
ENV_FILE = DASHBOARD_APP_DIR / ".env"
WEBHOOK_ENDPOINT = f"{DASHBOARD_URL}/api/webhook/gitea"

View File

@@ -28,5 +28,5 @@ docker compose build --no-cache && docker compose up -d
## Accès
- URL : https://dashboard.santinova-soft.org
- URL : https://dashboard-santinova.santinova-soft.org
- Login : adminItinova / Itinova69!

View File

@@ -31,7 +31,7 @@ module.exports = {
},
dsiAlerts: {
url: process.env.IA_REFERENTIEL_URL || 'https://referentiel-ia.recette.santinova-soft.org',
url: process.env.IA_REFERENTIEL_URL || 'https://referentiel-ia-santinova.recette.santinova-soft.org',
token: process.env.IA_REFERENTIEL_TOKEN || null,
email: process.env.DSI_ALERT_EMAIL || null,
emailQueue: process.env.CI_ALERT_EMAIL_QUEUE || '/opt/manus-deploy/secrets/ci-alerts/outbox',

View File

@@ -25,9 +25,9 @@ const { getActiveAiConfiguration } = require('../src/healthcheck');
const result = await getActiveAiConfiguration({
ai: { activeConfigurationPath: '/api/ai/active-configuration' },
}, 'https://contacts.recette.santinova-soft.org');
}, 'https://contacts-itinova.recette.santinova-soft.org');
assert.equal(requestedUrl, 'https://contacts.recette.santinova-soft.org/api/ai/active-configuration');
assert.equal(requestedUrl, 'https://contacts-itinova.recette.santinova-soft.org/api/ai/active-configuration');
assert.deepEqual(result, {
active: true,
runtimeStatus: 'active',

View File

@@ -29,8 +29,8 @@ try {
activeConfigurationPath: '/api/ai/active-configuration',
},
urls: {
recette: 'https://contacts.recette.santinova-soft.org',
prod: 'https://contacts.santinova-soft.org',
recette: 'https://contacts-itinova.recette.santinova-soft.org',
prod: 'https://contacts-itinova.santinova-soft.org',
},
});
@@ -39,8 +39,8 @@ try {
name: 'Dashboard',
category: 'INFRA',
urls: {
recette: 'https://dashboard.recette.santinova-soft.org',
prod: 'https://dashboard.santinova-soft.org',
recette: 'https://dashboard-santinova.recette.santinova-soft.org',
prod: 'https://dashboard-santinova.santinova-soft.org',
},
});
@@ -48,7 +48,7 @@ try {
id: 'falc-generator',
name: 'FALC Generator',
category: 'ITINOVA',
urls: { recette: 'https://falc.recette.santinova-soft.org' },
urls: { recette: 'https://falc-itinova.recette.santinova-soft.org' },
});
writeManifest(sandbox, 'invalide', {

View File

@@ -0,0 +1,14 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const dockerfile = fs.readFileSync(path.resolve(__dirname, '../../../Dockerfile'), 'utf8');
assert.match(dockerfile, /COPY src\/frontend\/package\.json src\/frontend\/pnpm-lock\.yaml\* \.\//);
assert.match(dockerfile, /COPY src\/frontend\/ \.\//);
assert.match(dockerfile, /COPY src\/backend\/package\.json src\/backend\/package-lock\.json \.\/backend\//);
assert.match(dockerfile, /COPY src\/backend\/ \.\//);
assert.doesNotMatch(dockerfile, /COPY frontend\//);
assert.doesNotMatch(dockerfile, /COPY backend\//);
console.log('OK dockerfile source paths');

View File

@@ -17,7 +17,7 @@ services:
- GITEA_RECETTE_URL=http://gitea:3000
- GITEA_TOKEN_PROD=${GITEA_TOKEN_PROD:-}
- WEBHOOK_SECRET=${WEBHOOK_SECRET:?WEBHOOK_SECRET requis dans le fichier .env serveur}
- IA_REFERENTIEL_URL=${IA_REFERENTIEL_URL:-https://referentiel-ia.recette.santinova-soft.org}
- IA_REFERENTIEL_URL=https://referentiel-ia-santinova.santinova-soft.org
- IA_REFERENTIEL_TOKEN=${IA_REFERENTIEL_TOKEN:-}
- DSI_ALERT_EMAIL=${DSI_ALERT_EMAIL:-}
- CI_ALERT_EMAIL_QUEUE=/opt/manus-deploy/secrets/ci-alerts/outbox

View File

@@ -55,9 +55,9 @@ const RECETTE = {
icon: LayoutDashboard,
color: 'blue',
name: 'Dashboard',
url: 'dashboard.recette.santinova-soft.org',
url: 'dashboard-santinova.recette.santinova-soft.org',
desc: 'Monitoring CPU / RAM / Disque',
href: 'https://dashboard.recette.santinova-soft.org',
href: 'https://dashboard-santinova.recette.santinova-soft.org',
},
{
icon: Globe,
@@ -80,38 +80,38 @@ const RECETTE = {
icon: '🧾',
color: 'green',
name: 'Démat. Facturation DSI',
url: 'demat-facturation.recette.santinova-soft.org',
href: 'https://demat-facturation.recette.santinova-soft.org',
url: 'demat-facturation-santinova.recette.santinova-soft.org',
href: 'https://demat-facturation-santinova.recette.santinova-soft.org',
},
{
icon: '👁️',
color: 'indigo',
name: 'Veille Réglementaire',
url: 'veille.recette.santinova-soft.org',
href: 'https://veille.recette.santinova-soft.org',
url: 'veille-itinova.recette.santinova-soft.org',
href: 'https://veille-itinova.recette.santinova-soft.org',
},
{
icon: '🗺️',
color: 'cyan',
name: 'SONUM',
url: 'sonum.recette.santinova-soft.org',
url: 'sonum-santinova.recette.santinova-soft.org',
desc: 'Cartographie solutions numériques FEHAP',
href: 'https://sonum.recette.santinova-soft.org',
href: 'https://sonum-santinova.recette.santinova-soft.org',
},
{
icon: '👤',
color: 'pink',
name: 'Itinova Contacts',
url: 'contacts.recette.santinova-soft.org',
href: 'https://contacts.recette.santinova-soft.org',
url: 'contacts-itinova.recette.santinova-soft.org',
href: 'https://contacts-itinova.recette.santinova-soft.org',
},
{
icon: BrainCircuit,
color: 'purple',
name: 'Référentiel IA Santinova',
url: 'referentiel-ia.recette.santinova-soft.org',
url: 'referentiel-ia-santinova.recette.santinova-soft.org',
desc: 'Décisions, contrats sécurisés et budgets IA',
href: 'https://referentiel-ia.recette.santinova-soft.org',
href: 'https://referentiel-ia-santinova.recette.santinova-soft.org',
},
{
icon: ServerCog,
@@ -125,15 +125,15 @@ const RECETTE = {
icon: '🚗',
color: 'yellow',
name: 'Flotte Véhicules',
url: 'flotte.recette.santinova-soft.org',
href: 'https://flotte.recette.santinova-soft.org',
url: 'flotte-itinova.recette.santinova-soft.org',
href: 'https://flotte-itinova.recette.santinova-soft.org',
},
{
icon: '🎧',
color: 'orange',
name: 'Podcasts Itinova',
url: 'podcasts.recette.santinova-soft.org',
href: 'https://podcasts.recette.santinova-soft.org',
url: 'podcasts-itinova.recette.santinova-soft.org',
href: 'https://podcasts-itinova.recette.santinova-soft.org',
},
],
};
@@ -159,9 +159,9 @@ const PRODUCTION = {
icon: LayoutDashboard,
color: 'blue',
name: 'Dashboard',
url: 'dashboard.santinova-soft.org',
url: 'dashboard-santinova.santinova-soft.org',
desc: 'Monitoring temps réel',
href: 'https://dashboard.santinova-soft.org',
href: 'https://dashboard-santinova.santinova-soft.org',
},
{
icon: Globe,

View File

@@ -7,49 +7,49 @@ const APPS_CONFIG = [
id: 'itinova-contacts',
name: 'Itinova Contacts',
repoName: 'itinova-contacts',
urlRecette: 'https://contacts.recette.santinova-soft.org',
urlProd: 'https://contacts.santinova-soft.org',
urlRecette: 'https://contacts-itinova.recette.santinova-soft.org',
urlProd: 'https://contacts-itinova.santinova-soft.org',
},
{
id: 'itinova-podcasts',
name: 'Itinova Podcasts',
repoName: 'itinova-podcasts',
urlRecette: 'https://podcasts.recette.santinova-soft.org',
urlProd: 'https://podcasts.santinova-soft.org',
urlRecette: 'https://podcasts-itinova.recette.santinova-soft.org',
urlProd: 'https://podcasts-itinova.santinova-soft.org',
},
{
id: 'veille-reglementaire',
name: 'Veille Réglementaire',
repoName: 'veille-reglementaire',
urlRecette: 'https://veille.recette.santinova-soft.org',
urlProd: 'https://veille.santinova-soft.org',
urlRecette: 'https://veille-itinova.recette.santinova-soft.org',
urlProd: 'https://veille-itinova.santinova-soft.org',
},
{
id: 'itinova-vehicle-exchange',
name: 'Itinova Gestion de Flotte',
repoName: 'itinova-vehicle-exchange',
urlRecette: 'https://flotte.recette.santinova-soft.org',
urlProd: 'https://flotte.santinova-soft.org',
urlRecette: 'https://flotte-itinova.recette.santinova-soft.org',
urlProd: 'https://flotte-itinova.santinova-soft.org',
},
{
id: 'sonum',
name: 'SONUM',
repoName: 'sonum',
urlRecette: 'https://sonum.recette.santinova-soft.org',
urlProd: 'https://sonum.santinova-soft.org',
urlRecette: 'https://sonum-santinova.recette.santinova-soft.org',
urlProd: 'https://sonum-santinova.santinova-soft.org',
},
{
id: 'demat-facturation-dsi',
name: 'Démat. Facturation DSI',
repoName: 'demat-facturation-dsi',
urlRecette: 'https://demat-facturation.recette.santinova-soft.org',
urlProd: 'https://demat-facturation.santinova-soft.org',
urlRecette: 'https://demat-facturation-santinova.recette.santinova-soft.org',
urlProd: 'https://demat-facturation-santinova.santinova-soft.org',
},
{
id: 'facturation-santinova',
name: 'Facturation Santinova',
repoName: 'facturation-santinova',
urlRecette: 'https://facturation.recette.santinova-soft.org',
urlRecette: 'https://facturation-santinova.recette.santinova-soft.org',
urlProd: null,
},
{
@@ -57,28 +57,28 @@ const APPS_CONFIG = [
name: 'Formation Manager',
repoName: 'formation-manager-itinova',
urlRecette: 'https://formation.recette.santinova-soft.org',
urlProd: 'https://formations.itinova.org',
urlProd: 'https://formations-itinova.santinova-soft.org',
},
{
id: 'pilotage-masse-salariale',
name: 'Pilotage Masse Salariale',
repoName: 'pilotage-masse-salariale',
urlRecette: 'https://pilotage-ms.recette.santinova-soft.org',
urlRecette: 'https://pilotage-ms-itinova.recette.santinova-soft.org',
urlProd: null,
},
{
id: 'itinova-budget-si',
name: 'Gestion Budget Informatique',
repoName: 'itinova-budget-si',
urlRecette: 'https://budget-si.recette.santinova-soft.org',
urlRecette: 'https://budget-si-santinova.recette.santinova-soft.org',
urlProd: null,
},
{
id: 'falc-generator',
name: 'FALC Generator',
repoName: 'falc-generator',
urlRecette: 'https://falc.recette.santinova-soft.org',
urlProd: 'https://falc.santinova-soft.org',
urlRecette: 'https://falc-itinova.recette.santinova-soft.org',
urlProd: 'https://falc-itinova.santinova-soft.org',
},
{
id: 'portail-santinova',
@@ -91,8 +91,8 @@ const APPS_CONFIG = [
id: 'manus-dashboard',
name: 'Dashboard Production',
repoName: 'manus-dashboard',
urlRecette: 'https://dashboard.recette.santinova-soft.org',
urlProd: 'https://dashboard.santinova-soft.org',
urlRecette: 'https://dashboard-santinova.recette.santinova-soft.org',
urlProd: 'https://dashboard-santinova.santinova-soft.org',
},
];

View File

@@ -22,7 +22,7 @@ test('le workflow rappelle la séparation des budgets et des accès internes', a
assert.match(source, /Aucune clé fournisseur ni URL Ollama privée/);
assert.match(source, /aria-labelledby="ia-workflow-title"/);
assert.match(source, /aria-label="Modes de décision du moteur IA"/);
assert.match(source, /referentiel-ia\.recette\.santinova-soft\.org/);
assert.match(source, /referentiel-ia-santinova\.recette\.santinova-soft\.org/);
assert.match(source, /llm-local\.recette\.santinova-soft\.org/);
assert.doesNotMatch(source, /http:\/\/ollama:11434/);
});

View File

@@ -4,7 +4,7 @@ import test from 'node:test';
import { getEnvironmentConfig } from '../src/utils/environment.js';
test('détecte le dashboard de recette depuis son domaine public', () => {
const config = getEnvironmentConfig('dashboard.recette.santinova-soft.org');
const config = getEnvironmentConfig('dashboard-santinova.recette.santinova-soft.org');
assert.equal(config.isRecette, true);
assert.equal(config.label, 'Recette');
assert.equal(config.serverPublicIp, '78.138.58.109');
@@ -13,7 +13,7 @@ test('détecte le dashboard de recette depuis son domaine public', () => {
});
test('détecte le dashboard de production depuis son domaine public', () => {
const config = getEnvironmentConfig('dashboard.santinova-soft.org');
const config = getEnvironmentConfig('dashboard-santinova.santinova-soft.org');
assert.equal(config.isRecette, false);
assert.equal(config.label, 'Production');
assert.equal(config.serverPublicIp, '180.149.196.138');