Files
36-character-flower/src/api/auth-api.ts
JiaJun 9ade40c29b feat(auth): 更新用户认证类型定义并优化提款功能
- 添加 AuthCoinAmount 类型和 AuthWithdrawAccount 接口
- 在 AuthUser 和相关 DTO 中增加余额和提款账户字段
- 将获取用户资料的请求方法从 POST 改为 GET
- 实现提款账户信息的默认值设置和状态管理
- 添加 JackpotPoolTicker 组件并在状态栏中显示
- 更新多语言文件中的游戏规则说明
- 实现提款表单的预填充和状态重置功能
2026-07-15 18:00:33 +08:00

234 lines
5.3 KiB
TypeScript

import {
API_SUCCESS_CODE,
AUTH_ENDPOINTS,
AUTH_SKIP_REFRESH_CONTEXT_KEY,
SMS_SEND_EVENT_REGISTER,
} from '@/constants'
import { api } from '@/lib/api/api-client'
import { ApiError } from '@/lib/api/api-error'
import {
mergeAuthUsers,
normalizeAuthSession,
normalizeAuthUserProfile,
normalizeRefreshAuthSession,
} from '@/lib/auth/auth-normalizers'
import { getAuthDeviceId } from '@/store/auth'
import type {
ApiResponse,
AuthSessionDto,
AuthSessionInput,
AuthUserProfileDto,
LoginPayload,
LoginRequestDto,
LogoutPayload,
LogoutRequestDto,
RefreshTokenDto,
RefreshTokenRequestDto,
RegisterPayload,
RegisterRequestDto,
SendSmsCodeDto,
SendSmsCodePayload,
SendSmsCodeRequestDto,
SendSmsCodeResult,
} from '@/type'
const shouldLogAuthLifecycle =
import.meta.env.VITE_ENABLE_REQUEST_LOG === 'true'
function unwrapEnvelope<T>(
response: ApiResponse<T>,
fallbackErrorKey = 'auth.errors.requestFailed',
) {
if (response.code === API_SUCCESS_CODE) {
return response.data
}
throw new ApiError({
data: response,
message:
typeof response.msg === 'string' && response.msg.length > 0
? response.msg
: typeof response.message === 'string' && response.message.length > 0
? response.message
: fallbackErrorKey,
})
}
function logAuthSessionExpiry(action: string, session: AuthSessionInput) {
if (!shouldLogAuthLifecycle || !session.accessTokenExpiresAt) {
return
}
console.info(
`[auth] ${action} user-token expires at ${new Date(
session.accessTokenExpiresAt,
).toISOString()} (${session.accessTokenExpiresAt})`,
)
}
async function getCurrentUserProfileByToken(userToken: string) {
const response = await api.get<AuthUserProfileDto>(AUTH_ENDPOINTS.profile, {
headers: {
Authorization: `Bearer ${userToken}`,
'user-token': userToken,
},
})
return normalizeAuthUserProfile(
unwrapEnvelope(
response as ApiResponse<AuthUserProfileDto>,
'auth.errors.requestFailed',
),
)
}
async function buildEnrichedAuthSession(dto: AuthSessionDto) {
const session = normalizeAuthSession(dto)
try {
const profileUser = await getCurrentUserProfileByToken(session.accessToken)
return {
...session,
currentUser: mergeAuthUsers(session.currentUser, profileUser),
} satisfies AuthSessionInput
} catch {
return session
}
}
export async function loginWithPassword(
payload: LoginPayload,
): Promise<AuthSessionInput> {
const response = await api.post<AuthSessionDto, LoginRequestDto>(
AUTH_ENDPOINTS.login,
{
json: {
device_id: getAuthDeviceId(),
password: payload.password,
username: payload.username,
},
},
)
const session = await buildEnrichedAuthSession(
unwrapEnvelope(
response as ApiResponse<AuthSessionDto>,
'auth.login.errors.submitFailed',
),
)
logAuthSessionExpiry('login', session)
return session
}
export async function logoutWithPassword(
payload: LogoutPayload,
): Promise<void> {
const response = await api.post<null, LogoutRequestDto>(
AUTH_ENDPOINTS.logout,
{
json: {
password: payload.password,
username: payload.username,
},
},
)
unwrapEnvelope(
response as ApiResponse<null>,
'auth.logout.errors.submitFailed',
)
}
export async function registerWithPassword(
payload: RegisterPayload,
): Promise<AuthSessionInput> {
const response = await api.post<AuthSessionDto, RegisterRequestDto>(
AUTH_ENDPOINTS.register,
{
json: {
captcha: payload.captcha,
device_id: getAuthDeviceId(),
invite_code: payload.inviteCode,
password: payload.password,
username: payload.mobile,
},
},
)
const session = await buildEnrichedAuthSession(
unwrapEnvelope(
response as ApiResponse<AuthSessionDto>,
'auth.register.errors.submitFailed',
),
)
logAuthSessionExpiry('register', session)
return session
}
export async function sendSmsCode(
payload: SendSmsCodePayload,
): Promise<SendSmsCodeResult> {
const response = await api.post<SendSmsCodeDto, SendSmsCodeRequestDto>(
AUTH_ENDPOINTS.sendSmsCode,
{
json: {
event: SMS_SEND_EVENT_REGISTER,
mobile: payload.mobile,
},
},
)
const data = unwrapEnvelope(
response as ApiResponse<SendSmsCodeDto>,
'auth.register.sms.errors.submitFailed',
)
return {
expiresIn: data.expires_in,
messageId: data.message_id,
}
}
export async function getCurrentUserProfile() {
const response = await api.get<AuthUserProfileDto>(AUTH_ENDPOINTS.profile)
return normalizeAuthUserProfile(
unwrapEnvelope(
response as ApiResponse<AuthUserProfileDto>,
'auth.errors.requestFailed',
),
)
}
export async function refreshAuthSession(
refreshToken: string,
): Promise<AuthSessionInput | null> {
const response = await api.post<RefreshTokenDto, RefreshTokenRequestDto>(
AUTH_ENDPOINTS.refreshToken,
{
context: {
[AUTH_SKIP_REFRESH_CONTEXT_KEY]: true,
},
json: {
refresh_token: refreshToken,
},
},
)
const session = normalizeRefreshAuthSession(
unwrapEnvelope(
response as ApiResponse<RefreshTokenDto>,
'auth.errors.requestFailed',
),
)
logAuthSessionExpiry('refresh', session)
return session
}