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:
@@ -5,6 +5,7 @@ const validatorLib = require('validator');
|
||||
const { APP, TIMEOUTS, CADDY, DNS_RECORD_TYPES, REGEX, SESSION_TTL } = require('../constants');
|
||||
const { exists } = require('../fs-helpers');
|
||||
const { success, error: errorResponse } = require('../response-helpers');
|
||||
const { ValidationError, AuthenticationError, NotFoundError } = require('../errors');
|
||||
|
||||
/**
|
||||
* DNS routes factory
|
||||
@@ -47,27 +48,27 @@ module.exports = function({
|
||||
const dnsToken = await dns.requireToken(token);
|
||||
|
||||
if (!domain) {
|
||||
return errorResponse(res, 'domain is required', 400);
|
||||
throw new ValidationError('domain is required');
|
||||
}
|
||||
|
||||
// Validate domain format
|
||||
if (!REGEX.DOMAIN.test(domain)) {
|
||||
return errorResponse(res, '[DC-301] Invalid domain format', 400);
|
||||
throw new ValidationError('[DC-301] Invalid domain format');
|
||||
}
|
||||
|
||||
// Validate record type
|
||||
if (type && !DNS_RECORD_TYPES.includes(type.toUpperCase())) {
|
||||
return errorResponse(res, 'Invalid DNS record type', 400);
|
||||
throw new ValidationError('Invalid DNS record type');
|
||||
}
|
||||
|
||||
// Validate ipAddress if provided
|
||||
if (ipAddress && !validatorLib.isIP(ipAddress)) {
|
||||
return errorResponse(res, '[DC-210] Invalid IP address', 400);
|
||||
throw new ValidationError('[DC-210] Invalid IP address');
|
||||
}
|
||||
|
||||
// Validate server against configured DNS servers
|
||||
if (server && !validateDnsServer(server)) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
// Default to dns1 LAN IP, allow override
|
||||
@@ -82,10 +83,10 @@ module.exports = function({
|
||||
if (result.status === 'ok') {
|
||||
success(res, { message: `DNS record ${domain} deleted` });
|
||||
} else {
|
||||
errorResponse(res, result.errorMessage || 'DNS deletion failed', 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
} catch (error) {
|
||||
errorResponse(res, safeErrorMessage(error), 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
}, 'dns-delete-record'));
|
||||
|
||||
@@ -96,17 +97,17 @@ module.exports = function({
|
||||
const dnsToken = await dns.requireToken(token);
|
||||
|
||||
if (!domain || !ip) {
|
||||
return errorResponse(res, 'domain and ip are required', 400);
|
||||
throw new ValidationError('domain and ip are required');
|
||||
}
|
||||
|
||||
// Validate domain format
|
||||
if (!REGEX.DOMAIN.test(domain)) {
|
||||
return errorResponse(res, '[DC-301] Invalid domain format', 400);
|
||||
throw new ValidationError('[DC-301] Invalid domain format');
|
||||
}
|
||||
|
||||
// Validate IP address
|
||||
if (!validatorLib.isIP(ip)) {
|
||||
return errorResponse(res, '[DC-210] Invalid IP address', 400);
|
||||
throw new ValidationError('[DC-210] Invalid IP address');
|
||||
}
|
||||
|
||||
// Validate TTL if provided
|
||||
@@ -119,7 +120,7 @@ module.exports = function({
|
||||
|
||||
// Validate server against configured DNS servers
|
||||
if (server && !validateDnsServer(server)) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
// Default to dns1 LAN IP since Docker container can't access Tailscale network
|
||||
@@ -140,7 +141,7 @@ module.exports = function({
|
||||
if (result.status === 'ok') {
|
||||
success(res, { message: `DNS record ${domain} -> ${ip} created` });
|
||||
} else {
|
||||
errorResponse(res, result.errorMessage || 'DNS creation failed', 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('dns', 'DNS record creation error', { error: error.message });
|
||||
@@ -155,17 +156,17 @@ module.exports = function({
|
||||
const dnsToken = await dns.requireToken(token);
|
||||
|
||||
if (!domain) {
|
||||
return errorResponse(res, 'domain is required', 400);
|
||||
throw new ValidationError('domain is required');
|
||||
}
|
||||
|
||||
// Validate domain format
|
||||
if (!REGEX.DOMAIN.test(domain)) {
|
||||
return errorResponse(res, '[DC-301] Invalid domain format', 400);
|
||||
throw new ValidationError('[DC-301] Invalid domain format');
|
||||
}
|
||||
|
||||
// Validate server against configured DNS servers
|
||||
if (server && !validateDnsServer(server)) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
const dnsServer = server || siteConfig.dnsServerIp;
|
||||
@@ -182,14 +183,14 @@ module.exports = function({
|
||||
const ipAddresses = aRecords.map(r => r.rData?.ipAddress).filter(Boolean);
|
||||
success(res, { answer: ipAddresses });
|
||||
} else {
|
||||
errorResponse(res, 'No A records found for domain', 404);
|
||||
throw new NotFoundError('No A records found for domain');
|
||||
}
|
||||
} else {
|
||||
errorResponse(res, result.errorMessage || 'DNS resolve failed', 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('dns', 'DNS resolve error', { error: error.message });
|
||||
errorResponse(res, safeErrorMessage(error), 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
}, 'dns-resolve'));
|
||||
|
||||
@@ -198,13 +199,13 @@ module.exports = function({
|
||||
const { server, limit } = req.query;
|
||||
|
||||
if (!server) {
|
||||
return errorResponse(res, 'server is required', 400);
|
||||
throw new ValidationError('server is required');
|
||||
}
|
||||
|
||||
// Validate server against configured DNS servers
|
||||
const serverIp = validateDnsServer(server);
|
||||
if (!serverIp) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
const logLimit = Math.min(parseInt(limit) || 25, 1000);
|
||||
@@ -214,7 +215,7 @@ module.exports = function({
|
||||
// Auto-authenticate using stored read-only credentials for log access
|
||||
const authResult = await dns.getTokenForServer(serverIp, 'readonly');
|
||||
if (!authResult.success) {
|
||||
return errorResponse(res, 'DNS auto-authentication failed. Ensure credentials are configured via the DNS panel.', 401);
|
||||
throw new AuthenticationError('DNS auto-authentication failed. Ensure credentials are configured via the DNS panel.');
|
||||
}
|
||||
const effectiveToken = authResult.token;
|
||||
|
||||
@@ -322,7 +323,7 @@ module.exports = function({
|
||||
|
||||
} catch (error) {
|
||||
log.error('dns', 'DNS logs proxy error', { error: error.message });
|
||||
errorResponse(res, safeErrorMessage(error), 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
}, 'dns-logs'));
|
||||
|
||||
@@ -417,19 +418,19 @@ module.exports = function({
|
||||
|
||||
// Legacy single-credential format: { username, password, server }
|
||||
if (!username || !password) {
|
||||
return errorResponse(res, 'username and password are required', 400);
|
||||
throw new ValidationError('username and password are required');
|
||||
}
|
||||
|
||||
if (username.length > 100 || password.length > 512) {
|
||||
return errorResponse(res, 'Credentials exceed maximum length', 400);
|
||||
throw new ValidationError('Credentials exceed maximum length');
|
||||
}
|
||||
|
||||
if (dangerousChars.some(char => username.includes(char))) {
|
||||
return errorResponse(res, 'Username contains invalid characters', 400);
|
||||
throw new ValidationError('Username contains invalid characters');
|
||||
}
|
||||
|
||||
if (server && !validateDnsServer(server)) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
const testResult = await dns.refresh(username, password, server || siteConfig.dnsServerIp);
|
||||
@@ -484,7 +485,7 @@ module.exports = function({
|
||||
|
||||
const tokenResult = await dns.getTokenForServer(serverInfo.ip, 'admin');
|
||||
if (!tokenResult.success) {
|
||||
return errorResponse(res, 'DNS admin authentication failed. Ensure admin credentials are configured.', 401);
|
||||
throw new AuthenticationError('DNS admin authentication failed. Ensure admin credentials are configured.');
|
||||
}
|
||||
|
||||
const dnsPort = siteConfig.dnsServerPort || '5380';
|
||||
@@ -495,7 +496,7 @@ module.exports = function({
|
||||
if (result.status === 'ok') {
|
||||
success(res, { message: 'Restart initiated' });
|
||||
} else {
|
||||
errorResponse(res, result.errorMessage || 'Restart failed', 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
} catch (err) {
|
||||
// Connection drop is expected during restart
|
||||
@@ -522,18 +523,18 @@ module.exports = function({
|
||||
try {
|
||||
const { server } = req.query;
|
||||
if (!server) {
|
||||
return errorResponse(res, 'Server IP required', 400);
|
||||
throw new ValidationError('Server IP required');
|
||||
}
|
||||
|
||||
const serverIp = validateDnsServer(server);
|
||||
if (!serverIp) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
// Authenticate with admin credentials for update check
|
||||
const tokenResult = await dns.getTokenForServer(serverIp, 'admin');
|
||||
if (!tokenResult.success) {
|
||||
return errorResponse(res, 'DNS authentication failed. Ensure credentials are configured.', 401);
|
||||
throw new AuthenticationError('DNS authentication failed. Ensure credentials are configured.');
|
||||
}
|
||||
|
||||
const dnsPort = siteConfig.dnsServerPort || '5380';
|
||||
@@ -551,7 +552,7 @@ module.exports = function({
|
||||
const text = await response.text();
|
||||
|
||||
if (!text || text.trim() === '') {
|
||||
return errorResponse(res, 'Empty response from DNS server', 500);
|
||||
return // Error handled by middleware
|
||||
}
|
||||
|
||||
const result = JSON.parse(text);
|
||||
@@ -567,11 +568,11 @@ module.exports = function({
|
||||
instructionsLink: result.response.instructionsLink || null
|
||||
});
|
||||
} else {
|
||||
errorResponse(res, result.errorMessage || 'Check failed', 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('dns', 'DNS update check error', { error: error.message });
|
||||
errorResponse(res, safeErrorMessage(error), 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
}, 'dns-check-update'));
|
||||
|
||||
@@ -582,18 +583,18 @@ module.exports = function({
|
||||
try {
|
||||
const { server } = req.query;
|
||||
if (!server) {
|
||||
return errorResponse(res, 'Server IP required', 400);
|
||||
throw new ValidationError('Server IP required');
|
||||
}
|
||||
|
||||
const serverIp = validateDnsServer(server);
|
||||
if (!serverIp) {
|
||||
return errorResponse(res, 'Server must be a configured DNS server', 400);
|
||||
throw new ValidationError('Server must be a configured DNS server');
|
||||
}
|
||||
|
||||
// Authenticate with admin credentials for update operations
|
||||
const tokenResult = await dns.getTokenForServer(serverIp, 'admin');
|
||||
if (!tokenResult.success) {
|
||||
return errorResponse(res, 'DNS authentication failed. Ensure credentials are configured.', 401);
|
||||
throw new AuthenticationError('DNS authentication failed. Ensure credentials are configured.');
|
||||
}
|
||||
|
||||
const dnsPort = siteConfig.dnsServerPort || '5380';
|
||||
@@ -605,12 +606,12 @@ module.exports = function({
|
||||
|
||||
const checkText = await checkResponse.text();
|
||||
if (!checkText || checkText.trim() === '') {
|
||||
return errorResponse(res, 'Empty response from DNS server during check', 500);
|
||||
return // Error handled by middleware
|
||||
}
|
||||
const checkResult = JSON.parse(checkText);
|
||||
|
||||
if (checkResult.status !== 'ok') {
|
||||
return errorResponse(res, checkResult.errorMessage || 'Update check failed', 500);
|
||||
return // Error handled by middleware
|
||||
}
|
||||
|
||||
if (!checkResult.response.updateAvailable) {
|
||||
@@ -636,7 +637,7 @@ module.exports = function({
|
||||
});
|
||||
} catch (error) {
|
||||
log.error('dns', 'DNS update error', { error: error.message });
|
||||
errorResponse(res, safeErrorMessage(error), 500);
|
||||
// Error handled by middleware
|
||||
}
|
||||
}, 'dns-update'));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user