128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
import axios from 'axios'
|
||
|
||
import {
|
||
ApiResponseError,
|
||
unwrapApiResponse,
|
||
unwrapNullableApiResponse,
|
||
} from '@/utils/api-response'
|
||
import { request } from '@/utils/request'
|
||
import type {
|
||
AuthenticatedUser,
|
||
CaptchaResponse,
|
||
CurrentAuthorization,
|
||
CurrentUserResponse,
|
||
LoginRequest,
|
||
LoginResponse,
|
||
PasswordChangeRequest,
|
||
ProfileUpdateRequest,
|
||
} from '@/types/auth'
|
||
import type { ApiResponse } from '@/types/common'
|
||
|
||
export { ApiResponseError } from '@/utils/api-response'
|
||
|
||
export async function getCaptcha(): Promise<CaptchaResponse> {
|
||
const response = await request.get<ApiResponse<CaptchaResponse>>('/v1/auth/captcha')
|
||
return unwrapApiResponse(response)
|
||
}
|
||
|
||
function normalizePermissions(value: unknown): string[] {
|
||
if (Array.isArray(value)) {
|
||
return value.filter((item): item is string => typeof item === 'string')
|
||
}
|
||
|
||
if (typeof value === 'string') {
|
||
try {
|
||
const parsed = JSON.parse(value) as unknown
|
||
return Array.isArray(parsed)
|
||
? parsed.filter((item): item is string => typeof item === 'string')
|
||
: []
|
||
} catch {
|
||
return []
|
||
}
|
||
}
|
||
|
||
return []
|
||
}
|
||
|
||
function normalizeAuthenticatedUser(user: AuthenticatedUser): AuthenticatedUser {
|
||
return {
|
||
...user,
|
||
permissions: normalizePermissions(user.permissions),
|
||
}
|
||
}
|
||
|
||
export async function login(payload: LoginRequest): Promise<LoginResponse> {
|
||
const response = await request.post<ApiResponse<LoginResponse>>('/v1/auth/login', payload)
|
||
const session = unwrapApiResponse(response)
|
||
|
||
if (!session.token || !session.user) {
|
||
throw new Error('登录接口响应缺少 token 或用户信息')
|
||
}
|
||
|
||
const normalizedSession: LoginResponse = {
|
||
...session,
|
||
expiresAt: session.expiresAt || '',
|
||
user: normalizeAuthenticatedUser(session.user),
|
||
}
|
||
|
||
return normalizedSession
|
||
}
|
||
|
||
export async function getCurrentUser(): Promise<CurrentUserResponse> {
|
||
const response = await request.get<ApiResponse<CurrentUserResponse>>('/v1/auth/me')
|
||
const user = unwrapApiResponse(response)
|
||
|
||
return {
|
||
...user,
|
||
permissions: normalizePermissions(user.permissions),
|
||
}
|
||
}
|
||
|
||
export async function getCurrentAuthorization(): Promise<CurrentAuthorization> {
|
||
const response = await request.get<ApiResponse<CurrentAuthorization>>('/v1/authorization/me')
|
||
return unwrapApiResponse(response)
|
||
}
|
||
|
||
export async function logout(): Promise<void> {
|
||
const response = await request.post<ApiResponse<Record<string, never>>>('/v1/auth/logout')
|
||
unwrapApiResponse(response)
|
||
}
|
||
|
||
export async function changePassword(payload: PasswordChangeRequest): Promise<void> {
|
||
const response = await request.post<ApiResponse<null>>('/v1/auth/password/change', payload)
|
||
unwrapNullableApiResponse(response)
|
||
}
|
||
|
||
export function getAuthErrorMessage(error: unknown) {
|
||
if (error instanceof ApiResponseError) {
|
||
return error.message
|
||
}
|
||
|
||
if (axios.isAxiosError(error)) {
|
||
if (!error.response) {
|
||
return '无法连接登录服务,请检查网络或接口地址'
|
||
}
|
||
|
||
if (error.response.status === 401) {
|
||
return '账号或密码错误'
|
||
}
|
||
|
||
return `登录失败(HTTP ${error.response.status})`
|
||
}
|
||
|
||
if (error instanceof Error && error.message) {
|
||
return error.message
|
||
}
|
||
|
||
return '登录失败,请稍后重试'
|
||
}
|
||
|
||
export async function updateProfile(payload: ProfileUpdateRequest): Promise<CurrentUserResponse> {
|
||
const response = await request.request<ApiResponse<CurrentUserResponse>>({
|
||
url: '/v1/auth/me',
|
||
method: 'PATCH',
|
||
data: payload,
|
||
})
|
||
return unwrapApiResponse(response)
|
||
}
|