refactor: Add Telegram binding storage and auth

This commit is contained in:
weishu
2025-12-31 17:43:57 +08:00
parent a0b1bb1524
commit 8f7dcf848b
16 changed files with 406 additions and 202 deletions
+47 -1
View File
@@ -18,6 +18,33 @@ type ApiClientOptions = {
onUnauthorized?: () => Promise<string | null>
}
type ErrorPayload = {
error?: unknown
}
function parseErrorCode(bodyText: string): string | undefined {
try {
const parsed = JSON.parse(bodyText) as ErrorPayload
return typeof parsed.error === 'string' ? parsed.error : undefined
} catch {
return undefined
}
}
export class ApiError extends Error {
status: number
code?: string
body?: string
constructor(message: string, status: number, code?: string, body?: string) {
super(message)
this.name = 'ApiError'
this.status = status
this.code = code
this.body = body
}
}
export class ApiClient {
private token: string
private readonly baseUrl: string | null
@@ -93,7 +120,26 @@ export class ApiClient {
if (!res.ok) {
const body = await res.text().catch(() => '')
throw new Error(`Auth failed: HTTP ${res.status} ${res.statusText}: ${body}`)
const code = parseErrorCode(body)
const detail = body ? `: ${body}` : ''
throw new ApiError(`Auth failed: HTTP ${res.status} ${res.statusText}${detail}`, res.status, code, body || undefined)
}
return await res.json() as AuthResponse
}
async bind(auth: { initData: string; accessToken: string }): Promise<AuthResponse> {
const res = await fetch(this.buildUrl('/api/bind'), {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(auth)
})
if (!res.ok) {
const body = await res.text().catch(() => '')
const code = parseErrorCode(body)
const detail = body ? `: ${body}` : ''
throw new ApiError(`Bind failed: HTTP ${res.status} ${res.statusText}${detail}`, res.status, code, body || undefined)
}
return await res.json() as AuthResponse