feat: aligner le dashboard production sur recette
This commit is contained in:
188
backend/src/healthcheck.js
Normal file
188
backend/src/healthcheck.js
Normal file
@@ -0,0 +1,188 @@
|
||||
const axios = require('axios');
|
||||
const https = require('https');
|
||||
const config = require('./config');
|
||||
const { getContainerInfo } = require('./docker');
|
||||
|
||||
// Store pour les statuts des apps
|
||||
const appStatuses = new Map();
|
||||
|
||||
// Store pour les logs de déploiement
|
||||
const deploymentLogs = [];
|
||||
const MAX_DEPLOYMENT_LOGS = 50;
|
||||
|
||||
// Store pour les commits Gitea
|
||||
const giteaCommits = new Map();
|
||||
|
||||
// Agent HTTPS qui ignore les certificats auto-signés
|
||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
||||
|
||||
// Au démarrage : marquer tous les déploiements "running" orphelins comme "failed"
|
||||
// (ils ont été interrompus lors du dernier redémarrage du Dashboard)
|
||||
function cleanupOrphanedDeployments() {
|
||||
const orphans = deploymentLogs.filter((l) => l.status === 'running');
|
||||
if (orphans.length > 0) {
|
||||
console.log(`[healthcheck] Nettoyage de ${orphans.length} déploiement(s) orphelin(s) au démarrage`);
|
||||
orphans.forEach((l) => {
|
||||
l.status = 'failed';
|
||||
l.message = 'Déploiement interrompu (redémarrage du Dashboard)';
|
||||
l.endedAt = new Date().toISOString();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifier la santé d'une URL
|
||||
*/
|
||||
async function checkUrl(url) {
|
||||
try {
|
||||
const start = Date.now();
|
||||
const response = await axios.get(url, {
|
||||
timeout: 10000,
|
||||
httpsAgent,
|
||||
validateStatus: (status) => status < 500,
|
||||
});
|
||||
const responseTime = Date.now() - start;
|
||||
return {
|
||||
online: true,
|
||||
statusCode: response.status,
|
||||
responseTime,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
online: false,
|
||||
statusCode: null,
|
||||
responseTime: null,
|
||||
error: err.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifier la santé d'une application
|
||||
*/
|
||||
async function checkApp(appConfig) {
|
||||
const result = {
|
||||
id: appConfig.id,
|
||||
name: appConfig.name,
|
||||
description: appConfig.description,
|
||||
urls: appConfig.urls,
|
||||
containerName: appConfig.containerName,
|
||||
lastCheck: new Date().toISOString(),
|
||||
status: 'unknown',
|
||||
container: null,
|
||||
health: {
|
||||
recette: null,
|
||||
prod: null,
|
||||
},
|
||||
};
|
||||
|
||||
// Vérifier le conteneur Docker
|
||||
const containerInfo = await getContainerInfo(appConfig.containerName);
|
||||
result.container = containerInfo;
|
||||
|
||||
// Health check recette
|
||||
if (appConfig.urls.recette) {
|
||||
result.health.recette = await checkUrl(appConfig.urls.recette);
|
||||
}
|
||||
|
||||
// Health check prod
|
||||
if (appConfig.urls.prod) {
|
||||
result.health.prod = await checkUrl(appConfig.urls.prod);
|
||||
}
|
||||
|
||||
// Déterminer le statut global
|
||||
if (!containerInfo || !containerInfo.running) {
|
||||
result.status = 'offline';
|
||||
} else if (result.health.recette && result.health.recette.online) {
|
||||
result.status = 'online';
|
||||
} else if (containerInfo.running) {
|
||||
result.status = 'error';
|
||||
} else {
|
||||
result.status = 'offline';
|
||||
}
|
||||
|
||||
// Stocker le résultat
|
||||
appStatuses.set(appConfig.id, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lancer les health checks pour toutes les apps
|
||||
*/
|
||||
async function checkAllApps() {
|
||||
const results = [];
|
||||
for (const app of config.apps) {
|
||||
const result = await checkApp(app);
|
||||
results.push(result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajouter un log de déploiement (retourne l'entrée créée avec son id)
|
||||
*/
|
||||
function addDeploymentLog(appId, log) {
|
||||
const entry = {
|
||||
id: Date.now().toString(),
|
||||
appId,
|
||||
timestamp: new Date().toISOString(),
|
||||
...log,
|
||||
};
|
||||
deploymentLogs.unshift(entry);
|
||||
if (deploymentLogs.length > MAX_DEPLOYMENT_LOGS) {
|
||||
deploymentLogs.pop();
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour un log de déploiement existant par son id
|
||||
* Permet de passer de "running" à "success" ou "failed" sans créer une nouvelle entrée
|
||||
*/
|
||||
function updateDeploymentLog(entryId, updates) {
|
||||
const entry = deploymentLogs.find((l) => l.id === entryId);
|
||||
if (entry) {
|
||||
Object.assign(entry, updates, { updatedAt: new Date().toISOString() });
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les logs de déploiement
|
||||
*/
|
||||
function getDeploymentLogs(appId = null) {
|
||||
if (appId) {
|
||||
return deploymentLogs.filter((l) => l.appId === appId);
|
||||
}
|
||||
return deploymentLogs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer le statut d'une app
|
||||
*/
|
||||
function getAppStatus(appId) {
|
||||
return appStatuses.get(appId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer tous les statuts
|
||||
*/
|
||||
function getAllStatuses() {
|
||||
return Array.from(appStatuses.values());
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkUrl,
|
||||
checkApp,
|
||||
checkAllApps,
|
||||
addDeploymentLog,
|
||||
updateDeploymentLog,
|
||||
getDeploymentLogs,
|
||||
getAppStatus,
|
||||
getAllStatuses,
|
||||
cleanupOrphanedDeployments,
|
||||
appStatuses,
|
||||
giteaCommits,
|
||||
};
|
||||
Reference in New Issue
Block a user