Fix input validation and error handling across API endpoints

- Deploy endpoint: validate appId, config, and subdomain before use (prevents 500 crash on empty body)
- Container ops: return 404 instead of 500 for non-existent containers
- Update-subdomain: require oldSubdomain/newSubdomain fields (prevents false 200 with undefined values)
- Global error handler: catch-all that never leaks stack traces or internal paths
- API 404 catch-all: return JSON instead of HTML for unmatched /api/* routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 20:21:21 -08:00
parent 77030931b7
commit 3a6d2ce93d
4 changed files with 48 additions and 6 deletions

View File

@@ -184,6 +184,15 @@ module.exports = function(ctx, helpers) {
// Deploy new app
router.post('/apps/deploy', ctx.asyncHandler(async (req, res) => {
const { appId, config } = req.body;
if (!appId || typeof appId !== 'string') {
return ctx.errorResponse(res, 400, 'appId is required');
}
if (!config || typeof config !== 'object') {
return ctx.errorResponse(res, 400, 'config object is required');
}
if (!config.subdomain || typeof config.subdomain !== 'string') {
return ctx.errorResponse(res, 400, 'config.subdomain is required');
}
try {
ctx.log.info('deploy', 'Deploying app', { appId, subdomain: config.subdomain });
const template = ctx.APP_TEMPLATES[appId];

View File

@@ -1,5 +1,6 @@
const express = require('express');
const { exists } = require('../../fs-helpers');
const { REGEX } = require('../../constants');
module.exports = function(ctx, helpers) {
const router = express.Router();
@@ -54,6 +55,15 @@ module.exports = function(ctx, helpers) {
// Update subdomain for deployed app
router.post('/apps/update-subdomain', ctx.asyncHandler(async (req, res) => {
const { serviceId, oldSubdomain, newSubdomain, containerId, ip } = req.body;
if (!oldSubdomain || typeof oldSubdomain !== 'string') {
return ctx.errorResponse(res, 400, 'oldSubdomain is required');
}
if (!newSubdomain || typeof newSubdomain !== 'string') {
return ctx.errorResponse(res, 400, 'newSubdomain is required');
}
if (!REGEX.SUBDOMAIN.test(newSubdomain)) {
return ctx.errorResponse(res, 400, '[DC-301] Invalid subdomain format for newSubdomain');
}
ctx.log.info('deploy', 'Updating subdomain', { oldSubdomain, newSubdomain });
const results = { oldDns: null, newDns: null, caddy: null, service: null };