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 { exists } = require('../fs-helpers');
|
||||
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();
|
||||
|
||||
// List containers with logs
|
||||
router.get('/logs/containers', ctx.asyncHandler(async (req, res) => {
|
||||
const containers = await ctx.docker.client.listContainers({ all: true });
|
||||
router.get('/logs/containers', asyncHandler(async (req, res) => {
|
||||
const containers = await docker.client.listContainers({ all: true });
|
||||
const containerList = containers.map(c => ({
|
||||
id: c.Id.slice(0, 12),
|
||||
name: c.Names[0]?.replace(/^\//, '') || 'unknown',
|
||||
@@ -25,13 +35,13 @@ module.exports = function(ctx) {
|
||||
}, 'logs-containers'));
|
||||
|
||||
// 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 tail = parseInt(req.query.tail) || 100;
|
||||
const since = req.query.since || 0;
|
||||
const timestamps = req.query.timestamps !== 'false';
|
||||
|
||||
const container = ctx.docker.client.getContainer(containerId);
|
||||
const container = docker.client.getContainer(containerId);
|
||||
let info;
|
||||
try {
|
||||
info = await container.inspect();
|
||||
@@ -80,9 +90,9 @@ module.exports = function(ctx) {
|
||||
}, 'logs-container'));
|
||||
|
||||
// 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 container = ctx.docker.client.getContainer(containerId);
|
||||
const container = docker.client.getContainer(containerId);
|
||||
try {
|
||||
await container.inspect();
|
||||
} catch (err) {
|
||||
@@ -129,7 +139,7 @@ module.exports = function(ctx) {
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -139,9 +149,9 @@ module.exports = function(ctx) {
|
||||
}, 'logs-stream'));
|
||||
|
||||
// Get latest daily digest
|
||||
router.get('/logs/digest/latest', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
||||
const digest = await ctx.logDigest.getLatestDigest();
|
||||
router.get('/logs/digest/latest', asyncHandler(async (req, res) => {
|
||||
if (!logDigest) throw new Error('Log digest not available');
|
||||
const digest = await logDigest.getLatestDigest();
|
||||
if (!digest) {
|
||||
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'));
|
||||
|
||||
// Get live digest data (today's accumulated stats)
|
||||
router.get('/logs/digest/live', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
||||
const live = ctx.logDigest.getLiveData();
|
||||
router.get('/logs/digest/live', asyncHandler(async (req, res) => {
|
||||
if (!logDigest) throw new Error('Log digest not available');
|
||||
const live = logDigest.getLiveData();
|
||||
res.json({ success: true, ...live });
|
||||
}, 'logs-digest-live'));
|
||||
|
||||
// List available digest dates
|
||||
router.get('/logs/digest/history', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
||||
const dates = await ctx.logDigest.listDigests();
|
||||
router.get('/logs/digest/history', asyncHandler(async (req, res) => {
|
||||
if (!logDigest) throw new Error('Log digest not available');
|
||||
const dates = await logDigest.listDigests();
|
||||
res.json({ success: true, dates });
|
||||
}, 'logs-digest-history'));
|
||||
|
||||
// Generate digest on demand (for today or a specific date)
|
||||
router.post('/logs/digest/generate', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
||||
router.post('/logs/digest/generate', asyncHandler(async (req, res) => {
|
||||
if (!logDigest) throw new Error('Log digest not available');
|
||||
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 });
|
||||
}, 'logs-digest-generate'));
|
||||
|
||||
// Get digest for a specific date (JSON)
|
||||
router.get('/logs/digest/:date', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.logDigest) return ctx.errorResponse(res, 503, 'Log digest not available');
|
||||
router.get('/logs/digest/:date', asyncHandler(async (req, res) => {
|
||||
if (!logDigest) throw new Error('Log digest not available');
|
||||
const { date } = req.params;
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
||||
throw new ValidationError('Invalid date format. Use YYYY-MM-DD.');
|
||||
}
|
||||
const format = req.query.format || 'json';
|
||||
if (format === 'text') {
|
||||
const text = await ctx.logDigest.getDigestText(date);
|
||||
if (!text) return ctx.errorResponse(res, 404, `No digest found for ${date}`);
|
||||
const text = await logDigest.getDigestText(date);
|
||||
if (!text) throw new NotFoundError(`Digest for ${date}`);
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
return res.send(text);
|
||||
}
|
||||
const digest = await ctx.logDigest.getDigestByDate(date);
|
||||
if (!digest) return ctx.errorResponse(res, 404, `No digest found for ${date}`);
|
||||
const digest = await logDigest.getDigestByDate(date);
|
||||
if (!digest) throw new NotFoundError(`Digest for ${date}`);
|
||||
res.json({ success: true, digest });
|
||||
}, 'logs-digest-date'));
|
||||
|
||||
// Get Docker disk usage snapshot
|
||||
router.get('/logs/docker-disk', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.dockerMaintenance) return ctx.errorResponse(res, 503, 'Docker maintenance not available');
|
||||
const diskUsage = await ctx.dockerMaintenance.getDiskUsage();
|
||||
const status = ctx.dockerMaintenance.getStatus();
|
||||
router.get('/logs/docker-disk', asyncHandler(async (req, res) => {
|
||||
if (!dockerMaintenance) throw new Error('Docker maintenance not available');
|
||||
const diskUsage = await dockerMaintenance.getDiskUsage();
|
||||
const status = dockerMaintenance.getStatus();
|
||||
res.json({ success: true, diskUsage, maintenance: status });
|
||||
}, 'logs-docker-disk'));
|
||||
|
||||
// Trigger Docker maintenance manually
|
||||
router.post('/logs/docker-maintenance', ctx.asyncHandler(async (req, res) => {
|
||||
if (!ctx.dockerMaintenance) return ctx.errorResponse(res, 503, 'Docker maintenance not available');
|
||||
const result = await ctx.dockerMaintenance.runMaintenance();
|
||||
router.post('/logs/docker-maintenance', asyncHandler(async (req, res) => {
|
||||
if (!dockerMaintenance) throw new Error('Docker maintenance not available');
|
||||
const result = await dockerMaintenance.runMaintenance();
|
||||
res.json({ success: true, result });
|
||||
}, 'logs-docker-maintenance'));
|
||||
|
||||
// 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;
|
||||
|
||||
if (!logPath) {
|
||||
|
||||
@@ -365,7 +365,12 @@ async function createApp() {
|
||||
}));
|
||||
apiRouter.use(arrRoutes(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({
|
||||
backupManager: ctx.backupManager,
|
||||
asyncHandler: ctx.asyncHandler
|
||||
|
||||
Reference in New Issue
Block a user