Fix jwt-token

This commit is contained in:
2025-09-14 18:21:45 +02:00
parent b795af5561
commit be27f8723b
4 changed files with 435 additions and 164 deletions

View File

@@ -203,24 +203,24 @@ router.get('/system-info', async (req, res) => {
}
// Fallback to Docker stats for ALL containers (not just our apps)
try {
const { stdout } = await execAsync('docker stats --no-stream --format "table {{.Container}}\\t{{.CPUPerc}}\\t{{.MemUsage}}\\t{{.MemPerc}}\\t{{.NetIO}}\\t{{.BlockIO}}"');
const lines = stdout.trim().split('\n').slice(1);
lines.forEach(line => {
const [container, cpu, memUsage, memPerc, netIO, blockIO] = line.split('\t');
if (container && cpu) {
// Determine container type
let type = 'unknown';
if (container.includes('postgres') || container.includes('mysql') || container.includes('mongo')) type = 'database';
else if (container.includes('redis') || container.includes('memcached')) type = 'cache';
else if (container.includes('nginx') || container.includes('proxy') || container.includes('traefik')) type = 'proxy';
else if (container.includes('drone-detection') || container.includes('uamils')) type = 'application';
else if (container.includes('elasticsearch') || container.includes('kibana') || container.includes('logstash')) type = 'logging';
else if (container.includes('prometheus') || container.includes('grafana')) type = 'monitoring';
// If we don't have health endpoint data, use docker stats
if (!containerMetrics[container]) {
if (Object.keys(containerMetrics).length === 0 || Object.values(containerMetrics).every(m => m.status === 'unreachable')) {
try {
const { stdout } = await execAsync('docker stats --no-stream --format "table {{.Container}}\\t{{.CPUPerc}}\\t{{.MemUsage}}\\t{{.MemPerc}}\\t{{.NetIO}}\\t{{.BlockIO}}"');
const lines = stdout.trim().split('\n').slice(1);
lines.forEach(line => {
const [container, cpu, memUsage, memPerc, netIO, blockIO] = line.split('\t');
if (container && cpu) {
// Determine container type
let type = 'unknown';
const name = container.toLowerCase();
if (name.includes('postgres') || name.includes('mysql') || name.includes('mongo')) type = 'database';
else if (name.includes('redis') || name.includes('memcached')) type = 'cache';
else if (name.includes('nginx') || name.includes('proxy') || name.includes('traefik')) type = 'proxy';
else if (name.includes('drone-detection') || name.includes('uamils')) type = 'application';
else if (name.includes('elasticsearch') || name.includes('kibana') || name.includes('logstash')) type = 'logging';
else if (name.includes('prometheus') || name.includes('grafana')) type = 'monitoring';
containerMetrics[container] = {
cpu: cpu,
memory: { usage: memUsage, percentage: memPerc },
@@ -229,72 +229,82 @@ router.get('/system-info', async (req, res) => {
type: type,
source: 'docker_stats'
};
} else {
// Enhance existing health data with docker stats
containerMetrics[container] = {
...containerMetrics[container],
cpu: cpu,
memory: { usage: memUsage, percentage: memPerc },
network: netIO,
disk: blockIO
};
}
}
});
} catch (dockerError) {
console.log('Docker stats failed, trying docker compose...');
// Try container inspection via docker compose
try {
const { stdout: composeStatus } = await execAsync('docker-compose ps --format json');
const containers = JSON.parse(`[${composeStatus.split('\n').filter(line => line.trim()).join(',')}]`);
containers.forEach(container => {
if (container.Name && !containerMetrics[container.Name]) {
let type = 'unknown';
const name = container.Name.toLowerCase();
if (name.includes('postgres') || name.includes('mysql') || name.includes('mongo')) type = 'database';
else if (name.includes('redis') || name.includes('memcached')) type = 'cache';
else if (name.includes('nginx') || name.includes('proxy')) type = 'proxy';
else if (name.includes('drone-detection') || name.includes('uamils')) type = 'application';
containerMetrics[container.Name] = {
status: container.State,
health: container.Health || 'unknown',
ports: container.Ports,
type: type,
source: 'docker_compose'
};
}
});
} catch (composeError) {
// Final fallback - try to detect containers via process list
} catch (dockerError) {
console.log('Docker stats failed, trying compose and processes...');
// Try container inspection via docker compose
try {
const { stdout: processes } = await execAsync('ps aux | grep -E "(postgres|redis|nginx|docker)" | grep -v grep');
const processLines = processes.split('\n').filter(line => line.trim());
const { stdout: composeStatus } = await execAsync('docker-compose ps --services 2>/dev/null || docker compose ps --services 2>/dev/null');
const services = composeStatus.trim().split('\n').filter(s => s.trim());
const detectedServices = {};
processLines.forEach(line => {
if (line.includes('postgres')) detectedServices['postgres-process'] = { status: 'running', type: 'database', source: 'process_list' };
if (line.includes('redis')) detectedServices['redis-process'] = { status: 'running', type: 'cache', source: 'process_list' };
if (line.includes('nginx')) detectedServices['nginx-process'] = { status: 'running', type: 'proxy', source: 'process_list' };
});
if (Object.keys(detectedServices).length > 0) {
containerMetrics = { ...containerMetrics, ...detectedServices };
} else {
if (services.length > 0) {
for (const service of services) {
let type = 'unknown';
const name = service.toLowerCase();
if (name.includes('postgres') || name.includes('mysql') || name.includes('mongo') || name.includes('db')) type = 'database';
else if (name.includes('redis') || name.includes('cache')) type = 'cache';
else if (name.includes('nginx') || name.includes('proxy')) type = 'proxy';
else if (name.includes('drone-detection') || name.includes('uamils') || name.includes('app') || name.includes('backend') || name.includes('frontend')) type = 'application';
containerMetrics[service] = {
status: 'detected',
health: 'unknown',
type: type,
source: 'docker_compose_services'
};
}
}
} catch (composeError) {
// Final fallback - try to detect running services via different methods
try {
// Check for common database ports
const portChecks = [
{ port: 5432, name: 'postgresql', type: 'database' },
{ port: 3306, name: 'mysql', type: 'database' },
{ port: 6379, name: 'redis', type: 'cache' },
{ port: 80, name: 'nginx', type: 'proxy' },
{ port: 443, name: 'nginx-ssl', type: 'proxy' }
];
const { stdout: netstatOutput } = await execAsync('netstat -tlnp 2>/dev/null || ss -tlnp 2>/dev/null || echo "no netstat"');
for (const { port, name, type } of portChecks) {
if (netstatOutput.includes(`:${port} `)) {
containerMetrics[`${name}-service`] = {
status: 'port_listening',
port: port,
type: type,
source: 'port_detection'
};
}
}
// If still no containers found, show a helpful message
if (Object.keys(containerMetrics).length === 0) {
containerMetrics = {
info: 'No containers detected',
message: 'This could mean Docker is not running, no containers are active, or the monitoring system needs Docker access',
suggestions: [
'Check if Docker is running: docker ps',
'Ensure management container has Docker socket access',
'Try: docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ...'
]
};
}
} catch (finalError) {
containerMetrics = {
error: 'All container monitoring methods failed',
attempts: ['health_endpoints', 'docker_stats', 'docker_compose', 'process_list'],
lastError: composeError.message
attempts: ['health_endpoints', 'docker_stats', 'docker_compose', 'port_detection'],
lastError: finalError.message,
troubleshooting: {
docker_access: 'Ensure management container can access Docker daemon',
permissions: 'Container may need privileged access or Docker socket mount',
environment: 'Check if running in Docker environment vs local development'
}
};
}
} catch (processError) {
containerMetrics = {
error: 'All container monitoring methods failed',
attempts: ['health_endpoints', 'docker_stats', 'docker_compose', 'process_list'],
lastError: processError.message
};
}
}
}
@@ -302,28 +312,68 @@ router.get('/system-info', async (req, res) => {
// Get system memory and CPU info
let systemMetrics = {};
try {
const { stdout: memInfo } = await execAsync('free -m');
const memLines = memInfo.split('\n')[1].split(/\s+/);
const totalMem = parseInt(memLines[1]);
const usedMem = parseInt(memLines[2]);
const { stdout: cpuInfo } = await execAsync('top -bn1 | grep "Cpu(s)" | sed "s/.*, *\\([0-9.]*\\)%* id.*/\\1/" | awk \'{print 100 - $1}\'');
const cpuUsage = parseFloat(cpuInfo.trim());
const { stdout: diskInfo } = await execAsync('df -h / | awk \'NR==2{print $3 " / " $2 " (" $5 ")"}\'');
systemMetrics = {
memory: {
// Try Linux commands first
try {
const { stdout: memInfo } = await execAsync('free -m');
const memLines = memInfo.split('\n')[1].split(/\s+/);
const totalMem = parseInt(memLines[1]);
const usedMem = parseInt(memLines[2]);
systemMetrics.memory = {
used: `${usedMem}MB`,
total: `${totalMem}MB`,
percentage: Math.round((usedMem / totalMem) * 100)
},
cpu: {
};
} catch (memError) {
// Fallback for Windows or other systems
const totalMem = Math.round(require('os').totalmem() / 1024 / 1024);
const freeMem = Math.round(require('os').freemem() / 1024 / 1024);
const usedMem = totalMem - freeMem;
systemMetrics.memory = {
used: `${usedMem}MB`,
total: `${totalMem}MB`,
percentage: Math.round((usedMem / totalMem) * 100)
};
}
// CPU usage - fix negative values
try {
const { stdout: cpuInfo } = await execAsync('top -bn1 | grep "Cpu(s)" | sed "s/.*, *\\([0-9.]*\\)%* id.*/\\1/" | awk \'{print 100 - $1}\'');
let cpuUsage = parseFloat(cpuInfo.trim());
// Fix negative or invalid CPU values
if (isNaN(cpuUsage) || cpuUsage < 0 || cpuUsage > 100) {
// Fallback to load average calculation
const loadAvg = require('os').loadavg()[0];
const cpuCount = require('os').cpus().length;
cpuUsage = Math.min((loadAvg / cpuCount) * 100, 100);
}
systemMetrics.cpu = {
usage: `${cpuUsage.toFixed(1)}%`,
percentage: cpuUsage
},
disk: diskInfo.trim()
};
};
} catch (cpuError) {
// Ultimate fallback
const loadAvg = require('os').loadavg()[0];
const cpuCount = require('os').cpus().length;
const cpuUsage = Math.min((loadAvg / cpuCount) * 100, 100);
systemMetrics.cpu = {
usage: `${cpuUsage.toFixed(1)}%`,
percentage: cpuUsage
};
}
// Disk usage
try {
const { stdout: diskInfo } = await execAsync('df -h / | awk \'NR==2{print $3 " / " $2 " (" $5 ")"}\'');
systemMetrics.disk = diskInfo.trim();
} catch (diskError) {
systemMetrics.disk = 'N/A';
}
} catch (sysError) {
console.log('System metrics not available:', sysError.message);
systemMetrics = {
@@ -341,37 +391,54 @@ router.get('/system-info', async (req, res) => {
const options = {
hostname: hostname,
port: 443,
method: 'GET',
timeout: 5000
method: 'HEAD',
timeout: 5000,
// Allow self-signed certificates for development
rejectUnauthorized: false
};
const req = https.request(options, (res) => {
const cert = res.connection.getPeerCertificate();
if (cert && cert.valid_to) {
const expiryDate = new Date(cert.valid_to);
const daysUntilExpiry = Math.ceil((expiryDate - new Date()) / (1000 * 60 * 60 * 24));
const now = new Date();
const daysUntilExpiry = Math.ceil((expiryDate - now) / (1000 * 60 * 60 * 24));
resolve({
status: daysUntilExpiry > 30 ? 'valid' : daysUntilExpiry > 7 ? 'warning' : 'critical',
expiresAt: expiryDate.toISOString(),
daysUntilExpiry: daysUntilExpiry,
issuer: cert.issuer?.O || 'Unknown',
subject: cert.subject?.CN || hostname
issuer: cert.issuer?.O || cert.issuer?.CN || 'Unknown',
subject: cert.subject?.CN || hostname,
fingerprint: cert.fingerprint || 'N/A'
});
} else {
resolve({
status: 'error',
expiresAt: null,
error: 'Certificate not found'
error: 'Certificate information not available'
});
}
});
req.on('error', () => {
req.on('error', (error) => {
// Try to determine the type of error
let errorMessage = error.message;
if (error.code === 'ENOTFOUND') {
errorMessage = 'Domain not found (DNS resolution failed)';
} else if (error.code === 'ECONNREFUSED') {
errorMessage = 'Connection refused (service not running on port 443)';
} else if (error.code === 'ETIMEDOUT') {
errorMessage = 'Connection timeout';
} else if (error.code === 'CERT_HAS_EXPIRED') {
errorMessage = 'Certificate has expired';
}
resolve({
status: 'error',
expiresAt: null,
error: 'Connection failed'
error: errorMessage,
errorCode: error.code
});
});
@@ -380,7 +447,8 @@ router.get('/system-info', async (req, res) => {
resolve({
status: 'error',
expiresAt: null,
error: 'Timeout'
error: 'Connection timeout (5 seconds)',
errorCode: 'TIMEOUT'
});
});