refactor(routes): Phase 3.5 - standardize logs.js
This commit is contained in:
@@ -4,13 +4,23 @@ const fsp = require('fs').promises;
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { exists } = require('../fs-helpers');
|
const { exists } = require('../fs-helpers');
|
||||||
const { paginate, parsePaginationParams } = require('../pagination');
|
const { paginate, parsePaginationParams } = require('../pagination');
|
||||||
|
const { NotFoundError } = require('../errors');
|
||||||
|
|
||||||
module.exports = function(ctx) {
|
/**
|
||||||
|
* Logs route factory
|
||||||
|
* @param {Object} deps - Explicit dependencies
|
||||||
|
* @param {Function} deps.asyncHandler - Async route handler wrapper
|
||||||
|
* @param {Object} deps.docker - Docker client
|
||||||
|
* @param {Object} deps.logDigest - Log digest manager (optional)
|
||||||
|
* @param {Object} deps.dockerMaintenance - Docker maintenance module (optional)
|
||||||
|
* @returns {express.Router}
|
||||||
|
*/
|
||||||
|
module.exports = function({ asyncHandler, docker, logDigest, dockerMaintenance }) {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// List containers with logs
|
// List containers with logs
|
||||||
router.get('/logs/containers', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/containers', asyncHandler(async (req, res) => {
|
||||||
const containers = await ctx.docker.client.listContainers({ all: true });
|
const containers = await docker.client.listContainers({ all: true });
|
||||||
const containerList = containers.map(c => ({
|
const containerList = containers.map(c => ({
|
||||||
id: c.Id.slice(0, 12),
|
id: c.Id.slice(0, 12),
|
||||||
name: c.Names[0]?.replace(/^\//, '') || 'unknown',
|
name: c.Names[0]?.replace(/^\//, '') || 'unknown',
|
||||||
@@ -25,13 +35,13 @@ module.exports = function(ctx) {
|
|||||||
}, 'logs-containers'));
|
}, 'logs-containers'));
|
||||||
|
|
||||||
// Get logs for a specific container
|
// Get logs for a specific container
|
||||||
router.get('/logs/container/:id', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/container/:id', asyncHandler(async (req, res) => {
|
||||||
const containerId = req.params.id;
|
const containerId = req.params.id;
|
||||||
const tail = parseInt(req.query.tail) || 100;
|
const tail = parseInt(req.query.tail) || 100;
|
||||||
const since = req.query.since || 0;
|
const since = req.query.since || 0;
|
||||||
const timestamps = req.query.timestamps !== 'false';
|
const timestamps = req.query.timestamps !== 'false';
|
||||||
|
|
||||||
const container = ctx.docker.client.getContainer(containerId);
|
const container = docker.client.getContainer(containerId);
|
||||||
let info;
|
let info;
|
||||||
try {
|
try {
|
||||||
info = await container.inspect();
|
info = await container.inspect();
|
||||||
@@ -80,9 +90,9 @@ module.exports = function(ctx) {
|
|||||||
}, 'logs-container'));
|
}, 'logs-container'));
|
||||||
|
|
||||||
// Stream logs (SSE)
|
// Stream logs (SSE)
|
||||||
router.get('/logs/stream/:id', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/stream/:id', asyncHandler(async (req, res) => {
|
||||||
const containerId = req.params.id;
|
const containerId = req.params.id;
|
||||||
const container = ctx.docker.client.getContainer(containerId);
|
const container = docker.client.getContainer(containerId);
|
||||||
try {
|
try {
|
||||||
await container.inspect();
|
await container.inspect();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -129,7 +139,7 @@ module.exports = function(ctx) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
logStream.on('error', (err) => {
|
logStream.on('error', (err) => {
|
||||||
res.write(`data: ${JSON.stringify({ error: ctx.safeErrorMessage(err) })}\n\n`);
|
res.write(`data: ${JSON.stringify({ error: err.message || String(err) })}\n\n`);
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,9 +149,9 @@ module.exports = function(ctx) {
|
|||||||
}, 'logs-stream'));
|
}, 'logs-stream'));
|
||||||
|
|
||||||
// Get latest daily digest
|
// Get latest daily digest
|
||||||
router.get('/logs/digest/latest', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/digest/latest', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
if (!logDigest) throw new Error('Log digest not available');
|
||||||
const digest = await ctx.logDigest.getLatestDigest();
|
const digest = await logDigest.getLatestDigest();
|
||||||
if (!digest) {
|
if (!digest) {
|
||||||
return res.json({ success: true, digest: null, message: 'No digest available yet. First digest is generated at midnight.' });
|
return res.json({ success: true, digest: null, message: 'No digest available yet. First digest is generated at midnight.' });
|
||||||
}
|
}
|
||||||
@@ -149,63 +159,63 @@ module.exports = function(ctx) {
|
|||||||
}, 'logs-digest-latest'));
|
}, 'logs-digest-latest'));
|
||||||
|
|
||||||
// Get live digest data (today's accumulated stats)
|
// Get live digest data (today's accumulated stats)
|
||||||
router.get('/logs/digest/live', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/digest/live', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
if (!logDigest) throw new Error('Log digest not available');
|
||||||
const live = ctx.logDigest.getLiveData();
|
const live = logDigest.getLiveData();
|
||||||
res.json({ success: true, ...live });
|
res.json({ success: true, ...live });
|
||||||
}, 'logs-digest-live'));
|
}, 'logs-digest-live'));
|
||||||
|
|
||||||
// List available digest dates
|
// List available digest dates
|
||||||
router.get('/logs/digest/history', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/digest/history', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
if (!logDigest) throw new Error('Log digest not available');
|
||||||
const dates = await ctx.logDigest.listDigests();
|
const dates = await logDigest.listDigests();
|
||||||
res.json({ success: true, dates });
|
res.json({ success: true, dates });
|
||||||
}, 'logs-digest-history'));
|
}, 'logs-digest-history'));
|
||||||
|
|
||||||
// Generate digest on demand (for today or a specific date)
|
// Generate digest on demand (for today or a specific date)
|
||||||
router.post('/logs/digest/generate', ctx.asyncHandler(async (req, res) => {
|
router.post('/logs/digest/generate', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
if (!logDigest) throw new Error('Log digest not available');
|
||||||
const date = req.body.date || new Date().toISOString().slice(0, 10);
|
const date = req.body.date || new Date().toISOString().slice(0, 10);
|
||||||
const digest = await ctx.logDigest.generateDailyDigest(date);
|
const digest = await logDigest.generateDailyDigest(date);
|
||||||
res.json({ success: true, digest });
|
res.json({ success: true, digest });
|
||||||
}, 'logs-digest-generate'));
|
}, 'logs-digest-generate'));
|
||||||
|
|
||||||
// Get digest for a specific date (JSON)
|
// Get digest for a specific date (JSON)
|
||||||
router.get('/logs/digest/:date', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/digest/:date', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
if (!logDigest) throw new Error('Log digest not available');
|
||||||
const { date } = req.params;
|
const { date } = req.params;
|
||||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
||||||
throw new ValidationError('Invalid date format. Use YYYY-MM-DD.');
|
throw new ValidationError('Invalid date format. Use YYYY-MM-DD.');
|
||||||
}
|
}
|
||||||
const format = req.query.format || 'json';
|
const format = req.query.format || 'json';
|
||||||
if (format === 'text') {
|
if (format === 'text') {
|
||||||
const text = await ctx.logDigest.getDigestText(date);
|
const text = await logDigest.getDigestText(date);
|
||||||
if (!text) return ctx.errorResponse(res, 404, `No digest found for ${date}`);
|
if (!text) throw new NotFoundError(`Digest for ${date}`);
|
||||||
res.setHeader('Content-Type', 'text/plain');
|
res.setHeader('Content-Type', 'text/plain');
|
||||||
return res.send(text);
|
return res.send(text);
|
||||||
}
|
}
|
||||||
const digest = await ctx.logDigest.getDigestByDate(date);
|
const digest = await logDigest.getDigestByDate(date);
|
||||||
if (!digest) return ctx.errorResponse(res, 404, `No digest found for ${date}`);
|
if (!digest) throw new NotFoundError(`Digest for ${date}`);
|
||||||
res.json({ success: true, digest });
|
res.json({ success: true, digest });
|
||||||
}, 'logs-digest-date'));
|
}, 'logs-digest-date'));
|
||||||
|
|
||||||
// Get Docker disk usage snapshot
|
// Get Docker disk usage snapshot
|
||||||
router.get('/logs/docker-disk', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/docker-disk', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.dockerMaintenance) return ctx.errorResponse(res, 503, 'Docker maintenance not available');
|
if (!dockerMaintenance) throw new Error('Docker maintenance not available');
|
||||||
const diskUsage = await ctx.dockerMaintenance.getDiskUsage();
|
const diskUsage = await dockerMaintenance.getDiskUsage();
|
||||||
const status = ctx.dockerMaintenance.getStatus();
|
const status = dockerMaintenance.getStatus();
|
||||||
res.json({ success: true, diskUsage, maintenance: status });
|
res.json({ success: true, diskUsage, maintenance: status });
|
||||||
}, 'logs-docker-disk'));
|
}, 'logs-docker-disk'));
|
||||||
|
|
||||||
// Trigger Docker maintenance manually
|
// Trigger Docker maintenance manually
|
||||||
router.post('/logs/docker-maintenance', ctx.asyncHandler(async (req, res) => {
|
router.post('/logs/docker-maintenance', asyncHandler(async (req, res) => {
|
||||||
if (!ctx.dockerMaintenance) return ctx.errorResponse(res, 503, 'Docker maintenance not available');
|
if (!dockerMaintenance) throw new Error('Docker maintenance not available');
|
||||||
const result = await ctx.dockerMaintenance.runMaintenance();
|
const result = await dockerMaintenance.runMaintenance();
|
||||||
res.json({ success: true, result });
|
res.json({ success: true, result });
|
||||||
}, 'logs-docker-maintenance'));
|
}, 'logs-docker-maintenance'));
|
||||||
|
|
||||||
// Get logs from a file path (for native applications)
|
// Get logs from a file path (for native applications)
|
||||||
router.get('/logs/file', ctx.asyncHandler(async (req, res) => {
|
router.get('/logs/file', asyncHandler(async (req, res) => {
|
||||||
const { path: logPath, tail = 100 } = req.query;
|
const { path: logPath, tail = 100 } = req.query;
|
||||||
|
|
||||||
if (!logPath) {
|
if (!logPath) {
|
||||||
|
|||||||
@@ -365,7 +365,12 @@ async function createApp() {
|
|||||||
}));
|
}));
|
||||||
apiRouter.use(arrRoutes(ctx));
|
apiRouter.use(arrRoutes(ctx));
|
||||||
apiRouter.use(appsRoutes(ctx));
|
apiRouter.use(appsRoutes(ctx));
|
||||||
apiRouter.use(logsRoutes(ctx));
|
apiRouter.use(logsRoutes({
|
||||||
|
asyncHandler: ctx.asyncHandler,
|
||||||
|
docker: ctx.docker,
|
||||||
|
logDigest: ctx.logDigest,
|
||||||
|
dockerMaintenance: ctx.dockerMaintenance
|
||||||
|
}));
|
||||||
apiRouter.use(backupsRoutes({
|
apiRouter.use(backupsRoutes({
|
||||||
backupManager: ctx.backupManager,
|
backupManager: ctx.backupManager,
|
||||||
asyncHandler: ctx.asyncHandler
|
asyncHandler: ctx.asyncHandler
|
||||||
|
|||||||
Reference in New Issue
Block a user