#!/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;