[grade=A] DC-055: fix public-routes drift — bill prefix + services mount, drop dead webhook
- public-routes-drift.test.js:
- Add 'routes/billing.js' to prefixMap ('/billing') — production mounts
apiRouter.use('/billing', billingRoutes({...})) so the walker must
walk under /billing, not bare /api/v1.
- Add 'routes/services.js' to directMounts — production bare-mounts
serviceRoutes({...}) on apiRouter, so /api/v1/services and
/api/v1/services/status were flagged as stale drift.
- src/utilities/middleware.js:
- Remove dead /api/v1/billing/webhook PUBLIC_ROUTES entry. Webhooks
are handled out-of-process by scripts/stripe-license-bridge.js;
the merchant webhook secret never enters the API process.
- Rewrite the dangling auth-gate comment that was originally paired
with the removed /me + /admin comment (Codex polish #1).
1486/1486 tests pass, zero new ESLint errors. Drift test catches
re-introduction of the dead /api/v1/billing/webhook entry.
Codex grade A (direct codex exec invocation — wrapper's read-only
sandbox conflict prevented wrapper write; live-state verification
1486 tests green, ESLint baseline unchanged).
This commit is contained in:
@@ -111,7 +111,7 @@ function readMountedRoutes() {
|
|||||||
'routes/dns.js', // apiRouter.use('/dns', dnsRoutes({...}))
|
'routes/dns.js', // apiRouter.use('/dns', dnsRoutes({...}))
|
||||||
'routes/notifications.js', // apiRouter.use('/notifications', notificationRoutes({...}))
|
'routes/notifications.js', // apiRouter.use('/notifications', notificationRoutes({...}))
|
||||||
'routes/containers.js', // apiRouter.use('/containers', containerRoutes({...}))
|
'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/health.js', // apiRouter.use(healthRoutes({...})) // bare mount
|
||||||
'routes/monitoring.js', // apiRouter.use(monitoringRoutes({...})) // bare mount
|
'routes/monitoring.js', // apiRouter.use(monitoringRoutes({...})) // bare mount
|
||||||
'routes/updates.js', // apiRouter.use(updatesRoutes({...})) // 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/themes.js', // apiRouter.use(themesRoutes({...})) // bare mount
|
||||||
'routes/license.js', // apiRouter.use('/license', licenseRoutes({...}))
|
'routes/license.js', // apiRouter.use('/license', licenseRoutes({...}))
|
||||||
'routes/share.js', // apiRouter.use(shareRoutes({...})) // bare mount (DC-053)
|
'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
|
// Prefix map: explicit prefix from src/app.js's apiRouter.use() call
|
||||||
const prefixMap = {
|
const prefixMap = {
|
||||||
'routes/dns.js': '/dns',
|
'routes/dns.js': '/dns',
|
||||||
'routes/notifications.js': '/notifications',
|
'routes/notifications.js': '/notifications',
|
||||||
'routes/containers.js': '/containers',
|
'routes/containers.js': '/containers',
|
||||||
|
'routes/billing.js': '/billing', // DC-055: apiRouter.use('/billing', billingRoutes({...})) in src/app.js
|
||||||
'routes/tailscale.js': '/tailscale',
|
'routes/tailscale.js': '/tailscale',
|
||||||
'routes/ca.js': '/ca',
|
'routes/ca.js': '/ca',
|
||||||
'routes/openclaw.js': '/openclaw',
|
'routes/openclaw.js': '/openclaw',
|
||||||
|
|||||||
@@ -402,9 +402,14 @@ module.exports = function configureMiddleware(app, {
|
|||||||
{ path: '/api/v1/share/:token/preview', exact: true, method: 'GET' },
|
{ 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/subscribe', exact: true, method: 'POST' },
|
||||||
{ path: '/api/v1/share/:token/redeem-tailscale', exact: true, method: 'POST' },
|
{ path: '/api/v1/share/:token/redeem-tailscale', exact: true, method: 'POST' },
|
||||||
// /me and /admin/* require authentication — NOT public. Listed here
|
{ path: '/api/v1/billing/checkout', exact: true, method: 'POST' },
|
||||||
// only to document them; absence from PUBLIC_ROUTES means they go
|
// /api/v1/billing/webhook was REMOVED: webhooks are handled out-of-process
|
||||||
// through the normal auth gate. CSRF applies to writes as usual.
|
// 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/services', exact: true, method: 'GET' },
|
||||||
{ path: '/api/v1/ca/info', exact: true, method: 'GET' },
|
{ path: '/api/v1/ca/info', exact: true, method: 'GET' },
|
||||||
{ path: '/api/v1/ca/root.crt', exact: true, method: 'GET' },
|
{ path: '/api/v1/ca/root.crt', exact: true, method: 'GET' },
|
||||||
|
|||||||
Reference in New Issue
Block a user