From 8adce51961ebee695133dcff66716c31c515248d Mon Sep 17 00:00:00 2001 From: chenchen Date: Mon, 18 May 2026 18:44:41 +0800 Subject: [PATCH] fix: add missing fetch timeouts across server API layer Prevent potential hangs from fetch calls without AbortSignal: - heicode-auth.ts: token exchange (15s) - heicode-tasks.ts: forwardJson upstream calls (60s) - createDirectConnectSession.ts: session creation (15s) Co-Authored-By: Claude Opus 4.6 --- cc-haha/src/server/api/heicode-auth.ts | 3 +++ cc-haha/src/server/api/heicode-tasks.ts | 2 ++ cc-haha/src/server/createDirectConnectSession.ts | 1 + 3 files changed, 6 insertions(+) diff --git a/cc-haha/src/server/api/heicode-auth.ts b/cc-haha/src/server/api/heicode-auth.ts index de5604d..3c7f7b8 100644 --- a/cc-haha/src/server/api/heicode-auth.ts +++ b/cc-haha/src/server/api/heicode-auth.ts @@ -162,6 +162,7 @@ export async function handleHeicodeAuthApi( const upstream = await fetch(`${mcpBase}/api/user/heicode/balance`, { method: 'GET', headers: { 'Authorization': `Bearer ${active.mcpAuth.accessToken}` }, + signal: AbortSignal.timeout(15_000), }) // Happy path — mcp-server knows the user. if (upstream.ok) { @@ -179,6 +180,7 @@ export async function handleHeicodeAuthApi( const self = await fetch(`${heicodeBase}/api/user/self`, { method: 'GET', headers: { 'Authorization': `Bearer ${active.apiKey}` }, + signal: AbortSignal.timeout(15_000), }) if (self.ok) { const json = await self.json() as { @@ -836,6 +838,7 @@ async function exchangeCodeForToken(input: { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString(), + signal: AbortSignal.timeout(15_000), }) if (!res.ok) { throw new Error(`token 交换失败: ${res.status} ${res.statusText}`) diff --git a/cc-haha/src/server/api/heicode-tasks.ts b/cc-haha/src/server/api/heicode-tasks.ts index 8b7470b..d8f0d50 100644 --- a/cc-haha/src/server/api/heicode-tasks.ts +++ b/cc-haha/src/server/api/heicode-tasks.ts @@ -60,6 +60,7 @@ async function forwardJson( ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}), }, ...(body !== undefined ? { body: JSON.stringify(body) } : {}), + signal: AbortSignal.timeout(60_000), } let upstream: Response try { @@ -96,6 +97,7 @@ async function forwardSse(path: string): Promise { Accept: 'text/event-stream', 'Cache-Control': 'no-cache', }, + signal: AbortSignal.timeout(600_000), }) if (!upstream.ok || !upstream.body) { const text = await upstream.text().catch(() => '') diff --git a/cc-haha/src/server/createDirectConnectSession.ts b/cc-haha/src/server/createDirectConnectSession.ts index 21fc494..1729141 100644 --- a/cc-haha/src/server/createDirectConnectSession.ts +++ b/cc-haha/src/server/createDirectConnectSession.ts @@ -55,6 +55,7 @@ export async function createDirectConnectSession({ dangerously_skip_permissions: true, }), }), + signal: AbortSignal.timeout(15_000), }) } catch (err) { throw new DirectConnectError(