[object Object]

← back to Norma Platform

IG /spoonflower: server-inject data into the page so it renders without an authed XHR

1e26b119125047d1e229160b3966047fa95111c7 · 2026-08-17 09:14:22 -0700 · Steve Abrams

Chrome strips URL-embedded basic-auth creds, so fetch('/api/spoonflower') 401'd and
the board showed empty. Now the route injects the JSON into #sf-data and the page
first-paints from that; the XHR is refresh-only and fails silently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 1e26b119125047d1e229160b3966047fa95111c7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 17 09:14:22 2026 -0700

    IG /spoonflower: server-inject data into the page so it renders without an authed XHR
    
    Chrome strips URL-embedded basic-auth creds, so fetch('/api/spoonflower') 401'd and
    the board showed empty. Now the route injects the JSON into #sf-data and the page
    first-paints from that; the XHR is refresh-only and fails silently.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 agents/instagram-agent/public/spoonflower-viewer.html | 13 ++++++++++---
 agents/instagram-agent/spoonflower-api.js             |  7 ++++++-
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/agents/instagram-agent/public/spoonflower-viewer.html b/agents/instagram-agent/public/spoonflower-viewer.html
index ffafb01..14029d5 100644
--- a/agents/instagram-agent/public/spoonflower-viewer.html
+++ b/agents/instagram-agent/public/spoonflower-viewer.html
@@ -48,11 +48,15 @@
   <div class="grid" id="grid"></div>
   <div class="note" id="note"></div>
 </main>
+<script id="sf-data" type="application/json">/*__SPOONFLOWER_JSON__*/</script>
 <script>
 const esc = (s) => (s||'').replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
 const hi = (s) => esc(s).replace(/(spoonflower)/ig, '<b>$1</b>');
-async function load() {
-  const r = await fetch('/api/spoonflower'); const d = await r.json();
+// Server injects the data into #sf-data so the board renders even if the authed
+// XHR is blocked (Chrome strips URL creds → /api/spoonflower 401). XHR is refresh-only.
+function injected() { try { return JSON.parse(document.getElementById('sf-data').textContent); } catch { return null; } }
+function render(d) {
+  if (!d || !d.posts) return;
   const stats = document.getElementById('stats');
   stats.innerHTML =
     `<span class="pill">Total flagged <b>${d.total}</b></span>` +
@@ -76,7 +80,10 @@ async function load() {
     '<b>hashtag</b> = a genuine 2022 repost where #spoonflowerwallpaper is 1 of 16 tags (Steve chose: delete it too). ' +
     'Deletion needs the openclaw Chrome logged in as each owning account, then runs serialized + verified.';
 }
-load(); setInterval(load, 15000);
+async function refresh() { try { const r = await fetch('/api/spoonflower'); if (r.ok) render(await r.json()); } catch {} }
+render(injected());          // first paint from server-injected data (no auth needed)
+refresh();                   // then try a live refresh (silently ignored if XHR is 401)
+setInterval(refresh, 15000);
 </script>
 </body>
 </html>
diff --git a/agents/instagram-agent/spoonflower-api.js b/agents/instagram-agent/spoonflower-api.js
index f207d83..0192494 100644
--- a/agents/instagram-agent/spoonflower-api.js
+++ b/agents/instagram-agent/spoonflower-api.js
@@ -54,7 +54,12 @@ function getSpoonflower() {
 function registerSpoonflowerRoutes(app) {
   app.get('/spoonflower', (req, res) => {
     if (!fs.existsSync(VIEWER)) return res.status(404).send('spoonflower-viewer.html missing');
-    res.type('html').send(fs.readFileSync(VIEWER, 'utf8'));
+    // Inject the data straight into the page so it renders without a separate authed XHR
+    // (browsers strip URL creds → /api/spoonflower 401 → empty board).
+    let html = fs.readFileSync(VIEWER, 'utf8');
+    const json = JSON.stringify(getSpoonflower()).replace(/</g, '\\u003c');
+    html = html.replace('/*__SPOONFLOWER_JSON__*/', json);
+    res.type('html').send(html);
   });
   app.get('/api/spoonflower', (req, res) => {
     try { res.json(getSpoonflower()); } catch (e) { res.status(500).json({ error: e.message }); }

← c573cf5 IG: add /spoonflower purge viewer (thumbnails + live delete-  ·  back to Norma Platform  ·  Harden IG delete-originals.js: retry stale refs, recover dea 04b7ade →