mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
fix(desktop): cleanup HahaOAuthService — 删除 dead code、加 fetch timeout、log refresh errors
Code review follow-ups on b0ca5861: - 删除 dead code exchangeFn/setExchangeFn (completeSession 总走 exchangeWithCustomCallback) - exchangeWithCustomCallback fetch 加 15s AbortController timeout (parity with client.ts) - ensureFreshAccessToken refresh 失败时 console.error,不再静默吞异常 - test 删除未用的 mock import - getOauthConfig 改为 static import - saveTokens 加注释说明 atomic write 意图 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b0ca586190
commit
a1b2530fab
@ -2,7 +2,7 @@
|
||||
* Unit tests for HahaOAuthService — haha 自管 OAuth 的核心 service 层。
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test'
|
||||
import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
|
||||
import * as fs from 'fs/promises'
|
||||
import * as path from 'path'
|
||||
import * as os from 'os'
|
||||
|
||||
@ -19,7 +19,6 @@ import {
|
||||
} from '../../services/oauth/crypto.js'
|
||||
import {
|
||||
buildAuthUrl,
|
||||
exchangeCodeForTokens,
|
||||
refreshOAuthToken,
|
||||
isOAuthTokenExpired,
|
||||
parseScopes,
|
||||
@ -29,6 +28,7 @@ import type {
|
||||
OAuthTokenExchangeResponse,
|
||||
SubscriptionType,
|
||||
} from '../../services/oauth/types.js'
|
||||
import { getOauthConfig } from '../../constants/oauth.js'
|
||||
|
||||
export type StoredOAuthTokens = {
|
||||
accessToken: string
|
||||
@ -47,29 +47,17 @@ export type OAuthSession = {
|
||||
}
|
||||
|
||||
type RefreshFn = (refreshToken: string, opts?: { scopes?: string[] }) => Promise<OAuthTokens>
|
||||
type ExchangeFn = (
|
||||
code: string,
|
||||
state: string,
|
||||
verifier: string,
|
||||
port: number,
|
||||
) => Promise<OAuthTokenExchangeResponse>
|
||||
|
||||
const SESSION_TTL_MS = 5 * 60 * 1000
|
||||
|
||||
export class HahaOAuthService {
|
||||
private sessions = new Map<string, OAuthSession>()
|
||||
private refreshFn: RefreshFn = refreshOAuthToken
|
||||
private exchangeFn: ExchangeFn = (code, state, verifier, port) =>
|
||||
exchangeCodeForTokens(code, state, verifier, port, false)
|
||||
|
||||
setRefreshFn(fn: RefreshFn): void {
|
||||
this.refreshFn = fn
|
||||
}
|
||||
|
||||
setExchangeFn(fn: ExchangeFn): void {
|
||||
this.exchangeFn = fn
|
||||
}
|
||||
|
||||
private getOAuthFilePath(): string {
|
||||
const configDir =
|
||||
process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude')
|
||||
@ -89,6 +77,8 @@ export class HahaOAuthService {
|
||||
async saveTokens(tokens: StoredOAuthTokens): Promise<void> {
|
||||
const filePath = this.getOAuthFilePath()
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
||||
// 写临时文件再 rename,防止写到一半被其他读者读到残缺 JSON。
|
||||
// 单进程 desktop 下 pid 后缀足够隔离。
|
||||
const tmp = `${filePath}.tmp.${process.pid}`
|
||||
await fs.writeFile(tmp, JSON.stringify(tokens, null, 2), { mode: 0o600 })
|
||||
await fs.rename(tmp, filePath)
|
||||
@ -194,7 +184,6 @@ export class HahaOAuthService {
|
||||
verifier: string,
|
||||
port: number,
|
||||
): Promise<OAuthTokenExchangeResponse> {
|
||||
const { getOauthConfig } = await import('../../constants/oauth.js')
|
||||
const requestBody = {
|
||||
grant_type: 'authorization_code',
|
||||
code,
|
||||
@ -204,11 +193,19 @@ export class HahaOAuthService {
|
||||
state,
|
||||
}
|
||||
|
||||
const res = await fetch(getOauthConfig().TOKEN_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(requestBody),
|
||||
})
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15_000)
|
||||
let res: Response
|
||||
try {
|
||||
res = await fetch(getOauthConfig().TOKEN_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(requestBody),
|
||||
signal: controller.signal,
|
||||
})
|
||||
} finally {
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Token exchange failed (${res.status}): ${await res.text()}`,
|
||||
@ -240,7 +237,11 @@ export class HahaOAuthService {
|
||||
}
|
||||
await this.saveTokens(updated)
|
||||
return updated.accessToken
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'[HahaOAuthService] token refresh failed:',
|
||||
err instanceof Error ? err.message : err,
|
||||
)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user