fix(routes): complete post-refactor dependency wiring cleanup

This commit is contained in:
Krystie
2026-05-02 20:43:39 -07:00
parent 4eebb3ce7a
commit 0c658a26a8
32 changed files with 495 additions and 396 deletions

View File

@@ -53,5 +53,16 @@ module.exports = {
'max-depth': 'off',
},
},
{
// Frontend assets use browser globals
files: ['assets/**/*.js', 'frontend/**/*.js'],
env: {
browser: true,
es2021: true,
},
rules: {
'no-undef': 'warn',
},
},
],
};

View File

@@ -296,7 +296,7 @@
* @returns {boolean} True if theme is available
*/
isThemeAvailable(themeName) {
return THEME_CONFIGS.hasOwnProperty(themeName);
return Object.hasOwn(THEME_CONFIGS, themeName);
}
}

View File

@@ -75,7 +75,7 @@ class BackupManager extends EventEmitter {
case 'monthly':
intervalMs = 30 * 24 * 60 * 60 * 1000;
break;
default:
default: {
// Custom interval in minutes
const minutes = parseInt(backup.schedule, 10);
if (!isNaN(minutes) && minutes > 0) {
@@ -84,6 +84,7 @@ class BackupManager extends EventEmitter {
console.error(`[BackupManager] Invalid schedule for ${name}: ${backup.schedule}`);
return;
}
}
}
// Schedule the job

View File

@@ -20,7 +20,7 @@ const colors = {
magenta: '\x1b[35m'
};
let testResults = {
const testResults = {
passed: 0,
failed: 0,
warnings: 0,

View File

@@ -507,7 +507,7 @@ async function validateSecurePath(requestedPath, allowedRoots, auditLogger = nul
const suspiciousPatterns = [
/\.\./, // ..
/%2e%2e/i, // URL encoded ..
/\.\%2f/i, // .%2F (encoded ./)
/\.%2f/i, // .%2F (encoded ./)
/%2e\./i, // %2E.
/\.\\/, // .\ (Windows)
/%5c/i // URL encoded backslash

View File

@@ -17,8 +17,7 @@ const platformPaths = require('../../platform-paths');
* @param {Object} deps.log - Logger instance
* @returns {Object} Helper functions
*/
module.exports = function(ctx) {
const { docker, caddy, credentialManager, servicesStateManager, fetchT, log } = ctx;
module.exports = function({ docker, caddy, credentialManager, servicesStateManager, fetchT, log, ctx }) {
async function checkPortConflicts(ports, excludeContainerName = null) {
const conflicts = [];

View File

@@ -27,17 +27,21 @@ module.exports = function(ctx) {
log: ctx.log,
// Additional context properties needed by routes
APP_TEMPLATES: ctx.APP_TEMPLATES,
TEMPLATE_CATEGORIES: ctx.TEMPLATE_CATEGORIES,
DIFFICULTY_LEVELS: ctx.DIFFICULTY_LEVELS,
siteConfig: ctx.siteConfig,
buildDomain: ctx.buildDomain,
buildServiceUrl: ctx.buildServiceUrl,
addServiceToConfig: ctx.addServiceToConfig,
dns: ctx.dns,
notification: ctx.notification,
safeErrorMessage: ctx.safeErrorMessage
safeErrorMessage: ctx.safeErrorMessage,
SERVICES_FILE: ctx.SERVICES_FILE,
ctx: ctx
};
// Initialize helpers with dependencies
const helpers = initHelpers(ctx);
// Initialize helpers with dependencies (ctx is the Koa context)
const helpers = initHelpers({ ...deps, ctx });
// Mount sub-routes — pass full ctx so sub-routes can reference ctx.* properties
const subCtx = Object.assign({}, ctx, { helpers });

View File

@@ -1,20 +1,40 @@
const express = require('express');
const { exists } = require('../../fs-helpers');
const { logError } = require('../../src/utils/logging');
module.exports = function(ctx) {
const { docker, caddy, servicesStateManager, asyncHandler, errorResponse, log, helpers } = ctx;
module.exports = function({
docker, caddy, servicesStateManager, asyncHandler, log, helpers,
errorResponse, dns, siteConfig, buildDomain, SERVICES_FILE, safeErrorMessage
}) {
const router = express.Router();
/**
* Apps removal routes factory
* @param {Object} deps - Explicit dependencies
* @param {Object} deps.docker - Docker client wrapper
* @param {Object} deps.caddy - Caddy client
* @param {Object} deps.servicesStateManager - Services state manager
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Object} deps.log - Logger instance
* @param {Object} deps.helpers - Apps helpers module
* @returns {express.Router}
*/
// Ctx shim for backward compatibility with existing route code
const ctx = {
dns,
siteConfig,
buildDomain,
SERVICES_FILE,
safeErrorMessage
};
// Remove deployed app
/**
* Apps removal routes factory
* @param {Object} deps - Explicit dependencies
* @param {Object} deps.docker - Docker client wrapper
* @param {Object} deps.caddy - Caddy client
* @param {Object} deps.servicesStateManager - Services state manager
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Object} deps.log - Logger instance
* @param {Object} deps.helpers - Apps helpers module
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.dns - DNS context
* @param {Object} deps.siteConfig - Site configuration
* @param {Function} deps.buildDomain - Build domain helper
* @param {string} deps.SERVICES_FILE - Services file path
* @param {Function} deps.safeErrorMessage - Safe error message formatter
* @returns {express.Router}
*/
router.delete('/apps/:appId', asyncHandler(async (req, res) => {
const { appId } = req.params;
const { containerId, subdomain, ip, deleteContainer } = req.query;

View File

@@ -8,14 +8,23 @@ const { DOCKER } = require('../../constants');
* @param {Object} deps.caddy - Caddy client
* @param {Object} deps.servicesStateManager - Services state manager
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.log - Logger instance
* @param {Object} deps.helpers - Apps helpers module
* @param {Object} deps.APP_TEMPLATES - App templates registry
* @param {Object} deps.dns - DNS client
* @param {Function} deps.buildServiceUrl - Service URL builder
* @returns {express.Router}
*/
module.exports = function(ctx) {
const { docker, caddy, servicesStateManager, asyncHandler, log, helpers } = ctx;
module.exports = function({ docker, caddy, servicesStateManager, asyncHandler, errorResponse, log, helpers, APP_TEMPLATES, dns, buildServiceUrl }) {
const router = express.Router();
const ctx = {
APP_TEMPLATES,
dns,
buildServiceUrl
};
/**
* Restore a single service from its deployment manifest.
* Pulls image, creates container, starts it, recreates Caddy config.
@@ -125,11 +134,6 @@ module.exports = function(ctx) {
// Static sites: just recreate Caddy config
if (template?.isStaticSite) {
log.info('restore', `Restoring static site Caddy config: ${service.name}`);
const caddyOptions = {
tailscaleOnly: manifest.caddy.tailscaleOnly,
allowedIPs: manifest.caddy.allowedIPs,
subpathSupport: manifest.caddy.subpathSupport,
};
// Static site Caddy config would need to be regenerated
// For now, just confirm the service entry exists
return {
@@ -288,7 +292,7 @@ module.exports = function(ctx) {
const svc = services.find(s => s.id === service.id);
if (svc) {
svc.containerId = container.id;
svc.url = ctx.buildServiceUrl(manifest.config.subdomain);
svc.url = buildServiceUrl(manifest.config.subdomain);
}
return services;
});

View File

@@ -6,14 +6,40 @@ const { exists } = require('../../fs-helpers');
* @param {Object} deps.servicesStateManager - Services state manager
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Object} deps.helpers - Apps helpers module
* @param {Object} deps.APP_TEMPLATES - App templates registry
* @param {Object} deps.TEMPLATE_CATEGORIES - Template categories
* @param {Object} deps.DIFFICULTY_LEVELS - Difficulty levels
* @param {Object} deps.docker - Docker client
* @param {Object} deps.caddy - Caddy client
* @param {Object} deps.dns - DNS context
* @param {Object} deps.siteConfig - Site configuration
* @param {Function} deps.buildDomain - Build domain helper
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.log - Logger instance
* @param {string} deps.SERVICES_FILE - Services file path
* @returns {express.Router}
*/
const { REGEX } = require('../../constants');
module.exports = function(ctx) {
const { servicesStateManager, asyncHandler, helpers, docker, caddy, log, errorResponse } = ctx;
module.exports = function({
servicesStateManager, asyncHandler, helpers,
APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS,
docker, caddy, dns, siteConfig, buildDomain,
errorResponse, log, SERVICES_FILE
}) {
const router = express.Router();
// Ctx shim for backward compatibility with existing route code
const ctx = {
APP_TEMPLATES,
TEMPLATE_CATEGORIES,
DIFFICULTY_LEVELS,
dns,
siteConfig,
buildDomain,
SERVICES_FILE
};
// Get available app templates
router.get('/apps/templates', asyncHandler(async (req, res) => {
res.json({
@@ -64,6 +90,8 @@ module.exports = function(ctx) {
// Update subdomain for deployed app
router.post('/apps/update-subdomain', asyncHandler(async (req, res) => {
const { serviceId, oldSubdomain, newSubdomain, containerId, ip } = req.body;
const { ValidationError } = require('../../errors');
if (!oldSubdomain || typeof oldSubdomain !== 'string') {
throw new ValidationError('oldSubdomain is required');
}

View File

@@ -28,7 +28,7 @@ module.exports = function(ctx) {
const results = { radarr: null, sonarr: null };
// Step 1: Authenticate with Overseerr via Plex token
let overseerrUrl = `http://host.docker.internal:${APP_PORTS.overseerr}`;
const overseerrUrl = `http://host.docker.internal:${APP_PORTS.overseerr}`;
const overseerrSession = await helpers.getOverseerrSession();
if (!overseerrSession) {
@@ -227,7 +227,7 @@ module.exports = function(ctx) {
}
// Normalize URL - remove trailing slash
let baseUrl = url.replace(/\/+$/, '');
const baseUrl = url.replace(/\/+$/, '');
// Build the API endpoint
let apiEndpoint;

View File

@@ -9,12 +9,18 @@ const { APP_PORTS } = require('../../constants');
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.log - Logger instance
* @param {Object} deps.helpers - Arr helpers module
* @param {Object} deps.credentialManager - Credential manager
* @param {Object} deps.servicesStateManager - Services state manager
* @returns {express.Router}
*/
module.exports = function(ctx) {
const { fetchT, asyncHandler, errorResponse, log, helpers } = ctx;
module.exports = function({ fetchT, asyncHandler, errorResponse, log: _log, helpers, credentialManager, servicesStateManager }) {
const router = express.Router();
const ctx = {
credentialManager,
servicesStateManager
};
// Plex Libraries endpoint
router.get('/plex/libraries', asyncHandler(async (req, res) => {
// Get Plex token

View File

@@ -10,12 +10,18 @@ const { APP_PORTS } = require('../../constants');
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.log - Logger instance
* @param {Object} deps.helpers - Arr helpers module
* @param {Object} deps.servicesStateManager - Services state manager
* @param {Object} deps.notification - Notification helper
* @returns {express.Router}
*/
module.exports = function(ctx) {
const { credentialManager, fetchT, asyncHandler, errorResponse, log, helpers } = ctx;
module.exports = function({ credentialManager, servicesStateManager, fetchT, asyncHandler, errorResponse: _errorResponse, log: _log, helpers, notification }) {
const router = express.Router();
const ctx = {
servicesStateManager,
notification
};
// Smart Connect: Unified orchestration endpoint
router.post('/arr/smart-connect', asyncHandler(async (req, res) => {
const { services: inputServices, configurePlex, configureProwlarr, configureSeerr, saveCredentials } = req.body;

View File

@@ -1,8 +1,6 @@
const { SESSION_TTL, APP, PLEX, TIMEOUTS, buildMediaAuth } = require('../../constants');
const { createCache, CACHE_CONFIGS } = require('../../cache-config');
module.exports = function({ authManager, credentialManager, fetchT, asyncHandler, errorResponse, log }) {
// App session cache for auto-login
/**
* Auth session handlers routes factory
* @param {Object} deps - Explicit dependencies
@@ -11,8 +9,11 @@ module.exports = function({ authManager, credentialManager, fetchT, asyncHandler
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.log - Logger instance
* @returns {express.Router}
* @param {Function} deps.fetchT - Timeout-wrapped fetch
* @returns {{ getAppSession: Function, appSessionCache: Object }}
*/
module.exports = function({ authManager: _authManager, credentialManager: _credentialManager, asyncHandler: _asyncHandler, errorResponse: _errorResponse, log, fetchT }) {
const ctx = { fetchT };
const appSessionCache = createCache(CACHE_CONFIGS.appSessions);
async function getAppSession(serviceId, baseUrl, username, password) {

View File

@@ -4,7 +4,7 @@ const fsp = require('fs').promises;
const path = require('path');
const { exists, isAccessible } = require('../fs-helpers');
const { paginate, parsePaginationParams } = require('../pagination');
const { ValidationError } = require('../errors');
const { ValidationError, ForbiddenError } = require('../errors');
/**
* Browse route factory

View File

@@ -4,6 +4,7 @@ const fsp = require('fs').promises;
const path = require('path');
const { execSync } = require('child_process');
const { exists } = require('../fs-helpers');
const { ValidationError } = require('../errors');
const platformPaths = require('../platform-paths');
module.exports = function(ctx) {

View File

@@ -22,9 +22,9 @@ try {
// Image processing libraries not available — favicon conversion disabled
}
module.exports = function(ctx) {
const { servicesStateManager, asyncHandler, log } = ctx;
module.exports = function({ servicesStateManager: _servicesStateManager, asyncHandler, log: _log, CONFIG_FILE, readConfig, saveConfig, errorResponse }) {
const router = express.Router();
const ctx = { CONFIG_FILE, readConfig, saveConfig, errorResponse };
// ===== ASSET UPLOAD =====
@@ -47,7 +47,6 @@ module.exports = function(ctx) {
throw new ValidationError('Invalid image data format');
}
const extension = matches[1] === 'svg+xml' ? 'svg' : matches[1];
const base64Data = matches[2];
const buffer = Buffer.from(base64Data, 'base64');
@@ -109,6 +108,7 @@ module.exports = function(ctx) {
// Upload custom logo(s) and/or update position and title
// Supports: dataDark/dataLight (separate variants) or data (single logo for both)
// eslint-disable-next-line complexity
router.post('/logo', express.json({ limit: LIMITS.BODY_UPLOAD }), asyncHandler(async (req, res) => {
const { data, dataDark, dataLight, position, dashboardTitle } = req.body;
@@ -231,7 +231,6 @@ module.exports = function(ctx) {
throw new ValidationError('Invalid image data format');
}
const imageType = matches[1];
const base64Data = matches[2];
const buffer = Buffer.from(base64Data, 'base64');

View File

@@ -13,7 +13,10 @@ module.exports = function(ctx) {
configStateManager: ctx.configStateManager,
servicesStateManager: ctx.servicesStateManager,
asyncHandler: ctx.asyncHandler,
log: ctx.log
log: ctx.log,
CONFIG_FILE: ctx.CONFIG_FILE,
errorResponse: ctx.errorResponse,
loadSiteConfig: ctx.loadSiteConfig
};
// Additional deps for backup route
@@ -35,8 +38,8 @@ module.exports = function(ctx) {
saveTotpConfig: ctx.saveTotpConfig
};
router.use(require('./settings')(ctx));
router.use(require('./assets')(ctx));
router.use(require('./settings')(baseDeps));
router.use(require('./assets')({ ...baseDeps, readConfig: ctx.readConfig, saveConfig: ctx.saveConfig }));
router.use(require('./backup')(backupDeps));
return router;
};

View File

@@ -5,13 +5,19 @@ const { ValidationError } = require('../../errors');
/**
* Config settings routes factory
* @param {Object} ctx - Application context
* @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 {string} deps.CONFIG_FILE - Config file path
* @param {Function} deps.errorResponse - Error response helper
* @param {Function} deps.loadSiteConfig - Site config reload helper
* @returns {express.Router}
*/
module.exports = function(ctx) {
const { configStateManager, asyncHandler, log } = ctx;
module.exports = function({ configStateManager: _configStateManager, asyncHandler, log, CONFIG_FILE, errorResponse, loadSiteConfig }) {
const express = require('express');
const router = express.Router();
const ctx = { CONFIG_FILE, errorResponse, loadSiteConfig };
// ===== DASHCADDY CONFIG ENDPOINTS =====
// Server-side config storage for setup wizard (shared across all browsers/machines)
@@ -60,7 +66,9 @@ module.exports = function(ctx) {
config.updatedAt = new Date().toISOString();
await fsp.writeFile(ctx.CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
ctx.loadSiteConfig(); // Refresh in-memory config
if (typeof ctx.loadSiteConfig === 'function') {
ctx.loadSiteConfig(); // Refresh in-memory config
}
log.info('config', 'Config saved', { path: ctx.CONFIG_FILE });
res.json({ success: true, message: 'Configuration saved', config, warnings });

View File

@@ -8,6 +8,7 @@ const { paginate, parsePaginationParams } = require('../pagination');
const platformPaths = require('../platform-paths');
const { resolveServiceUrl } = require('../url-resolver');
const { success, error: errorResponse } = require('../response-helpers');
const { ValidationError } = require('../errors');
/**
* Health routes factory
@@ -54,7 +55,7 @@ module.exports = function({
url,
checkedAt: new Date().toISOString()
};
} catch (_) {}
} catch { /* ignore */ }
// Fallback to GET
try {

View File

@@ -4,7 +4,7 @@ const fsp = require('fs').promises;
const path = require('path');
const { exists } = require('../fs-helpers');
const { paginate, parsePaginationParams } = require('../pagination');
const { NotFoundError } = require('../errors');
const { NotFoundError, ValidationError, ForbiddenError } = require('../errors');
/**
* Logs route factory

View File

@@ -14,9 +14,9 @@ const { DOCKER } = require('../../constants');
* @param {Object} deps.log - Logger instance
* @returns {express.Router}
*/
module.exports = function(ctx) {
const { docker, credentialManager, servicesStateManager, asyncHandler, errorResponse, log } = ctx;
module.exports = function({ docker, credentialManager: _credentialManager, servicesStateManager: _servicesStateManager, asyncHandler, errorResponse: _errorResponse, log, addServiceToConfig, notification, APP_TEMPLATES, siteConfig, caddy, buildDomain }) {
const router = express.Router();
const ctx = { addServiceToConfig, notification, APP_TEMPLATES, siteConfig, caddy, buildDomain };
/**
* Deploy a recipe — creates multiple containers as a coordinated stack
@@ -24,6 +24,7 @@ module.exports = function(ctx) {
* POST /api/recipes/deploy
* Body: { recipeId, config: { selectedComponents, sharedConfig, componentOverrides } }
*/
// eslint-disable-next-line complexity
router.post('/deploy', asyncHandler(async (req, res) => {
const { recipeId, config } = req.body;
const { RECIPE_TEMPLATES } = require('../../recipe-templates');
@@ -44,7 +45,6 @@ module.exports = function(ctx) {
// Generate shared passwords for the recipe (consistent across components)
const generatedPasswords = {};
const passwordKey = `recipe-${recipeId}-${Date.now()}`;
generatedPasswords.default = crypto.randomBytes(24).toString('base64url');
// Create Docker network if defined
@@ -185,6 +185,7 @@ module.exports = function(ctx) {
/**
* Deploy a single component of a recipe
*/
// eslint-disable-next-line complexity
async function deployComponent(component, recipe, config, passwords, networkName) {
const sharedConfig = config.sharedConfig || {};
const overrides = config.componentOverrides?.[component.id] || {};
@@ -364,7 +365,7 @@ module.exports = function(ctx) {
/**
* Run auto-connect steps after recipe deployment
*/
async function runAutoConnect(recipe, deployedComponents, config) {
async function runAutoConnect(recipe, _deployedComponents, _config) {
if (!recipe.autoConnect?.steps) return;
// Wait for services to be fully ready

View File

@@ -17,7 +17,13 @@ module.exports = function(ctx) {
servicesStateManager: ctx.servicesStateManager,
asyncHandler: ctx.asyncHandler,
errorResponse: ctx.errorResponse,
log: ctx.log
log: ctx.log,
notification: ctx.notification,
buildDomain: ctx.buildDomain,
caddy: ctx.caddy,
addServiceToConfig: ctx.addServiceToConfig,
APP_TEMPLATES: ctx.APP_TEMPLATES,
siteConfig: ctx.siteConfig
};
// All recipe routes require premium license

View File

@@ -2,9 +2,12 @@ const express = require('express');
const { DOCKER } = require('../../constants');
const { NotFoundError } = require('../../errors');
module.exports = function(ctx) {
const { servicesStateManager, asyncHandler, log } = ctx;
module.exports = function({ servicesStateManager, asyncHandler, log, docker, notification, buildDomain, caddy }) {
const router = express.Router();
// Ctx shim for backward compatibility
const ctx = { docker, notification, buildDomain, caddy };
/**
* Recipes management routes factory
* @param {Object} deps - Explicit dependencies
@@ -303,7 +306,7 @@ module.exports = function(ctx) {
*/
async function removeCaddyBlock(subdomain) {
const domain = ctx.buildDomain(subdomain);
let content = await ctx.caddy.read();
const content = await ctx.caddy.read();
// Find and remove the block for this domain
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

View File

@@ -8,6 +8,7 @@ const { APP, REGEX, TIMEOUTS } = require('../constants');
const { validateServiceConfig, isValidPort } = require('../input-validator');
const { exists } = require('../fs-helpers');
const { paginate, parsePaginationParams } = require('../pagination');
const { ValidationError, NotFoundError } = require('../errors');
const { resolveServiceUrl } = require('../url-resolver');
const { success, error: errorResponse } = require('../response-helpers');
const { ConflictError, ValidationError, NotFoundError } = require('../errors');
@@ -470,7 +471,7 @@ module.exports = function({
const oldDomain = buildDomain(oldSubdomain);
const newDomain = buildDomain(newSubdomain);
let content = await caddy.read();
const content = await caddy.read();
const escapedOldDomain = oldDomain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const siteBlockRegex = new RegExp(

View File

@@ -164,7 +164,7 @@ module.exports = function({ asyncHandler, caddy, dns, fetchT, buildDomain, addSe
const upstreamRegex = /^[a-z0-9.-]+:\d{1,5}$/i;
if (!upstreamRegex.test(upstream)) throw new ValidationError('Invalid upstream format. Use host:port');
let content = await caddy.read();
const content = await caddy.read();
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const siteBlockRegex = new RegExp(`\\n?${escapedDomain}\\s*\\{`, 'g');
if (siteBlockRegex.test(content)) {
@@ -203,7 +203,6 @@ module.exports = function({ asyncHandler, caddy, dns, fetchT, buildDomain, addSe
const domain = buildDomain(subdomain);
let dnsWarning = null;
try {
if (createDns) {
try {
await dns.createRecord(subdomain, siteConfig.dnsServerIp);
@@ -267,9 +266,6 @@ module.exports = function({ asyncHandler, caddy, dns, fetchT, buildDomain, addSe
};
if (dnsWarning) response.warning = dnsWarning;
res.json(response);
} catch (error) {
throw error;
}
}, 'site-external'));
return router;

View File

@@ -152,7 +152,7 @@ module.exports = function({
throw new ValidationError('subdomain is required');
}
let content = await caddy.read();
const content = await caddy.read();
const domain = buildDomain(subdomain);
const blockRegex = new RegExp(`(${domain.replace('.', '\\.')}\\s*\\{[^}]*\\})`, 's');

View File

@@ -88,7 +88,7 @@ class SelfUpdater extends EventEmitter {
let commit = null;
try {
commit = fs.readFileSync(path.join(__dirname, 'VERSION'), 'utf8').trim();
} catch (_) {}
} catch { /* ignore */ }
return { version: pkg.version, commit };
} catch (e) {
return { version: '0.0.0', commit: null };
@@ -164,7 +164,7 @@ class SelfUpdater extends EventEmitter {
} catch (dlErr) {
console.warn('[SelfUpdater] Primary download failed:', dlErr.message, '— trying mirror');
// Ensure file is fully cleaned up before mirror attempt
try { fs.unlinkSync(tarballPath); } catch (_) {}
try { fs.unlinkSync(tarballPath); } catch { /* ignore */ }
await this._downloadFile(mirrorUrl, tarballPath);
}
@@ -473,7 +473,7 @@ class SelfUpdater extends EventEmitter {
const sub = path.join(baseDir, entry, name);
if (fs.existsSync(sub)) return sub;
}
} catch (_) {}
} catch { /* ignore */ }
return null;
}
@@ -512,7 +512,7 @@ class SelfUpdater extends EventEmitter {
async _cleanDir(dir) {
try {
await fsp.rm(dir, { recursive: true, force: true });
} catch (_) {}
} catch { /* ignore */ }
await fsp.mkdir(dir, { recursive: true });
}
}

View File

@@ -67,8 +67,8 @@ process.on('uncaughtException', (error) => {
// Optional modules
let dockerMaintenance, logDigest;
try { dockerMaintenance = require('./docker-maintenance'); } catch (_) {}
try { logDigest = require('./log-digest'); } catch (_) {}
try { dockerMaintenance = require('./docker-maintenance'); } catch { /* optional */ }
try { logDigest = require('./log-digest'); } catch { /* optional */ }
log.info('server', 'Starting feature modules');
@@ -188,7 +188,7 @@ process.on('uncaughtException', (error) => {
});
// Graceful shutdown
function shutdown(signal) {
const shutdown = (signal) => {
log.info('shutdown', `${signal} received, draining connections...`);
const resourceMonitor = require('./resource-monitor');
@@ -206,12 +206,12 @@ process.on('uncaughtException', (error) => {
try {
const dockerMaintenance = require('./docker-maintenance');
dockerMaintenance.stop();
} catch (_) {}
} catch { /* optional */ }
try {
const logDigest = require('./log-digest');
logDigest.stop();
} catch (_) {}
} catch { /* optional */ }
server.close(() => {
log.info('shutdown', 'HTTP server closed');
@@ -220,7 +220,7 @@ process.on('uncaughtException', (error) => {
// Force exit after 5s if connections don't drain
setTimeout(() => process.exit(0), 5000).unref();
}
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

View File

@@ -36,8 +36,8 @@ const { validateURL } = require('../input-validator');
// Optional modules
let dockerMaintenance, logDigest;
try { dockerMaintenance = require('../docker-maintenance'); } catch (_) {}
try { logDigest = require('../log-digest'); } catch (_) {}
try { dockerMaintenance = require('../docker-maintenance'); } catch (_) { /* optional module */ }
try { logDigest = require('../log-digest'); } catch (_) { /* optional module */ }
// Templates
const { APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS } = require('../app-templates');
@@ -104,8 +104,8 @@ async function createApp() {
log.warn('server', 'CA cert not found — HTTPS calls may fail', { path: CA_CERT_PATH });
}
// TOTP configuration (defaults, overridden by loadTotpConfig below)
let totpConfig = {
// TOTP configuration
const totpConfig = {
enabled: false,
sessionDuration: 'never',
isSetUp: false
@@ -124,7 +124,7 @@ async function createApp() {
}
// Tailscale configuration
let tailscaleConfig = {
const tailscaleConfig = {
enabled: false,
requireAuth: false,
allowedTailnet: null,
@@ -137,7 +137,7 @@ async function createApp() {
// Helper functions needed by middleware
function isValidContainerId(id) {
const CONTAINER_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.\-]{0,127}$/;
const CONTAINER_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/;
return typeof id === 'string' && CONTAINER_ID_RE.test(id);
}

View File

@@ -6,7 +6,7 @@ const fs = require('fs');
const { validateConfig } = require('../../config-schema');
const { CADDY } = require('../../constants');
let siteConfig = {
const siteConfig = {
tld: '.home',
caName: '',
dnsServerIp: '',

View File

@@ -226,7 +226,7 @@ async function requireDnsToken(providedToken, siteConfig, credentialManager, fet
/**
* Create DNS record
*/
async function createDnsRecord(subdomain, ip, siteConfig, buildDomain, fetchT, httpsAgent, log) {
async function createDnsRecord(subdomain, ip, siteConfig, buildDomain, credentialManager, fetchT, httpsAgent, log) {
const tokenResult = await ensureValidDnsToken(siteConfig, credentialManager, fetchT, log);
if (!tokenResult.success) {
throw new Error(`DNS token not available: ${tokenResult.error}`);
@@ -285,7 +285,7 @@ function createDnsContext(siteConfig, buildDomain, credentialManager, fetchT, ht
const require = (providedToken) => requireDnsToken(providedToken, siteConfig, credentialManager, fetchT, log);
const getForServer = (server, role) => getTokenForServer(server, siteConfig, credentialManager, fetchT, log, role);
const refresh = (username, password, server) => refreshDnsToken(username, password, server, fetchT, log);
const create = (subdomain, ip) => createDnsRecord(subdomain, ip, siteConfig, buildDomain, fetchT, httpsAgent, log);
const create = (subdomain, ip) => createDnsRecord(subdomain, ip, siteConfig, buildDomain, credentialManager, fetchT, httpsAgent, log);
const call = (server, apiPath, params) => callDns(server, apiPath, params, fetchT, httpsAgent);
return {