diff --git a/docker-compose.yml b/docker-compose.yml
index 8feb276..70a8294 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -29,7 +29,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.recette.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"
diff --git a/src/backend/src/config.js b/src/backend/src/config.js
index 3a60082..afd7218 100644
--- a/src/backend/src/config.js
+++ b/src/backend/src/config.js
@@ -110,5 +110,20 @@ module.exports = {
healthCheckUrl: 'https://facturation.recette.santinova-soft.org',
port: 3001,
},
+ {
+ id: 'demat-facturation-dsi',
+ name: 'Démat Facturation DSI',
+ description: 'Dématérialisation des factures DSI - Import email/dossier, analyse IA, export SharePoint/PDF, validation BAP',
+ directory: 'demat-facturation-dsi',
+ giteaRepo: 'demat-facturation-dsi',
+ giteaOwner: 'manus-admin',
+ urls: {
+ recette: 'https://demat-facturation.recette.santinova-soft.org',
+ prod: null,
+ },
+ containerName: 'demat-facturation-app',
+ healthCheckUrl: 'https://demat-facturation.recette.santinova-soft.org',
+ port: 3000,
+ },
],
};
diff --git a/src/backend/src/routes.js b/src/backend/src/routes.js
index e83a978..fbd26db 100644
--- a/src/backend/src/routes.js
+++ b/src/backend/src/routes.js
@@ -475,3 +475,76 @@ router.get('/system/metrics', authMiddleware, async (req, res) => {
});
module.exports = router;
+
+// ===== INVENTAIRE DES APPLICATIONS =====
+const { exec } = require('child_process');
+const GITEA_RECETTE_INTERNAL = process.env.GITEA_RECETTE_URL || 'http://gitea:3000';
+const GITEA_PROD_EXTERNAL = 'https://git.santinova-soft.org';
+const GITEA_USER_INV = process.env.GITEA_USERNAME || 'manus-admin';
+const GITEA_PASS_REC = process.env.GITEA_PASSWORD || 'Itinova69!';
+const GITEA_PASS_PRD = process.env.GITEA_PASSWORD_PROD || 'ManusGitea2026!';
+
+const INVENTORY_APPS = [
+ { id: 'itinova-contacts', name: 'Itinova Contacts', repoName: 'itinova-contacts' },
+ { id: 'itinova-podcasts', name: 'Itinova Podcasts', repoName: 'itinova-podcasts' },
+ { id: 'veille-reglementaire', name: 'Veille Réglementaire', repoName: 'veille-reglementaire' },
+ { id: 'itinova-vehicle-exchange', name: 'Itinova Gestion de Flotte', repoName: 'itinova-vehicle-exchange' },
+ { id: 'sonum', name: 'SONUM', repoName: 'sonum' },
+ { id: 'demat-facturation-dsi', name: 'Démat. Facturation DSI', repoName: 'demat-facturation-dsi' },
+ { id: 'facturation-santinova', name: 'Facturation Santinova', repoName: 'facturation-santinova' },
+ { id: 'formation-manager-itinova', name: 'Formation Manager', repoName: 'formation-manager-itinova' },
+ { id: 'portail-santinova', name: 'Portail Applicatif', repoName: 'portail-santinova' },
+ { id: 'manus-dashboard', name: 'Dashboard Manus', repoName: 'manus-dashboard' },
+];
+
+function curlGitea(baseUrl, owner, repo, pass) {
+ return new Promise((resolve) => {
+ const auth = `${GITEA_USER_INV}:${pass}`;
+ const cmd = `curl -sk --max-time 6 -u "${auth}" "${baseUrl}/api/v1/repos/${owner}/${repo}"`;
+ exec(cmd, { timeout: 7000 }, (err, stdout) => {
+ if (err || !stdout) return resolve({ present: false, url: null, version: null });
+ try {
+ const data = JSON.parse(stdout);
+ if (!data.id) return resolve({ present: false, url: null, version: null });
+ // Récupérer le dernier commit
+ const cmd2 = `curl -sk --max-time 6 -u "${auth}" "${baseUrl}/api/v1/repos/${owner}/${repo}/commits?limit=1"`;
+ exec(cmd2, { timeout: 7000 }, (err2, stdout2) => {
+ let version = null;
+ try {
+ const commits = JSON.parse(stdout2 || '[]');
+ if (commits.length > 0) {
+ const c = commits[0];
+ const sha = c.sha ? c.sha.substring(0, 7) : null;
+ const date = c.commit && c.commit.author && c.commit.author.date
+ ? new Date(c.commit.author.date).toLocaleDateString('fr-FR') : null;
+ version = sha && date ? `${sha} (${date})` : sha;
+ }
+ } catch(e) {}
+ resolve({ present: true, url: `${baseUrl}/${owner}/${repo}`, version });
+ });
+ } catch(e) {
+ resolve({ present: false, url: null, version: null });
+ }
+ });
+ });
+}
+
+router.get('/inventory', authMiddleware, async (req, res) => {
+ try {
+ const owner = GITEA_USER_INV;
+ const results = await Promise.all(
+ INVENTORY_APPS.map(async (app) => {
+ const [repoRecette, repoProd] = await Promise.all([
+ curlGitea(GITEA_RECETTE_INTERNAL, owner, app.repoName, GITEA_PASS_REC),
+ curlGitea(GITEA_PROD_EXTERNAL, owner, app.repoName, GITEA_PASS_PRD),
+ ]);
+ return { ...app, repoRecette, repoProd };
+ })
+ );
+ res.json(results);
+ } catch (err) {
+ console.error('Erreur /inventory:', err.message);
+ res.status(500).json({ error: err.message });
+ }
+});
+;
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index 8134de9..1c9a8f0 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -11,21 +11,25 @@ services:
- JWT_SECRET=manus-dashboard-jwt-secret-2026-recette
- ADMIN_USERNAME=adminItinova
- ADMIN_PASSWORD=Itinova69!
- - GITEA_URL=https://git.santinova-soft.org
+ - GITEA_URL=https://git.recette.santinova-soft.org
- GITEA_USERNAME=manus-admin
- - GITEA_PASSWORD=ManusGitea2026!
+ - GITEA_PASSWORD=Itinova69!
+ - GITEA_RECETTE_URL=http://gitea:3000
+ - GITEA_PASSWORD_PROD=ManusGitea2026!
- APPS_BASE_PATH=/opt/manus-deploy/apps
- INFRA_BASE_PATH=/opt/manus-deploy/infrastructure
- HEALTH_CHECK_INTERVAL=30000
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/manus-deploy:/opt/manus-deploy
+ extra_hosts:
+ - "git.santinova-soft.org:180.149.196.138"
networks:
- web
labels:
- "traefik.enable=true"
- # Route HTTPS via dashboard.santinova-soft.org
- - "traefik.http.routers.manus-dashboard.rule=Host(`dashboard.santinova-soft.org`)"
+ # Route HTTPS via dashboard.recette.santinova-soft.org
+ - "traefik.http.routers.manus-dashboard.rule=Host(`dashboard.recette.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"
diff --git a/src/frontend/public/infra_schema_v2.png b/src/frontend/public/infra_schema_v2.png
new file mode 100644
index 0000000..43fb240
Binary files /dev/null and b/src/frontend/public/infra_schema_v2.png differ
diff --git a/src/frontend/src/App.jsx b/src/frontend/src/App.jsx
index 9c415b8..6642b20 100644
--- a/src/frontend/src/App.jsx
+++ b/src/frontend/src/App.jsx
@@ -6,16 +6,16 @@ import MonitoringPage from './pages/MonitoringPage';
import DeploymentsPage from './pages/DeploymentsPage';
import GiteaPage from './pages/GiteaPage';
import DockerPage from './pages/DockerPage';
+import DocumentationPage from './pages/DocumentationPage';
+import InventairePage from './pages/InventairePage';
import Sidebar from './components/Sidebar';
import useWebSocket from './hooks/useWebSocket';
import { getMe, getApps, logout as apiLogout } from './utils/api';
-
export default function App() {
const [authenticated, setAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState('dashboard');
const [apps, setApps] = useState([]);
-
// Vérifier l'authentification au chargement
useEffect(() => {
const token = localStorage.getItem('dashboard_token');
@@ -34,7 +34,6 @@ export default function App() {
setLoading(false);
}
}, []);
-
const fetchApps = async () => {
try {
const res = await getApps();
@@ -43,23 +42,19 @@ export default function App() {
console.error('Erreur chargement apps:', err);
}
};
-
// WebSocket handler
const handleWsMessage = useCallback((data) => {
if (data.type === 'health_update') {
setApps(data.data);
}
}, []);
-
const { connected: wsConnected } = useWebSocket(
authenticated ? handleWsMessage : null
);
-
const handleLogin = (data) => {
setAuthenticated(true);
fetchApps();
};
-
const handleLogout = async () => {
try {
await apiLogout();
@@ -70,7 +65,6 @@ export default function App() {
setAuthenticated(false);
setApps([]);
};
-
if (loading) {
return (
@@ -96,11 +90,9 @@ export default function App() {
);
}
-
if (!authenticated) {
return ;
}
-
const renderPage = () => {
switch (currentPage) {
case 'dashboard':
@@ -115,11 +107,14 @@ export default function App() {
return ;
case 'monitoring':
return ;
+ case 'documentation':
+ return ;
+ case 'inventaire':
+ return ;
default:
return ;
}
};
-
return (
-
{/* Navigation */}
-