From 0572c4dff60fef450b978bb8fc6c752ba0d9b806 Mon Sep 17 00:00:00 2001
From: Manus Dashboard Automation
Date: Sat, 5 Sep 2026 02:25:03 +0000
Subject: [PATCH] =?UTF-8?q?fix:=20partager=20le=20m=C3=AAme=20commit=20ent?=
=?UTF-8?q?re=20recette=20et=20production?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitea/workflows/validate.yml | 1 +
docker-compose.yml | 28 +++++++++------
src/frontend/index.html | 2 +-
src/frontend/package.json | 1 +
src/frontend/src/components/AddAppModal.jsx | 11 +++---
src/frontend/src/components/Sidebar.jsx | 3 +-
src/frontend/src/pages/DashboardPage.jsx | 7 ++--
src/frontend/src/pages/GiteaPage.jsx | 5 +--
src/frontend/src/pages/LoginPage.jsx | 5 +--
src/frontend/src/pages/TerminalPage.jsx | 7 ++--
src/frontend/src/utils/environment.js | 39 +++++++++++++++++++++
src/frontend/test/environment.test.mjs | 27 ++++++++++++++
12 files changed, 108 insertions(+), 28 deletions(-)
create mode 100644 src/frontend/src/utils/environment.js
create mode 100644 src/frontend/test/environment.test.mjs
diff --git a/.gitea/workflows/validate.yml b/.gitea/workflows/validate.yml
index 501d730..7d0f583 100644
--- a/.gitea/workflows/validate.yml
+++ b/.gitea/workflows/validate.yml
@@ -22,4 +22,5 @@ jobs:
working-directory: src/frontend
run: |
npm ci
+ npm test
npm run build
diff --git a/docker-compose.yml b/docker-compose.yml
index 1de61c9..a454dd0 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,17 +9,22 @@ services:
# Le dashboard doit repartir seul après un arrêt anormal du processus.
restart: always
mem_limit: 512m
+ env_file:
+ - .env
environment:
- - NODE_ENV=production
- - DASHBOARD_ENV=recette
- - PORT=3001
- - JWT_SECRET=manus-dashboard-jwt-secret-2026-recette
- - ADMIN_USERNAME=adminItinova
- - ADMIN_PASSWORD=Itinova69!
- - GITEA_URL=https://git.recette.santinova-soft.org
- - GITEA_USERNAME=manus-admin
+ - NODE_ENV=${NODE_ENV:-production}
+ - DASHBOARD_ENV=${DASHBOARD_ENV:-recette}
+ - PORT=${PORT:-3001}
+ - JWT_SECRET=${JWT_SECRET:?JWT_SECRET requis dans le fichier .env serveur}
+ - ADMIN_USERNAME=${ADMIN_USERNAME:?ADMIN_USERNAME requis dans le fichier .env serveur}
+ - ADMIN_PASSWORD=${ADMIN_PASSWORD:?ADMIN_PASSWORD requis dans le fichier .env serveur}
+ - GITEA_URL=${GITEA_URL:?GITEA_URL requis dans le fichier .env serveur}
+ - GITEA_USERNAME=${GITEA_USERNAME:-manus-admin}
- GITEA_TOKEN=${GITEA_TOKEN:?GITEA_TOKEN requis dans le fichier .env serveur}
- - GITEA_RECETTE_URL=http://gitea:3000
+ - GITEA_RECETTE_URL=${GITEA_RECETTE_URL:-http://gitea:3000}
+ - GITEA_RECETTE_PUBLIC_URL=${GITEA_RECETTE_PUBLIC_URL:-https://git.recette.santinova-soft.org}
+ - GITEA_RECETTE_TOKEN=${GITEA_RECETTE_TOKEN:-}
+ - GITEA_PROD_URL=${GITEA_PROD_URL:-https://git.santinova-soft.org}
- 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}
@@ -35,12 +40,13 @@ services:
- /opt/manus-deploy:/opt/manus-deploy
extra_hosts:
- "git.santinova-soft.org:180.149.196.138"
+ - "git.recette.santinova-soft.org:78.138.58.109"
networks:
- web
labels:
- "traefik.enable=true"
- # Route HTTPS via dashboard.recette.santinova-soft.org
- - "traefik.http.routers.manus-dashboard.rule=Host(`dashboard.recette.santinova-soft.org`)"
+ # Le domaine public est spécifique au serveur, mais le commit reste commun.
+ - "traefik.http.routers.manus-dashboard.rule=Host(`${DASHBOARD_DOMAIN:?DASHBOARD_DOMAIN requis dans le fichier .env serveur}`)"
- "traefik.http.routers.manus-dashboard.entrypoints=websecure"
- "traefik.http.routers.manus-dashboard.tls=true"
- "traefik.http.routers.manus-dashboard.tls.certresolver=letsencrypt"
diff --git a/src/frontend/index.html b/src/frontend/index.html
index 4b7f321..2cbebf5 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -4,7 +4,7 @@
- Dashboard Recette - Gestion des Applications
+ Dashboard - Gestion des Applications
diff --git a/src/frontend/package.json b/src/frontend/package.json
index c395304..dca03f8 100644
--- a/src/frontend/package.json
+++ b/src/frontend/package.json
@@ -5,6 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
+ "test": "node --test test/*.test.mjs",
"build": "vite build",
"preview": "vite preview"
},
diff --git a/src/frontend/src/components/AddAppModal.jsx b/src/frontend/src/components/AddAppModal.jsx
index 9c17ff0..459ed2d 100644
--- a/src/frontend/src/components/AddAppModal.jsx
+++ b/src/frontend/src/components/AddAppModal.jsx
@@ -14,6 +14,7 @@ import {
ChevronDown,
} from 'lucide-react';
import { getAvailableStacks, createApp, getGiteaRepos } from '../utils/api';
+import { APP_DOMAIN_SUFFIX, ENVIRONMENT_LABEL_LOWER } from '../utils/environment';
const STEPS = [
{ id: 1, title: 'Informations', icon: FileText },
@@ -180,7 +181,7 @@ export default function AddAppModal({ isOpen, onClose, onSuccess }) {
Ajouter une application
- Déployez une nouvelle application sur le serveur de recette
+ Déployez une nouvelle application sur le serveur de {ENVIRONMENT_LABEL_LOWER}
- .recette.santinova-soft.org
+ .{APP_DOMAIN_SUFFIX}
{errors.subdomain && (
@@ -280,7 +281,7 @@ export default function AddAppModal({ isOpen, onClose, onSuccess }) {
{formData.subdomain && !errors.subdomain && (
- URL: https://{formData.subdomain}.recette.santinova-soft.org
+ URL: https://{formData.subdomain}.{APP_DOMAIN_SUFFIX}
)}
@@ -545,7 +546,7 @@ export default function AddAppModal({ isOpen, onClose, onSuccess }) {
L'application est en cours de déploiement. Suivez la progression dans l'onglet Déploiements.
- URL: https://{formData.subdomain}.recette.santinova-soft.org
+ URL: https://{formData.subdomain}.{APP_DOMAIN_SUFFIX}
) : (
@@ -573,7 +574,7 @@ export default function AddAppModal({ isOpen, onClose, onSuccess }) {
URL
- https://{formData.subdomain}.recette.santinova-soft.org
+ https://{formData.subdomain}.{APP_DOMAIN_SUFFIX}
diff --git a/src/frontend/src/components/Sidebar.jsx b/src/frontend/src/components/Sidebar.jsx
index 199c992..956a7db 100644
--- a/src/frontend/src/components/Sidebar.jsx
+++ b/src/frontend/src/components/Sidebar.jsx
@@ -12,6 +12,7 @@ import { LayoutList, TerminalSquare, ShieldCheck,
Activity,
BookOpen,
} from 'lucide-react';
+import { ENVIRONMENT_LABEL } from '../utils/environment';
const navItems = [
{ id: 'dashboard', label: 'Tableau de bord', icon: LayoutDashboard },
{ id: 'apps', label: 'Applications', icon: Box },
@@ -40,7 +41,7 @@ export default function Sidebar({
Dashboard
-
Recette
+
{ENVIRONMENT_LABEL}
diff --git a/src/frontend/src/pages/DashboardPage.jsx b/src/frontend/src/pages/DashboardPage.jsx
index 05a1757..3073dbb 100644
--- a/src/frontend/src/pages/DashboardPage.jsx
+++ b/src/frontend/src/pages/DashboardPage.jsx
@@ -13,6 +13,7 @@ import {
import { getApps, getSystemInfo } from '../utils/api';
import StatusBadge from '../components/StatusBadge';
import { sortAppsWithInfraFirst } from '../utils/appSorting';
+import { ENVIRONMENT_LABEL, GITEA_HOST, GITEA_URL, SERVER_PUBLIC_IP } from '../utils/environment';
function StatCard({ icon: Icon, label, value, color, subtext }) {
return (
@@ -101,7 +102,7 @@ export default function DashboardPage({ apps }) {
Serveur
- 78.138.58.109 (Recette)
+ {SERVER_PUBLIC_IP} ({ENVIRONMENT_LABEL})
@@ -114,12 +115,12 @@ export default function DashboardPage({ apps }) {
diff --git a/src/frontend/src/pages/GiteaPage.jsx b/src/frontend/src/pages/GiteaPage.jsx
index 162c29c..c6f2654 100644
--- a/src/frontend/src/pages/GiteaPage.jsx
+++ b/src/frontend/src/pages/GiteaPage.jsx
@@ -19,6 +19,7 @@ import {
ShieldCheck,
} from 'lucide-react';
import { approveGiteaTokenRotation, getGiteaRepos, getGiteaCommits, getGiteaTokenRotation, getGiteaWorkflowRuns, getGiteaWorkflowSummary, prepareGiteaTokenRotation } from '../utils/api';
+import { GITEA_HOST, GITEA_URL } from '../utils/environment';
function formatDate(dateStr) {
if (!dateStr) return 'N/A';
@@ -137,12 +138,12 @@ export default function GiteaPage() {
Explorez les dépôts sur{' '}
- git.recette.santinova-soft.org
+ {GITEA_HOST}
diff --git a/src/frontend/src/pages/LoginPage.jsx b/src/frontend/src/pages/LoginPage.jsx
index 2cda7e6..6f61e52 100644
--- a/src/frontend/src/pages/LoginPage.jsx
+++ b/src/frontend/src/pages/LoginPage.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Lock, User, AlertCircle, Server } from 'lucide-react';
import { login } from '../utils/api';
+import { ENVIRONMENT_LABEL, ENVIRONMENT_LABEL_LOWER } from '../utils/environment';
export default function LoginPage({ onLogin }) {
const [username, setUsername] = useState('');
@@ -34,7 +35,7 @@ export default function LoginPage({ onLogin }) {
-
Dashboard Recette
+
Dashboard {ENVIRONMENT_LABEL}
Gestion des applications
@@ -130,7 +131,7 @@ export default function LoginPage({ onLogin }) {
- Santinova Soft — Serveur de recette
+ Santinova Soft — Serveur de {ENVIRONMENT_LABEL_LOWER}
diff --git a/src/frontend/src/pages/TerminalPage.jsx b/src/frontend/src/pages/TerminalPage.jsx
index d1cf1d4..d1a9e40 100644
--- a/src/frontend/src/pages/TerminalPage.jsx
+++ b/src/frontend/src/pages/TerminalPage.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Terminal, Wifi, WifiOff, X, Plus, ChevronDown } from 'lucide-react';
+import { DEFAULT_SSH_PRESET, ENVIRONMENT_LABEL, SERVER_PUBLIC_IP } from '../utils/environment';
// Serveurs SSH prédéfinis
const PRESET_SERVERS = [
@@ -30,8 +31,8 @@ export default function TerminalPage() {
// Formulaire de connexion
const [form, setForm] = useState({
- preset: 'recette',
- host: '78.138.58.109',
+ preset: DEFAULT_SSH_PRESET,
+ host: SERVER_PUBLIC_IP,
port: 22,
username: 'root',
password: '',
@@ -116,7 +117,7 @@ export default function TerminalPage() {
fitAddonRef.current = fitAddon;
term.writeln('\x1b[1;34m╔══════════════════════════════════════════════════╗\x1b[0m');
- term.writeln('\x1b[1;34m║ Terminal SSH — Dashboard Recette Santinova ║\x1b[0m');
+ term.writeln(`\x1b[1;34m║ Terminal SSH — Dashboard ${ENVIRONMENT_LABEL} Santinova ║\x1b[0m`);
term.writeln('\x1b[1;34m╚══════════════════════════════════════════════════╝\x1b[0m');
term.writeln('');
term.writeln('\x1b[90mSélectionnez un serveur et connectez-vous pour démarrer.\x1b[0m');
diff --git a/src/frontend/src/utils/environment.js b/src/frontend/src/utils/environment.js
new file mode 100644
index 0000000..18975c9
--- /dev/null
+++ b/src/frontend/src/utils/environment.js
@@ -0,0 +1,39 @@
+/**
+ * Identité de l'environnement courant, déterminée par le domaine public.
+ * Le même artefact frontend peut ainsi être construit et exécuté en recette
+ * comme en production sans branche ni modification de code spécifique.
+ */
+export function getEnvironmentConfig(hostname) {
+ const normalizedHostname = hostname.toLowerCase();
+ const isRecette =
+ normalizedHostname.includes('recette') ||
+ normalizedHostname === 'localhost' ||
+ normalizedHostname === '127.0.0.1';
+
+ return {
+ isRecette,
+ label: isRecette ? 'Recette' : 'Production',
+ labelLower: isRecette ? 'recette' : 'production',
+ serverPublicIp: isRecette ? '78.138.58.109' : '180.149.196.138',
+ giteaUrl: isRecette
+ ? 'https://git.recette.santinova-soft.org'
+ : 'https://git.santinova-soft.org',
+ appDomainSuffix: isRecette
+ ? 'recette.santinova-soft.org'
+ : 'santinova-soft.org',
+ defaultSshPreset: isRecette ? 'recette' : 'production',
+ };
+}
+
+const currentHostname = typeof window === 'undefined' ? 'localhost' : window.location.hostname;
+const environment = getEnvironmentConfig(currentHostname);
+
+export const IS_RECETTE = environment.isRecette;
+export const ENVIRONMENT_LABEL = environment.label;
+export const ENVIRONMENT_LABEL_LOWER = environment.labelLower;
+export const SERVER_PUBLIC_IP = environment.serverPublicIp;
+export const GITEA_URL = environment.giteaUrl;
+export const GITEA_HOST = new URL(GITEA_URL).hostname;
+export const APP_DOMAIN_SUFFIX = environment.appDomainSuffix;
+export const DEFAULT_SSH_PRESET = environment.defaultSshPreset;
+
diff --git a/src/frontend/test/environment.test.mjs b/src/frontend/test/environment.test.mjs
new file mode 100644
index 0000000..4e149ec
--- /dev/null
+++ b/src/frontend/test/environment.test.mjs
@@ -0,0 +1,27 @@
+import assert from 'node:assert/strict';
+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');
+ assert.equal(config.isRecette, true);
+ assert.equal(config.label, 'Recette');
+ assert.equal(config.serverPublicIp, '78.138.58.109');
+ assert.equal(config.giteaUrl, 'https://git.recette.santinova-soft.org');
+ assert.equal(config.appDomainSuffix, 'recette.santinova-soft.org');
+});
+
+test('détecte le dashboard de production depuis son domaine public', () => {
+ const config = getEnvironmentConfig('dashboard.santinova-soft.org');
+ assert.equal(config.isRecette, false);
+ assert.equal(config.label, 'Production');
+ assert.equal(config.serverPublicIp, '180.149.196.138');
+ assert.equal(config.giteaUrl, 'https://git.santinova-soft.org');
+ assert.equal(config.appDomainSuffix, 'santinova-soft.org');
+});
+
+test('utilise la recette pour le développement local', () => {
+ assert.equal(getEnvironmentConfig('localhost').isRecette, true);
+ assert.equal(getEnvironmentConfig('127.0.0.1').isRecette, true);
+});