Merge krystie-improvements into main

Resolves 24 conflicts between Hermes (DC-008/009/010 + response-helper
envelope standardization) and Krystie (DC-005 src/ refactor path fixes,
DC-006 TOTP integration, DC-007 new test suites, cloud backup
destinations).

Conflict resolutions:
- src/utils/logging.js:    took ours (consumers depend on logError/
                            safeErrorMessage/createLogger exports)
- src/config/site.js:      merged (her factored validateAndLogConfig +
                            applyConfigFields helpers)
- src/context/dns.js:      took hers (admin/readonly role iteration for
                            write operations)
- src/utilities/backup-
  manager.js:              took hers (Dropbox/WebDAV/SFTP cloud feature)
- status/dist/*, status/
  sw.js:                   took hers (minified bundles + newer SW cache)

Additional fix (post-merge regression):
- src/monitoring/health-checker.js: fixed DC-005 path miss —
  'require(./platform-paths)' → 'require(../../platform-paths)'

Test status: 921/922 passing. One known failure in logging.test.js
(async file-handle timing) tracked as follow-up.
This commit is contained in:
Hermes
2026-06-25 16:43:10 -07:00
171 changed files with 11759 additions and 1006 deletions
+6 -7
View File
@@ -1,5 +1,6 @@
const express = require('express');
const { ValidationError, ForbiddenError, NotFoundError } = require('../../errors');
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
@@ -39,7 +40,7 @@ module.exports = function({ authManager, asyncHandler, log }) {
}
const keys = await authManager.listAPIKeys();
res.json({ success: true, keys });
ok(res, { keys });
}, 'auth-keys-list'));
// Generate new API key
@@ -66,8 +67,7 @@ module.exports = function({ authManager, asyncHandler, log }) {
scopes || ['read', 'write']
);
res.json({
success: true,
ok(res, {
key: keyData.key,
id: keyData.id,
name: keyData.name,
@@ -93,7 +93,7 @@ module.exports = function({ authManager, asyncHandler, log }) {
const success = await authManager.revokeAPIKey(keyId);
if (success) {
res.json({ success: true, message: 'API key revoked successfully' });
successMessage(res, 'API key revoked successfully');
} else {
throw new NotFoundError(`API key ${keyId}`);
}
@@ -126,8 +126,7 @@ module.exports = function({ authManager, asyncHandler, log }) {
const expiresInMs = parseExpiration(expiresIn || '24h');
const expiresAt = new Date(Date.now() + expiresInMs).toISOString();
res.json({
success: true,
ok(res, {
token,
expiresAt,
usage: 'Include in Authorization header as: Bearer <token>'
@@ -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
+2 -2
View File
@@ -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
+8 -9
View File
@@ -1,5 +1,6 @@
const express = require('express');
const { ValidationError, AuthenticationError } = require('../../errors');
const { ValidationError, AuthenticationError } = require('../../src/utilities/errors');
const { ok, successMessage } = require('../../src/utils/responses');
/**
* Auth TOTP routes factory
@@ -27,8 +28,7 @@ module.exports = function({ authManager, credentialManager, totpConfig, saveTotp
// Get current TOTP config (public route)
router.get('/totp/config', asyncHandler(async (req, res) => {
res.json({
success: true,
ok(res, {
config: {
enabled: ctx.totpConfig.enabled,
sessionDuration: ctx.totpConfig.sessionDuration,
@@ -122,7 +122,7 @@ module.exports = function({ authManager, credentialManager, totpConfig, saveTotp
color: { dark: '#ffffff', light: '#00000000' }
});
res.json({ success: true, qrCode: qrDataUrl, manualKey: secret, issuer: 'DashCaddy', imported: !!req.body?.secret });
ok(res, { qrCode: qrDataUrl, manualKey: secret, issuer: 'DashCaddy', imported: !!req.body?.secret });
}, 'totp-setup'));
// Verify first code to confirm setup, then activate TOTP
@@ -159,7 +159,7 @@ module.exports = function({ authManager, credentialManager, totpConfig, saveTotp
ctx.session.create(req, ctx.totpConfig.sessionDuration);
ctx.session.setCookie(res, ctx.totpConfig.sessionDuration);
res.json({ success: true, message: 'TOTP enabled successfully', sessionDuration: ctx.totpConfig.sessionDuration });
ok(res, { message: 'TOTP enabled successfully', sessionDuration: ctx.totpConfig.sessionDuration });
}, 'totp-verify-setup'));
// Login: verify TOTP code and set session cookie
@@ -193,7 +193,7 @@ module.exports = function({ authManager, credentialManager, totpConfig, saveTotp
const newCsrfToken = renewCSRFToken(res, req.secure || req.protocol === 'https');
log.debug('auth', 'Session created', { sessions: ctx.session.ipSessions.size });
res.json({ success: true, message: 'Authenticated successfully', sessionDuration: ctx.totpConfig.sessionDuration, csrfToken: newCsrfToken });
ok(res, { message: 'Authenticated successfully', sessionDuration: ctx.totpConfig.sessionDuration, csrfToken: newCsrfToken });
}, 'totp-verify'));
// Check session validity (used by Caddy forward_auth)
@@ -245,7 +245,7 @@ module.exports = function({ authManager, credentialManager, totpConfig, saveTotp
ctx.session.clear(req);
ctx.session.clearCookie(res);
res.json({ success: true, message: 'TOTP disabled' });
successMessage(res, 'TOTP disabled');
}, 'totp-disable'));
// Update TOTP settings (session duration)
@@ -264,8 +264,7 @@ module.exports = function({ authManager, credentialManager, totpConfig, saveTotp
}
await ctx.saveTotpConfig();
res.json({
success: true,
ok(res, {
config: { enabled: ctx.totpConfig.enabled, sessionDuration: ctx.totpConfig.sessionDuration, isSetUp: ctx.totpConfig.isSetUp }
});
}, 'totp-config'));