From cabcbcf98a9ee45a2a92832e3295211db11028cd Mon Sep 17 00:00:00 2001 From: Krystie Date: Sun, 29 Mar 2026 20:14:33 -0700 Subject: [PATCH] refactor(routes): Phase 3.5 - standardize logs.js --- dashcaddy-api/routes/logs.js | 78 ++++++++++++++++++++---------------- dashcaddy-api/src/app.js | 7 +++- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/dashcaddy-api/routes/logs.js b/dashcaddy-api/routes/logs.js index e062c75..8668b6c 100644 --- a/dashcaddy-api/routes/logs.js +++ b/dashcaddy-api/routes/logs.js @@ -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) { diff --git a/dashcaddy-api/src/app.js b/dashcaddy-api/src/app.js index 0ad2473..b48ce62 100644 --- a/dashcaddy-api/src/app.js +++ b/dashcaddy-api/src/app.js @@ -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