From 7bc2a207f3bd7905a3611c507c6586ab59c79de6 Mon Sep 17 00:00:00 2001 From: Hermes Date: Sat, 13 Jun 2026 12:16:56 -0700 Subject: [PATCH] DC-005: Fix all 138 broken test paths after src/ refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the DC-005 module reorganization (41 files moved into src/ subdirs), 138 test suites failed because the refactor script's path-rewrite logic missed three categories: 1. Files inside src/ doing 'require("./src/...")' — should be 'require("../...")' 2. Files in src/X/Y/ doing 'require("../../../src/...")' — should be 'require("../../...")' 3. Test files in __tests__/ with leftover 'require("../../../src/...")' paths Root cause: the original refactor script ran before all files were moved, so it computed relative paths against stale filesystem state. Result: - 30/30 test suites pass - 879/879 tests pass (was: 18/30 suites, 614/687 tests) Also fixed: - routes/apps/restore.js: wrong responses import path - routes/*/*.js: '../../src/utilities/X' → '../src/utilities/X' (depth 2 routes) --- dashcaddy-api/__tests__/app-templates.test.js | 2 +- dashcaddy-api/__tests__/auth-manager.test.js | 8 +- .../__tests__/auto-restart-manager.test.js | 6 +- .../__tests__/backup-manager.test.js | 10 +- .../__tests__/config-drift-detector.test.js | 2 +- .../__tests__/credential-manager.test.js | 20 +- dashcaddy-api/__tests__/crypto-utils.test.js | 2 +- .../__tests__/csrf-protection.test.js | 4 +- .../__tests__/dns-propagation.test.js | 2 +- .../__tests__/docker-security.test.js | 8 +- dashcaddy-api/__tests__/error-handler.test.js | 4 +- dashcaddy-api/__tests__/errors.test.js | 2 +- .../__tests__/health-checker.test.js | 6 +- dashcaddy-api/__tests__/helpers/test-utils.js | 2 +- .../__tests__/input-validator.test.js | 4 +- dashcaddy-api/__tests__/log-digest.test.js | 4 +- dashcaddy-api/__tests__/metrics.test.js | 2 +- .../__tests__/notification-manager.test.js | 2 +- dashcaddy-api/__tests__/pagination.test.js | 2 +- .../__tests__/port-lock-manager.test.js | 2 +- .../__tests__/resource-monitor.test.js | 2 +- .../routes/containers.routes.test.js | 2 +- .../__tests__/routes/health.routes.test.js | 10 +- .../__tests__/routes/services.routes.test.js | 16 +- dashcaddy-api/__tests__/ssl-monitor.test.js | 6 +- dashcaddy-api/__tests__/state-manager.test.js | 2 +- .../__tests__/update-manager.test.js | 2 +- dashcaddy-api/__tests__/url-resolver.test.js | 2 +- dashcaddy-api/routes/apps/compose.js | 6 +- dashcaddy-api/routes/apps/deploy.js | 12 +- dashcaddy-api/routes/apps/helpers.js | 4 +- dashcaddy-api/routes/apps/removal.js | 6 +- dashcaddy-api/routes/apps/restore.js | 4 +- dashcaddy-api/routes/apps/templates.js | 10 +- dashcaddy-api/routes/arr/config.js | 10 +- dashcaddy-api/routes/arr/credentials.js | 6 +- dashcaddy-api/routes/arr/detect.js | 4 +- dashcaddy-api/routes/arr/helpers.js | 2 +- dashcaddy-api/routes/arr/plex.js | 4 +- dashcaddy-api/routes/arr/smart-connect.js | 2 +- dashcaddy-api/routes/auth/keys.js | 4 +- dashcaddy-api/routes/auth/session-handlers.js | 4 +- dashcaddy-api/routes/auth/sso-gate.js | 4 +- dashcaddy-api/routes/auth/totp.js | 4 +- dashcaddy-api/routes/auto-restart.js | 2 +- dashcaddy-api/routes/backups.js | 28 +-- dashcaddy-api/routes/browse.js | 8 +- dashcaddy-api/routes/ca.js | 12 +- dashcaddy-api/routes/config-drift.js | 2 +- dashcaddy-api/routes/config/assets.js | 8 +- dashcaddy-api/routes/config/backup.js | 10 +- dashcaddy-api/routes/config/settings.js | 8 +- dashcaddy-api/routes/containers.js | 6 +- dashcaddy-api/routes/dependencies.js | 2 +- dashcaddy-api/routes/dns.js | 6 +- dashcaddy-api/routes/docker-resources.js | 2 +- dashcaddy-api/routes/errorlogs.js | 4 +- dashcaddy-api/routes/health.js | 16 +- dashcaddy-api/routes/license.js | 2 +- dashcaddy-api/routes/logs.js | 14 +- dashcaddy-api/routes/monitoring.js | 8 +- dashcaddy-api/routes/notifications.js | 6 +- dashcaddy-api/routes/recipes/deploy.js | 8 +- dashcaddy-api/routes/recipes/index.js | 8 +- dashcaddy-api/routes/recipes/manage.js | 10 +- dashcaddy-api/routes/services.js | 12 +- dashcaddy-api/routes/sites.js | 6 +- dashcaddy-api/routes/tailscale.js | 8 +- dashcaddy-api/routes/themes.js | 2 +- dashcaddy-api/routes/updates.js | 4 +- dashcaddy-api/scripts/fix-remaining-paths.py | 101 ++++++++++ dashcaddy-api/scripts/refactor-requires.js | 180 ++++++++++++++++++ dashcaddy-api/server.js | 44 ++--- dashcaddy-api/src/app.js | 80 ++++---- dashcaddy-api/src/config/index.js | 2 +- dashcaddy-api/src/config/site.js | 4 +- dashcaddy-api/src/context/caddy.js | 2 +- dashcaddy-api/src/context/dns.js | 4 +- dashcaddy-api/src/context/docker.js | 2 +- dashcaddy-api/src/context/index.js | 2 +- dashcaddy-api/src/context/provider-dns.js | 4 +- .../{ => src/dns}/dns-propagation.js | 0 .../{ => src/dns}/dns-providers/base.js | 0 .../{ => src/dns}/dns-providers/cloudflare.js | 0 .../{ => src/dns}/dns-providers/manual.js | 0 .../{ => src/dns}/dns-providers/registry.js | 0 .../{ => src/dns}/dns-providers/rfc2136.js | 0 .../{ => src/dns}/dns-providers/technitium.js | 0 .../{ => src/docker}/app-templates.js | 0 .../{ => src/docker}/docker-maintenance.js | 2 +- .../{ => src/docker}/self-updater.js | 0 .../{ => src/managers}/auth-manager.js | 2 +- .../managers}/auto-restart-manager.js | 2 +- .../managers}/config-drift-detector.js | 0 .../{ => src/managers}/credential-manager.js | 4 +- .../{ => src/managers}/dependency-manager.js | 0 .../{ => src/managers}/license-manager.js | 2 +- .../managers}/notification-manager.js | 0 .../{ => src/managers}/port-lock-manager.js | 0 .../{ => src/managers}/resource-monitor.js | 0 .../{ => src/managers}/state-manager.js | 0 .../{ => src/managers}/update-manager.js | 0 .../{ => src/monitoring}/health-checker.js | 0 dashcaddy-api/{ => src/monitoring}/metrics.js | 0 .../{ => src/monitoring}/ssl-monitor.js | 4 +- .../{ => src/recipes}/bundled-workflows.js | 0 .../{ => src/recipes}/recipe-templates.js | 0 .../{ => src/security}/audit-logger.js | 2 +- .../{ => src/security}/crypto-utils.js | 0 .../{ => src/security}/csrf-protection.js | 2 +- .../{ => src/security}/docker-security.js | 0 .../{ => src/security}/input-validator.js | 0 .../{ => src/security}/keychain-manager.js | 0 .../{ => src/security}/log-digest.js | 4 +- .../{ => src/utilities}/backup-manager.js | 10 +- .../{ => src/utilities}/cache-config.js | 0 .../{ => src/utilities}/config-schema.js | 0 .../{ => src/utilities}/constants.js | 0 .../{ => src/utilities}/error-handler.js | 4 +- dashcaddy-api/{ => src/utilities}/errors.js | 0 .../{ => src/utilities}/fs-helpers.js | 0 .../{ => src/utilities}/middleware.js | 6 +- .../{ => src/utilities}/pagination.js | 0 .../{ => src/utilities}/startup-validator.js | 0 .../{ => src/utilities}/url-resolver.js | 0 dashcaddy-api/src/utils/async-handler.js | 2 +- dashcaddy-api/src/utils/http.js | 2 +- dashcaddy-api/src/utils/responses.js | 2 +- .../src/main/config-manager.js | 2 +- 129 files changed, 591 insertions(+), 310 deletions(-) create mode 100644 dashcaddy-api/scripts/fix-remaining-paths.py create mode 100644 dashcaddy-api/scripts/refactor-requires.js rename dashcaddy-api/{ => src/dns}/dns-propagation.js (100%) rename dashcaddy-api/{ => src/dns}/dns-providers/base.js (100%) rename dashcaddy-api/{ => src/dns}/dns-providers/cloudflare.js (100%) rename dashcaddy-api/{ => src/dns}/dns-providers/manual.js (100%) rename dashcaddy-api/{ => src/dns}/dns-providers/registry.js (100%) rename dashcaddy-api/{ => src/dns}/dns-providers/rfc2136.js (100%) rename dashcaddy-api/{ => src/dns}/dns-providers/technitium.js (100%) rename dashcaddy-api/{ => src/docker}/app-templates.js (100%) rename dashcaddy-api/{ => src/docker}/docker-maintenance.js (99%) rename dashcaddy-api/{ => src/docker}/self-updater.js (100%) rename dashcaddy-api/{ => src/managers}/auth-manager.js (99%) rename dashcaddy-api/{ => src/managers}/auto-restart-manager.js (99%) rename dashcaddy-api/{ => src/managers}/config-drift-detector.js (100%) rename dashcaddy-api/{ => src/managers}/credential-manager.js (99%) rename dashcaddy-api/{ => src/managers}/dependency-manager.js (100%) rename dashcaddy-api/{ => src/managers}/license-manager.js (99%) rename dashcaddy-api/{ => src/managers}/notification-manager.js (100%) rename dashcaddy-api/{ => src/managers}/port-lock-manager.js (100%) rename dashcaddy-api/{ => src/managers}/resource-monitor.js (100%) rename dashcaddy-api/{ => src/managers}/state-manager.js (100%) rename dashcaddy-api/{ => src/managers}/update-manager.js (100%) rename dashcaddy-api/{ => src/monitoring}/health-checker.js (100%) rename dashcaddy-api/{ => src/monitoring}/metrics.js (100%) rename dashcaddy-api/{ => src/monitoring}/ssl-monitor.js (98%) rename dashcaddy-api/{ => src/recipes}/bundled-workflows.js (100%) rename dashcaddy-api/{ => src/recipes}/recipe-templates.js (100%) rename dashcaddy-api/{ => src/security}/audit-logger.js (99%) rename dashcaddy-api/{ => src/security}/crypto-utils.js (100%) rename dashcaddy-api/{ => src/security}/csrf-protection.js (99%) rename dashcaddy-api/{ => src/security}/docker-security.js (100%) rename dashcaddy-api/{ => src/security}/input-validator.js (100%) rename dashcaddy-api/{ => src/security}/keychain-manager.js (100%) rename dashcaddy-api/{ => src/security}/log-digest.js (99%) rename dashcaddy-api/{ => src/utilities}/backup-manager.js (98%) rename dashcaddy-api/{ => src/utilities}/cache-config.js (100%) rename dashcaddy-api/{ => src/utilities}/config-schema.js (100%) rename dashcaddy-api/{ => src/utilities}/constants.js (100%) rename dashcaddy-api/{ => src/utilities}/error-handler.js (96%) rename dashcaddy-api/{ => src/utilities}/errors.js (100%) rename dashcaddy-api/{ => src/utilities}/fs-helpers.js (100%) rename dashcaddy-api/{ => src/utilities}/middleware.js (99%) rename dashcaddy-api/{ => src/utilities}/pagination.js (100%) rename dashcaddy-api/{ => src/utilities}/startup-validator.js (100%) rename dashcaddy-api/{ => src/utilities}/url-resolver.js (100%) diff --git a/dashcaddy-api/__tests__/app-templates.test.js b/dashcaddy-api/__tests__/app-templates.test.js index 1f83a91..10e329b 100644 --- a/dashcaddy-api/__tests__/app-templates.test.js +++ b/dashcaddy-api/__tests__/app-templates.test.js @@ -1,4 +1,4 @@ -const { APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS } = require('../app-templates'); +const { APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS } = require('../src/docker/app-templates'); describe('App Templates', () => { const templates = Object.values(APP_TEMPLATES); diff --git a/dashcaddy-api/__tests__/auth-manager.test.js b/dashcaddy-api/__tests__/auth-manager.test.js index 577fe39..37a4fad 100644 --- a/dashcaddy-api/__tests__/auth-manager.test.js +++ b/dashcaddy-api/__tests__/auth-manager.test.js @@ -1,11 +1,11 @@ // Must mock crypto-utils BEFORE auth-manager is required, // because auth-manager.js line 13: const JWT_SECRET = cryptoUtils.loadOrCreateKey() const mockFixedKey = Buffer.alloc(32, 'jwt-test-key-pad'); -jest.mock('../crypto-utils', () => ({ +jest.mock('../src/security/crypto-utils', () => ({ loadOrCreateKey: jest.fn(() => mockFixedKey), })); -jest.mock('../credential-manager', () => ({ +jest.mock('../src/managers/credential-manager', () => ({ store: jest.fn().mockResolvedValue(true), retrieve: jest.fn().mockResolvedValue(null), delete: jest.fn().mockResolvedValue(true), @@ -13,8 +13,8 @@ jest.mock('../credential-manager', () => ({ })); const crypto = require('crypto'); -const authManager = require('../auth-manager'); -const credentialManager = require('../credential-manager'); +const authManager = require('../src/managers/auth-manager'); +const credentialManager = require('../src/managers/credential-manager'); describe('AuthManager', () => { beforeEach(() => { diff --git a/dashcaddy-api/__tests__/auto-restart-manager.test.js b/dashcaddy-api/__tests__/auto-restart-manager.test.js index bd90fac..a1624d2 100644 --- a/dashcaddy-api/__tests__/auto-restart-manager.test.js +++ b/dashcaddy-api/__tests__/auto-restart-manager.test.js @@ -9,14 +9,14 @@ */ const EventEmitter = require('events'); -const { AutoRestartManager, DEFAULT_POLICY } = require('../auto-restart-manager'); +const { AutoRestartManager, DEFAULT_POLICY } = require('../src/managers/auto-restart-manager'); -jest.mock('../fs-helpers', () => ({ +jest.mock('../src/utilities/fs-helpers', () => ({ readJsonFile: jest.fn().mockResolvedValue({}), writeJsonFile: jest.fn().mockResolvedValue(undefined), })); -const fsHelpers = require('../fs-helpers'); +const fsHelpers = require('../src/utilities/fs-helpers'); function makeManager(overrides = {}) { const servicesStateManager = { diff --git a/dashcaddy-api/__tests__/backup-manager.test.js b/dashcaddy-api/__tests__/backup-manager.test.js index 3ebe08e..425ee5a 100644 --- a/dashcaddy-api/__tests__/backup-manager.test.js +++ b/dashcaddy-api/__tests__/backup-manager.test.js @@ -3,19 +3,19 @@ jest.mock('fs'); jest.mock('child_process'); -jest.mock('../credential-manager', () => ({ +jest.mock('../src/managers/credential-manager', () => ({ exportBackup: jest.fn().mockReturnValue({ encrypted: 'cred-data' }), importBackup: jest.fn() })); -jest.mock('../resource-monitor', () => ({ +jest.mock('../src/managers/resource-monitor', () => ({ exportStats: jest.fn().mockReturnValue({ stats: [{ cpu: 10 }] }), importStats: jest.fn() })); const fs = require('fs'); const crypto = require('crypto'); -const credentialManager = require('../credential-manager'); -const resourceMonitor = require('../resource-monitor'); +const credentialManager = require('../src/managers/credential-manager'); +const resourceMonitor = require('../src/managers/resource-monitor'); // Setup defaults BEFORE requiring singleton (constructor calls loadConfig/loadHistory) fs.existsSync.mockReturnValue(false); @@ -24,7 +24,7 @@ fs.writeFileSync.mockReturnValue(undefined); fs.mkdirSync.mockReturnValue(undefined); fs.unlinkSync.mockReturnValue(undefined); -const backupManager = require('../backup-manager'); +const backupManager = require('../src/utilities/backup-manager'); beforeEach(() => { jest.clearAllMocks(); diff --git a/dashcaddy-api/__tests__/config-drift-detector.test.js b/dashcaddy-api/__tests__/config-drift-detector.test.js index 6f23523..2c8e118 100644 --- a/dashcaddy-api/__tests__/config-drift-detector.test.js +++ b/dashcaddy-api/__tests__/config-drift-detector.test.js @@ -6,7 +6,7 @@ */ const EventEmitter = require('events'); -const { ConfigDriftDetector } = require('../config-drift-detector'); +const { ConfigDriftDetector } = require('../src/managers/config-drift-detector'); function makeContainer(overrides = {}) { return { diff --git a/dashcaddy-api/__tests__/credential-manager.test.js b/dashcaddy-api/__tests__/credential-manager.test.js index 6c80430..ca19bb7 100644 --- a/dashcaddy-api/__tests__/credential-manager.test.js +++ b/dashcaddy-api/__tests__/credential-manager.test.js @@ -1,12 +1,12 @@ // Mock dependencies before requiring the module -jest.mock('../keychain-manager', () => ({ +jest.mock('../src/security/keychain-manager', () => ({ available: false, store: jest.fn().mockResolvedValue(false), retrieve: jest.fn().mockResolvedValue(null), delete: jest.fn().mockResolvedValue(true), })); -jest.mock('../crypto-utils', () => ({ +jest.mock('../src/security/crypto-utils', () => ({ encrypt: jest.fn(data => `enc:tag:${Buffer.from(String(data)).toString('base64')}`), decrypt: jest.fn(data => { const parts = data.split(':'); @@ -40,8 +40,8 @@ describe('CredentialManager', () => { // Re-get mocked modules fs = require('fs'); lockfile = require('proper-lockfile'); - keychainManager = require('../keychain-manager'); - cryptoUtils = require('../crypto-utils'); + keychainManager = require('../src/security/keychain-manager'); + cryptoUtils = require('../src/security/crypto-utils'); // Reset mock implementations fs.existsSync.mockReturnValue(true); @@ -50,7 +50,7 @@ describe('CredentialManager', () => { lockfile.lock.mockResolvedValue(jest.fn().mockResolvedValue()); keychainManager.available = false; - credentialManager = require('../credential-manager'); + credentialManager = require('../src/managers/credential-manager'); credentialManager.cache.clear(); }); @@ -72,10 +72,10 @@ describe('CredentialManager', () => { fs.writeFileSync.mockImplementation(() => {}); lockfile = require('proper-lockfile'); lockfile.lock.mockResolvedValue(jest.fn().mockResolvedValue()); - keychainManager = require('../keychain-manager'); + keychainManager = require('../src/security/keychain-manager'); keychainManager.available = true; keychainManager.store.mockResolvedValue(true); - credentialManager = require('../credential-manager'); + credentialManager = require('../src/managers/credential-manager'); const result = await credentialManager.store('test.key', 'value'); expect(result).toBe(true); @@ -91,11 +91,11 @@ describe('CredentialManager', () => { fs.writeFileSync.mockImplementation(() => {}); lockfile = require('proper-lockfile'); lockfile.lock.mockResolvedValue(jest.fn().mockResolvedValue()); - keychainManager = require('../keychain-manager'); + keychainManager = require('../src/security/keychain-manager'); keychainManager.available = true; keychainManager.store.mockResolvedValue(false); - cryptoUtils = require('../crypto-utils'); - credentialManager = require('../credential-manager'); + cryptoUtils = require('../src/security/crypto-utils'); + credentialManager = require('../src/managers/credential-manager'); const result = await credentialManager.store('test.key', 'value'); expect(result).toBe(true); diff --git a/dashcaddy-api/__tests__/crypto-utils.test.js b/dashcaddy-api/__tests__/crypto-utils.test.js index 2a4c9cb..70ab40f 100644 --- a/dashcaddy-api/__tests__/crypto-utils.test.js +++ b/dashcaddy-api/__tests__/crypto-utils.test.js @@ -11,7 +11,7 @@ const TEST_KEY_HEX = TEST_KEY.toString('hex'); // Load the module once — no jest.resetModules() needed // We control key state via clearCachedKey() + env vars process.env.DASHCADDY_ENCRYPTION_KEY = TEST_KEY_HEX; -const cryptoUtils = require('../crypto-utils'); +const cryptoUtils = require('../src/security/crypto-utils'); describe('Crypto Utils', () => { beforeEach(() => { diff --git a/dashcaddy-api/__tests__/csrf-protection.test.js b/dashcaddy-api/__tests__/csrf-protection.test.js index 4943d84..9708600 100644 --- a/dashcaddy-api/__tests__/csrf-protection.test.js +++ b/dashcaddy-api/__tests__/csrf-protection.test.js @@ -2,7 +2,7 @@ const crypto = require('crypto'); // Mock crypto-utils to provide a predictable signing key const mockFixedKey = Buffer.alloc(32, 'test-key-material'); -jest.mock('../crypto-utils', () => ({ +jest.mock('../src/security/crypto-utils', () => ({ loadOrCreateKey: jest.fn(() => mockFixedKey), })); @@ -16,7 +16,7 @@ const { csrfCookieMiddleware, csrfValidationMiddleware, renewCSRFToken -} = require('../csrf-protection'); +} = require('../src/security/csrf-protection'); const { createMockReqRes } = require('./helpers/test-utils'); describe('CSRF Protection', () => { diff --git a/dashcaddy-api/__tests__/dns-propagation.test.js b/dashcaddy-api/__tests__/dns-propagation.test.js index fa2987c..5e3c62c 100644 --- a/dashcaddy-api/__tests__/dns-propagation.test.js +++ b/dashcaddy-api/__tests__/dns-propagation.test.js @@ -24,7 +24,7 @@ jest.mock('dns', () => { }; }); -const DNSPropagationChecker = require('../dns-propagation'); +const DNSPropagationChecker = require('../src/dns/dns-propagation'); describe('DNSPropagationChecker', () => { let checker; diff --git a/dashcaddy-api/__tests__/docker-security.test.js b/dashcaddy-api/__tests__/docker-security.test.js index 5757ef4..8198f36 100644 --- a/dashcaddy-api/__tests__/docker-security.test.js +++ b/dashcaddy-api/__tests__/docker-security.test.js @@ -27,7 +27,7 @@ describe('DockerSecurity Module', () => { // Reset modules to get fresh instance jest.resetModules(); - dockerSecurity = require('../docker-security'); + dockerSecurity = require('../src/security/docker-security'); }); afterEach(() => { @@ -58,7 +58,7 @@ describe('DockerSecurity Module', () => { // Force module reload jest.resetModules(); - const freshInstance = require('../docker-security'); + const freshInstance = require('../src/security/docker-security'); const status = freshInstance.getStatus(); expect(status.trustedImagesCount).toBe(1); @@ -77,7 +77,7 @@ describe('DockerSecurity Module', () => { fs.writeFileSync(TEST_CONFIG_FILE, 'INVALID JSON{{{'); jest.resetModules(); - const freshInstance = require('../docker-security'); + const freshInstance = require('../src/security/docker-security'); const status = freshInstance.getStatus(); // Should fall back to default config @@ -89,7 +89,7 @@ describe('DockerSecurity Module', () => { process.env.DOCKER_SECURITY_CONFIG = '/nonexistent/path/config.json'; jest.resetModules(); - const freshInstance = require('../docker-security'); + const freshInstance = require('../src/security/docker-security'); const status = freshInstance.getStatus(); // Should fall back to default config diff --git a/dashcaddy-api/__tests__/error-handler.test.js b/dashcaddy-api/__tests__/error-handler.test.js index 5a742fd..f7f54b8 100644 --- a/dashcaddy-api/__tests__/error-handler.test.js +++ b/dashcaddy-api/__tests__/error-handler.test.js @@ -12,7 +12,7 @@ jest.mock('../src/utils/logging', () => ({ LOG_LEVELS: { debug: 0, info: 1, warn: 2, error: 3 } })); -const { errorMiddleware, notFoundHandler } = require('../error-handler'); +const { errorMiddleware, notFoundHandler } = require('../src/utilities/error-handler'); const { AppError, ValidationError, @@ -20,7 +20,7 @@ const { NotFoundError, RateLimitError, DockerError, -} = require('../errors'); +} = require('../src/utilities/errors'); describe('Error Handler', () => { let req, res, next; diff --git a/dashcaddy-api/__tests__/errors.test.js b/dashcaddy-api/__tests__/errors.test.js index 51b861f..e6c29b7 100644 --- a/dashcaddy-api/__tests__/errors.test.js +++ b/dashcaddy-api/__tests__/errors.test.js @@ -10,7 +10,7 @@ const { CaddyError, DNSError, ServiceUnavailableError -} = require('../errors'); +} = require('../src/utilities/errors'); describe('Error Classes', () => { describe('AppError', () => { diff --git a/dashcaddy-api/__tests__/health-checker.test.js b/dashcaddy-api/__tests__/health-checker.test.js index 60cfa71..3b0fc24 100644 --- a/dashcaddy-api/__tests__/health-checker.test.js +++ b/dashcaddy-api/__tests__/health-checker.test.js @@ -17,7 +17,7 @@ describe('HealthChecker', () => { fs.writeFileSync.mockImplementation(() => {}); // Fresh instance each test - HealthChecker = require('../health-checker').constructor; + HealthChecker = require('../src/monitoring/health-checker').constructor; healthChecker = new HealthChecker(); }); @@ -41,7 +41,7 @@ describe('HealthChecker', () => { services: { svc1: { url: 'http://test.local', enabled: true } } })); - HealthChecker = require('../health-checker').constructor; + HealthChecker = require('../src/monitoring/health-checker').constructor; const hc = new HealthChecker(); expect(hc.config.services.svc1).toBeDefined(); }); @@ -52,7 +52,7 @@ describe('HealthChecker', () => { fs.existsSync.mockReturnValue(true); fs.readFileSync.mockReturnValue('invalid json'); - HealthChecker = require('../health-checker').constructor; + HealthChecker = require('../src/monitoring/health-checker').constructor; const hc = new HealthChecker(); expect(hc.config).toEqual({ services: {} }); }); diff --git a/dashcaddy-api/__tests__/helpers/test-utils.js b/dashcaddy-api/__tests__/helpers/test-utils.js index 28b0bf7..b0c1f27 100644 --- a/dashcaddy-api/__tests__/helpers/test-utils.js +++ b/dashcaddy-api/__tests__/helpers/test-utils.js @@ -90,7 +90,7 @@ function buildTestApp(routeFactory, deps, prefix = '/api') { const router = routeFactory(deps); app.use(prefix, router); // Error handler - const { errorMiddleware } = require('../../error-handler'); + const { errorMiddleware } = require('../../../src/utilities/error-handler'); app.use(errorMiddleware); return app; } diff --git a/dashcaddy-api/__tests__/input-validator.test.js b/dashcaddy-api/__tests__/input-validator.test.js index 309748c..b74f9be 100644 --- a/dashcaddy-api/__tests__/input-validator.test.js +++ b/dashcaddy-api/__tests__/input-validator.test.js @@ -11,7 +11,7 @@ const { isValidPort, isPrivateIP, validateSecurePath -} = require('../input-validator'); +} = require('../src/security/input-validator'); describe('Input Validator', () => { function fail(message) { @@ -480,7 +480,7 @@ describe('Input Validator', () => { // Re-require after mocking fs function getValidateSecurePath() { - return require('../input-validator').validateSecurePath; + return require('../src/security/input-validator').validateSecurePath; } it('resolves valid path within allowed roots', async () => { diff --git a/dashcaddy-api/__tests__/log-digest.test.js b/dashcaddy-api/__tests__/log-digest.test.js index a64ed49..84ffc6b 100644 --- a/dashcaddy-api/__tests__/log-digest.test.js +++ b/dashcaddy-api/__tests__/log-digest.test.js @@ -29,13 +29,13 @@ jest.mock('fs', () => { }; }); -jest.mock('../docker-maintenance', () => ({ +jest.mock('../src/docker/docker-maintenance', () => ({ getDiskUsage: jest.fn().mockResolvedValue(null), })); const Docker = require('dockerode'); const fs = require('fs'); -const logDigest = require('../log-digest'); +const logDigest = require('../src/security/log-digest'); describe('LogDigest (singleton)', () => { let dockerInstance; diff --git a/dashcaddy-api/__tests__/metrics.test.js b/dashcaddy-api/__tests__/metrics.test.js index f1e6da4..5f293b6 100644 --- a/dashcaddy-api/__tests__/metrics.test.js +++ b/dashcaddy-api/__tests__/metrics.test.js @@ -7,7 +7,7 @@ * state in beforeEach. */ -const metrics = require('../metrics'); +const metrics = require('../src/monitoring/metrics'); describe('Metrics (singleton)', () => { beforeEach(() => { diff --git a/dashcaddy-api/__tests__/notification-manager.test.js b/dashcaddy-api/__tests__/notification-manager.test.js index 0ff7819..24f5939 100644 --- a/dashcaddy-api/__tests__/notification-manager.test.js +++ b/dashcaddy-api/__tests__/notification-manager.test.js @@ -20,7 +20,7 @@ jest.mock('nodemailer', () => ({ const fs = require('fs'); const nodemailer = require('nodemailer'); -const NotificationManager = require('../notification-manager'); +const NotificationManager = require('../src/managers/notification-manager'); describe('NotificationManager', () => { let nm; diff --git a/dashcaddy-api/__tests__/pagination.test.js b/dashcaddy-api/__tests__/pagination.test.js index 26ab26f..0bfdf0f 100644 --- a/dashcaddy-api/__tests__/pagination.test.js +++ b/dashcaddy-api/__tests__/pagination.test.js @@ -1,4 +1,4 @@ -const { paginate, parsePaginationParams, DEFAULT_LIMIT, MAX_LIMIT } = require('../pagination'); +const { paginate, parsePaginationParams, DEFAULT_LIMIT, MAX_LIMIT } = require('../src/utilities/pagination'); describe('Pagination — DashCaddy list endpoints', () => { diff --git a/dashcaddy-api/__tests__/port-lock-manager.test.js b/dashcaddy-api/__tests__/port-lock-manager.test.js index 2b2df49..4db50a1 100644 --- a/dashcaddy-api/__tests__/port-lock-manager.test.js +++ b/dashcaddy-api/__tests__/port-lock-manager.test.js @@ -16,7 +16,7 @@ fs.unlinkSync.mockReturnValue(undefined); lockfile.lock.mockResolvedValue(jest.fn().mockResolvedValue()); lockfile.check.mockResolvedValue(false); -const portLockManager = require('../port-lock-manager'); +const portLockManager = require('../src/managers/port-lock-manager'); beforeEach(() => { jest.clearAllMocks(); diff --git a/dashcaddy-api/__tests__/resource-monitor.test.js b/dashcaddy-api/__tests__/resource-monitor.test.js index 27417d4..9c738f0 100644 --- a/dashcaddy-api/__tests__/resource-monitor.test.js +++ b/dashcaddy-api/__tests__/resource-monitor.test.js @@ -12,7 +12,7 @@ fs.existsSync.mockReturnValue(false); fs.readFileSync.mockReturnValue('{}'); fs.writeFileSync.mockReturnValue(undefined); -const resourceMonitor = require('../resource-monitor'); +const resourceMonitor = require('../src/managers/resource-monitor'); function makeStat(overrides = {}) { return { diff --git a/dashcaddy-api/__tests__/routes/containers.routes.test.js b/dashcaddy-api/__tests__/routes/containers.routes.test.js index 0f85da0..65521cf 100644 --- a/dashcaddy-api/__tests__/routes/containers.routes.test.js +++ b/dashcaddy-api/__tests__/routes/containers.routes.test.js @@ -9,7 +9,7 @@ function buildApp(mockDeps) { const app = express(); app.use(express.json()); - const { errorMiddleware } = require('../../error-handler'); + const { errorMiddleware } = require('../../src/utilities/error-handler'); const containersRouteFactory = require('../../routes/containers'); app.use('/api/containers', containersRouteFactory(mockDeps)); app.use(errorMiddleware); diff --git a/dashcaddy-api/__tests__/routes/health.routes.test.js b/dashcaddy-api/__tests__/routes/health.routes.test.js index a059da6..0a04cd1 100644 --- a/dashcaddy-api/__tests__/routes/health.routes.test.js +++ b/dashcaddy-api/__tests__/routes/health.routes.test.js @@ -52,21 +52,21 @@ jest.mock('../../platform-paths', () => ({ })); // Mock fs-helpers.exists -jest.mock('../../fs-helpers', () => ({ +jest.mock('../../src/utilities/fs-helpers', () => ({ exists: jest.fn().mockResolvedValue(true), })); -jest.mock('../../url-resolver', () => ({ +jest.mock('../../src/utilities/url-resolver', () => ({ resolveServiceUrl: jest.fn((id) => `https://${id}.test`), })); -jest.mock('../../pagination', () => ({ +jest.mock('../../src/utilities/pagination', () => ({ paginate: jest.fn((data, params) => ({ data, pagination: null })), parsePaginationParams: jest.fn(() => null), })); -const { exists } = require('../../fs-helpers'); -const { resolveServiceUrl } = require('../../url-resolver'); +const { exists } = require('../../src/utilities/fs-helpers'); +const { resolveServiceUrl } = require('../../src/utilities/url-resolver'); const { execSync } = require('child_process'); describe('Health Routes', () => { diff --git a/dashcaddy-api/__tests__/routes/services.routes.test.js b/dashcaddy-api/__tests__/routes/services.routes.test.js index 2c0be37..08c4911 100644 --- a/dashcaddy-api/__tests__/routes/services.routes.test.js +++ b/dashcaddy-api/__tests__/routes/services.routes.test.js @@ -9,27 +9,27 @@ function asyncHandler(fn) { } // Mock modules that services.js requires at top-level -jest.mock('../../constants', () => ({ +jest.mock('../../src/utilities/constants', () => ({ APP: { USER_AGENTS: { PROBE: 'DashCaddy/1.0' } }, REGEX: { SUBDOMAIN: /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/ }, TIMEOUTS: { DEFAULT: 10000 }, HTTP_STATUS: { OK: 200, CREATED: 201, NO_CONTENT: 204, BAD_REQUEST: 400, UNAUTHORIZED: 401, FORBIDDEN: 403, NOT_FOUND: 404, CONFLICT: 409, INTERNAL_ERROR: 500 } })); -jest.mock('../../input-validator', () => ({ +jest.mock('../../src/security/input-validator', () => ({ validateServiceConfig: jest.fn(), isValidPort: jest.fn(p => p >= 1 && p <= 65535), })); -jest.mock('../../fs-helpers', () => ({ +jest.mock('../../src/utilities/fs-helpers', () => ({ exists: jest.fn().mockResolvedValue(true), })); -jest.mock('../../url-resolver', () => ({ +jest.mock('../../src/utilities/url-resolver', () => ({ resolveServiceUrl: jest.fn((id) => `https://${id}.test`), })); -jest.mock('../../pagination', () => ({ +jest.mock('../../src/utilities/pagination', () => ({ paginate: jest.fn((data, params) => ({ data, pagination: null })), parsePaginationParams: jest.fn(() => null), })); @@ -45,8 +45,8 @@ jest.mock('../../src/utils/responses', () => ({ // errors module NOT mocked — used for real ValidationError/NotFoundError/ConflictError -const { exists } = require('../../fs-helpers'); -const { validateServiceConfig } = require('../../input-validator'); +const { exists } = require('../../src/utilities/fs-helpers'); +const { validateServiceConfig } = require('../../src/security/input-validator'); function createApp(depsOverride = {}) { const defaultDeps = { @@ -450,7 +450,7 @@ describe('Services Routes', () => { }); it('rejects invalid port', async () => { - const { isValidPort } = require('../../input-validator'); + const { isValidPort } = require('../../src/security/input-validator'); isValidPort.mockReturnValue(false); const { app } = createApp(); const res = await request(app) diff --git a/dashcaddy-api/__tests__/ssl-monitor.test.js b/dashcaddy-api/__tests__/ssl-monitor.test.js index d05a88a..a6861cc 100644 --- a/dashcaddy-api/__tests__/ssl-monitor.test.js +++ b/dashcaddy-api/__tests__/ssl-monitor.test.js @@ -8,14 +8,14 @@ jest.mock('tls', () => ({ connect: jest.fn(), })); -jest.mock('../fs-helpers', () => ({ +jest.mock('../src/utilities/fs-helpers', () => ({ readJsonFile: jest.fn().mockResolvedValue(null), writeJsonFile: jest.fn().mockResolvedValue(undefined), })); const tls = require('tls'); -const fsHelpers = require('../fs-helpers'); -const SSLMonitor = require('../ssl-monitor'); +const fsHelpers = require('../src/utilities/fs-helpers'); +const SSLMonitor = require('../src/monitoring/ssl-monitor'); function makeSocket({ cert = null, error = null } = {}) { const { EventEmitter } = require('events'); diff --git a/dashcaddy-api/__tests__/state-manager.test.js b/dashcaddy-api/__tests__/state-manager.test.js index 116615f..1ec428d 100644 --- a/dashcaddy-api/__tests__/state-manager.test.js +++ b/dashcaddy-api/__tests__/state-manager.test.js @@ -11,7 +11,7 @@ jest.mock('fs', () => ({ const lockfile = require('proper-lockfile'); const fs = require('fs'); -const StateManager = require('../state-manager'); +const StateManager = require('../src/managers/state-manager'); describe('StateManager', () => { let sm; diff --git a/dashcaddy-api/__tests__/update-manager.test.js b/dashcaddy-api/__tests__/update-manager.test.js index edcfe66..19a6bea 100644 --- a/dashcaddy-api/__tests__/update-manager.test.js +++ b/dashcaddy-api/__tests__/update-manager.test.js @@ -22,7 +22,7 @@ fs.existsSync.mockReturnValue(false); fs.readFileSync.mockReturnValue('{}'); fs.writeFileSync.mockReturnValue(undefined); -const updateManager = require('../update-manager'); +const updateManager = require('../src/managers/update-manager'); // Helper to create a fake https request that responds with a given statusCode/headers/body function mockHttpsResponse({ statusCode = 200, headers = {}, body = '' } = {}) { diff --git a/dashcaddy-api/__tests__/url-resolver.test.js b/dashcaddy-api/__tests__/url-resolver.test.js index ced64f2..d51d617 100644 --- a/dashcaddy-api/__tests__/url-resolver.test.js +++ b/dashcaddy-api/__tests__/url-resolver.test.js @@ -1,4 +1,4 @@ -const { resolveServiceUrl } = require('../url-resolver'); +const { resolveServiceUrl } = require('../src/utilities/url-resolver'); describe('URL Resolver — DashCaddy service URL resolution', () => { const buildServiceUrl = jest.fn(id => `https://${id}.sami`); diff --git a/dashcaddy-api/routes/apps/compose.js b/dashcaddy-api/routes/apps/compose.js index 64d19e5..50a1a2a 100644 --- a/dashcaddy-api/routes/apps/compose.js +++ b/dashcaddy-api/routes/apps/compose.js @@ -1,9 +1,9 @@ const express = require('express'); const yaml = require('js-yaml'); -const { DOCKER, REGEX } = require('../../constants'); -const { ValidationError } = require('../../errors'); +const { DOCKER, REGEX } = require('../../../src/utilities/constants'); +const { ValidationError } = require('../../../src/utilities/errors'); const platformPaths = require('../../platform-paths'); -const { ok } = require('../../src/utils/responses'); +const { ok } = require('../src/utils/responses'); /** * Docker Compose import routes diff --git a/dashcaddy-api/routes/apps/deploy.js b/dashcaddy-api/routes/apps/deploy.js index 386a0fc..f2dc089 100644 --- a/dashcaddy-api/routes/apps/deploy.js +++ b/dashcaddy-api/routes/apps/deploy.js @@ -2,13 +2,13 @@ const express = require('express'); const fsp = require('fs').promises; const path = require('path'); const validatorLib = require('validator'); -const { REGEX, DOCKER } = require('../../constants'); -const { isValidPort } = require('../../input-validator'); -const { exists } = require('../../fs-helpers'); +const { REGEX, DOCKER } = require('../../../src/utilities/constants'); +const { isValidPort } = require('../../../src/security/input-validator'); +const { exists } = require('../../../src/utilities/fs-helpers'); const platformPaths = require('../../platform-paths'); -const { ValidationError } = require('../../errors'); -const { logError } = require('../../src/utils/logging'); -const { ok } = require('../../src/utils/responses'); +const { ValidationError } = require('../../../src/utilities/errors'); +const { logError } = require('../src/utils/logging'); +const { ok } = require('../src/utils/responses'); /** * Apps deployment routes factory * @param {Object} deps - Explicit dependencies diff --git a/dashcaddy-api/routes/apps/helpers.js b/dashcaddy-api/routes/apps/helpers.js index f0000b2..f041321 100644 --- a/dashcaddy-api/routes/apps/helpers.js +++ b/dashcaddy-api/routes/apps/helpers.js @@ -2,8 +2,8 @@ const fs = require('fs'); const fsp = require('fs').promises; const path = require('path'); const crypto = require('crypto'); -const { REGEX, DOCKER } = require('../../constants'); -const { exists } = require('../../fs-helpers'); +const { REGEX, DOCKER } = require('../../../src/utilities/constants'); +const { exists } = require('../../../src/utilities/fs-helpers'); const platformPaths = require('../../platform-paths'); /** diff --git a/dashcaddy-api/routes/apps/removal.js b/dashcaddy-api/routes/apps/removal.js index 5ef33da..7a678a7 100644 --- a/dashcaddy-api/routes/apps/removal.js +++ b/dashcaddy-api/routes/apps/removal.js @@ -1,7 +1,7 @@ const express = require('express'); -const { exists } = require('../../fs-helpers'); -const { logError } = require('../../src/utils/logging'); -const { ok } = require('../../src/utils/responses'); +const { exists } = require('../../../src/utilities/fs-helpers'); +const { logError } = require('../src/utils/logging'); +const { ok } = require('../src/utils/responses'); module.exports = function({ docker, caddy, servicesStateManager, asyncHandler, log, helpers, diff --git a/dashcaddy-api/routes/apps/restore.js b/dashcaddy-api/routes/apps/restore.js index 0decad5..9e9b316 100644 --- a/dashcaddy-api/routes/apps/restore.js +++ b/dashcaddy-api/routes/apps/restore.js @@ -1,8 +1,8 @@ const express = require('express'); const path = require('path'); const fs = require('fs'); -const { DOCKER } = require('../../constants'); -const { ok, validationError, notFound, errorResponse } = require('../../src/utils/responses'); +const { DOCKER } = require('../../../src/utilities/constants'); +const { ok, validationError, notFound, errorResponse } = require('../../../src/utilities/responses'); const DEFAULT_BACKUP_DIR = process.env.BACKUP_DIR || path.join(__dirname, '..', 'backups'); diff --git a/dashcaddy-api/routes/apps/templates.js b/dashcaddy-api/routes/apps/templates.js index b5a2442..7b8f84a 100644 --- a/dashcaddy-api/routes/apps/templates.js +++ b/dashcaddy-api/routes/apps/templates.js @@ -1,5 +1,5 @@ const express = require('express'); -const { exists } = require('../../fs-helpers'); +const { exists } = require('../../../src/utilities/fs-helpers'); /** * Apps templates routes factory * @param {Object} deps - Explicit dependencies @@ -19,8 +19,8 @@ const { exists } = require('../../fs-helpers'); * @param {string} deps.SERVICES_FILE - Services file path * @returns {express.Router} */ -const { REGEX } = require('../../constants'); -const { ok } = require('../../src/utils/responses'); +const { REGEX } = require('../../../src/utilities/constants'); +const { ok } = require('../src/utils/responses'); module.exports = function({ servicesStateManager, asyncHandler, helpers, @@ -55,7 +55,7 @@ module.exports = function({ const { appId } = req.params; const template = ctx.APP_TEMPLATES[appId]; if (!template) { - const { NotFoundError } = require('../../errors'); + const { NotFoundError } = require('../../../src/utilities/errors'); throw new NotFoundError('App template'); } ok(res, { template }); @@ -90,7 +90,7 @@ module.exports = function({ // Update subdomain for deployed app router.post('/update-subdomain', asyncHandler(async (req, res) => { const { serviceId, oldSubdomain, newSubdomain, containerId, ip } = req.body; - const { ValidationError } = require('../../errors'); + const { ValidationError } = require('../../../src/utilities/errors'); if (!oldSubdomain || typeof oldSubdomain !== 'string') { throw new ValidationError('oldSubdomain is required'); diff --git a/dashcaddy-api/routes/arr/config.js b/dashcaddy-api/routes/arr/config.js index d0171b7..534860b 100644 --- a/dashcaddy-api/routes/arr/config.js +++ b/dashcaddy-api/routes/arr/config.js @@ -1,9 +1,9 @@ const express = require('express'); -const { APP_PORTS, ARR_SERVICES } = require('../../constants'); -const { validateURL, validateToken } = require('../../input-validator'); -const { ValidationError, AuthenticationError, NotFoundError } = require('../../errors'); -const { logError } = require('../../src/utils/logging'); -const { ok, successMessage } = require('../../src/utils/responses'); +const { APP_PORTS, ARR_SERVICES } = require('../../../src/utilities/constants'); +const { validateURL, validateToken } = require('../../../src/security/input-validator'); +const { ValidationError, AuthenticationError, NotFoundError } = require('../../../src/utilities/errors'); +const { logError } = require('../src/utils/logging'); +const { ok, successMessage } = require('../src/utils/responses'); /** * Arr configuration routes factory diff --git a/dashcaddy-api/routes/arr/credentials.js b/dashcaddy-api/routes/arr/credentials.js index 4fec496..2e2eec2 100644 --- a/dashcaddy-api/routes/arr/credentials.js +++ b/dashcaddy-api/routes/arr/credentials.js @@ -1,7 +1,7 @@ const express = require('express'); -const { validateURL, validateToken } = require('../../input-validator'); -const { ValidationError } = require('../../errors'); -const { ok, successMessage } = require('../../src/utils/responses'); +const { validateURL, validateToken } = require('../../../src/security/input-validator'); +const { ValidationError } = require('../../../src/utilities/errors'); +const { ok, successMessage } = require('../src/utils/responses'); /** * Arr credentials routes factory diff --git a/dashcaddy-api/routes/arr/detect.js b/dashcaddy-api/routes/arr/detect.js index 714171e..fa529ff 100644 --- a/dashcaddy-api/routes/arr/detect.js +++ b/dashcaddy-api/routes/arr/detect.js @@ -1,6 +1,6 @@ const express = require('express'); -const { APP_PORTS, ARR_SERVICES } = require('../../constants'); -const { ok } = require('../../src/utils/responses'); +const { APP_PORTS, ARR_SERVICES } = require('../../../src/utilities/constants'); +const { ok } = require('../src/utils/responses'); /** * Arr service detection routes factory diff --git a/dashcaddy-api/routes/arr/helpers.js b/dashcaddy-api/routes/arr/helpers.js index 312ccdf..f99a161 100644 --- a/dashcaddy-api/routes/arr/helpers.js +++ b/dashcaddy-api/routes/arr/helpers.js @@ -1,4 +1,4 @@ -const { APP_PORTS } = require('../../constants'); +const { APP_PORTS } = require('../../../src/utilities/constants'); /** * Arr helpers factory diff --git a/dashcaddy-api/routes/arr/plex.js b/dashcaddy-api/routes/arr/plex.js index e4a62db..2f2d903 100644 --- a/dashcaddy-api/routes/arr/plex.js +++ b/dashcaddy-api/routes/arr/plex.js @@ -1,6 +1,6 @@ const express = require('express'); -const { APP_PORTS } = require('../../constants'); -const { ok } = require('../../src/utils/responses'); +const { APP_PORTS } = require('../../../src/utilities/constants'); +const { ok } = require('../src/utils/responses'); /** * Plex routes factory diff --git a/dashcaddy-api/routes/arr/smart-connect.js b/dashcaddy-api/routes/arr/smart-connect.js index b7f557d..3a40906 100644 --- a/dashcaddy-api/routes/arr/smart-connect.js +++ b/dashcaddy-api/routes/arr/smart-connect.js @@ -1,5 +1,5 @@ const express = require('express'); -const { APP_PORTS } = require('../../constants'); +const { APP_PORTS } = require('../../../src/utilities/constants'); /** * Arr smart-connect routes factory diff --git a/dashcaddy-api/routes/auth/keys.js b/dashcaddy-api/routes/auth/keys.js index 6b6b9e1..63243dc 100644 --- a/dashcaddy-api/routes/auth/keys.js +++ b/dashcaddy-api/routes/auth/keys.js @@ -1,6 +1,6 @@ const express = require('express'); -const { ValidationError, ForbiddenError, NotFoundError } = require('../../errors'); -const { ok, successMessage } = require('../../src/utils/responses'); +const { ValidationError, ForbiddenError, NotFoundError } = require('../../../src/utilities/errors'); +const { ok, successMessage } = require('../src/utils/responses'); /** * Auth API keys routes factory * @param {Object} deps - Explicit dependencies diff --git a/dashcaddy-api/routes/auth/session-handlers.js b/dashcaddy-api/routes/auth/session-handlers.js index d39d1d1..cc2dd72 100644 --- a/dashcaddy-api/routes/auth/session-handlers.js +++ b/dashcaddy-api/routes/auth/session-handlers.js @@ -1,5 +1,5 @@ -const { SESSION_TTL, APP, PLEX, TIMEOUTS, buildMediaAuth } = require('../../constants'); -const { createCache, CACHE_CONFIGS } = require('../../cache-config'); +const { SESSION_TTL, APP, PLEX, TIMEOUTS, buildMediaAuth } = require('../../../src/utilities/constants'); +const { createCache, CACHE_CONFIGS } = require('../../../src/utilities/cache-config'); /** * Auth session handlers routes factory diff --git a/dashcaddy-api/routes/auth/sso-gate.js b/dashcaddy-api/routes/auth/sso-gate.js index f8fff5c..6b7d5e5 100644 --- a/dashcaddy-api/routes/auth/sso-gate.js +++ b/dashcaddy-api/routes/auth/sso-gate.js @@ -1,6 +1,6 @@ const express = require('express'); -const { SESSION_TTL, APP, PLEX, TIMEOUTS, buildMediaAuth } = require('../../constants'); -const { AuthenticationError, NotFoundError } = require('../../errors'); +const { SESSION_TTL, APP, PLEX, TIMEOUTS, buildMediaAuth } = require('../../../src/utilities/constants'); +const { AuthenticationError, NotFoundError } = require('../../../src/utilities/errors'); /** * Auth SSO gate routes factory diff --git a/dashcaddy-api/routes/auth/totp.js b/dashcaddy-api/routes/auth/totp.js index 065b878..f9af335 100644 --- a/dashcaddy-api/routes/auth/totp.js +++ b/dashcaddy-api/routes/auth/totp.js @@ -1,6 +1,6 @@ const express = require('express'); -const { ValidationError, AuthenticationError } = require('../../errors'); -const { ok, successMessage } = require('../../src/utils/responses'); +const { ValidationError, AuthenticationError } = require('../../../src/utilities/errors'); +const { ok, successMessage } = require('../src/utils/responses'); /** * Auth TOTP routes factory diff --git a/dashcaddy-api/routes/auto-restart.js b/dashcaddy-api/routes/auto-restart.js index e3b246b..26fa542 100644 --- a/dashcaddy-api/routes/auto-restart.js +++ b/dashcaddy-api/routes/auto-restart.js @@ -9,7 +9,7 @@ const express = require('express'); const { success } = require('../src/utils/responses'); -const { ValidationError, NotFoundError } = require('../errors'); +const { ValidationError, NotFoundError } = require('../src/utilities/errors'); /** * Auto-restart route factory diff --git a/dashcaddy-api/routes/backups.js b/dashcaddy-api/routes/backups.js index b1ab149..3bb6504 100644 --- a/dashcaddy-api/routes/backups.js +++ b/dashcaddy-api/routes/backups.js @@ -55,7 +55,7 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { const { appId, enabled, schedule, retention, runImmediately, destination, destinationPath } = req.body; if (!appId) { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('appId is required'); } @@ -93,7 +93,7 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { const config = backupManager.getConfig(); if (!config.backups || !config.backups[appId]) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`No backup schedule found for app: ${appId}`, 'DC-404'); } @@ -153,7 +153,7 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { const backupConfig = config.backups && config.backups[appId]; if (!backupConfig) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`No backup schedule found for app: ${appId}`, 'DC-404'); } @@ -229,13 +229,13 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { // Security: prevent path traversal if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('Invalid filename'); } const filepath = path.join(DEFAULT_BACKUP_DIR, filename); if (!fs.existsSync(filepath)) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`Backup file not found: ${filename}`, 'DC-404'); } @@ -365,13 +365,13 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { // Security: prevent path traversal if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('Invalid filename'); } const filepath = path.join(DEFAULT_BACKUP_DIR, filename); if (!fs.existsSync(filepath)) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`Backup file not found: ${filename}`, 'DC-404'); } @@ -502,7 +502,7 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { router.post('/backups/test-destination', asyncHandler(async (req, res) => { const destination = req.body; if (!destination || !destination.type) { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('destination.type is required'); } const result = await backupManager.testDestination(destination); @@ -512,10 +512,10 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { // Get cloud credentials (masked) for a provider // Provider: dropbox | webdav | sftp router.get('/backups/credentials/:provider', asyncHandler(async (req, res) => { - const credentialManager = require('../credential-manager'); + const credentialManager = require('../src/managers/credential-manager'); const provider = req.params.provider; if (!['dropbox', 'webdav', 'sftp'].includes(provider)) { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('Invalid provider'); } @@ -544,8 +544,8 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { // Save cloud credentials for a provider router.post('/backups/credentials/:provider', asyncHandler(async (req, res) => { - const credentialManager = require('../credential-manager'); - const { ValidationError } = require('../errors'); + const credentialManager = require('../src/managers/credential-manager'); + const { ValidationError } = require('../src/utilities/errors'); const provider = req.params.provider; if (!['dropbox', 'webdav', 'sftp'].includes(provider)) { @@ -585,8 +585,8 @@ module.exports = function({ backupManager, licenseManager, asyncHandler }) { // Delete cloud credentials for a provider router.delete('/backups/credentials/:provider', asyncHandler(async (req, res) => { - const credentialManager = require('../credential-manager'); - const { ValidationError } = require('../errors'); + const credentialManager = require('../src/managers/credential-manager'); + const { ValidationError } = require('../src/utilities/errors'); const provider = req.params.provider; if (!['dropbox', 'webdav', 'sftp'].includes(provider)) { diff --git a/dashcaddy-api/routes/browse.js b/dashcaddy-api/routes/browse.js index 9800364..ca3370f 100644 --- a/dashcaddy-api/routes/browse.js +++ b/dashcaddy-api/routes/browse.js @@ -2,9 +2,9 @@ const express = require('express'); const fs = require('fs'); const fsp = require('fs').promises; const path = require('path'); -const { exists, isAccessible } = require('../fs-helpers'); -const { paginate, parsePaginationParams } = require('../pagination'); -const { ValidationError, ForbiddenError } = require('../errors'); +const { exists, isAccessible } = require('../src/utilities/fs-helpers'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); +const { ValidationError, ForbiddenError } = require('../src/utilities/errors'); const { ok } = require('../src/utils/responses'); /** @@ -99,7 +99,7 @@ module.exports = function({ asyncHandler, validateSecurePath, auditLogger, docke } if (!await exists(resolvedPath)) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Path'); } diff --git a/dashcaddy-api/routes/ca.js b/dashcaddy-api/routes/ca.js index 71a1ab7..a9ac9a8 100644 --- a/dashcaddy-api/routes/ca.js +++ b/dashcaddy-api/routes/ca.js @@ -3,8 +3,8 @@ const fs = require('fs'); const fsp = require('fs').promises; const path = require('path'); const { execSync } = require('child_process'); -const { exists } = require('../fs-helpers'); -const { ValidationError } = require('../errors'); +const { exists } = require('../src/utilities/fs-helpers'); +const { ValidationError } = require('../src/utilities/errors'); const { ok } = require('../src/utils/responses'); const platformPaths = require('../platform-paths'); @@ -19,7 +19,7 @@ module.exports = function(ctx) { if (await exists(certInfoPath)) { certInfoFile = certInfoPath; } else { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('CA certificate information'); } @@ -50,7 +50,7 @@ module.exports = function(ctx) { if (await exists(dashcaCertPath)) certPath = dashcaCertPath; else if (await exists(hostCertPath)) certPath = hostCertPath; else { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Root CA certificate'); } @@ -73,7 +73,7 @@ module.exports = function(ctx) { if (await exists(certInfoPath)) { certInfoFile = certInfoPath; } else { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('CA certificate information. Deploy DashCA first or ensure cert-info.json exists.'); } @@ -106,7 +106,7 @@ module.exports = function(ctx) { } if (!templateContent) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`Install script template (${templateName})`); } diff --git a/dashcaddy-api/routes/config-drift.js b/dashcaddy-api/routes/config-drift.js index 52e6c29..efab9ad 100644 --- a/dashcaddy-api/routes/config-drift.js +++ b/dashcaddy-api/routes/config-drift.js @@ -9,7 +9,7 @@ const express = require('express'); const { success } = require('../src/utils/responses'); -const { ValidationError, NotFoundError } = require('../errors'); +const { ValidationError, NotFoundError } = require('../src/utilities/errors'); /** * Config-drift route factory diff --git a/dashcaddy-api/routes/config/assets.js b/dashcaddy-api/routes/config/assets.js index 17bfc0f..76a3714 100644 --- a/dashcaddy-api/routes/config/assets.js +++ b/dashcaddy-api/routes/config/assets.js @@ -1,11 +1,11 @@ const express = require('express'); const fsp = require('fs').promises; const path = require('path'); -const { LIMITS } = require('../../constants'); -const { exists } = require('../../fs-helpers'); -const { ValidationError } = require('../../errors'); +const { LIMITS } = require('../../../src/utilities/constants'); +const { exists } = require('../../../src/utilities/fs-helpers'); +const { ValidationError } = require('../../../src/utilities/errors'); const platformPaths = require('../../platform-paths'); -const { ok, successMessage } = require('../../src/utils/responses'); +const { ok, successMessage } = require('../src/utils/responses'); /** * Config assets routes factory * @param {Object} deps - Explicit dependencies diff --git a/dashcaddy-api/routes/config/backup.js b/dashcaddy-api/routes/config/backup.js index 67d14d5..d8896b2 100644 --- a/dashcaddy-api/routes/config/backup.js +++ b/dashcaddy-api/routes/config/backup.js @@ -1,11 +1,11 @@ const fsp = require('fs').promises; const fs = require('fs'); const path = require('path'); -const { CADDY } = require('../../constants'); -const { exists } = require('../../fs-helpers'); -const { ValidationError, AuthenticationError } = require('../../errors'); +const { CADDY } = require('../../../src/utilities/constants'); +const { exists } = require('../../../src/utilities/fs-helpers'); +const { ValidationError, AuthenticationError } = require('../../../src/utilities/errors'); const platformPaths = require('../../platform-paths'); -const { ok } = require('../../src/utils/responses'); +const { ok } = require('../src/utils/responses'); /** * Config backup routes factory @@ -380,7 +380,7 @@ module.exports = function(deps) { if (results.restored.includes('encryptionKey')) { try { // Clear the cached key so crypto-utils reloads from the new file on next use - const cryptoUtils = require('../../crypto-utils'); + const cryptoUtils = require('../../../src/security/crypto-utils'); if (typeof cryptoUtils.clearCachedKey === 'function') { cryptoUtils.clearCachedKey(); } diff --git a/dashcaddy-api/routes/config/settings.js b/dashcaddy-api/routes/config/settings.js index dd09b6b..f1ebc6f 100644 --- a/dashcaddy-api/routes/config/settings.js +++ b/dashcaddy-api/routes/config/settings.js @@ -1,8 +1,8 @@ const fsp = require('fs').promises; -const { validateConfig } = require('../../config-schema'); -const { exists } = require('../../fs-helpers'); -const { ValidationError } = require('../../errors'); -const { ok, successMessage } = require('../../src/utils/responses'); +const { validateConfig } = require('../../../src/utilities/config-schema'); +const { exists } = require('../../../src/utilities/fs-helpers'); +const { ValidationError } = require('../../../src/utilities/errors'); +const { ok, successMessage } = require('../src/utils/responses'); /** * Config settings routes factory diff --git a/dashcaddy-api/routes/containers.js b/dashcaddy-api/routes/containers.js index e4cef15..cd63eab 100644 --- a/dashcaddy-api/routes/containers.js +++ b/dashcaddy-api/routes/containers.js @@ -1,7 +1,7 @@ const express = require('express'); -const { DOCKER } = require('../constants'); -const { paginate, parsePaginationParams } = require('../pagination'); -const { NotFoundError } = require('../errors'); +const { DOCKER } = require('../src/utilities/constants'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); +const { NotFoundError } = require('../src/utilities/errors'); const { success } = require('../src/utils/responses'); /** diff --git a/dashcaddy-api/routes/dependencies.js b/dashcaddy-api/routes/dependencies.js index b5c12a7..3cdf314 100644 --- a/dashcaddy-api/routes/dependencies.js +++ b/dashcaddy-api/routes/dependencies.js @@ -16,7 +16,7 @@ const express = require('express'); const { success, error: errorResponse } = require('../src/utils/responses'); -const { NotFoundError, ValidationError } = require('../errors'); +const { NotFoundError, ValidationError } = require('../src/utilities/errors'); /** * Dependencies route factory diff --git a/dashcaddy-api/routes/dns.js b/dashcaddy-api/routes/dns.js index 01b6cc2..c003f1f 100644 --- a/dashcaddy-api/routes/dns.js +++ b/dashcaddy-api/routes/dns.js @@ -2,10 +2,10 @@ const express = require('express'); const fs = require('fs'); const fsp = require('fs').promises; const validatorLib = require('validator'); -const { APP, TIMEOUTS, CADDY, DNS_RECORD_TYPES, REGEX, SESSION_TTL } = require('../constants'); -const { exists } = require('../fs-helpers'); +const { APP, TIMEOUTS, CADDY, DNS_RECORD_TYPES, REGEX, SESSION_TTL } = require('../src/utilities/constants'); +const { exists } = require('../src/utilities/fs-helpers'); const { success, error: errorResponse } = require('../src/utils/responses'); -const { ValidationError, AuthenticationError, NotFoundError } = require('../errors'); +const { ValidationError, AuthenticationError, NotFoundError } = require('../src/utilities/errors'); /** * DNS routes factory diff --git a/dashcaddy-api/routes/docker-resources.js b/dashcaddy-api/routes/docker-resources.js index aa68cd7..bdb5220 100644 --- a/dashcaddy-api/routes/docker-resources.js +++ b/dashcaddy-api/routes/docker-resources.js @@ -1,6 +1,6 @@ const express = require('express'); const { success } = require('../src/utils/responses'); -const { ValidationError } = require('../errors'); +const { ValidationError } = require('../src/utilities/errors'); /** * Docker resources route factory (volumes, networks, disk usage) diff --git a/dashcaddy-api/routes/errorlogs.js b/dashcaddy-api/routes/errorlogs.js index 7d3f016..0478413 100644 --- a/dashcaddy-api/routes/errorlogs.js +++ b/dashcaddy-api/routes/errorlogs.js @@ -1,8 +1,8 @@ const express = require('express'); const fs = require('fs'); const fsp = require('fs').promises; -const { exists } = require('../fs-helpers'); -const { paginate, parsePaginationParams } = require('../pagination'); +const { exists } = require('../src/utilities/fs-helpers'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); const { success } = require('../src/utils/responses'); /** diff --git a/dashcaddy-api/routes/health.js b/dashcaddy-api/routes/health.js index b26c569..6508d74 100644 --- a/dashcaddy-api/routes/health.js +++ b/dashcaddy-api/routes/health.js @@ -2,13 +2,13 @@ const express = require('express'); const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); -const { TIMEOUTS } = require('../constants'); -const { exists } = require('../fs-helpers'); -const { paginate, parsePaginationParams } = require('../pagination'); +const { TIMEOUTS } = require('../src/utilities/constants'); +const { exists } = require('../src/utilities/fs-helpers'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); const platformPaths = require('../platform-paths'); -const { resolveServiceUrl } = require('../url-resolver'); +const { resolveServiceUrl } = require('../src/utilities/url-resolver'); const { success, error: errorResponse, errorResponse: sendError, ok, notFound } = require('../src/utils/responses'); -const { ValidationError } = require('../errors'); +const { ValidationError } = require('../src/utilities/errors'); /** * Health routes factory @@ -190,7 +190,7 @@ module.exports = function({ // Load service config if (!await exists(SERVICES_FILE)) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Services file'); } @@ -199,7 +199,7 @@ module.exports = function({ const service = services.find(s => (s.id || s.name?.toLowerCase()) === serviceId); if (!service) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Service'); } @@ -331,7 +331,7 @@ module.exports = function({ const hours = parseInt(req.query.hours) || 24; const stats = healthChecker.getServiceStats(req.params.serviceId, hours); if (!stats) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Service'); } success(res, { stats }); diff --git a/dashcaddy-api/routes/license.js b/dashcaddy-api/routes/license.js index 45b91a1..9132535 100644 --- a/dashcaddy-api/routes/license.js +++ b/dashcaddy-api/routes/license.js @@ -1,6 +1,6 @@ const express = require('express'); const { success, error: errorResponse } = require('../src/utils/responses'); -const { ValidationError } = require('../errors'); +const { ValidationError } = require('../src/utilities/errors'); /** * License routes factory diff --git a/dashcaddy-api/routes/logs.js b/dashcaddy-api/routes/logs.js index 392482f..d7b91e5 100644 --- a/dashcaddy-api/routes/logs.js +++ b/dashcaddy-api/routes/logs.js @@ -2,9 +2,9 @@ const express = require('express'); const fs = require('fs'); const fsp = require('fs').promises; const path = require('path'); -const { exists } = require('../fs-helpers'); -const { paginate, parsePaginationParams } = require('../pagination'); -const { NotFoundError, ValidationError, ForbiddenError } = require('../errors'); +const { exists } = require('../src/utilities/fs-helpers'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); +const { NotFoundError, ValidationError, ForbiddenError } = require('../src/utilities/errors'); const { ok } = require('../src/utils/responses'); /** @@ -48,7 +48,7 @@ module.exports = function({ asyncHandler, docker, logDigest, dockerMaintenance } info = await container.inspect(); } catch (err) { if (err.statusCode === 404 || (err.message && err.message.includes('no such container'))) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`Container ${containerId}`); } throw err; @@ -97,7 +97,7 @@ module.exports = function({ asyncHandler, docker, logDigest, dockerMaintenance } await container.inspect(); } catch (err) { if (err.statusCode === 404 || (err.message && err.message.includes('no such container'))) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`Container ${containerId}`); } throw err; @@ -232,7 +232,7 @@ module.exports = function({ asyncHandler, docker, logDigest, dockerMaintenance } try { resolvedPath = await fsp.realpath(normalizedPath); } catch { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Log file'); } @@ -247,7 +247,7 @@ module.exports = function({ asyncHandler, docker, logDigest, dockerMaintenance } } if (!await exists(resolvedPath)) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Log file'); } diff --git a/dashcaddy-api/routes/monitoring.js b/dashcaddy-api/routes/monitoring.js index 4a512e6..7ffb704 100644 --- a/dashcaddy-api/routes/monitoring.js +++ b/dashcaddy-api/routes/monitoring.js @@ -39,7 +39,7 @@ module.exports = function({ resourceMonitor, docker, asyncHandler, log, notifica router.get('/monitoring/stats/:containerId', asyncHandler(async (req, res) => { const stats = resourceMonitor.getCurrentStats(req.params.containerId); if (!stats) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Container'); } success(res, { stats }); @@ -55,7 +55,7 @@ module.exports = function({ resourceMonitor, docker, asyncHandler, log, notifica const startTime = parseInt(req.query.startTime, 10); const endTime = parseInt(req.query.endTime, 10); if (Number.isNaN(startTime) || Number.isNaN(endTime) || startTime >= endTime) { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('Invalid startTime/endTime'); } const result = resourceMonitor.getHistoryByRange(containerId, startTime, endTime); @@ -74,7 +74,7 @@ module.exports = function({ resourceMonitor, docker, asyncHandler, log, notifica const hours = parseInt(req.query.hours) || 24; const aggregated = resourceMonitor.getAggregatedStats(req.params.containerId, hours); if (!aggregated) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError('Monitoring data'); } success(res, { aggregated, hours }); @@ -92,7 +92,7 @@ module.exports = function({ resourceMonitor, docker, asyncHandler, log, notifica router.post('/monitoring/alerts/config', asyncHandler(async (req, res) => { const { configs } = req.body; if (!configs || typeof configs !== 'object') { - const { ValidationError } = require('../errors'); + const { ValidationError } = require('../src/utilities/errors'); throw new ValidationError('configs object required'); } for (const [containerId, config] of Object.entries(configs)) { diff --git a/dashcaddy-api/routes/notifications.js b/dashcaddy-api/routes/notifications.js index 16e9619..82b1048 100644 --- a/dashcaddy-api/routes/notifications.js +++ b/dashcaddy-api/routes/notifications.js @@ -1,8 +1,8 @@ const express = require('express'); -const { validateURL, validateToken } = require('../input-validator'); +const { validateURL, validateToken } = require('../src/security/input-validator'); const validatorLib = require('validator'); -const { paginate, parsePaginationParams } = require('../pagination'); -const { ValidationError } = require('../errors'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); +const { ValidationError } = require('../src/utilities/errors'); const { ok, successMessage } = require('../src/utils/responses'); /** diff --git a/dashcaddy-api/routes/recipes/deploy.js b/dashcaddy-api/routes/recipes/deploy.js index 4830d7f..d3bff7b 100644 --- a/dashcaddy-api/routes/recipes/deploy.js +++ b/dashcaddy-api/routes/recipes/deploy.js @@ -1,8 +1,8 @@ const express = require('express'); -const { ValidationError } = require('../../errors'); +const { ValidationError } = require('../../../src/utilities/errors'); const crypto = require('crypto'); -const { DOCKER } = require('../../constants'); -const { ok } = require('../../src/utils/responses'); +const { DOCKER } = require('../../../src/utilities/constants'); +const { ok } = require('../src/utils/responses'); /** * Recipes deployment routes factory @@ -28,7 +28,7 @@ module.exports = function({ docker, credentialManager: _credentialManager, servi // eslint-disable-next-line complexity router.post('/deploy', asyncHandler(async (req, res) => { const { recipeId, config } = req.body; - const { RECIPE_TEMPLATES } = require('../../recipe-templates'); + const { RECIPE_TEMPLATES } = require('../../../src/recipes/recipe-templates'); const recipe = RECIPE_TEMPLATES[recipeId]; if (!recipe) throw new ValidationError('Invalid recipe template', 'recipeId'); diff --git a/dashcaddy-api/routes/recipes/index.js b/dashcaddy-api/routes/recipes/index.js index 3a2447e..fd87dd5 100644 --- a/dashcaddy-api/routes/recipes/index.js +++ b/dashcaddy-api/routes/recipes/index.js @@ -1,8 +1,8 @@ const express = require('express'); const deployRoutes = require('./deploy'); const manageRoutes = require('./manage'); -const { NotFoundError } = require('../../errors'); -const { ok } = require('../../src/utils/responses'); +const { NotFoundError } = require('../../../src/utilities/errors'); +const { ok } = require('../src/utils/responses'); /** * Recipes routes aggregator @@ -32,7 +32,7 @@ module.exports = function(ctx) { // GET /api/recipes/templates — list all recipe templates router.get('/templates', deps.asyncHandler(async (req, res) => { - const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('../../recipe-templates'); + const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('../../../src/recipes/recipe-templates'); const templates = Object.entries(RECIPE_TEMPLATES).map(([id, recipe]) => ({ id, name: recipe.name, @@ -61,7 +61,7 @@ module.exports = function(ctx) { // GET /api/recipes/templates/:recipeId — get single recipe template detail router.get('/templates/:recipeId', deps.asyncHandler(async (req, res) => { - const { RECIPE_TEMPLATES } = require('../../recipe-templates'); + const { RECIPE_TEMPLATES } = require('../../../src/recipes/recipe-templates'); const recipe = RECIPE_TEMPLATES[req.params.recipeId]; if (!recipe) throw new NotFoundError(`Recipe template ${req.params.recipeId}`); diff --git a/dashcaddy-api/routes/recipes/manage.js b/dashcaddy-api/routes/recipes/manage.js index e9f75b5..62c9845 100644 --- a/dashcaddy-api/routes/recipes/manage.js +++ b/dashcaddy-api/routes/recipes/manage.js @@ -1,7 +1,7 @@ const express = require('express'); -const { DOCKER } = require('../../constants'); -const { NotFoundError } = require('../../errors'); -const { ok } = require('../../src/utils/responses'); +const { DOCKER } = require('../../../src/utilities/constants'); +const { NotFoundError } = require('../../../src/utilities/errors'); +const { ok } = require('../src/utils/responses'); module.exports = function({ servicesStateManager, asyncHandler, log, docker, notification, buildDomain, caddy }) { const router = express.Router(); @@ -269,7 +269,7 @@ module.exports = function({ servicesStateManager, asyncHandler, log, docker, not * Find all Docker containers belonging to a recipe by label */ async function findRecipeContainers(recipeId) { - const { RECIPE_TEMPLATES } = require('../../recipe-templates'); + const { RECIPE_TEMPLATES } = require('../../../src/recipes/recipe-templates'); const recipe = RECIPE_TEMPLATES[recipeId]; const recipeLabel = recipe ? recipe.name.toLowerCase().replace(/\s+/g, '-') @@ -293,7 +293,7 @@ module.exports = function({ servicesStateManager, asyncHandler, log, docker, not * Find recipe ID by its label (name slug) */ function findRecipeIdByLabel(label) { - const { RECIPE_TEMPLATES } = require('../../recipe-templates'); + const { RECIPE_TEMPLATES } = require('../../../src/recipes/recipe-templates'); for (const [id, recipe] of Object.entries(RECIPE_TEMPLATES)) { if (recipe.name.toLowerCase().replace(/\s+/g, '-') === label) { return id; diff --git a/dashcaddy-api/routes/services.js b/dashcaddy-api/routes/services.js index 41f18b0..a55e5f4 100644 --- a/dashcaddy-api/routes/services.js +++ b/dashcaddy-api/routes/services.js @@ -4,12 +4,12 @@ const http = require('http'); const https = require('https'); const tls = require('tls'); const validatorLib = require('validator'); -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, ConflictError } = require('../errors'); -const { resolveServiceUrl } = require('../url-resolver'); +const { APP, REGEX, TIMEOUTS } = require('../src/utilities/constants'); +const { validateServiceConfig, isValidPort } = require('../src/security/input-validator'); +const { exists } = require('../src/utilities/fs-helpers'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); +const { ValidationError, NotFoundError, ConflictError } = require('../src/utilities/errors'); +const { resolveServiceUrl } = require('../src/utilities/url-resolver'); const { success, error: errorResponse } = require('../src/utils/responses'); const platformPaths = require('../platform-paths'); diff --git a/dashcaddy-api/routes/sites.js b/dashcaddy-api/routes/sites.js index b2be215..ade0936 100644 --- a/dashcaddy-api/routes/sites.js +++ b/dashcaddy-api/routes/sites.js @@ -1,8 +1,8 @@ const express = require('express'); const fs = require('fs'); -const { CADDY, REGEX, LIMITS } = require('../constants'); -const { ValidationError, ConflictError, NotFoundError } = require('../errors'); -const { validateURL } = require('../input-validator'); +const { CADDY, REGEX, LIMITS } = require('../src/utilities/constants'); +const { ValidationError, ConflictError, NotFoundError } = require('../src/utilities/errors'); +const { validateURL } = require('../src/security/input-validator'); const { ok, successMessage } = require('../src/utils/responses'); /** diff --git a/dashcaddy-api/routes/tailscale.js b/dashcaddy-api/routes/tailscale.js index 4fcae96..3056ffe 100644 --- a/dashcaddy-api/routes/tailscale.js +++ b/dashcaddy-api/routes/tailscale.js @@ -1,8 +1,8 @@ const express = require('express'); const fs = require('fs'); -const { TAILSCALE } = require('../constants'); -const { exists } = require('../fs-helpers'); -const { ValidationError, NotFoundError } = require('../errors'); +const { TAILSCALE } = require('../src/utilities/constants'); +const { exists } = require('../src/utilities/fs-helpers'); +const { ValidationError, NotFoundError } = require('../src/utilities/errors'); const { ok, successMessage, unauthorized } = require('../src/utils/responses'); /** @@ -156,7 +156,7 @@ module.exports = function({ const match = content.match(blockRegex); if (!match) { - const { NotFoundError } = require('../errors'); + const { NotFoundError } = require('../src/utilities/errors'); throw new NotFoundError(`Service ${domain} in Caddyfile`); } diff --git a/dashcaddy-api/routes/themes.js b/dashcaddy-api/routes/themes.js index 404ea34..073a238 100644 --- a/dashcaddy-api/routes/themes.js +++ b/dashcaddy-api/routes/themes.js @@ -2,7 +2,7 @@ const express = require('express'); const fs = require('fs'); const path = require('path'); const { success } = require('../src/utils/responses'); -const { ValidationError, NotFoundError } = require('../errors'); +const { ValidationError, NotFoundError } = require('../src/utilities/errors'); const platformPaths = require('../platform-paths'); /** diff --git a/dashcaddy-api/routes/updates.js b/dashcaddy-api/routes/updates.js index 43b8e15..4384220 100644 --- a/dashcaddy-api/routes/updates.js +++ b/dashcaddy-api/routes/updates.js @@ -1,6 +1,6 @@ const express = require('express'); -const { paginate, parsePaginationParams } = require('../pagination'); -const { ValidationError } = require('../errors'); +const { paginate, parsePaginationParams } = require('../src/utilities/pagination'); +const { ValidationError } = require('../src/utilities/errors'); const { ok, successMessage } = require('../src/utils/responses'); /** diff --git a/dashcaddy-api/scripts/fix-remaining-paths.py b/dashcaddy-api/scripts/fix-remaining-paths.py new file mode 100644 index 0000000..9c44da3 --- /dev/null +++ b/dashcaddy-api/scripts/fix-remaining-paths.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +""" +Fix the remaining broken require paths after DC-005 refactor. + +Two patterns to fix: +1. `require('./src/...')` and `require('../../src/...')` and `require('../../../src/...')` + in files inside `src/` directories → should be `require('../...')` (relative to src/) +2. `require('../../../src/...')` in test files in `__tests__/` → should be `require('../src/...')` +""" +import os +import re +from pathlib import Path + +DASHCADDY_API = Path('/root/dashcaddy-krystie/dashcaddy-api') + +# Pattern to match require('../../../src/X/Y') and capture +# We need to detect the file's location and rewrite based on that +# A simple approach: find any require that contains 'src/' in the path, +# and rewrite it to be relative to the file's location. + +def fix_file(filepath: Path) -> bool: + """Returns True if file was changed.""" + content = filepath.read_text() + original = content + + # Find the file's directory relative to dashcaddy-api root + rel_dir = filepath.parent.relative_to(DASHCADDY_API) + depth = len(rel_dir.parts) + + # If file is in src/X/Y/file.js, depth is 3 (src, X, Y) + # If file is in __tests__/file.js, depth is 1 + # If file is in __tests__/routes/file.js, depth is 2 + + # Find all require() calls that contain 'src/' + # Pattern: require('(.....)*src/path') + def replacer(match): + quote = match.group(1) # the quote char + path = match.group(2) # the path inside quotes + # Calculate what the path SHOULD be + if 'src/' not in path: + return match.group(0) + + # Extract the part after 'src/' + idx = path.find('src/') + after_src = path[idx + 4:] # everything after 'src/' + + if filepath.parts[-3] == 'src': + # File is in src/X/file.js - depth 3 + # Should be '../' + new_path = '../' + after_src + elif filepath.parts[-4] == 'src': + # File is in src/X/Y/file.js - depth 4 + # Should be '../../' + new_path = '../../' + after_src + elif filepath.parts[-2] == '__tests__' or filepath.parent.name == '__tests__': + # File is in __tests__/file.js - depth 1 (relative to api root) + # Should be '../src/' + new_path = '../src/' + after_src + elif filepath.parts[-2] == 'routes' and filepath.parts[-3] == '__tests__': + # File is in __tests__/routes/file.js - depth 2 + # Should be '../../src/' + new_path = '../../src/' + after_src + elif 'src' in rel_dir.parts: + # Other src nested location + # Count how many .. we need + src_depth = len(rel_dir.parts) - list(rel_dir.parts).index('src') - 1 + new_path = '../' * src_depth + after_src + else: + # Other location, leave it + return match.group(0) + + return f"require({quote}{new_path}{quote})" + + new_content = re.sub( + r"require\((['\"])([^'\"]*src/[^'\"]*)\1\)", + replacer, + content + ) + + if new_content != original: + filepath.write_text(new_content) + return True + return False + + +def main(): + changed = [] + for js_file in DASHCADDY_API.rglob('*.js'): + # Skip node_modules + if 'node_modules' in js_file.parts: + continue + if fix_file(js_file): + changed.append(str(js_file.relative_to(DASHCADDY_API))) + + print(f"Changed {len(changed)} files:") + for f in changed: + print(f" {f}") + + +if __name__ == '__main__': + main() diff --git a/dashcaddy-api/scripts/refactor-requires.js b/dashcaddy-api/scripts/refactor-requires.js new file mode 100644 index 0000000..7e1390a --- /dev/null +++ b/dashcaddy-api/scripts/refactor-requires.js @@ -0,0 +1,180 @@ +#!/usr/bin/env node +/** + * Refactor helper: rewrites require('./xxx') / require('../xxx') paths in + * dashcaddy-api to point to the new src//xxx.js locations. + * + * Algorithm: + * 1. For each require() call with a relative spec: + * 2. If the resolved file exists, leave it alone. + * 3. If the resolved file does NOT exist, the bare name of the spec + * (or the directory name 'dns-providers') might be one of the + * modules that was moved out of the repo root. In that case, rewrite + * the spec to the correct relative path to the new location. + * 4. Otherwise leave alone. + */ +const fs = require('fs'); +const path = require('path'); + +const REPO = process.cwd(); + +// Map: bare module name (no extension) -> new repo-relative path (no extension) +const NEW_LOCATIONS = { + 'auth-manager': 'src/managers/auth-manager', + 'credential-manager': 'src/managers/credential-manager', + 'license-manager': 'src/managers/license-manager', + 'port-lock-manager': 'src/managers/port-lock-manager', + 'state-manager': 'src/managers/state-manager', + 'notification-manager': 'src/managers/notification-manager', + 'resource-monitor': 'src/managers/resource-monitor', + 'config-drift-detector': 'src/managers/config-drift-detector', + 'auto-restart-manager': 'src/managers/auto-restart-manager', + 'update-manager': 'src/managers/update-manager', + 'dependency-manager': 'src/managers/dependency-manager', + 'csrf-protection': 'src/security/csrf-protection', + 'crypto-utils': 'src/security/crypto-utils', + 'docker-security': 'src/security/docker-security', + 'input-validator': 'src/security/input-validator', + 'keychain-manager': 'src/security/keychain-manager', + 'log-digest': 'src/security/log-digest', + 'audit-logger': 'src/security/audit-logger', + 'docker-maintenance': 'src/docker/docker-maintenance', + 'app-templates': 'src/docker/app-templates', + 'self-updater': 'src/docker/self-updater', + 'dns-propagation': 'src/dns/dns-propagation', + 'recipe-templates': 'src/recipes/recipe-templates', + 'bundled-workflows': 'src/recipes/bundled-workflows', + 'health-checker': 'src/monitoring/health-checker', + 'metrics': 'src/monitoring/metrics', + 'ssl-monitor': 'src/monitoring/ssl-monitor', + 'backup-manager': 'src/utilities/backup-manager', + 'error-handler': 'src/utilities/error-handler', + 'errors': 'src/utilities/errors', + 'fs-helpers': 'src/utilities/fs-helpers', + 'pagination': 'src/utilities/pagination', + 'url-resolver': 'src/utilities/url-resolver', + 'config-schema': 'src/utilities/config-schema', + 'constants': 'src/utilities/constants', + 'middleware': 'src/utilities/middleware', + 'startup-validator': 'src/utilities/startup-validator', + 'cache-config': 'src/utilities/cache-config', +}; + +const SKIP_DIRS = new Set(['node_modules', '.git']); +const SKIP_FILE_PATTERNS = [/\/scripts\/refactor-requires\.js$/]; + +function* walk(dir) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (SKIP_DIRS.has(entry.name)) continue; + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + yield* walk(full); + } else if (entry.name.endsWith('.js')) { + yield full; + } + } +} + +function toRelativeFromFile(filePath, targetRel) { + const fromDir = path.dirname(filePath); + const targetAbs = path.resolve(REPO, targetRel); + let rel = path.relative(fromDir, targetAbs); + if (!rel.startsWith('.')) rel = './' + rel; + return rel.split(path.sep).join('/'); +} + +function fileExistsWithJsOrIndex(p) { + // exists if p is a file, or p is a dir with index.js + try { + if (fs.existsSync(p) && fs.statSync(p).isFile()) return true; + } catch (_) {} + try { + if (fs.existsSync(p + '.js') && fs.statSync(p + '.js').isFile()) return true; + } catch (_) {} + try { + if ( + fs.existsSync(p) && + fs.statSync(p).isDirectory() && + fs.existsSync(path.join(p, 'index.js')) + ) + return true; + } catch (_) {} + return false; +} + +function refactor(filePath) { + const relFile = path.relative(REPO, filePath); + if (SKIP_FILE_PATTERNS.some((re) => re.test(relFile))) return false; + + const content = fs.readFileSync(filePath, 'utf8'); + let changed = false; + + const requireRe = /require\(\s*(['"])([^'"]+)\1\s*\)/g; + const newContent = content.replace(requireRe, (full, quote, spec) => { + if (!spec.startsWith('.')) return full; // package require, leave alone + const fromDir = path.dirname(filePath); + const resolvedBase = path.resolve(fromDir, spec); + // If the resolved file exists, the require is correct as-is. + if (fileExistsWithJsOrIndex(resolvedBase)) { + // But — check for the special case: require to /dns-providers/x + // which after move becomes /src/dns/dns-providers/x — wait, + // that doesn't exist anymore. The dir was moved. + const dnsProvidersOld = path.resolve(REPO, 'dns-providers'); + if ( + resolvedBase === dnsProvidersOld || + resolvedBase.startsWith(dnsProvidersOld + path.sep) + ) { + const subPath = resolvedBase === dnsProvidersOld ? '' : resolvedBase.slice(dnsProvidersOld.length + 1); + const newResolved = path.resolve(REPO, 'src/dns/dns-providers', subPath); + let rel = path.relative(fromDir, newResolved); + if (!rel.startsWith('.')) rel = './' + rel; + const newSpec = rel.split(path.sep).join('/'); + changed = true; + return `require(${quote}${newSpec}${quote})`; + } + return full; + } + // The file does not exist. Check if the bare name is a moved module. + const bare = path.basename(resolvedBase); + if (bare in NEW_LOCATIONS) { + const target = NEW_LOCATIONS[bare]; + const newSpec = toRelativeFromFile(filePath, target); + changed = true; + return `require(${quote}${newSpec}${quote})`; + } + // Bare not in map. Check for the special case: the spec points into + // the OLD dns-providers dir (now src/dns/dns-providers). E.g. spec + // could be '../dns-providers/registry' or './dns-providers/registry' + // from somewhere else. + if (spec.includes('dns-providers')) { + const dnsProvidersOld = path.resolve(REPO, 'dns-providers'); + if ( + resolvedBase === dnsProvidersOld || + resolvedBase.startsWith(dnsProvidersOld + path.sep) + ) { + const subPath = + resolvedBase === dnsProvidersOld ? '' : resolvedBase.slice(dnsProvidersOld.length + 1); + const newResolved = path.resolve(REPO, 'src/dns/dns-providers', subPath); + let rel = path.relative(fromDir, newResolved); + if (!rel.startsWith('.')) rel = './' + rel; + const newSpec = rel.split(path.sep).join('/'); + changed = true; + return `require(${quote}${newSpec}${quote})`; + } + } + return full; + }); + + if (changed) { + fs.writeFileSync(filePath, newContent); + } + return changed; +} + +let count = 0; +for (const file of walk(REPO)) { + if (refactor(file)) { + count += 1; + console.log('rewrote', path.relative(REPO, file)); + } +} +console.log(`\nDone: rewrote ${count} file(s).`); diff --git a/dashcaddy-api/server.js b/dashcaddy-api/server.js index 97a89d7..2d0cf7c 100644 --- a/dashcaddy-api/server.js +++ b/dashcaddy-api/server.js @@ -33,7 +33,7 @@ process.on('uncaughtException', (error) => { const CONFIG_FILE = process.env.CONFIG_FILE || platformPaths.servicesFile.replace('services.json', 'config.json'); // Validate startup configuration - const { validateStartupConfig } = require('./startup-validator'); + const { validateStartupConfig } = require('../src/utilities/startup-validator'); await validateStartupConfig({ log, CADDYFILE_PATH, @@ -56,23 +56,23 @@ process.on('uncaughtException', (error) => { // Attach WebSocket exec handler (with auth) const attachExecWS = require('./routes/exec'); - const authManager = require('./auth-manager'); + const authManager = require('../src/managers/auth-manager'); attachExecWS(server, log, authManager); log.info('server', 'WebSocket exec handler attached (auth enforced)'); // Start feature modules - const resourceMonitor = require('./resource-monitor'); - const backupManager = require('./backup-manager'); - const healthChecker = require('./health-checker'); - const updateManager = require('./update-manager'); - const selfUpdater = require('./self-updater'); - const portLockManager = require('./port-lock-manager'); + const resourceMonitor = require('../src/managers/resource-monitor'); + const backupManager = require('../src/utilities/backup-manager'); + const healthChecker = require('../src/monitoring/health-checker'); + const updateManager = require('../src/managers/update-manager'); + const selfUpdater = require('../src/docker/self-updater'); + const portLockManager = require('../src/managers/port-lock-manager'); // Optional modules let dockerMaintenance, logDigest, bundledWorkflows; - try { dockerMaintenance = require('./docker-maintenance'); } catch { /* optional */ } - try { logDigest = require('./log-digest'); } catch { /* optional */ } - try { bundledWorkflows = require('./bundled-workflows'); } catch { /* optional */ } + try { dockerMaintenance = require('../src/docker/docker-maintenance'); } catch { /* optional */ } + try { logDigest = require('../src/security/log-digest'); } catch { /* optional */ } + try { bundledWorkflows = require('../src/recipes/bundled-workflows'); } catch { /* optional */ } // Initialize workflow engine if bundled-workflows is available // NOTE: createApp() already initializes the workflow engine in src/app.js @@ -85,7 +85,7 @@ process.on('uncaughtException', (error) => { // Create a context with needed services const workflowCtx = { docker: { client: require('dockerode')() }, - notification: require('./notification-manager')({ + notification: require('../src/managers/notification-manager')({ NOTIFICATIONS_FILE: process.env.NOTIFICATIONS_FILE || require('./platform-paths').notificationsFile, fetchT, log, @@ -137,8 +137,8 @@ process.on('uncaughtException', (error) => { // Health checker (with service sync) (async () => { try { - const { syncHealthCheckerServices } = require('./startup-validator'); - const StateManager = require('./state-manager'); + const { syncHealthCheckerServices } = require('../src/utilities/startup-validator'); + const StateManager = require('../src/managers/state-manager'); const servicesStateManager = new StateManager(SERVICES_FILE); await syncHealthCheckerServices({ @@ -150,7 +150,7 @@ process.on('uncaughtException', (error) => { ? `https://${config.domain}/${subdomain}` : `https://${subdomain}${config.tld}`, siteConfig: config, - APP: require('./constants').APP + APP: require('../src/utilities/constants').APP }); healthChecker.start(); @@ -232,11 +232,11 @@ process.on('uncaughtException', (error) => { const shutdown = (signal) => { log.info('shutdown', `${signal} received, draining connections...`); - const resourceMonitor = require('./resource-monitor'); - const backupManager = require('./backup-manager'); - const healthChecker = require('./health-checker'); - const updateManager = require('./update-manager'); - const selfUpdater = require('./self-updater'); + const resourceMonitor = require('../src/managers/resource-monitor'); + const backupManager = require('../src/utilities/backup-manager'); + const healthChecker = require('../src/monitoring/health-checker'); + const updateManager = require('../src/managers/update-manager'); + const selfUpdater = require('../src/docker/self-updater'); resourceMonitor.stop(); backupManager.stop(); @@ -245,12 +245,12 @@ process.on('uncaughtException', (error) => { selfUpdater.stop(); try { - const dockerMaintenance = require('./docker-maintenance'); + const dockerMaintenance = require('../src/docker/docker-maintenance'); dockerMaintenance.stop(); } catch { /* optional */ } try { - const logDigest = require('./log-digest'); + const logDigest = require('../src/security/log-digest'); logDigest.stop(); } catch { /* optional */ } diff --git a/dashcaddy-api/src/app.js b/dashcaddy-api/src/app.js index 75746d9..17f1822 100644 --- a/dashcaddy-api/src/app.js +++ b/dashcaddy-api/src/app.js @@ -15,41 +15,41 @@ const { errorResponse, ok } = require('./utils/responses'); const { asyncHandler } = require('./utils/async-handler'); // Managers and utilities -const StateManager = require('../state-manager'); +const StateManager = require('managers/state-manager'); const platformPaths = require('../platform-paths'); -const { LicenseManager } = require('../license-manager'); -const credentialManager = require('../credential-manager'); -const authManager = require('../auth-manager'); -const dockerSecurity = require('../docker-security'); -const auditLogger = require('../audit-logger'); -const portLockManager = require('../port-lock-manager'); -const resourceMonitor = require('../resource-monitor'); -const backupManager = require('../backup-manager'); -const healthChecker = require('../health-checker'); -const updateManager = require('../update-manager'); -const selfUpdater = require('../self-updater'); -const configureMiddleware = require('../middleware'); -const { validateStartupConfig: _validateStartupConfig, syncHealthCheckerServices } = require('../startup-validator'); -const { CSRF_HEADER_NAME } = require('../csrf-protection'); -const { resolveServiceUrl } = require('../url-resolver'); -const metrics = require('../metrics'); -const { validateURL } = require('../input-validator'); +const { LicenseManager } = require('managers/license-manager'); +const credentialManager = require('managers/credential-manager'); +const authManager = require('managers/auth-manager'); +const dockerSecurity = require('security/docker-security'); +const auditLogger = require('security/audit-logger'); +const portLockManager = require('managers/port-lock-manager'); +const resourceMonitor = require('managers/resource-monitor'); +const backupManager = require('utilities/backup-manager'); +const healthChecker = require('monitoring/health-checker'); +const updateManager = require('managers/update-manager'); +const selfUpdater = require('docker/self-updater'); +const configureMiddleware = require('utilities/middleware'); +const { validateStartupConfig: _validateStartupConfig, syncHealthCheckerServices } = require('utilities/startup-validator'); +const { CSRF_HEADER_NAME } = require('security/csrf-protection'); +const { resolveServiceUrl } = require('utilities/url-resolver'); +const metrics = require('monitoring/metrics'); +const { validateURL } = require('security/input-validator'); // Optional modules let dockerMaintenance, logDigest; -try { dockerMaintenance = require('../docker-maintenance'); } catch (_) { /* optional module */ } -try { logDigest = require('../log-digest'); } catch (_) { /* optional module */ } +try { dockerMaintenance = require('docker/docker-maintenance'); } catch (_) { /* optional module */ } +try { logDigest = require('security/log-digest'); } catch (_) { /* optional module */ } // Workflow engine (bundled workflows) let bundledWorkflowsModule; let workflowEngine = null; try { - bundledWorkflowsModule = require('../bundled-workflows'); + bundledWorkflowsModule = require('recipes/bundled-workflows'); } catch (_) { /* optional module */ } // Templates -const { APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS } = require('../app-templates'); -const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('../recipe-templates'); +const { APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS } = require('docker/app-templates'); +const { RECIPE_TEMPLATES, RECIPE_CATEGORIES } = require('recipes/recipe-templates'); // Route modules const healthRoutes = require('../routes/health'); @@ -79,17 +79,17 @@ const dockerResourcesRoutes = require('../routes/docker-resources'); const eventsRoutes = require('../routes/events'); const workflowsRoutes = require('../routes/workflows'); const dependenciesRoutes = require('../routes/dependencies'); -const DependencyManager = require('../dependency-manager'); +const DependencyManager = require('managers/dependency-manager'); const autoRestartRoutes = require('../routes/auto-restart'); const configDriftRoutes = require('../routes/config-drift'); const sslMonitorRoutes = require('../routes/ssl-monitor'); -const { AutoRestartManager } = require('../auto-restart-manager'); -const { ConfigDriftDetector } = require('../config-drift-detector'); -const SSLMonitor = require('../ssl-monitor'); -const DNSPropagationChecker = require('../dns-propagation'); +const { AutoRestartManager } = require('managers/auto-restart-manager'); +const { ConfigDriftDetector } = require('managers/config-drift-detector'); +const SSLMonitor = require('monitoring/ssl-monitor'); +const DNSPropagationChecker = require('dns/dns-propagation'); // Constants -const { APP } = require('../constants'); +const { APP } = require('utilities/constants'); /** * Create and configure the Express application @@ -216,15 +216,15 @@ async function createApp() { auditLogger, authManager, log, - cryptoUtils: require('../crypto-utils'), + cryptoUtils: require('security/crypto-utils'), isValidContainerId, isTailscaleIP, getTailscaleStatus, - RATE_LIMITS: require('../constants').RATE_LIMITS, - LIMITS: require('../constants').LIMITS, - APP: require('../constants').APP, - CACHE_CONFIGS: require('../cache-config').CACHE_CONFIGS, - createCache: require('../cache-config').createCache, + RATE_LIMITS: require('utilities/constants').RATE_LIMITS, + LIMITS: require('utilities/constants').LIMITS, + APP: require('utilities/constants').APP, + CACHE_CONFIGS: require('utilities/cache-config').CACHE_CONFIGS, + createCache: require('utilities/cache-config').createCache, }); const { strictLimiter } = middlewareResult; @@ -237,7 +237,7 @@ async function createApp() { // eslint-disable-next-line require-await -- may grow awaits as config loading evolves async function readConfig() { - const { readJsonFile } = require('../fs-helpers'); + const { readJsonFile } = require('utilities/fs-helpers'); return readJsonFile(config.CONFIG_FILE, {}); } @@ -260,7 +260,7 @@ async function createApp() { async function saveTotpConfig() { try { - const { writeJsonFile } = require('../fs-helpers'); + const { writeJsonFile } = require('utilities/fs-helpers'); await writeJsonFile(config.TOTP_CONFIG_FILE, totpConfig); } catch (e) { log.error('config', 'Could not save TOTP config', { error: e.message }); @@ -731,7 +731,7 @@ async function createApp() { // Lightweight probe endpoint app.get('/probe/:id', boundAsyncHandler(async (req, res) => { const id = req.params.id; - const { exists } = require('../fs-helpers'); + const { exists } = require('utilities/fs-helpers'); let service = null; if (id !== 'internet' && await exists(config.SERVICES_FILE)) { @@ -871,7 +871,7 @@ async function createApp() { app.get('/api/v1/docs/spec', boundAsyncHandler(async (req, res) => { const path = require('path'); - const { exists } = require('../fs-helpers'); + const { exists } = require('utilities/fs-helpers'); const fsp = require('fs').promises; const specPath = path.join(__dirname, '../openapi.yaml'); @@ -884,7 +884,7 @@ async function createApp() { }, 'api-docs-spec')); // Error handlers (MUST be last) - const { notFoundHandler, errorMiddleware } = require('../error-handler'); + const { notFoundHandler, errorMiddleware } = require('utilities/error-handler'); app.use('/api', notFoundHandler); app.use(errorMiddleware); diff --git a/dashcaddy-api/src/config/index.js b/dashcaddy-api/src/config/index.js index ae1a4e8..09119b4 100644 --- a/dashcaddy-api/src/config/index.js +++ b/dashcaddy-api/src/config/index.js @@ -4,7 +4,7 @@ */ const paths = require('./paths'); const site = require('./site'); -const { APP, LIMITS, TIMEOUTS, RETRIES, CADDY } = require('../../constants'); +const { APP, LIMITS, TIMEOUTS, RETRIES, CADDY } = require('../utilities/constants'); // Load logging level const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 }; diff --git a/dashcaddy-api/src/config/site.js b/dashcaddy-api/src/config/site.js index c3354b9..ad23979 100644 --- a/dashcaddy-api/src/config/site.js +++ b/dashcaddy-api/src/config/site.js @@ -7,8 +7,8 @@ * updated config back, and the rest of the app only ever sees the current * schema. */ -const { validateConfig } = require('../../config-schema'); -const { CADDY } = require('../../constants'); +const { validateConfig } = require('../utilities/config-schema'); +const { CADDY } = require('../utilities/constants'); const { loadAndMigrate, CURRENT_VERSION } = require('./migrations'); const siteConfig = { diff --git a/dashcaddy-api/src/context/caddy.js b/dashcaddy-api/src/context/caddy.js index 00b7895..8fa07ea 100644 --- a/dashcaddy-api/src/context/caddy.js +++ b/dashcaddy-api/src/context/caddy.js @@ -2,7 +2,7 @@ * Caddy context - Caddyfile manipulation and reload */ const fsp = require('fs').promises; -const { RETRIES } = require('../../constants'); +const { RETRIES } = require('../utilities/constants'); /** * Atomically read-modify-write the Caddyfile and reload Caddy. diff --git a/dashcaddy-api/src/context/dns.js b/dashcaddy-api/src/context/dns.js index f8072e3..04c76ed 100644 --- a/dashcaddy-api/src/context/dns.js +++ b/dashcaddy-api/src/context/dns.js @@ -6,8 +6,8 @@ * * This module now delegates to the provider system internally. */ -const { TIMEOUTS, SESSION_TTL, CADDY } = require('../../constants'); -const { createCache, CACHE_CONFIGS } = require('../../cache-config'); +const { TIMEOUTS, SESSION_TTL, CADDY } = require('../utilities/constants'); +const { createCache, CACHE_CONFIGS } = require('../utilities/cache-config'); const { createProviderDnsContext } = require('./provider-dns'); // DNS token management diff --git a/dashcaddy-api/src/context/docker.js b/dashcaddy-api/src/context/docker.js index 5beb572..fc44ea2 100644 --- a/dashcaddy-api/src/context/docker.js +++ b/dashcaddy-api/src/context/docker.js @@ -2,7 +2,7 @@ * Docker context - Docker client and operations */ const Docker = require('dockerode'); -const { DOCKER } = require('../../constants'); +const { DOCKER } = require('../utilities/constants'); const docker = new Docker(); diff --git a/dashcaddy-api/src/context/index.js b/dashcaddy-api/src/context/index.js index 9e07d17..2346b5d 100644 --- a/dashcaddy-api/src/context/index.js +++ b/dashcaddy-api/src/context/index.js @@ -6,7 +6,7 @@ const { createDockerContext } = require('./docker'); const { createCaddyContext } = require('./caddy'); const { createDnsContext } = require('./dns'); const { createSessionContext } = require('./session'); -const NotificationManager = require('../../notification-manager'); +const NotificationManager = require('../managers/notification-manager'); /** * Assemble the full application context diff --git a/dashcaddy-api/src/context/provider-dns.js b/dashcaddy-api/src/context/provider-dns.js index e625e13..bf44a67 100644 --- a/dashcaddy-api/src/context/provider-dns.js +++ b/dashcaddy-api/src/context/provider-dns.js @@ -6,8 +6,8 @@ * Falls back to legacy Technitium context for backward compatibility * when no provider is explicitly configured. */ -const { createCache, CACHE_CONFIGS } = require('../../cache-config'); -const { TIMEOUTS, SESSION_TTL, CADDY } = require('../../constants'); +const { createCache, CACHE_CONFIGS } = require('../utilities/cache-config'); +const { TIMEOUTS, SESSION_TTL, CADDY } = require('../utilities/constants'); const registry = require('../../dns-providers/registry'); // Per-server token cache (legacy Technitium) diff --git a/dashcaddy-api/dns-propagation.js b/dashcaddy-api/src/dns/dns-propagation.js similarity index 100% rename from dashcaddy-api/dns-propagation.js rename to dashcaddy-api/src/dns/dns-propagation.js diff --git a/dashcaddy-api/dns-providers/base.js b/dashcaddy-api/src/dns/dns-providers/base.js similarity index 100% rename from dashcaddy-api/dns-providers/base.js rename to dashcaddy-api/src/dns/dns-providers/base.js diff --git a/dashcaddy-api/dns-providers/cloudflare.js b/dashcaddy-api/src/dns/dns-providers/cloudflare.js similarity index 100% rename from dashcaddy-api/dns-providers/cloudflare.js rename to dashcaddy-api/src/dns/dns-providers/cloudflare.js diff --git a/dashcaddy-api/dns-providers/manual.js b/dashcaddy-api/src/dns/dns-providers/manual.js similarity index 100% rename from dashcaddy-api/dns-providers/manual.js rename to dashcaddy-api/src/dns/dns-providers/manual.js diff --git a/dashcaddy-api/dns-providers/registry.js b/dashcaddy-api/src/dns/dns-providers/registry.js similarity index 100% rename from dashcaddy-api/dns-providers/registry.js rename to dashcaddy-api/src/dns/dns-providers/registry.js diff --git a/dashcaddy-api/dns-providers/rfc2136.js b/dashcaddy-api/src/dns/dns-providers/rfc2136.js similarity index 100% rename from dashcaddy-api/dns-providers/rfc2136.js rename to dashcaddy-api/src/dns/dns-providers/rfc2136.js diff --git a/dashcaddy-api/dns-providers/technitium.js b/dashcaddy-api/src/dns/dns-providers/technitium.js similarity index 100% rename from dashcaddy-api/dns-providers/technitium.js rename to dashcaddy-api/src/dns/dns-providers/technitium.js diff --git a/dashcaddy-api/app-templates.js b/dashcaddy-api/src/docker/app-templates.js similarity index 100% rename from dashcaddy-api/app-templates.js rename to dashcaddy-api/src/docker/app-templates.js diff --git a/dashcaddy-api/docker-maintenance.js b/dashcaddy-api/src/docker/docker-maintenance.js similarity index 99% rename from dashcaddy-api/docker-maintenance.js rename to dashcaddy-api/src/docker/docker-maintenance.js index 25bcab1..d5811f1 100644 --- a/dashcaddy-api/docker-maintenance.js +++ b/dashcaddy-api/src/docker/docker-maintenance.js @@ -9,7 +9,7 @@ const Docker = require('dockerode'); const EventEmitter = require('events'); -const { DOCKER } = require('./constants'); +const { DOCKER } = require('../utilities/constants'); const docker = new Docker(); diff --git a/dashcaddy-api/self-updater.js b/dashcaddy-api/src/docker/self-updater.js similarity index 100% rename from dashcaddy-api/self-updater.js rename to dashcaddy-api/src/docker/self-updater.js diff --git a/dashcaddy-api/auth-manager.js b/dashcaddy-api/src/managers/auth-manager.js similarity index 99% rename from dashcaddy-api/auth-manager.js rename to dashcaddy-api/src/managers/auth-manager.js index bb0a9bc..e3e14cc 100644 --- a/dashcaddy-api/auth-manager.js +++ b/dashcaddy-api/src/managers/auth-manager.js @@ -7,7 +7,7 @@ const jwt = require('jsonwebtoken'); const crypto = require('crypto'); const credentialManager = require('./credential-manager'); -const cryptoUtils = require('./crypto-utils'); +const cryptoUtils = require('../security/crypto-utils'); // JWT signing secret - derived from encryption key for consistency const JWT_SECRET = cryptoUtils.loadOrCreateKey(); diff --git a/dashcaddy-api/auto-restart-manager.js b/dashcaddy-api/src/managers/auto-restart-manager.js similarity index 99% rename from dashcaddy-api/auto-restart-manager.js rename to dashcaddy-api/src/managers/auto-restart-manager.js index 3ca79cb..3c7d1d3 100644 --- a/dashcaddy-api/auto-restart-manager.js +++ b/dashcaddy-api/src/managers/auto-restart-manager.js @@ -10,7 +10,7 @@ const EventEmitter = require('events'); const path = require('path'); -const { readJsonFile, writeJsonFile } = require('./fs-helpers'); +const { readJsonFile, writeJsonFile } = require('../utilities/fs-helpers'); /** * Default policy values applied when a new policy is created. diff --git a/dashcaddy-api/config-drift-detector.js b/dashcaddy-api/src/managers/config-drift-detector.js similarity index 100% rename from dashcaddy-api/config-drift-detector.js rename to dashcaddy-api/src/managers/config-drift-detector.js diff --git a/dashcaddy-api/credential-manager.js b/dashcaddy-api/src/managers/credential-manager.js similarity index 99% rename from dashcaddy-api/credential-manager.js rename to dashcaddy-api/src/managers/credential-manager.js index 56782e2..af84dea 100644 --- a/dashcaddy-api/credential-manager.js +++ b/dashcaddy-api/src/managers/credential-manager.js @@ -4,8 +4,8 @@ * Uses OS keychain when available, falls back to encrypted file storage */ -const keychainManager = require('./keychain-manager'); -const cryptoUtils = require('./crypto-utils'); +const keychainManager = require('../security/keychain-manager'); +const cryptoUtils = require('../security/crypto-utils'); const lockfile = require('proper-lockfile'); const fs = require('fs'); const path = require('path'); diff --git a/dashcaddy-api/dependency-manager.js b/dashcaddy-api/src/managers/dependency-manager.js similarity index 100% rename from dashcaddy-api/dependency-manager.js rename to dashcaddy-api/src/managers/dependency-manager.js diff --git a/dashcaddy-api/license-manager.js b/dashcaddy-api/src/managers/license-manager.js similarity index 99% rename from dashcaddy-api/license-manager.js rename to dashcaddy-api/src/managers/license-manager.js index 1efaa2a..3a6913e 100644 --- a/dashcaddy-api/license-manager.js +++ b/dashcaddy-api/src/managers/license-manager.js @@ -15,7 +15,7 @@ const os = require('os'); const fs = require('fs'); const path = require('path'); const { verifyCode, parseCode, VALID_DURATIONS } = require('./license-keygen'); -const { errorResponse } = require('./src/utils/responses'); +const { errorResponse } = require('../utils/responses'); const LICENSE_CRED_KEY = 'license.activation'; const LICENSE_SERVER_URL = process.env.LICENSE_SERVER_URL || null; // Set when license server exists diff --git a/dashcaddy-api/notification-manager.js b/dashcaddy-api/src/managers/notification-manager.js similarity index 100% rename from dashcaddy-api/notification-manager.js rename to dashcaddy-api/src/managers/notification-manager.js diff --git a/dashcaddy-api/port-lock-manager.js b/dashcaddy-api/src/managers/port-lock-manager.js similarity index 100% rename from dashcaddy-api/port-lock-manager.js rename to dashcaddy-api/src/managers/port-lock-manager.js diff --git a/dashcaddy-api/resource-monitor.js b/dashcaddy-api/src/managers/resource-monitor.js similarity index 100% rename from dashcaddy-api/resource-monitor.js rename to dashcaddy-api/src/managers/resource-monitor.js diff --git a/dashcaddy-api/state-manager.js b/dashcaddy-api/src/managers/state-manager.js similarity index 100% rename from dashcaddy-api/state-manager.js rename to dashcaddy-api/src/managers/state-manager.js diff --git a/dashcaddy-api/update-manager.js b/dashcaddy-api/src/managers/update-manager.js similarity index 100% rename from dashcaddy-api/update-manager.js rename to dashcaddy-api/src/managers/update-manager.js diff --git a/dashcaddy-api/health-checker.js b/dashcaddy-api/src/monitoring/health-checker.js similarity index 100% rename from dashcaddy-api/health-checker.js rename to dashcaddy-api/src/monitoring/health-checker.js diff --git a/dashcaddy-api/metrics.js b/dashcaddy-api/src/monitoring/metrics.js similarity index 100% rename from dashcaddy-api/metrics.js rename to dashcaddy-api/src/monitoring/metrics.js diff --git a/dashcaddy-api/ssl-monitor.js b/dashcaddy-api/src/monitoring/ssl-monitor.js similarity index 98% rename from dashcaddy-api/ssl-monitor.js rename to dashcaddy-api/src/monitoring/ssl-monitor.js index 78eef6d..6935482 100644 --- a/dashcaddy-api/ssl-monitor.js +++ b/dashcaddy-api/src/monitoring/ssl-monitor.js @@ -9,8 +9,8 @@ const tls = require('tls'); const EventEmitter = require('events'); const path = require('path'); -const { readJsonFile, writeJsonFile } = require('./fs-helpers'); -const { resolveServiceUrl } = require('./url-resolver'); +const { readJsonFile, writeJsonFile } = require('../utilities/fs-helpers'); +const { resolveServiceUrl } = require('../utilities/url-resolver'); /** Default check interval: 1 hour */ const DEFAULT_INTERVAL_MS = 3600000; diff --git a/dashcaddy-api/bundled-workflows.js b/dashcaddy-api/src/recipes/bundled-workflows.js similarity index 100% rename from dashcaddy-api/bundled-workflows.js rename to dashcaddy-api/src/recipes/bundled-workflows.js diff --git a/dashcaddy-api/recipe-templates.js b/dashcaddy-api/src/recipes/recipe-templates.js similarity index 100% rename from dashcaddy-api/recipe-templates.js rename to dashcaddy-api/src/recipes/recipe-templates.js diff --git a/dashcaddy-api/audit-logger.js b/dashcaddy-api/src/security/audit-logger.js similarity index 99% rename from dashcaddy-api/audit-logger.js rename to dashcaddy-api/src/security/audit-logger.js index da05fc4..dcb0047 100644 --- a/dashcaddy-api/audit-logger.js +++ b/dashcaddy-api/src/security/audit-logger.js @@ -1,5 +1,5 @@ const path = require('path'); -const StateManager = require('./state-manager'); +const StateManager = require('../managers/state-manager'); const crypto = require('crypto'); const AUDIT_LOG_FILE = process.env.AUDIT_LOG_FILE || path.join(__dirname, 'audit-log.json'); diff --git a/dashcaddy-api/crypto-utils.js b/dashcaddy-api/src/security/crypto-utils.js similarity index 100% rename from dashcaddy-api/crypto-utils.js rename to dashcaddy-api/src/security/crypto-utils.js diff --git a/dashcaddy-api/csrf-protection.js b/dashcaddy-api/src/security/csrf-protection.js similarity index 99% rename from dashcaddy-api/csrf-protection.js rename to dashcaddy-api/src/security/csrf-protection.js index 7f1ecd4..cefbf79 100644 --- a/dashcaddy-api/csrf-protection.js +++ b/dashcaddy-api/src/security/csrf-protection.js @@ -8,7 +8,7 @@ const crypto = require('crypto'); const cryptoUtils = require('./crypto-utils'); -const { errorResponse } = require('./src/utils/responses'); +const { errorResponse } = require('../utils/responses'); const CSRF_TOKEN_LENGTH = 32; const CSRF_COOKIE_NAME = 'dashcaddy_csrf'; diff --git a/dashcaddy-api/docker-security.js b/dashcaddy-api/src/security/docker-security.js similarity index 100% rename from dashcaddy-api/docker-security.js rename to dashcaddy-api/src/security/docker-security.js diff --git a/dashcaddy-api/input-validator.js b/dashcaddy-api/src/security/input-validator.js similarity index 100% rename from dashcaddy-api/input-validator.js rename to dashcaddy-api/src/security/input-validator.js diff --git a/dashcaddy-api/keychain-manager.js b/dashcaddy-api/src/security/keychain-manager.js similarity index 100% rename from dashcaddy-api/keychain-manager.js rename to dashcaddy-api/src/security/keychain-manager.js diff --git a/dashcaddy-api/log-digest.js b/dashcaddy-api/src/security/log-digest.js similarity index 99% rename from dashcaddy-api/log-digest.js rename to dashcaddy-api/src/security/log-digest.js index 7242ba7..b3cbe3b 100644 --- a/dashcaddy-api/log-digest.js +++ b/dashcaddy-api/src/security/log-digest.js @@ -10,7 +10,7 @@ const EventEmitter = require('events'); const fs = require('fs'); const fsp = require('fs').promises; const path = require('path'); -const { DOCKER } = require('./constants'); +const { DOCKER } = require('../utilities/constants'); const docker = new Docker(); @@ -314,7 +314,7 @@ class LogDigest extends EventEmitter { // Get Docker disk usage let diskUsage = null; try { - const dockerMaintenance = require('./docker-maintenance'); + const dockerMaintenance = require('../docker/docker-maintenance'); diskUsage = await dockerMaintenance.getDiskUsage(); } catch (e) { // Module may not be loaded yet diff --git a/dashcaddy-api/backup-manager.js b/dashcaddy-api/src/utilities/backup-manager.js similarity index 98% rename from dashcaddy-api/backup-manager.js rename to dashcaddy-api/src/utilities/backup-manager.js index 57c1bb8..92b1d64 100644 --- a/dashcaddy-api/backup-manager.js +++ b/dashcaddy-api/src/utilities/backup-manager.js @@ -296,7 +296,7 @@ class BackupManager extends EventEmitter { */ backupCredentials() { try { - const credentialManager = require('./credential-manager'); + const credentialManager = require('../managers/credential-manager'); return credentialManager.exportBackup(); } catch (error) { console.error('[BackupManager] Error backing up credentials:', error.message); @@ -309,7 +309,7 @@ class BackupManager extends EventEmitter { */ backupStats() { try { - const resourceMonitor = require('./resource-monitor'); + const resourceMonitor = require('../managers/resource-monitor'); return resourceMonitor.exportStats(); } catch (error) { console.error('[BackupManager] Error backing up stats:', error.message); @@ -626,7 +626,7 @@ class BackupManager extends EventEmitter { * Throws if required fields are missing. */ async _getCloudCredentials(provider) { - const credentialManager = require('./credential-manager'); + const credentialManager = require('../managers/credential-manager'); const creds = {}; if (provider === 'dropbox') { creds.token = await credentialManager.retrieve('backup.dropbox.token'); @@ -1004,7 +1004,7 @@ class BackupManager extends EventEmitter { * Restore credentials */ restoreCredentials(credentials) { - const credentialManager = require('./credential-manager'); + const credentialManager = require('../managers/credential-manager'); credentialManager.importBackup(credentials); console.log('[BackupManager] Credentials restored'); } @@ -1013,7 +1013,7 @@ class BackupManager extends EventEmitter { * Restore stats */ restoreStats(stats) { - const resourceMonitor = require('./resource-monitor'); + const resourceMonitor = require('../managers/resource-monitor'); resourceMonitor.importStats(stats); console.log('[BackupManager] Stats restored'); } diff --git a/dashcaddy-api/cache-config.js b/dashcaddy-api/src/utilities/cache-config.js similarity index 100% rename from dashcaddy-api/cache-config.js rename to dashcaddy-api/src/utilities/cache-config.js diff --git a/dashcaddy-api/config-schema.js b/dashcaddy-api/src/utilities/config-schema.js similarity index 100% rename from dashcaddy-api/config-schema.js rename to dashcaddy-api/src/utilities/config-schema.js diff --git a/dashcaddy-api/constants.js b/dashcaddy-api/src/utilities/constants.js similarity index 100% rename from dashcaddy-api/constants.js rename to dashcaddy-api/src/utilities/constants.js diff --git a/dashcaddy-api/error-handler.js b/dashcaddy-api/src/utilities/error-handler.js similarity index 96% rename from dashcaddy-api/error-handler.js rename to dashcaddy-api/src/utilities/error-handler.js index 87618ef..216968d 100644 --- a/dashcaddy-api/error-handler.js +++ b/dashcaddy-api/src/utilities/error-handler.js @@ -10,8 +10,8 @@ const path = require('path'); const { AppError } = require('./errors'); const { LIMITS } = require('./constants'); -const { logError: unifiedLogError, safeErrorMessage } = require('./src/utils/logging'); -const { errorResponse } = require('./src/utils/responses'); +const { logError: unifiedLogError, safeErrorMessage } = require('../utils/logging'); +const { errorResponse } = require('../utils/responses'); const ERROR_LOG_FILE = path.join(__dirname, 'error.log'); const MAX_ERROR_LOG_SIZE = LIMITS.ERROR_LOG_SIZE; diff --git a/dashcaddy-api/errors.js b/dashcaddy-api/src/utilities/errors.js similarity index 100% rename from dashcaddy-api/errors.js rename to dashcaddy-api/src/utilities/errors.js diff --git a/dashcaddy-api/fs-helpers.js b/dashcaddy-api/src/utilities/fs-helpers.js similarity index 100% rename from dashcaddy-api/fs-helpers.js rename to dashcaddy-api/src/utilities/fs-helpers.js diff --git a/dashcaddy-api/middleware.js b/dashcaddy-api/src/utilities/middleware.js similarity index 99% rename from dashcaddy-api/middleware.js rename to dashcaddy-api/src/utilities/middleware.js index 5f336e0..c4e4c9f 100644 --- a/dashcaddy-api/middleware.js +++ b/dashcaddy-api/src/utilities/middleware.js @@ -13,9 +13,9 @@ const helmet = require('helmet'); const compression = require('compression'); const crypto = require('crypto'); const rateLimit = require('express-rate-limit'); -const { createCSRFMiddleware, csrfValidationMiddleware, CSRF_HEADER_NAME } = require('./csrf-protection'); +const { createCSRFMiddleware, csrfValidationMiddleware, CSRF_HEADER_NAME } = require('../security/csrf-protection'); const { RATE_LIMITS, LIMITS, APP } = require('./constants'); -const { errorResponse, unauthorized, forbidden, validationError } = require('./src/utils/responses'); +const { errorResponse, unauthorized, forbidden, validationError } = require('../utils/responses'); const { CACHE_CONFIGS, createCache } = require('./cache-config'); /** @@ -285,7 +285,7 @@ module.exports = function configureMiddleware(app, { if (process.env.MONITORING_PUBLIC === 'true') return true; // Default: check config.json if loaded try { - const cfg = require('./src/config/site').siteConfig; + const cfg = require('../config/site').siteConfig; if (cfg && cfg.monitoring && typeof cfg.monitoring.public === 'boolean') { return cfg.monitoring.public; } diff --git a/dashcaddy-api/pagination.js b/dashcaddy-api/src/utilities/pagination.js similarity index 100% rename from dashcaddy-api/pagination.js rename to dashcaddy-api/src/utilities/pagination.js diff --git a/dashcaddy-api/startup-validator.js b/dashcaddy-api/src/utilities/startup-validator.js similarity index 100% rename from dashcaddy-api/startup-validator.js rename to dashcaddy-api/src/utilities/startup-validator.js diff --git a/dashcaddy-api/url-resolver.js b/dashcaddy-api/src/utilities/url-resolver.js similarity index 100% rename from dashcaddy-api/url-resolver.js rename to dashcaddy-api/src/utilities/url-resolver.js diff --git a/dashcaddy-api/src/utils/async-handler.js b/dashcaddy-api/src/utils/async-handler.js index b4960c7..e2703ee 100644 --- a/dashcaddy-api/src/utils/async-handler.js +++ b/dashcaddy-api/src/utils/async-handler.js @@ -1,7 +1,7 @@ /** * Async handler wrapper - Eliminates try/catch boilerplate */ -const { AppError } = require('../../errors'); +const { AppError } = require('../utilities/errors'); /** * Wrap async route handlers - catches errors and logs them diff --git a/dashcaddy-api/src/utils/http.js b/dashcaddy-api/src/utils/http.js index 76473e6..506e90d 100644 --- a/dashcaddy-api/src/utils/http.js +++ b/dashcaddy-api/src/utils/http.js @@ -3,7 +3,7 @@ */ const http = require('http'); const https = require('https'); -const { TIMEOUTS } = require('../../constants'); +const { TIMEOUTS } = require('../utilities/constants'); // HTTPS agent that trusts internal CA certs (self-signed .sami TLD etc.) // Lazy-initialized singleton to avoid creating a new agent per request. diff --git a/dashcaddy-api/src/utils/responses.js b/dashcaddy-api/src/utils/responses.js index eb59da0..d980a46 100644 --- a/dashcaddy-api/src/utils/responses.js +++ b/dashcaddy-api/src/utils/responses.js @@ -7,7 +7,7 @@ * All routes should import from this module — do not call res.json/res.status * directly with the response shape, use these helpers instead. */ -const { HTTP_STATUS } = require('../../constants'); +const { HTTP_STATUS } = require('../utilities/constants'); // ── Success helpers ──────────────────────────────────────────── diff --git a/dashcaddy-installer/src/main/config-manager.js b/dashcaddy-installer/src/main/config-manager.js index 4ec80a0..6d68d66 100644 --- a/dashcaddy-installer/src/main/config-manager.js +++ b/dashcaddy-installer/src/main/config-manager.js @@ -6,7 +6,7 @@ const { REQUIRED_DIRS } = require('../shared/constants'); let cryptoUtils; try { // Try to load from dashcaddy-api if available - cryptoUtils = require('../../dashcaddy-api/crypto-utils'); + cryptoUtils = require('../../../src/security/crypto-utils'); } catch { // Fallback: create minimal crypto implementation const crypto = require('crypto');