← back to Wallco Ai
/api/generator/refresh-orders: stop hanging on the broken-pipe pattern
5bb011a3d7df56d7afa68ae2a2bed83689ff3ae2 · 2026-05-13 11:07:48 -0700 · SteveStudio2
Same bug as run-now: detached + pipe stdio + waiting on 'exit' hangs
forever. UI button hung with 'Fetching…' and never returned.
- sync=true (default) — actually blocks, but inherits stdio (not detached)
so we get a real exit code. 2-min hard timeout via SIGKILL killer.
- sync=false — fire & return path with file-stdio + unref + log handle.
Now surfaces real failures. Smoke run revealed the actual blocker — the
Shopify token is missing read_orders scope:
ERROR: [preflight] Token is missing read_orders scope (have:
read_products, write_products, write_inventory, read_inventory,
write_publications, read_publications)
Add it at: https://designer-laboratory-sandbox.myshopify.com/admin/settings/apps/development
Expected scope: read_orders
Also routed SHOPIFY_STORE + SHOPIFY_ADMIN_TOKEN from secrets-manager/.env
into wallco-ai/.env so the script's dotenv path picks them up.
Files touched
Diff
commit 5bb011a3d7df56d7afa68ae2a2bed83689ff3ae2
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 11:07:48 2026 -0700
/api/generator/refresh-orders: stop hanging on the broken-pipe pattern
Same bug as run-now: detached + pipe stdio + waiting on 'exit' hangs
forever. UI button hung with 'Fetching…' and never returned.
- sync=true (default) — actually blocks, but inherits stdio (not detached)
so we get a real exit code. 2-min hard timeout via SIGKILL killer.
- sync=false — fire & return path with file-stdio + unref + log handle.
Now surfaces real failures. Smoke run revealed the actual blocker — the
Shopify token is missing read_orders scope:
ERROR: [preflight] Token is missing read_orders scope (have:
read_products, write_products, write_inventory, read_inventory,
write_publications, read_publications)
Add it at: https://designer-laboratory-sandbox.myshopify.com/admin/settings/apps/development
Expected scope: read_orders
Also routed SHOPIFY_STORE + SHOPIFY_ADMIN_TOKEN from secrets-manager/.env
into wallco-ai/.env so the script's dotenv path picks them up.
---
server.js | 38 ++++++++++++++++++++++++++++++++------
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/server.js b/server.js
index 9bd14a2..dd23078 100644
--- a/server.js
+++ b/server.js
@@ -9052,14 +9052,40 @@ app.post('/api/generator/run-now', (req, res) => {
});
app.post('/api/generator/refresh-orders', (req, res) => {
- const { limit = 50 } = req.body || {};
+ // Same bug as run-now had: detached + pipe + waiting on 'exit' hangs
+ // forever because the parent owns the pipes but the child is detached.
+ // Two-mode now:
+ // sync=1 (default for the UI button) — short timeout, stream output
+ // back inline so the alert() can show real success/failure
+ // sync=0 — fire & return immediately with a log handle
+ const limit = parseInt(req.body?.limit || '50', 10);
+ const sync = req.body?.sync === false ? false : true;
+ if (!sync) {
+ const logFile = path.join(__dirname, 'data', 'logs', `refresh-orders-${Date.now()}.log`);
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
+ const out = fs.openSync(logFile, 'a');
+ const proc = spawn('node',
+ [path.join(__dirname, 'scripts', 'fetch_recent_orders_palette.js'), '--limit', String(limit)],
+ { cwd: __dirname, detached: true, stdio: ['ignore', out, out] });
+ proc.unref();
+ return res.json({ ok: true, started_at: new Date().toISOString(), log: path.relative(__dirname, logFile), async: true });
+ }
+ // Sync path — block until the script exits or 2 min, then return its output
+ let buf = '';
const proc = spawn('node',
[path.join(__dirname, 'scripts', 'fetch_recent_orders_palette.js'), '--limit', String(limit)],
- { cwd: __dirname, detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
- let out = '';
- proc.stdout.on('data', d => out += d);
- proc.stderr.on('data', d => out += d);
- proc.on('exit', code => res.json({ ok: code === 0, output: out.slice(-3000) }));
+ { cwd: __dirname, stdio: ['ignore', 'pipe', 'pipe'] });
+ proc.stdout.on('data', d => buf += d);
+ proc.stderr.on('data', d => buf += d);
+ const killer = setTimeout(() => { try { proc.kill('SIGKILL'); } catch {} }, 120_000);
+ proc.on('exit', code => {
+ clearTimeout(killer);
+ res.json({ ok: code === 0, exit: code, output: buf.slice(-3000) });
+ });
+ proc.on('error', err => {
+ clearTimeout(killer);
+ res.json({ ok: false, error: err.message, output: buf.slice(-3000) });
+ });
});
app.get('/api/generator/runs', (_req, res) => {
← 10447d1 /generator: fix broken 'Run tick NOW' + DW palette slider gr
·
back to Wallco Ai
·
designs: butterfly+trellis batch generator + 10 new designs 9a13b0a →