From d865da32a73ca37e9b93829d982611d916a78936 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 30 Mar 2026 22:48:57 -0700 Subject: [PATCH] fix(rsocials): campaign wizard timeout + faster content generation - Add 120s AbortController timeout to apiFetch so wizard can't hang forever on slow networks (shows "Request timed out" instead of stuck) - Switch content generation from gemini-2.5-pro to gemini-2.5-flash to avoid Cloudflare/Traefik proxy timeouts (pro model took 30-60s+) Co-Authored-By: Claude Opus 4.6 --- .../components/folk-campaign-wizard.ts | 29 +++++++++++++------ modules/rsocials/mod.ts | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/modules/rsocials/components/folk-campaign-wizard.ts b/modules/rsocials/components/folk-campaign-wizard.ts index 09dbbe3..d12abef 100644 --- a/modules/rsocials/components/folk-campaign-wizard.ts +++ b/modules/rsocials/components/folk-campaign-wizard.ts @@ -133,17 +133,28 @@ export class FolkCampaignWizard extends HTMLElement { return `/${encodeURIComponent(this._space)}/rsocials`; } - private async apiFetch(path: string, opts: RequestInit = {}): Promise { + private async apiFetch(path: string, opts: RequestInit & { timeoutMs?: number } = {}): Promise { let token = ''; try { const s = JSON.parse(localStorage.getItem('encryptid_session') || ''); token = s.accessToken || ''; } catch {} - return fetch(`${this.basePath}${path}`, { - ...opts, - headers: { - 'Content-Type': 'application/json', - ...(token ? { Authorization: `Bearer ${token}` } : {}), - ...(opts.headers || {}), - }, - }); + const { timeoutMs = 120_000, ...fetchOpts } = opts; + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), timeoutMs); + try { + return await fetch(`${this.basePath}${path}`, { + ...fetchOpts, + signal: controller.signal, + headers: { + 'Content-Type': 'application/json', + ...(token ? { Authorization: `Bearer ${token}` } : {}), + ...(fetchOpts.headers || {}), + }, + }); + } catch (e: any) { + if (e.name === 'AbortError') throw new Error('Request timed out — please try again'); + throw e; + } finally { + clearTimeout(timer); + } } private async loadWizard(): Promise { diff --git a/modules/rsocials/mod.ts b/modules/rsocials/mod.ts index b70ec57..07de488 100644 --- a/modules/rsocials/mod.ts +++ b/modules/rsocials/mod.ts @@ -1521,7 +1521,7 @@ routes.post("/api/campaign/wizard/:id/content", async (c) => { const { GoogleGenerativeAI } = await import("@google/generative-ai"); const genAI = new GoogleGenerativeAI(GEMINI_API_KEY); const model = genAI.getGenerativeModel({ - model: "gemini-2.5-pro", + model: "gemini-2.5-flash", generationConfig: { responseMimeType: "application/json" } as any, });