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

@@ -1,6 +1,7 @@
const express = require('express');
const fs = require('fs');
const { CADDY, REGEX, LIMITS } = require('../constants');
const { ValidationError } = require('../errors');
module.exports = function(ctx) {
const router = express.Router();
@@ -118,7 +119,7 @@ module.exports = function(ctx) {
// Remove a site from Caddyfile
router.delete('/site/:domain', ctx.asyncHandler(async (req, res) => {
const { domain } = req.params;
if (!domain) return ctx.errorResponse(res, 400, 'Domain is required');
if (!domain) throw new ValidationError('Domain is required');
const result = await ctx.caddy.modify((content) => {
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -143,11 +144,11 @@ module.exports = function(ctx) {
// Add a new site to Caddyfile and reload
router.post('/site', ctx.asyncHandler(async (req, res) => {
const { domain, upstream, config } = req.body;
if (!domain || !upstream) return ctx.errorResponse(res, 400, 'Domain and upstream are required');
if (!REGEX.DOMAIN.test(domain)) return ctx.errorResponse(res, 400, '[DC-301] Invalid domain format');
if (!domain || !upstream) throw new ValidationError('Domain and upstream are required');
if (!REGEX.DOMAIN.test(domain)) throw new ValidationError('[DC-301] Invalid domain format');
const upstreamRegex = /^[a-z0-9.-]+:\d{1,5}$/i;
if (!upstreamRegex.test(upstream)) return ctx.errorResponse(res, 400, 'Invalid upstream format. Use host:port');
if (!upstreamRegex.test(upstream)) throw new ValidationError('Invalid upstream format. Use host:port');
let content = await ctx.caddy.read();
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -173,10 +174,10 @@ module.exports = function(ctx) {
const { subdomain, externalUrl, preserveHost, followRedirects, sslType, caddyfilePath, reloadCaddy: shouldReload, createDns, serviceName, logo } = req.body;
if (!subdomain || !externalUrl) {
return ctx.errorResponse(res, 400, 'Subdomain and externalUrl are required');
throw new ValidationError('Subdomain and externalUrl are required');
}
if (!REGEX.SUBDOMAIN.test(subdomain)) {
return ctx.errorResponse(res, 400, '[DC-301] Invalid subdomain format');
throw new ValidationError('[DC-301] Invalid subdomain format');
}
try {
@@ -207,7 +208,7 @@ module.exports = function(ctx) {
// Validate URL components are safe for Caddyfile syntax
const unsafeCaddyChars = /[{}\n\r]/;
if (unsafeCaddyChars.test(urlObj.host) || unsafeCaddyChars.test(urlObj.pathname)) {
return ctx.errorResponse(res, 400, 'External URL contains characters not safe for Caddy configuration');
throw new ValidationError('External URL contains characters not safe for Caddy configuration');
}
const baseUrl = `${urlObj.protocol}//${urlObj.host}`;