← back to Marketing Command Center
Add /posts/refresh-one endpoint: fresh IG image on 'Make it ours' click (kills stale-URL 403); reuses bulk-refresh path, handle-allowlisted, graceful degrade
796eba1a37ff55a26536a5c72d818664ad82ff6b · 2026-08-25 11:15:08 -0700 · Steve Abrams
Files touched
M modules/vendors/index.js
Diff
commit 796eba1a37ff55a26536a5c72d818664ad82ff6b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Aug 25 11:15:08 2026 -0700
Add /posts/refresh-one endpoint: fresh IG image on 'Make it ours' click (kills stale-URL 403); reuses bulk-refresh path, handle-allowlisted, graceful degrade
---
modules/vendors/index.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/modules/vendors/index.js b/modules/vendors/index.js
index d70eedc..89848c7 100644
--- a/modules/vendors/index.js
+++ b/modules/vendors/index.js
@@ -135,5 +135,51 @@ module.exports = {
}
res.json({ ok: true, discoveringIg: '@' + ig.username, refreshed: ok, failed, total: targets.length });
});
+
+ // Refresh ONE account's recent media and RETURN its fresh posts. This is the
+ // "Make it ours" refresh-on-click path (TK-10843): the branded-card compositor
+ // sources p.image, which is a SIGNED IG CDN url that expires (~a day) — a cached
+ // post that's days old returns 403 → /api/img-proxy 502s → the card can't
+ // compose. So before compositing, the client fetches a FRESH image url for that
+ // specific post via a targeted, single-handle refresh.
+ //
+ // Reuses the EXACT bulk-refresh code path (metaToken → discoveringIg →
+ // fetchPosts), scoped to one handle, and writes the SAME cache the bulk refresh
+ // writes (data/vendor-posts-cache.json) so the two stay consistent. It returns
+ // the account's fresh posts (id / permalink / image / timestamp / caption) so the
+ // client can match the clicked post to its refreshed image url.
+ //
+ // COST: Business Discovery is free within Meta rate limits (no metered call).
+ // GRACEFUL DEGRADE: a dead/expired Meta token (see the meta-token-canary
+ // dependency), a rate-limit, or an unknown handle returns { ok:false, error } —
+ // the client shows a clear inline message and does NOT hard-error.
+ router.post('/posts/refresh-one', async (req, res) => {
+ const handle = String((req.body && req.body.handle) || (req.query && req.query.handle) || '')
+ .replace(/^@/, '').trim().slice(0, 80);
+ if (!handle) return res.json({ ok: false, error: 'no handle' });
+ // Only refresh a handle that's actually in the vendor roster (don't let an
+ // arbitrary username be used to probe Business Discovery through our token).
+ const known = load().some(r => handleOf(r).toLowerCase() === handle.toLowerCase() && r.handle !== 'none found');
+ if (!known) return res.json({ ok: false, error: 'unknown handle' });
+
+ const token = metaToken();
+ if (!token) return res.json({ ok: false, error: 'No usable Meta token (META_ACCESS_TOKEN / IG_ACCESS_TOKEN) — check the meta-token canary' });
+ let ig;
+ try { ig = await discoveringIg(token); }
+ catch (e) { return res.json({ ok: false, error: 'token/IG check failed: ' + e.message + (/expired|session/i.test(e.message) ? ' — paste a fresh long-lived META_ACCESS_TOKEN' : '') }); }
+
+ const cache = loadCache();
+ try {
+ const data = await fetchPosts(ig.id, token, handle);
+ cache[handle] = { ...data, fetchedAt: new Date().toISOString(), error: null };
+ saveCache(cache);
+ // Business Discovery is not a metered call — no cost to report.
+ res.json({ ok: true, handle, discoveringIg: '@' + ig.username, fetchedAt: cache[handle].fetchedAt, posts: data.posts || [] });
+ } catch (e) {
+ cache[handle] = { ...(cache[handle] || {}), error: e.message, fetchedAt: new Date().toISOString() };
+ saveCache(cache);
+ res.json({ ok: false, handle, error: e.message + (/rate|limit|#4|#17/i.test(e.message) ? ' — rate-limited, try again shortly' : '') });
+ }
+ });
},
};
← 3c85e99 Composer: one create flow — photo → details → song → decide
·
back to Marketing Command Center
·
chore: v1.11.0 (session close) — vendor-amplify + Make-it-ou 33e8840 →