[object Object]

← 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 →