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
This commit is contained in:
Krystie
2026-03-29 18:53:03 -07:00
parent 64a0018d00
commit b172a21b63
25 changed files with 168 additions and 154 deletions

View File

@@ -244,7 +244,7 @@ module.exports = function({
router.post('/seedhost-creds', asyncHandler(async (req, res) => {
const { username, password, serviceId } = req.body;
if (!username) {
return errorResponse(res, 'Username required', 400);
throw new ValidationError('Username required');
}
await credentialManager.store('seedhost.username', username);
if (password) {
@@ -361,7 +361,7 @@ module.exports = function({
const { id, name, logo } = req.body;
if (!id || !name) {
return errorResponse(res, 'id and name are required', 400);
throw new ValidationError('id and name are required');
}
// Validate service configuration
@@ -388,7 +388,7 @@ module.exports = function({
if (error.message.includes('already exists')) {
errorResponse(res, safeErrorMessage(error), 409);
} else {
errorResponse(res, safeErrorMessage(error), 500);
// Error handled by middleware
}
}
}, 'services-update'));
@@ -398,12 +398,12 @@ module.exports = function({
const services = req.body;
if (!Array.isArray(services)) {
return errorResponse(res, 'Request body must be an array of services', 400);
throw new ValidationError('Request body must be an array of services');
}
for (const service of services) {
if (!service.id || !service.name) {
return errorResponse(res, 'Each service must have id and name fields', 400);
throw new ValidationError('Each service must have id and name fields');
}
try {
validateServiceConfig(service);
@@ -426,7 +426,7 @@ module.exports = function({
const { id } = req.params;
if (!await exists(SERVICES_FILE)) {
return errorResponse(res, 'No services found', 404);
throw new NotFoundError('No services found');
}
let found = false;
@@ -450,19 +450,19 @@ module.exports = function({
const { oldSubdomain, newSubdomain, port, ip, tailscaleOnly, name, logo } = req.body;
if (!oldSubdomain || !newSubdomain) {
return errorResponse(res, 'oldSubdomain and newSubdomain are required', 400);
throw new ValidationError('oldSubdomain and newSubdomain are required');
}
if (!REGEX.SUBDOMAIN.test(oldSubdomain) || !REGEX.SUBDOMAIN.test(newSubdomain)) {
return errorResponse(res, '[DC-301] Invalid subdomain format', 400);
throw new ValidationError('[DC-301] Invalid subdomain format');
}
if (port && !isValidPort(port)) {
return errorResponse(res, 'Invalid port number (must be 1-65535)', 400);
throw new ValidationError('Invalid port number (must be 1-65535)');
}
if (ip && ip !== 'localhost' && !validatorLib.isIP(ip)) {
return errorResponse(res, '[DC-210] Invalid IP address', 400);
throw new ValidationError('[DC-210] Invalid IP address');
}
const results = { dns: null, caddy: null, services: null };