Files
heicode-win/cc-haha/desktop/src/api/heicodeAuth.ts
T
chenchenandClaude Opus 4.7 c12e19b1ce feat: email+password login (path B from original design doc)
Per docs/integration/Heicode-登录接口对接文档.md, the original Heicode
desktop is supposed to take email+password directly, hand them to the
Manager (POST /api/user/login), and use the resulting session to
acquire an LLM access token. The previous flow opened a system browser
and redirected through /heicode/oauth/authorize, which works but
deviates from the design and forces an extra round trip.

This commit adds the documented in-process flow as the primary login
path while keeping browser OAuth as a fallback link:

  POST /api/heicode-auth/login-with-credentials
    1. POST <baseUrl>/api/user/login (username + password)
    2. Capture Set-Cookie from the response
    3. GET <baseUrl>/heicode/oauth/authorize?... with that cookie and
       redirect: 'manual'
    4. Parse Location: ...?token=sk-XXXX, hand it to loginAndActivate

The whole chain stays inside the local cc-haha server — no browser is
opened, no token leaves the user's machine.

UI changes:
  - ProviderLoginCard now shows email + password fields as the primary
    form, with the existing "or via browser" OAuth path demoted to a
    small link below.
  - Added store action loginWithCredentials and matching API client
    method.
  - i18n keys: login.creds.{email,password,submit,submitting} +
    login.oauth.altLink (zh + en).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:08:54 +08:00

100 lines
2.4 KiB
TypeScript

// desktop/src/api/heicodeAuth.ts
//
// Heicode 单 Provider 登录后端的客户端封装。
// 对应路由: src/server/api/heicode-auth.ts
import { api } from './client'
export type HeicodeProviderId = 'taijiaicloud'
export type HeicodeLoginProviderInfo = {
id: HeicodeProviderId
name: string
baseUrl: string
websiteUrl: string
apiKeyUrl?: string
promoText?: string
defaultModels: { main: string; haiku: string; sonnet: string; opus: string }
oauthEnabled: boolean
oauthStartUrl: string | null
}
export type HeicodeAuthStatus = {
loggedIn: boolean
source: 'cc-haha-provider' | 'original-settings' | 'env' | 'none'
activeProvider: {
id: string
presetId: string
name: string
baseUrl: string
models: { main: string; haiku: string; sonnet: string; opus: string }
} | null
}
export type HeicodeLoginInput = {
providerId: HeicodeProviderId
apiKey: string
displayName?: string
}
export type HeicodeCredentialsLoginInput = {
providerId: HeicodeProviderId
email: string
password: string
displayName?: string
}
export type HeicodeLoginResult = {
ok: true
provider: {
id: string
presetId: string
name: string
baseUrl: string
apiFormat: 'anthropic' | 'openai_chat' | 'openai_responses'
models: { main: string; haiku: string; sonnet: string; opus: string }
}
availableModels: Array<{ id: string; owned_by?: string }>
}
export const heicodeAuthApi = {
listProviders() {
return api.get<{ providers: HeicodeLoginProviderInfo[] }>(
'/api/heicode-auth/providers',
)
},
status() {
return api.get<HeicodeAuthStatus>('/api/heicode-auth/status')
},
loginWithApiKey(input: HeicodeLoginInput) {
return api.post<HeicodeLoginResult>(
'/api/heicode-auth/login',
input,
// 登录会调上游 /v1/models 探活,给 60s 余量。
{ timeout: 60_000 },
)
},
loginWithCredentials(input: HeicodeCredentialsLoginInput) {
return api.post<HeicodeLoginResult>(
'/api/heicode-auth/login-with-credentials',
input,
{ timeout: 60_000 },
)
},
// OAuth 当前是占位,但保留方法形态以便未来无痛切换。
startOAuth(providerId: HeicodeProviderId) {
return api.post<{ authorizeUrl: string; state: string }>(
'/api/heicode-auth/oauth/start',
{ providerId },
)
},
logout() {
return api.post<{ ok: true }>('/api/heicode-auth/logout')
},
}