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 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-30 22:48:57 -07:00
parent df202df06c
commit d865da32a7
2 changed files with 21 additions and 10 deletions

View File

@ -133,17 +133,28 @@ export class FolkCampaignWizard extends HTMLElement {
return `/${encodeURIComponent(this._space)}/rsocials`;
}
private async apiFetch(path: string, opts: RequestInit = {}): Promise<Response> {
private async apiFetch(path: string, opts: RequestInit & { timeoutMs?: number } = {}): Promise<Response> {
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<void> {

View File

@ -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,
});