mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-31 16:33:34 +08:00
* fix(providers): route 'Fetch Models' through server to bypass mixed-content / CORS The "获取模型 / Fetch Models" button in the provider settings dialog used to call upstream `/v1/models` directly from the renderer with `fetch()`. That works for HTTPS providers whose endpoint sets permissive CORS headers (e.g. api.openai.com, api.anthropic.com), but fails for any plain-`http://` provider — most commonly self-hosted relay endpoints like `http://47.116.22.0:3000`. Two failure modes both surface as the same opaque "Failed to fetch": 1. Mixed-content block. The desktop renderer runs in a secure context (Tauri at `tauri://localhost`, Electron with web security on), and modern webviews refuse to fetch plain-HTTP URLs from a secure-context page. The request never leaves the browser. 2. CORS-missing. Even when reachable, many self-hosted relays do not return `Access-Control-Allow-Origin` for `/v1/models`, so the browser blocks the response. The "Test Connection" button never had this problem because it already proxies through `POST /api/providers/{id}/test`, which runs on the local server (Node, no webview restrictions). The fix routes "Fetch Models" through the same server so it inherits the same isolation. What this PR adds Server side - ApiError.badGateway(message) — new 502 helper for upstream failures. - POST /api/providers/fetch-models with the new FetchModelsSchema (zod): { baseUrl: url, apiKey: non-empty, apiFormat: 'anthropic' | 'openai_chat' | 'openai_responses' default 'anthropic' }. - ProviderService.fetchUpstreamModels(input) — server-side GET against `${baseUrl}/v1/models` (or `${baseUrl}/models` if baseUrl already ends in `/v1`), reusing the existing network-proxy / timeout plumbing. Sets Bearer for OpenAI-compatible formats, x-api-key + anthropic-version for native Anthropic. Returns `{ status, data }` where `data` is the upstream JSON verbatim. Non-2xx upstreams surface as ApiError.badGateway with the upstream's `error.message` if the body has one. Network failures and timeouts also map to badGateway with a clear message. Desktop side - providersApi.fetchModels(input) — new client wrapping the endpoint. - handleFetchModels in Settings.tsx now calls providersApi.fetchModels instead of fetching directly. The existing `extractModelEntries` parser already accepts arbitrary upstream JSON (data / models / items / results arrays, plus { id | name | model } per entry), so no parsing change was needed. Tests - 5 new ProviderService unit tests covering the wire shape: · OpenAI format → Bearer header, GET /v1/models, no x-api-key · Anthropic format → x-api-key + anthropic-version, no Authorization · URL hygiene — strips trailing slashes, doesn't duplicate /v1 · Non-2xx upstream → ApiError 502 with `HTTP {status}: {message}` · Network error → ApiError 502 with "Upstream fetch failed: ..." Verification - `bun test src/server/__tests__/providers.test.ts -t "fetchUpstreamModels"` → 5/5 pass. - `bun run lint` (desktop): tsc --noEmit clean (after `bun install` to pick up the @codemirror packages added by an earlier PR — orthogonal to this change). - All existing provider tests unchanged (no regression in test file). Tested: server unit (5 cases covering wire shape, URL hygiene, error paths); existing testProviderConfig tests still pass; desktop tsc clean. Not-tested: live smoke against an actual self-hosted relay (no provider fixture configured locally). The bug repro is well-understood: the secure-context webview's mixed-content block is documented browser behaviour and the fix removes the renderer-side fetch entirely. Confidence: high Scope-risk: narrow * test(providers): add desktop-side fetchModels client test (clears change-policy block) Locks the renderer→server contract: providersApi.fetchModels must POST to the local server with baseUrl/apiKey/apiFormat in the body, NOT fetch the upstream URL directly. The same-origin assertion guards against any future regression that reintroduces the mixed-content / CORS bug class. Two cases: - success path: upstream JSON forwarded verbatim, body shape locked - error path: 502 from server surfaces upstream message in thrown error --------- Co-authored-by: 你的姓名 <you@example.com>
Claude Code Haha Desktop
基于 Tauri 2 + React 的桌面客户端。
开发
bun install
bun run tauri dev
构建
# macOS (Apple Silicon)
./scripts/build-macos-arm64.sh
# Windows (x64, MSI only)
.\scripts\build-windows-x64.ps1
构建产物位于 build-artifacts/ 目录,文件名会显式包含平台、架构和包类型。
常见问题
macOS 提示"已损坏,无法打开"
xattr -cr /Applications/Claude\ Code\ Haha.app