Files
dashcaddy/dashcaddy-api/routes/config/settings.js
Krystie b172a21b63 Migrate 25 route files to throw-based error handling
Converted routes:
- All auth routes (totp.js, keys.js, sso-gate.js)
- Recipe deployment routes (deploy.js, manage.js, index.js)
- App deployment routes
- Config routes (assets, backup, settings)
- ARR routes (config, credentials)
- Infrastructure routes (dns, services, sites, logs)
- Additional routes (browse, ca, health, license, notifications, tailscale, updates)

Changes:
- Replaced ctx.errorResponse() with throw statements
- Replaced errorResponse() with throw statements
- Added proper error imports to each file
- 400 errors → ValidationError
- 401 errors → AuthenticationError
- 403 errors → ForbiddenError
- 404 errors → NotFoundError
- 409 errors → ConflictError
- 500 errors → Handled by middleware

Result: 25 files migrated, ~150 error responses standardized
2026-03-29 18:53:03 -07:00

72 lines
2.4 KiB
JavaScript

const fsp = require('fs').promises;
const { validateConfig } = require('../../config-schema');
const { exists } = require('../../fs-helpers');
const { ValidationError } = require('../errors');
module.exports = function(ctx) {
const express = require('express');
const router = express.Router();
// ===== DASHCADDY CONFIG ENDPOINTS =====
// Server-side config storage for setup wizard (shared across all browsers/machines)
router.get('/config', ctx.asyncHandler(async (req, res) => {
if (!await exists(ctx.CONFIG_FILE)) {
return res.json({ setupComplete: false });
}
const data = await fsp.readFile(ctx.CONFIG_FILE, 'utf8');
const config = JSON.parse(data);
res.json(config);
}, 'config-get'));
router.post('/config', ctx.asyncHandler(async (req, res) => {
const incoming = req.body;
if (!incoming || typeof incoming !== 'object') {
throw new ValidationError('Invalid config object');
}
// Merge with existing config so partial saves don't wipe fields
let existing = {};
if (await exists(ctx.CONFIG_FILE)) {
try {
existing = JSON.parse(await fsp.readFile(ctx.CONFIG_FILE, 'utf8'));
} catch (_) { /* start fresh if file is corrupt */ }
}
const config = { ...existing, ...incoming };
// Merge nested dns object so partial dns updates don't wipe dns fields
if (existing.dns && incoming.dns) {
config.dns = { ...existing.dns, ...incoming.dns };
}
// Merge nested dnsServers object
if (existing.dnsServers && incoming.dnsServers) {
config.dnsServers = { ...existing.dnsServers, ...incoming.dnsServers };
}
// Validate merged config against schema
const { valid, errors, warnings } = validateConfig(config);
if (!valid) {
return ctx.errorResponse(res, 400, 'Config validation failed', { errors });
}
// Add timestamp
config.updatedAt = new Date().toISOString();
await fsp.writeFile(ctx.CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
ctx.loadSiteConfig(); // Refresh in-memory config
ctx.log.info('config', 'Config saved', { path: ctx.CONFIG_FILE });
res.json({ success: true, message: 'Configuration saved', config, warnings });
}, 'config-save'));
router.delete('/config', ctx.asyncHandler(async (req, res) => {
if (await exists(ctx.CONFIG_FILE)) {
await fsp.unlink(ctx.CONFIG_FILE);
}
res.json({ success: true, message: 'Configuration reset' });
}, 'config-delete'));
return router;
};