diff --git a/dashcaddy-api/routes/apps/helpers.js b/dashcaddy-api/routes/apps/helpers.js index 450e07f..9b996a4 100644 --- a/dashcaddy-api/routes/apps/helpers.js +++ b/dashcaddy-api/routes/apps/helpers.js @@ -17,7 +17,8 @@ const platformPaths = require('../../platform-paths'); * @param {Object} deps.log - Logger instance * @returns {Object} Helper functions */ -module.exports = function({ docker, caddy, credentialManager, servicesStateManager, fetchT, log }) { +module.exports = function(ctx) { + const { docker, caddy, credentialManager, servicesStateManager, fetchT, log } = ctx; async function checkPortConflicts(ports, excludeContainerName = null) { const conflicts = []; diff --git a/dashcaddy-api/routes/apps/index.js b/dashcaddy-api/routes/apps/index.js index 3c4128c..625ffe6 100644 --- a/dashcaddy-api/routes/apps/index.js +++ b/dashcaddy-api/routes/apps/index.js @@ -36,13 +36,14 @@ module.exports = function(ctx) { }; // Initialize helpers with dependencies - const helpers = initHelpers(deps); + const helpers = initHelpers(ctx); - // Mount sub-routes with explicit dependencies - router.use(initDeploy({ ...deps, helpers })); - router.use(initRemoval({ ...deps, helpers })); - router.use(initTemplates({ ...deps, helpers })); - router.use(initRestore({ ...deps, helpers })); + // Mount sub-routes — pass full ctx so sub-routes can reference ctx.* properties + const subCtx = Object.assign({}, ctx, { helpers }); + router.use(initDeploy(subCtx)); + router.use(initRemoval(subCtx)); + router.use(initTemplates(subCtx)); + router.use(initRestore(subCtx)); return router; }; diff --git a/dashcaddy-api/routes/apps/removal.js b/dashcaddy-api/routes/apps/removal.js index 7643dfb..ac677eb 100644 --- a/dashcaddy-api/routes/apps/removal.js +++ b/dashcaddy-api/routes/apps/removal.js @@ -1,10 +1,9 @@ const express = require('express'); const { exists } = require('../../fs-helpers'); -module.exports = function({ docker, caddy, servicesStateManager, asyncHandler, log, helpers }) { +module.exports = function(ctx) { + const { docker, caddy, servicesStateManager, asyncHandler, errorResponse, log, helpers } = ctx; const router = express.Router(); - - // Remove deployed app /** * Apps removal routes factory * @param {Object} deps - Explicit dependencies diff --git a/dashcaddy-api/routes/apps/restore.js b/dashcaddy-api/routes/apps/restore.js index 6b72c10..71e1a74 100644 --- a/dashcaddy-api/routes/apps/restore.js +++ b/dashcaddy-api/routes/apps/restore.js @@ -12,7 +12,8 @@ const { DOCKER } = require('../../constants'); * @param {Object} deps.helpers - Apps helpers module * @returns {express.Router} */ -module.exports = function({ docker, caddy, servicesStateManager, asyncHandler, log, helpers }) { +module.exports = function(ctx) { + const { docker, caddy, servicesStateManager, asyncHandler, log, helpers } = ctx; const router = express.Router(); /** diff --git a/dashcaddy-api/routes/apps/templates.js b/dashcaddy-api/routes/apps/templates.js index 1242916..d1eae64 100644 --- a/dashcaddy-api/routes/apps/templates.js +++ b/dashcaddy-api/routes/apps/templates.js @@ -10,7 +10,8 @@ const { exists } = require('../../fs-helpers'); */ const { REGEX } = require('../../constants'); -module.exports = function({ servicesStateManager, asyncHandler, helpers }) { +module.exports = function(ctx) { + const { servicesStateManager, asyncHandler, helpers, docker, caddy, log, errorResponse } = ctx; const router = express.Router(); // Get available app templates diff --git a/dashcaddy-api/routes/arr/config.js b/dashcaddy-api/routes/arr/config.js index 1b7f2bc..ed4afb0 100644 --- a/dashcaddy-api/routes/arr/config.js +++ b/dashcaddy-api/routes/arr/config.js @@ -17,7 +17,8 @@ const { logError } = require('../../src/utils/logging'); * @param {Object} deps.helpers - Arr helpers module * @returns {express.Router} */ -module.exports = function({ credentialManager, servicesStateManager, docker, fetchT, asyncHandler, errorResponse, log, helpers, notification, safeErrorMessage }) { +module.exports = function(ctx) { + const { credentialManager, servicesStateManager, docker, fetchT, asyncHandler, errorResponse, log, helpers, notification, safeErrorMessage } = ctx; const router = express.Router(); // Ctx shim for backward compatibility diff --git a/dashcaddy-api/routes/arr/index.js b/dashcaddy-api/routes/arr/index.js index 58d6ba7..f19d34a 100644 --- a/dashcaddy-api/routes/arr/index.js +++ b/dashcaddy-api/routes/arr/index.js @@ -24,14 +24,15 @@ module.exports = function(ctx) { }; // Initialize helpers with dependencies - const helpers = require('./helpers')(deps); + const helpers = require('./helpers')(ctx); - // Mount sub-routes with explicit dependencies - router.use(require('./detect')({ ...deps, helpers })); - router.use(require('./credentials')({ ...deps, helpers })); - router.use(require('./config')({ ...deps, helpers })); - router.use(require('./smart-connect')({ ...deps, helpers })); - router.use(require('./plex')({ ...deps, helpers })); + // Mount sub-routes — pass full ctx so sub-routes can reference ctx.* properties + const subCtx = Object.assign({}, ctx, { helpers }); + router.use(require('./detect')(subCtx)); + router.use(require('./credentials')(subCtx)); + router.use(require('./config')(subCtx)); + router.use(require('./smart-connect')(subCtx)); + router.use(require('./plex')(subCtx)); return router; }; diff --git a/dashcaddy-api/routes/arr/plex.js b/dashcaddy-api/routes/arr/plex.js index ee0771d..268f8a1 100644 --- a/dashcaddy-api/routes/arr/plex.js +++ b/dashcaddy-api/routes/arr/plex.js @@ -11,7 +11,8 @@ const { APP_PORTS } = require('../../constants'); * @param {Object} deps.helpers - Arr helpers module * @returns {express.Router} */ -module.exports = function({ fetchT, asyncHandler, errorResponse, log, helpers }) { +module.exports = function(ctx) { + const { fetchT, asyncHandler, errorResponse, log, helpers } = ctx; const router = express.Router(); // Plex Libraries endpoint diff --git a/dashcaddy-api/routes/arr/smart-connect.js b/dashcaddy-api/routes/arr/smart-connect.js index ce61b87..db17b60 100644 --- a/dashcaddy-api/routes/arr/smart-connect.js +++ b/dashcaddy-api/routes/arr/smart-connect.js @@ -12,7 +12,8 @@ const { APP_PORTS } = require('../../constants'); * @param {Object} deps.helpers - Arr helpers module * @returns {express.Router} */ -module.exports = function({ credentialManager, fetchT, asyncHandler, errorResponse, log, helpers }) { +module.exports = function(ctx) { + const { credentialManager, fetchT, asyncHandler, errorResponse, log, helpers } = ctx; const router = express.Router(); // Smart Connect: Unified orchestration endpoint diff --git a/dashcaddy-api/routes/config/assets.js b/dashcaddy-api/routes/config/assets.js index f027308..1fee826 100644 --- a/dashcaddy-api/routes/config/assets.js +++ b/dashcaddy-api/routes/config/assets.js @@ -22,7 +22,8 @@ try { // Image processing libraries not available — favicon conversion disabled } -module.exports = function({ servicesStateManager, asyncHandler, log }) { +module.exports = function(ctx) { + const { servicesStateManager, asyncHandler, log } = ctx; const router = express.Router(); // ===== ASSET UPLOAD ===== diff --git a/dashcaddy-api/routes/config/index.js b/dashcaddy-api/routes/config/index.js index 3f350bd..9ad2333 100644 --- a/dashcaddy-api/routes/config/index.js +++ b/dashcaddy-api/routes/config/index.js @@ -35,8 +35,8 @@ module.exports = function(ctx) { saveTotpConfig: ctx.saveTotpConfig }; - router.use(require('./settings')(baseDeps)); - router.use(require('./assets')({ ...baseDeps, CONFIG_FILE: ctx.CONFIG_FILE, readConfig: ctx.readConfig, saveConfig: ctx.saveConfig, errorResponse: ctx.errorResponse })); + router.use(require('./settings')(ctx)); + router.use(require('./assets')(ctx)); router.use(require('./backup')(backupDeps)); return router; }; diff --git a/dashcaddy-api/routes/config/settings.js b/dashcaddy-api/routes/config/settings.js index d5e55c3..439b88d 100644 --- a/dashcaddy-api/routes/config/settings.js +++ b/dashcaddy-api/routes/config/settings.js @@ -3,15 +3,13 @@ const { validateConfig } = require('../../config-schema'); const { exists } = require('../../fs-helpers'); const { ValidationError } = require('../../errors'); -module.exports = function({ configStateManager, asyncHandler, log }) { /** * Config settings routes factory - * @param {Object} deps - Explicit dependencies - * @param {Object} deps.configStateManager - Config state manager - * @param {Function} deps.asyncHandler - Async route handler wrapper - * @param {Object} deps.log - Logger instance + * @param {Object} ctx - Application context * @returns {express.Router} */ +module.exports = function(ctx) { + const { configStateManager, asyncHandler, log } = ctx; const express = require('express'); const router = express.Router(); diff --git a/dashcaddy-api/routes/recipes/deploy.js b/dashcaddy-api/routes/recipes/deploy.js index 3a82473..08bf367 100644 --- a/dashcaddy-api/routes/recipes/deploy.js +++ b/dashcaddy-api/routes/recipes/deploy.js @@ -14,7 +14,8 @@ const { DOCKER } = require('../../constants'); * @param {Object} deps.log - Logger instance * @returns {express.Router} */ -module.exports = function({ docker, credentialManager, servicesStateManager, asyncHandler, errorResponse, log }) { +module.exports = function(ctx) { + const { docker, credentialManager, servicesStateManager, asyncHandler, errorResponse, log } = ctx; const router = express.Router(); /** diff --git a/dashcaddy-api/routes/recipes/index.js b/dashcaddy-api/routes/recipes/index.js index 5d83f0c..12e3c80 100644 --- a/dashcaddy-api/routes/recipes/index.js +++ b/dashcaddy-api/routes/recipes/index.js @@ -61,9 +61,9 @@ module.exports = function(ctx) { res.json({ success: true, recipe: { id: req.params.recipeId, ...recipe } }); }, 'recipe-template-detail')); - // Mount deploy and manage sub-routes - router.use(deployRoutes(deps)); - router.use(manageRoutes(deps)); + // Mount deploy and manage sub-routes — pass full ctx for sub-routes that reference ctx.* + router.use(deployRoutes(ctx)); + router.use(manageRoutes(ctx)); return router; }; diff --git a/dashcaddy-api/routes/recipes/manage.js b/dashcaddy-api/routes/recipes/manage.js index d946a22..aefc9f8 100644 --- a/dashcaddy-api/routes/recipes/manage.js +++ b/dashcaddy-api/routes/recipes/manage.js @@ -2,7 +2,8 @@ const express = require('express'); const { DOCKER } = require('../../constants'); const { NotFoundError } = require('../../errors'); -module.exports = function({ servicesStateManager, asyncHandler, log }) { +module.exports = function(ctx) { + const { servicesStateManager, asyncHandler, log } = ctx; const router = express.Router(); /** * Recipes management routes factory diff --git a/dashcaddy-api/src/app.js b/dashcaddy-api/src/app.js index e7d4a73..97b760b 100644 --- a/dashcaddy-api/src/app.js +++ b/dashcaddy-api/src/app.js @@ -102,13 +102,25 @@ async function createApp() { log.warn('server', 'CA cert not found — HTTPS calls may fail', { path: CA_CERT_PATH }); } - // TOTP configuration + // TOTP configuration (defaults, overridden by loadTotpConfig below) let totpConfig = { enabled: false, sessionDuration: 'never', isSetUp: false }; + // Load TOTP config from file + try { + if (fs.existsSync(config.TOTP_CONFIG_FILE)) { + const loaded = JSON.parse(fs.readFileSync(config.TOTP_CONFIG_FILE, 'utf8')); + delete loaded.secret; // secret belongs only in credential-manager + Object.assign(totpConfig, loaded); + log.info('config', 'TOTP config loaded', { enabled: totpConfig.enabled }); + } + } catch (e) { + log.warn('config', 'Could not load TOTP config', { error: e.message }); + } + // Tailscale configuration let tailscaleConfig = { enabled: false, @@ -192,7 +204,12 @@ async function createApp() { } async function saveTotpConfig() { - // Stub - will be implemented + try { + const { writeJsonFile } = require('../fs-helpers'); + await writeJsonFile(config.TOTP_CONFIG_FILE, totpConfig); + } catch (e) { + log.error('config', 'Could not save TOTP config', { error: e.message }); + } } async function loadNotificationConfig() {