Full codebase including API server (32 modules + routes), dashboard frontend, DashCA certificate distribution, installer script, and deployment skills.
168 lines
6.3 KiB
JavaScript
168 lines
6.3 KiB
JavaScript
const express = require('express');
|
|
|
|
module.exports = function(ctx) {
|
|
const router = express.Router();
|
|
|
|
// ===== RESOURCE MONITORING ENDPOINTS =====
|
|
|
|
// Get all container stats (from resource monitor module)
|
|
router.get('/monitoring/stats', ctx.asyncHandler(async (req, res) => {
|
|
const stats = ctx.resourceMonitor.getAllStats();
|
|
res.json({ success: true, stats });
|
|
}, 'monitoring-stats'));
|
|
|
|
// Get stats for specific container
|
|
router.get('/monitoring/stats/:containerId', ctx.asyncHandler(async (req, res) => {
|
|
const stats = ctx.resourceMonitor.getCurrentStats(req.params.containerId);
|
|
if (!stats) {
|
|
const { NotFoundError } = require('../errors');
|
|
throw new NotFoundError('Container');
|
|
}
|
|
res.json({ success: true, stats });
|
|
}, 'monitoring-stats-container'));
|
|
|
|
// Get historical stats
|
|
router.get('/monitoring/history/:containerId', ctx.asyncHandler(async (req, res) => {
|
|
const hours = parseInt(req.query.hours) || 24;
|
|
const history = ctx.resourceMonitor.getHistoricalStats(req.params.containerId, hours);
|
|
res.json({ success: true, history, hours });
|
|
}, 'monitoring-history'));
|
|
|
|
// Get aggregated stats
|
|
router.get('/monitoring/aggregated/:containerId', ctx.asyncHandler(async (req, res) => {
|
|
const hours = parseInt(req.query.hours) || 24;
|
|
const aggregated = ctx.resourceMonitor.getAggregatedStats(req.params.containerId, hours);
|
|
if (!aggregated) {
|
|
const { NotFoundError } = require('../errors');
|
|
throw new NotFoundError('Monitoring data');
|
|
}
|
|
res.json({ success: true, aggregated, hours });
|
|
}, 'monitoring-aggregated'));
|
|
|
|
// Configure alerts
|
|
router.post('/monitoring/alerts/:containerId', ctx.asyncHandler(async (req, res) => {
|
|
ctx.resourceMonitor.setAlertConfig(req.params.containerId, req.body);
|
|
res.json({ success: true, message: 'Alert configuration saved' });
|
|
}, 'monitoring-alerts-set'));
|
|
|
|
// Get alert configuration
|
|
router.get('/monitoring/alerts/:containerId', ctx.asyncHandler(async (req, res) => {
|
|
const config = ctx.resourceMonitor.getAlertConfig(req.params.containerId);
|
|
res.json({ success: true, config: config || {} });
|
|
}, 'monitoring-alerts-get'));
|
|
|
|
// Delete alert configuration
|
|
router.delete('/monitoring/alerts/:containerId', ctx.asyncHandler(async (req, res) => {
|
|
ctx.resourceMonitor.removeAlertConfig(req.params.containerId);
|
|
res.json({ success: true, message: 'Alert configuration removed' });
|
|
}, 'monitoring-alerts-delete'));
|
|
|
|
// ===== CONTAINER STATS ENDPOINTS (legacy /stats/) =====
|
|
|
|
// Get all container stats (live Docker stats)
|
|
router.get('/stats/containers', ctx.asyncHandler(async (req, res) => {
|
|
const containers = await ctx.docker.client.listContainers({ all: false });
|
|
const stats = [];
|
|
|
|
for (const containerInfo of containers) {
|
|
try {
|
|
const container = ctx.docker.client.getContainer(containerInfo.Id);
|
|
const containerStats = await container.stats({ stream: false });
|
|
|
|
// Calculate CPU percentage
|
|
const cpuDelta = containerStats.cpu_stats.cpu_usage.total_usage -
|
|
(containerStats.precpu_stats.cpu_usage?.total_usage || 0);
|
|
const systemDelta = containerStats.cpu_stats.system_cpu_usage -
|
|
(containerStats.precpu_stats.system_cpu_usage || 0);
|
|
const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * 100 * (containerStats.cpu_stats.online_cpus || 1) : 0;
|
|
|
|
// Calculate memory usage
|
|
const memUsage = containerStats.memory_stats.usage || 0;
|
|
const memLimit = containerStats.memory_stats.limit || 1;
|
|
const memPercent = (memUsage / memLimit) * 100;
|
|
|
|
// Network stats
|
|
let netRx = 0, netTx = 0;
|
|
if (containerStats.networks) {
|
|
for (const net of Object.values(containerStats.networks)) {
|
|
netRx += net.rx_bytes || 0;
|
|
netTx += net.tx_bytes || 0;
|
|
}
|
|
}
|
|
|
|
stats.push({
|
|
id: containerInfo.Id.slice(0, 12),
|
|
name: containerInfo.Names[0]?.replace(/^\//, '') || 'unknown',
|
|
image: containerInfo.Image,
|
|
status: containerInfo.State,
|
|
cpu: {
|
|
percent: Math.round(cpuPercent * 100) / 100
|
|
},
|
|
memory: {
|
|
used: memUsage,
|
|
limit: memLimit,
|
|
percent: Math.round(memPercent * 100) / 100
|
|
},
|
|
network: {
|
|
rx: netRx,
|
|
tx: netTx
|
|
}
|
|
});
|
|
} catch (e) {
|
|
// Skip containers we can't get stats for
|
|
console.log(`Could not get stats for ${containerInfo.Names[0]}:`, e.message);
|
|
}
|
|
}
|
|
|
|
res.json({ success: true, stats, timestamp: new Date().toISOString() });
|
|
}, 'stats-containers'));
|
|
|
|
// Get single container stats
|
|
router.get('/stats/container/:id', ctx.asyncHandler(async (req, res) => {
|
|
const container = ctx.docker.client.getContainer(req.params.id);
|
|
const containerStats = await container.stats({ stream: false });
|
|
const info = await container.inspect();
|
|
|
|
// Calculate CPU percentage
|
|
const cpuDelta = containerStats.cpu_stats.cpu_usage.total_usage -
|
|
(containerStats.precpu_stats.cpu_usage?.total_usage || 0);
|
|
const systemDelta = containerStats.cpu_stats.system_cpu_usage -
|
|
(containerStats.precpu_stats.system_cpu_usage || 0);
|
|
const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * 100 * (containerStats.cpu_stats.online_cpus || 1) : 0;
|
|
|
|
// Memory
|
|
const memUsage = containerStats.memory_stats.usage || 0;
|
|
const memLimit = containerStats.memory_stats.limit || 1;
|
|
|
|
// Network
|
|
let netRx = 0, netTx = 0;
|
|
if (containerStats.networks) {
|
|
for (const net of Object.values(containerStats.networks)) {
|
|
netRx += net.rx_bytes || 0;
|
|
netTx += net.tx_bytes || 0;
|
|
}
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
stats: {
|
|
name: info.Name.replace(/^\//, ''),
|
|
image: info.Config.Image,
|
|
status: info.State.Status,
|
|
started: info.State.StartedAt,
|
|
cpu: {
|
|
percent: Math.round(cpuPercent * 100) / 100
|
|
},
|
|
memory: {
|
|
used: memUsage,
|
|
limit: memLimit,
|
|
percent: Math.round((memUsage / memLimit) * 100 * 100) / 100
|
|
},
|
|
network: { rx: netRx, tx: netTx }
|
|
}
|
|
});
|
|
}, 'stats-container'));
|
|
|
|
return router;
|
|
};
|