← back to Wallco Ai
security: admin-gate the entire /api/generator/* surface + /generator page
64c2db6a1b5afcba4f945861d089f2c81a956f7d · 2026-06-01 16:55:23 -0700 · Steve Abrams
The generator admin page and several of its API routes were anonymous-reachable on prod (recipes GET/POST/DELETE, palette, run-now, refresh-orders, and cron — which could launchctl start/stop the generator daemon, and refresh-orders which hit Shopify + wrote the DB). Added one app.use('/api/generator', requireAdmin) before the route block so no route can be left open, plus requireAdmin on the /generator page route. requireAdmin passes localhost (server-side ticks) + admin cookie/token (styleguides.html Generate button), 404s everyone else. Already-gated handlers keep their isAdmin checks as defense in depth.
Files touched
Diff
commit 64c2db6a1b5afcba4f945861d089f2c81a956f7d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jun 1 16:55:23 2026 -0700
security: admin-gate the entire /api/generator/* surface + /generator page
The generator admin page and several of its API routes were anonymous-reachable on prod (recipes GET/POST/DELETE, palette, run-now, refresh-orders, and cron — which could launchctl start/stop the generator daemon, and refresh-orders which hit Shopify + wrote the DB). Added one app.use('/api/generator', requireAdmin) before the route block so no route can be left open, plus requireAdmin on the /generator page route. requireAdmin passes localhost (server-side ticks) + admin cookie/token (styleguides.html Generate button), 404s everyone else. Already-gated handlers keep their isAdmin checks as defense in depth.
---
server.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/server.js b/server.js
index 5295823..ebad3d5 100644
--- a/server.js
+++ b/server.js
@@ -21581,6 +21581,14 @@ app.get('/api/voice/preview', async (req, res) => {
// ─────────────────────────────────────────────────────────────────────────
// WALLCO GENERATOR (30-min cron + recipe picker)
// ─────────────────────────────────────────────────────────────────────────
+// SECURITY (2026-06-01): gate the WHOLE /api/generator/* surface in one place so
+// no route is accidentally left open (recipes GET/POST/DELETE, palette, run-now,
+// refresh-orders, cron were all anonymous-reachable — cron could launchctl
+// start/stop the generator daemon; refresh-orders hit Shopify + wrote the DB).
+// requireAdmin passes localhost (server-side ticks) + admin-cookie/token, 404s
+// everyone else. Individual handlers keep their own isAdmin checks as defense in
+// depth. Registered before the route defs so it covers every one below.
+app.use('/api/generator', requireAdmin);
app.get('/api/generator/recipes', (_req, res) => {
try {
const raw = psqlQuery(`SELECT COALESCE(json_agg(t ORDER BY t.id), '[]'::json) FROM (
@@ -21942,7 +21950,7 @@ app.post('/api/generator/cron', (req, res) => {
});
// ── /generator admin page
-app.get('/generator', (_req, res) => {
+app.get('/generator', requireAdmin, (_req, res) => {
res.type('html').send(`${htmlHead({
title: 'Generator — wallco.ai admin',
description: '30-min recipe-driven pattern generator',
← a1e2cc7 deploy: run PDP theme smoke test as step [5b/6] after health
·
back to Wallco Ai
·
Replicate fallback: sanctioned feather seam-heal + fix post- f9f8f89 →