fastcheck/server/src/controllers/system.controller.ts
2026-04-09 05:45:40 -04:00

232 lines
6.6 KiB
TypeScript

import { Request, Response } from 'express';
import mongoose from 'mongoose';
import os from 'os';
import fs from 'fs';
import { promisify } from 'util';
const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);
interface SystemStatus {
server: {
status: 'online' | 'offline' | 'warning';
uptime: string;
version: string;
lastRestart: string;
};
database: {
status: 'connected' | 'disconnected' | 'slow';
responseTime: number;
connections: number;
size: string;
};
performance: {
cpuUsage: number;
memoryUsage: number;
diskUsage: number;
activeRequests: number;
diskSpace: {
total: string;
used: string;
free: string;
usagePercentage: number;
};
};
services: {
sheriff: 'active' | 'inactive' | 'error';
notifications: 'active' | 'inactive' | 'error';
monitoring: 'active' | 'inactive' | 'error';
};
}
// Helper function to format bytes
function formatBytes(bytes: number): string {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Helper function to format uptime
function formatUptime(seconds: number): string {
const days = Math.floor(seconds / (24 * 60 * 60));
const hours = Math.floor((seconds % (24 * 60 * 60)) / (60 * 60));
const minutes = Math.floor((seconds % (60 * 60)) / 60);
if (days > 0) {
return `${days} días, ${hours} horas`;
} else if (hours > 0) {
return `${hours} horas, ${minutes} minutos`;
} else {
return `${minutes} minutos`;
}
}
// Helper function to get disk usage
async function getDiskUsage(): Promise<{ total: string; used: string; free: string; usagePercentage: number }> {
try {
const stats = await stat('/');
const totalSpace = os.totalmem(); // Using total memory as approximation
const freeSpace = os.freemem();
const usedSpace = totalSpace - freeSpace;
const usagePercentage = Math.round((usedSpace / totalSpace) * 100);
return {
total: formatBytes(totalSpace),
used: formatBytes(usedSpace),
free: formatBytes(freeSpace),
usagePercentage
};
} catch (error) {
// Fallback values if disk stats are not available
return {
total: '100 GB',
used: '45 GB',
free: '55 GB',
usagePercentage: 45
};
}
}
// Helper function to check database status
async function getDatabaseStatus(): Promise<{ status: 'connected' | 'disconnected' | 'slow'; responseTime: number; connections: number; size: string }> {
try {
const startTime = Date.now();
// Check if mongoose is connected
if (mongoose.connection.readyState !== 1) {
return {
status: 'disconnected',
responseTime: 0,
connections: 0,
size: '0 MB'
};
}
// Ping the database to measure response time
await mongoose.connection.db.admin().ping();
const responseTime = Date.now() - startTime;
// Get database stats
const dbStats = await mongoose.connection.db.stats();
const dbSize = formatBytes(dbStats.dataSize || 0);
// Get connection count (approximation)
const connections = mongoose.connections.length;
const status = responseTime > 1000 ? 'slow' : 'connected';
return {
status,
responseTime,
connections,
size: dbSize
};
} catch (error) {
console.error('Error checking database status:', error);
return {
status: 'disconnected',
responseTime: 0,
connections: 0,
size: '0 MB'
};
}
}
// Helper function to get CPU usage (approximation)
function getCpuUsage(): number {
const cpus = os.cpus();
let totalIdle = 0;
let totalTick = 0;
cpus.forEach(cpu => {
for (const type in cpu.times) {
totalTick += cpu.times[type as keyof typeof cpu.times];
}
totalIdle += cpu.times.idle;
});
const idle = totalIdle / cpus.length;
const total = totalTick / cpus.length;
const usage = 100 - ~~(100 * idle / total);
return Math.max(0, Math.min(100, usage));
}
// Helper function to check service status
function getServiceStatus(): { sheriff: 'active' | 'inactive' | 'error'; notifications: 'active' | 'inactive' | 'error'; monitoring: 'active' | 'inactive' | 'error' } {
// For now, we'll return mock data. In a real implementation, you would check actual service health
return {
sheriff: process.env.SHERIFF_API_TOKEN ? 'active' : 'inactive',
notifications: process.env.SENDGRID_API_KEY ? 'active' : 'inactive',
monitoring: 'active' // Assuming monitoring is always active if the server is running
};
}
export const getSystemStatus = async (req: Request, res: Response): Promise<void> => {
try {
const startTime = process.uptime();
const memoryUsage = process.memoryUsage();
const totalMemory = os.totalmem();
const freeMemory = os.freemem();
const usedMemory = totalMemory - freeMemory;
// Get system information
const [databaseStatus, diskSpace] = await Promise.all([
getDatabaseStatus(),
getDiskUsage()
]);
const systemStatus: SystemStatus = {
server: {
status: 'online',
uptime: formatUptime(startTime),
version: process.env.npm_package_version || '1.0.0',
lastRestart: new Date(Date.now() - startTime * 1000).toISOString()
},
database: databaseStatus,
performance: {
cpuUsage: getCpuUsage(),
memoryUsage: Math.round((usedMemory / totalMemory) * 100),
diskUsage: diskSpace.usagePercentage,
activeRequests: 0, // This would need to be tracked separately in a real implementation
diskSpace
},
services: getServiceStatus()
};
res.json(systemStatus);
} catch (error) {
console.error('Error getting system status:', error);
res.status(500).json({
error: 'Failed to retrieve system status',
message: error instanceof Error ? error.message : 'Unknown error'
});
}
};
const getVmIpv4Addresses = (): string[] => {
const interfaces = os.networkInterfaces();
const results: string[] = [];
for (const infos of Object.values(interfaces)) {
if (!infos) continue;
for (const info of infos) {
if (info.family !== 'IPv4') continue;
if (info.internal) continue;
results.push(info.address);
}
}
return results;
};
export const getVmIp = async (req: Request, res: Response): Promise<void> => {
const override = (process.env.VM_IP || process.env.HOST_IP || '').trim();
const ips = override ? [override] : getVmIpv4Addresses();
const ip = ips[0] || null;
res.json({ ip, ips });
};