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
+20 -28
View File
@@ -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 { success, error: errorResponse } = require('../response-helpers');
const { ValidationError } = require('../errors');
const { resolveServiceUrl } = require('../src/utilities/url-resolver');
const { success, error: errorResponse, errorResponse: sendError, ok, notFound } = require('../src/utils/responses');
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');
}
@@ -273,11 +273,7 @@ module.exports = function({
try {
// Check if certificate exists
if (!await exists(rootCertPath)) {
return res.json({
status: 'error',
message: 'Root CA certificate not found',
daysUntilExpiration: null
});
return sendError(res, 404, 'Root CA certificate not found', { caStatus: 'error', daysUntilExpiration: null });
}
const dates = execSync(`openssl x509 -in "${rootCertPath}" -noout -dates`).toString();
@@ -286,36 +282,32 @@ module.exports = function({
const daysUntilExpiration = Math.floor((expirationDate - new Date()) / (1000 * 60 * 60 * 24));
// Alert thresholds
let status = 'healthy';
let caStatus = 'healthy';
let message = `CA certificate valid for ${daysUntilExpiration} days`;
if (daysUntilExpiration < 0) {
status = 'critical';
caStatus = 'critical';
message = `CA certificate EXPIRED ${Math.abs(daysUntilExpiration)} days ago!`;
} else if (daysUntilExpiration < 7) {
status = 'critical';
caStatus = 'critical';
message = `CA certificate expires in ${daysUntilExpiration} days!`;
} else if (daysUntilExpiration < 30) {
status = 'critical';
caStatus = 'critical';
message = `CA certificate expires in ${daysUntilExpiration} days!`;
} else if (daysUntilExpiration < 90) {
status = 'warning';
caStatus = 'warning';
message = `CA certificate expires in ${daysUntilExpiration} days`;
}
res.json({
status: status,
message: message,
daysUntilExpiration: daysUntilExpiration,
ok(res, {
caStatus,
message,
daysUntilExpiration,
expiresAt: notAfter
});
} catch (error) {
await logError('GET /api/health/ca', error);
res.json({
status: 'error',
message: error.message,
daysUntilExpiration: null
});
sendError(res, 500, error.message, { caStatus: 'error', daysUntilExpiration: null });
}
}, 'health-ca'));
@@ -349,7 +341,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 });