diff --git a/dashcaddy-api/__tests__/public-routes-drift.test.js b/dashcaddy-api/__tests__/public-routes-drift.test.js index 9e56104..0225927 100644 --- a/dashcaddy-api/__tests__/public-routes-drift.test.js +++ b/dashcaddy-api/__tests__/public-routes-drift.test.js @@ -111,7 +111,7 @@ function readMountedRoutes() { 'routes/dns.js', // apiRouter.use('/dns', dnsRoutes({...})) 'routes/notifications.js', // apiRouter.use('/notifications', notificationRoutes({...})) 'routes/containers.js', // apiRouter.use('/containers', containerRoutes({...})) - 'routes/services.js', // apiRouter.use(serviceRoutes({...})) // bare mount + 'routes/billing.js', // DC-055: apiRouter.use('/billing', billingRoutes({...})) 'routes/health.js', // apiRouter.use(healthRoutes({...})) // bare mount 'routes/monitoring.js', // apiRouter.use(monitoringRoutes({...})) // bare mount 'routes/updates.js', // apiRouter.use(updatesRoutes({...})) // bare mount @@ -130,12 +130,14 @@ function readMountedRoutes() { 'routes/themes.js', // apiRouter.use(themesRoutes({...})) // bare mount 'routes/license.js', // apiRouter.use('/license', licenseRoutes({...})) 'routes/share.js', // apiRouter.use(shareRoutes({...})) // bare mount (DC-053) + 'routes/services.js', // apiRouter.use(serviceRoutes({...})) // bare mount — needed for /api/v1/services + /api/v1/services/status PUBLIC_ROUTES ]; // Prefix map: explicit prefix from src/app.js's apiRouter.use() call const prefixMap = { 'routes/dns.js': '/dns', 'routes/notifications.js': '/notifications', 'routes/containers.js': '/containers', + 'routes/billing.js': '/billing', // DC-055: apiRouter.use('/billing', billingRoutes({...})) in src/app.js 'routes/tailscale.js': '/tailscale', 'routes/ca.js': '/ca', 'routes/openclaw.js': '/openclaw', diff --git a/dashcaddy-api/src/utilities/middleware.js b/dashcaddy-api/src/utilities/middleware.js index d4e9d9d..b3f2ace 100644 --- a/dashcaddy-api/src/utilities/middleware.js +++ b/dashcaddy-api/src/utilities/middleware.js @@ -402,9 +402,14 @@ module.exports = function configureMiddleware(app, { { path: '/api/v1/share/:token/preview', exact: true, method: 'GET' }, { path: '/api/v1/share/:token/subscribe', exact: true, method: 'POST' }, { path: '/api/v1/share/:token/redeem-tailscale', exact: true, method: 'POST' }, - // /me and /admin/* require authentication — NOT public. Listed here - // only to document them; absence from PUBLIC_ROUTES means they go - // through the normal auth gate. CSRF applies to writes as usual. + { path: '/api/v1/billing/checkout', exact: true, method: 'POST' }, + // /api/v1/billing/webhook was REMOVED: webhooks are handled out-of-process + // by scripts/stripe-license-bridge.js (the merchant webhook secret never + // enters the API process). The PUBLIC_ROUTES allowlist drift test would + // catch any re-add of this dead entry. + // /api/v1/services + status: read-only service metadata that the public + // dashboard needs before login (services list widget, status pill). + // Writes go through the normal auth gate. CSRF applies to writes as usual. { path: '/api/v1/services', exact: true, method: 'GET' }, { path: '/api/v1/ca/info', exact: true, method: 'GET' }, { path: '/api/v1/ca/root.crt', exact: true, method: 'GET' },