[object Object]

← back to Dw Slideshow Gen

auto-save: 2026-06-29T14:53:15 (2 files) — index.js lib/postiz.js

d018d37b5f28e01bb0536d60240cb53587afc5f6 · 2026-06-29 14:53:38 -0700 · Steve Abrams

Files touched

Diff

commit d018d37b5f28e01bb0536d60240cb53587afc5f6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jun 29 14:53:38 2026 -0700

    auto-save: 2026-06-29T14:53:15 (2 files) — index.js lib/postiz.js
---
 index.js      |  8 +++++++-
 lib/postiz.js | 24 ++++++++++++++++++------
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/index.js b/index.js
index a62d698..471eb00 100644
--- a/index.js
+++ b/index.js
@@ -112,7 +112,13 @@ if (cmd === 'post') {
     const marker = path.join(dir, 'posted.json')
     const id = path.basename(dir)
     if (!a.restage && fs.existsSync(marker)) { skipped++; continue }   // idempotent: already staged
-    const r = await stageToPostiz(dir, process.env, { confirm: a.confirm, channel: a.channel, schedule: a.schedule })
+    let r
+    try {
+      r = await stageToPostiz(dir, process.env, { confirm: a.confirm, channel: a.channel, schedule: a.schedule })
+    } catch (e) {                          // never let one item kill the whole batch
+      failed++; console.error(`  [post ${i}/${dirs.length}] FAIL ${id}: ${e.message}`)
+      continue
+    }
     const chNames = (r.channels || []).map(c => c.identifier).join('+')
     if (r.staged) {
       staged++
diff --git a/lib/postiz.js b/lib/postiz.js
index 13bd90a..a54671a 100644
--- a/lib/postiz.js
+++ b/lib/postiz.js
@@ -21,12 +21,24 @@ export class Postiz {
     this.key = apiKey; this.base = base
   }
   async #json(p, init = {}) {
-    const res = await fetch(`${this.base}/public/v1${p}`, {
-      ...init, headers: { Authorization: this.key, ...(init.headers || {}) },
-    })
-    const text = await res.text()
-    if (!res.ok) throw new Error(`Postiz ${init.method || 'GET'} ${p} -> ${res.status}: ${text.slice(0, 300)}`)
-    try { return text ? JSON.parse(text) : {} } catch { return text }
+    const sleep = ms => new Promise(r => setTimeout(r, ms))
+    const MAX = 6   // retries on throttle / transient 5xx
+    for (let attempt = 0; ; attempt++) {
+      const res = await fetch(`${this.base}/public/v1${p}`, {
+        ...init, headers: { Authorization: this.key, ...(init.headers || {}) },
+      })
+      const text = await res.text()
+      if (res.ok) { try { return text ? JSON.parse(text) : {} } catch { return text } }
+      // retry on 429 (Postiz ThrottlerException) and transient 5xx, honoring Retry-After
+      const retriable = res.status === 429 || res.status === 502 || res.status === 503 || res.status === 504
+      if (retriable && attempt < MAX) {
+        const ra = parseFloat(res.headers.get('retry-after') || '')
+        const wait = Number.isFinite(ra) ? ra * 1000 : Math.min(30000, 1000 * 2 ** attempt) // exp backoff, cap 30s
+        await sleep(wait)
+        continue
+      }
+      throw new Error(`Postiz ${init.method || 'GET'} ${p} -> ${res.status}: ${text.slice(0, 300)}`)
+    }
   }
 
   // GET connected channels: [{id, name, identifier, picture, disabled, profile}]

← 0479a3f RESUME.md: render complete (500/500) — resume is now a singl  ·  back to Dw Slideshow Gen  ·  postiz: unattended drip-resume watcher (throttle-gated, 12s/ a213767 →