206 lines
6.8 KiB
JavaScript

#!/usr/bin/env node
const express = require('express');
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
const app = express();
const PORT = 3006;
// Middleware CORS
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
// Services à surveiller
const services = [
{ name: 'Bitcoin Signet', container: 'bitcoin', port: 8332, protocol: 'RPC' },
{ name: 'BlindBit Oracle', container: 'blindbit', port: 8000, protocol: 'HTTP' },
{ name: 'SDK Relay', container: 'sdk_relay', port: 8090, protocol: 'WebSocket' },
{ name: 'SDK Storage', container: 'sdk_storage', port: 8080, protocol: 'HTTP' },
{ name: 'LeCoffre Backend', container: 'lecoffre-back', port: 8080, protocol: 'HTTP' },
{ name: 'LeCoffre Frontend', container: 'lecoffre-front', port: 3000, protocol: 'HTTP' },
{ name: 'IHM Client', container: 'ihm_client', port: 3001, protocol: 'HTTP' },
{ name: 'Tor Proxy', container: 'tor-proxy', port: 9050, protocol: 'SOCKS' },
{ name: 'Grafana', container: 'grafana', port: 3000, protocol: 'HTTP' },
{ name: 'Loki', container: 'loki', port: 3100, protocol: 'HTTP' },
{ name: 'Promtail', container: 'promtail', port: 9080, protocol: 'HTTP' },
{ name: 'Miner Signet', container: 'signet_miner', port: null, protocol: 'Bitcoin' }
];
const externalServices = [
{ name: 'Mempool Signet', url: 'https://mempool2.4nkweb.com', protocol: 'HTTPS' },
{ name: 'Relay Bootstrap', url: 'wss://dev3.4nkweb.com/ws/', protocol: 'WebSocket' },
{ name: 'Signer Bootstrap', url: 'https://dev3.4nkweb.com', protocol: 'HTTPS' },
{ name: 'Git Repository', url: 'git.4nkweb.com', protocol: 'SSH' }
];
async function getDockerContainers() {
try {
const { stdout } = await execAsync('docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"');
return stdout;
} catch (error) {
console.error('Erreur lors de la récupération des conteneurs:', error);
return '';
}
}
async function getContainerInfo(containerName) {
try {
const { stdout } = await execAsync(`docker inspect ${containerName} --format '{{.State.Status}}|{{.State.StartedAt}}|{{.Config.Image}}|{{.NetworkSettings.IPAddress}}|{{range $port, $binding := .NetworkSettings.Ports}}{{$port}}={{range $binding}}{{.HostIP}}:{{.HostPort}} {{end}}{{end}}'`);
return stdout.trim();
} catch (error) {
return 'stopped||N/A|N/A|';
}
}
async function getServiceHealth(containerName, port) {
if (!port) return 'unknown';
try {
const { stdout } = await execAsync(`docker exec ${containerName} wget -q --spider -O- http://localhost:${port}/health 2>/dev/null || echo "unhealthy"`);
return stdout.trim() === 'unhealthy' ? 'unhealthy' : 'healthy';
} catch (error) {
return 'unknown';
}
}
async function checkExternalService(url) {
const start = Date.now();
try {
// Skip WebSocket URLs for now
if (url.startsWith('wss://') || url.startsWith('ws://')) {
return {
status: 'running',
response_time: 'N/A (WebSocket)'
};
}
const http = require('http');
const https = require('https');
const urlObj = new URL(url);
const client = urlObj.protocol === 'https:' ? https : http;
return new Promise((resolve) => {
const req = client.get(url, { timeout: 5000 }, (res) => {
resolve({
status: 'running',
response_time: `${Date.now() - start}ms`
});
});
req.on('error', () => {
resolve({
status: 'error',
response_time: 'N/A'
});
});
req.on('timeout', () => {
resolve({
status: 'timeout',
response_time: '>5s'
});
});
});
} catch (error) {
return {
status: 'error',
response_time: 'N/A'
};
}
}
function calculateUptime(startedAt) {
if (!startedAt || startedAt === 'N/A') return 'N/A';
try {
const start = new Date(startedAt);
const now = new Date();
const diff = now - start;
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
if (days > 0) return `${days}j ${hours}h ${minutes}m`;
if (hours > 0) return `${hours}h ${minutes}m`;
return `${minutes}m`;
} catch (error) {
return 'N/A';
}
}
async function getServicesStatus() {
const servicesStatus = [];
for (const service of services) {
const containerInfo = await getContainerInfo(service.container);
const [status, startedAt, image, ip, ports] = containerInfo.split('|');
const portsArray = ports ? ports.split(' ').filter(p => p.trim()) : [];
const health = await getServiceHealth(service.container, service.port);
const uptime = calculateUptime(startedAt);
servicesStatus.push({
name: service.name,
status: status === 'running' ? 'running' : 'stopped',
image: image || 'N/A',
ip: ip || 'N/A',
ports: portsArray,
uptime: uptime,
health: health,
protocol: service.protocol,
port: service.port
});
}
return servicesStatus;
}
async function getExternalServicesStatus() {
const externalStatus = [];
for (const service of externalServices) {
const status = await checkExternalService(service.url);
externalStatus.push({
name: service.name,
url: service.url,
protocol: service.protocol,
...status
});
}
return externalStatus;
}
app.get('/api', async (req, res) => {
try {
const services = await getServicesStatus();
const external = await getExternalServicesStatus();
res.json({
timestamp: new Date().toISOString(),
services: services,
external: external
});
} catch (error) {
console.error('Erreur API:', error);
res.status(500).json({ error: 'Erreur interne du serveur' });
}
});
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`🚀 API Status démarrée sur http://0.0.0.0:${PORT}`);
});
module.exports = app;