refactor(routes): Phase 3.1 - standardize updates.js (explicit deps)

This commit is contained in:
Krystie
2026-03-29 20:05:06 -07:00
parent 5da1e572a1
commit 4e96c62708
2 changed files with 50 additions and 36 deletions

View File

@@ -2,87 +2,96 @@ const express = require('express');
const { paginate, parsePaginationParams } = require('../pagination'); const { paginate, parsePaginationParams } = require('../pagination');
const { ValidationError } = require('../errors'); const { ValidationError } = require('../errors');
module.exports = function(ctx) { /**
* Updates route factory
* @param {Object} deps - Explicit dependencies
* @param {Object} deps.updateManager - Container update manager
* @param {Object} deps.selfUpdater - DashCaddy self-update manager
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Function} deps.logError - Error logging function
* @returns {express.Router}
*/
module.exports = function({ updateManager, selfUpdater, asyncHandler, logError }) {
const router = express.Router(); const router = express.Router();
// ===== UPDATE MANAGEMENT ENDPOINTS ===== // ===== UPDATE MANAGEMENT ENDPOINTS =====
// Check for updates // Check for updates
router.post('/updates/check', ctx.asyncHandler(async (req, res) => { router.post('/updates/check', asyncHandler(async (req, res) => {
await ctx.updateManager.checkForUpdates(); await updateManager.checkForUpdates();
const updates = ctx.updateManager.getAvailableUpdates(); const updates = updateManager.getAvailableUpdates();
res.json({ success: true, updates, count: updates.length }); res.json({ success: true, updates, count: updates.length });
}, 'updates-check')); }, 'updates-check'));
// Get available updates // Get available updates
router.get('/updates/available', ctx.asyncHandler(async (req, res) => { router.get('/updates/available', asyncHandler(async (req, res) => {
const updates = ctx.updateManager.getAvailableUpdates(); const updates = updateManager.getAvailableUpdates();
const paginationParams = parsePaginationParams(req.query); const paginationParams = parsePaginationParams(req.query);
const result = paginate(updates, paginationParams); const result = paginate(updates, paginationParams);
res.json({ success: true, updates: result.data, count: updates.length, ...(result.pagination && { pagination: result.pagination }) }); res.json({ success: true, updates: result.data, count: updates.length, ...(result.pagination && { pagination: result.pagination }) });
}, 'updates-available')); }, 'updates-available'));
// Update a container // Update a container
router.post('/updates/update/:containerId', ctx.asyncHandler(async (req, res) => { router.post('/updates/update/:containerId', asyncHandler(async (req, res) => {
const result = await ctx.updateManager.updateContainer(req.params.containerId, req.body); const result = await updateManager.updateContainer(req.params.containerId, req.body);
res.json({ success: true, result }); res.json({ success: true, result });
}, 'updates-update')); }, 'updates-update'));
// Rollback update // Rollback update
router.post('/updates/rollback/:containerId', ctx.asyncHandler(async (req, res) => { router.post('/updates/rollback/:containerId', asyncHandler(async (req, res) => {
await ctx.updateManager.rollbackUpdate(req.params.containerId); await updateManager.rollbackUpdate(req.params.containerId);
res.json({ success: true, message: 'Rollback completed' }); res.json({ success: true, message: 'Rollback completed' });
}, 'updates-rollback')); }, 'updates-rollback'));
// Get update history // Get update history
router.get('/updates/history', ctx.asyncHandler(async (req, res) => { router.get('/updates/history', asyncHandler(async (req, res) => {
const paginationParams = parsePaginationParams(req.query); const paginationParams = parsePaginationParams(req.query);
// When paginating, fetch all history so pagination can slice correctly // When paginating, fetch all history so pagination can slice correctly
const fetchLimit = paginationParams ? Number.MAX_SAFE_INTEGER : (parseInt(req.query.limit) || 50); const fetchLimit = paginationParams ? Number.MAX_SAFE_INTEGER : (parseInt(req.query.limit) || 50);
const history = ctx.updateManager.getHistory(fetchLimit); const history = updateManager.getHistory(fetchLimit);
const result = paginate(history, paginationParams); const result = paginate(history, paginationParams);
res.json({ success: true, history: result.data, ...(result.pagination && { pagination: result.pagination }) }); res.json({ success: true, history: result.data, ...(result.pagination && { pagination: result.pagination }) });
}, 'updates-history')); }, 'updates-history'));
// Configure auto-update // Configure auto-update
router.post('/updates/auto-update/:containerId', ctx.asyncHandler(async (req, res) => { router.post('/updates/auto-update/:containerId', asyncHandler(async (req, res) => {
ctx.updateManager.configureAutoUpdate(req.params.containerId, req.body); updateManager.configureAutoUpdate(req.params.containerId, req.body);
res.json({ success: true, message: 'Auto-update configured' }); res.json({ success: true, message: 'Auto-update configured' });
}, 'updates-auto-update')); }, 'updates-auto-update'));
// Schedule update // Schedule update
router.post('/updates/schedule/:containerId', ctx.asyncHandler(async (req, res) => { router.post('/updates/schedule/:containerId', asyncHandler(async (req, res) => {
const { scheduledTime } = req.body; const { scheduledTime } = req.body;
if (!scheduledTime) { if (!scheduledTime) {
throw new ValidationError('scheduledTime is required'); throw new ValidationError('scheduledTime is required');
} }
ctx.updateManager.scheduleUpdate(req.params.containerId, scheduledTime); updateManager.scheduleUpdate(req.params.containerId, scheduledTime);
res.json({ success: true, message: 'Update scheduled', scheduledTime }); res.json({ success: true, message: 'Update scheduled', scheduledTime });
}, 'updates-schedule')); }, 'updates-schedule'));
// ===== DASHCADDY SELF-UPDATE ENDPOINTS ===== // ===== DASHCADDY SELF-UPDATE ENDPOINTS =====
// Get current version // Get current version
router.get('/system/version', ctx.asyncHandler(async (req, res) => { router.get('/system/version', asyncHandler(async (req, res) => {
const local = ctx.selfUpdater.getLocalVersion(); const local = selfUpdater.getLocalVersion();
res.json({ success: true, name: 'DashCaddy', version: local.version, commit: local.commit }); res.json({ success: true, name: 'DashCaddy', version: local.version, commit: local.commit });
}, 'system-version')); }, 'system-version'));
// Check for DashCaddy update // Check for DashCaddy update
router.get('/system/update-check', ctx.asyncHandler(async (req, res) => { router.get('/system/update-check', asyncHandler(async (req, res) => {
const result = await ctx.selfUpdater.checkForUpdate(); const result = await selfUpdater.checkForUpdate();
res.json({ success: true, ...result }); res.json({ success: true, ...result });
}, 'system-update-check')); }, 'system-update-check'));
// Apply available update // Apply available update
router.post('/system/update-apply', ctx.asyncHandler(async (req, res) => { router.post('/system/update-apply', asyncHandler(async (req, res) => {
const check = await ctx.selfUpdater.checkForUpdate(); const check = await selfUpdater.checkForUpdate();
if (!check.available) { if (!check.available) {
return res.json({ success: true, message: 'Already up to date' }); return res.json({ success: true, message: 'Already up to date' });
} }
// Start async — container may restart // Start async — container may restart
ctx.selfUpdater.applyUpdate(check.remote).catch(err => { selfUpdater.applyUpdate(check.remote).catch(err => {
ctx.logError('self-update', err); logError('self-update', err);
}); });
res.json({ res.json({
success: true, success: true,
@@ -93,33 +102,33 @@ module.exports = function(ctx) {
}, 'system-update-apply')); }, 'system-update-apply'));
// Get update status // Get update status
router.get('/system/update-status', ctx.asyncHandler(async (req, res) => { router.get('/system/update-status', asyncHandler(async (req, res) => {
res.json({ res.json({
success: true, success: true,
status: ctx.selfUpdater.getStatus(), status: selfUpdater.getStatus(),
lastCheck: ctx.selfUpdater.lastCheckTime, lastCheck: selfUpdater.lastCheckTime,
lastResult: ctx.selfUpdater.lastCheckResult, lastResult: selfUpdater.lastCheckResult,
}); });
}, 'system-update-status')); }, 'system-update-status'));
// Get self-update history // Get self-update history
router.get('/system/update-history', ctx.asyncHandler(async (req, res) => { router.get('/system/update-history', asyncHandler(async (req, res) => {
const history = ctx.selfUpdater.getUpdateHistory(); const history = selfUpdater.getUpdateHistory();
res.json({ success: true, history }); res.json({ success: true, history });
}, 'system-update-history')); }, 'system-update-history'));
// List rollback versions // List rollback versions
router.get('/system/rollback-versions', ctx.asyncHandler(async (req, res) => { router.get('/system/rollback-versions', asyncHandler(async (req, res) => {
const versions = ctx.selfUpdater.getAvailableRollbacks(); const versions = selfUpdater.getAvailableRollbacks();
res.json({ success: true, versions }); res.json({ success: true, versions });
}, 'system-rollback-versions')); }, 'system-rollback-versions'));
// Rollback to a previous version // Rollback to a previous version
router.post('/system/rollback', ctx.asyncHandler(async (req, res) => { router.post('/system/rollback', asyncHandler(async (req, res) => {
const { version } = req.body; const { version } = req.body;
if (!version) throw new ValidationError('version is required'); if (!version) throw new ValidationError('version is required');
ctx.selfUpdater.rollbackToVersion(version).catch(err => { selfUpdater.rollbackToVersion(version).catch(err => {
ctx.logError('self-rollback', err); logError('self-rollback', err);
}); });
res.json({ success: true, message: `Rollback to ${version} initiated` }); res.json({ success: true, message: `Rollback to ${version} initiated` });
}, 'system-rollback')); }, 'system-rollback'));

View File

@@ -339,7 +339,12 @@ async function createApp() {
docker: ctx.docker, docker: ctx.docker,
asyncHandler: ctx.asyncHandler asyncHandler: ctx.asyncHandler
})); }));
apiRouter.use(updatesRoutes(ctx)); apiRouter.use(updatesRoutes({
updateManager: ctx.updateManager,
selfUpdater: ctx.selfUpdater,
asyncHandler: ctx.asyncHandler,
logError: ctx.logError
}));
apiRouter.use('/tailscale', tailscaleRoutes(ctx)); apiRouter.use('/tailscale', tailscaleRoutes(ctx));
apiRouter.use(sitesRoutes(ctx)); apiRouter.use(sitesRoutes(ctx));
apiRouter.use(credentialsRoutes({ apiRouter.use(credentialsRoutes({