mirror of
https://github.com/stardrophere/InsightRadar.git
synced 2026-06-06 01:57:51 +08:00
107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
/**
|
||
* 认证 API:登录、注册、发送验证码(不走通用 client,无 Bearer)
|
||
*/
|
||
import type {
|
||
AuthTokenResponse,
|
||
LoginPayload,
|
||
LoginWithCodePayload,
|
||
MessageResponse,
|
||
RegisterPayload,
|
||
} from './auth.types'
|
||
import { fetchApi } from '@/config/apiBase'
|
||
|
||
type JsonValue = object | null
|
||
|
||
const MESSAGE_MAP: Record<string, string> = {
|
||
'Email is already registered': '该邮箱已注册',
|
||
'Email is not registered': '该邮箱未注册',
|
||
'Failed to send verification code': '验证码发送失败,请稍后重试',
|
||
'Verification code sent': '验证码已发送',
|
||
'Verification code does not exist or expired': '验证码不存在或已过期',
|
||
'Invalid verification code': '验证码错误',
|
||
'Invalid email or password': '邮箱或密码错误',
|
||
'Invalid email or verification code': '邮箱或验证码错误',
|
||
}
|
||
|
||
function localizeMessage(message: string): string {
|
||
return MESSAGE_MAP[message] ?? message
|
||
}
|
||
|
||
function localizeDetail(detail: string): string {
|
||
const direct = localizeMessage(detail)
|
||
if (direct !== detail) {
|
||
return direct
|
||
}
|
||
|
||
const cooldownMatch = detail.match(/^Please wait (\d+)s before requesting another verification code$/)
|
||
if (cooldownMatch) {
|
||
return `操作过于频繁,请 ${cooldownMatch[1]} 秒后再试`
|
||
}
|
||
|
||
return detail
|
||
}
|
||
|
||
async function request<T>(path: string, payload: JsonValue): Promise<T> {
|
||
const response = await fetchApi(path, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: payload ? JSON.stringify(payload) : undefined,
|
||
})
|
||
|
||
const raw = await response.text()
|
||
let data: Record<string, unknown> = {}
|
||
if (raw) {
|
||
try {
|
||
data = JSON.parse(raw) as Record<string, unknown>
|
||
} catch {
|
||
data = {}
|
||
}
|
||
}
|
||
|
||
if (!response.ok) {
|
||
const detail = data.detail
|
||
if (typeof detail === 'string') {
|
||
const error = new Error(localizeDetail(detail)) as Error & { retryAfter?: number }
|
||
if (response.status === 429) {
|
||
const retryAfterHeader = response.headers.get('Retry-After')
|
||
if (retryAfterHeader) {
|
||
const retryAfter = Number.parseInt(retryAfterHeader, 10)
|
||
if (Number.isFinite(retryAfter) && retryAfter > 0) {
|
||
error.retryAfter = retryAfter
|
||
}
|
||
}
|
||
}
|
||
throw error
|
||
}
|
||
throw new Error('请求失败,请稍后重试')
|
||
}
|
||
|
||
if (typeof data.message === 'string') {
|
||
data.message = localizeMessage(data.message)
|
||
}
|
||
|
||
return data as T
|
||
}
|
||
|
||
export function sendRegisterCode(email: string): Promise<MessageResponse> {
|
||
return request<MessageResponse>('/auth/register/send-code', { email })
|
||
}
|
||
|
||
export function sendLoginCode(email: string): Promise<MessageResponse> {
|
||
return request<MessageResponse>('/auth/login/send-code', { email })
|
||
}
|
||
|
||
export function register(payload: RegisterPayload): Promise<AuthTokenResponse> {
|
||
return request<AuthTokenResponse>('/auth/register', payload)
|
||
}
|
||
|
||
export function login(payload: LoginPayload): Promise<AuthTokenResponse> {
|
||
return request<AuthTokenResponse>('/auth/login', payload)
|
||
}
|
||
|
||
export function loginWithCode(payload: LoginWithCodePayload): Promise<AuthTokenResponse> {
|
||
return request<AuthTokenResponse>('/auth/login/code', payload)
|
||
}
|