9a1eb14fbb
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 8m29s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 10m0s
CI/CD Pipeline / Unit Tests (push) Successful in 10m37s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 12m2s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 12m32s
CI/CD Pipeline / Integration Tests (push) Successful in 4m32s
CI/CD Pipeline / Build Staging API Image (push) Successful in 17m42s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 21m21s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 46s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m31s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m8s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
188 lines
4.5 KiB
TypeScript
188 lines
4.5 KiB
TypeScript
/**
|
|
* 认证相关 API
|
|
*/
|
|
import axios from "axios"
|
|
import apiClient from "./client"
|
|
|
|
// 类型定义
|
|
export interface LoginRequest {
|
|
email: string
|
|
password: string
|
|
}
|
|
|
|
export interface LoginResponse {
|
|
access_token: string
|
|
refresh_token?: string | null
|
|
token_type: string
|
|
expires_in: number
|
|
user_id: string
|
|
email: string
|
|
username: string
|
|
display_name: string
|
|
}
|
|
|
|
export interface RegisterRequest {
|
|
email: string
|
|
password: string
|
|
username: string
|
|
display_name?: string
|
|
}
|
|
|
|
export interface User {
|
|
id: string
|
|
user_id: string
|
|
email: string
|
|
username: string
|
|
display_name: string
|
|
is_email_verified: boolean
|
|
email_verified: boolean
|
|
created_at?: string
|
|
}
|
|
|
|
export interface UserResponse {
|
|
id?: string
|
|
user_id?: string
|
|
email: string
|
|
username: string
|
|
display_name: string
|
|
is_email_verified?: boolean
|
|
email_verified?: boolean
|
|
created_at?: string
|
|
}
|
|
|
|
export const normalizeUser = (data: UserResponse): User => {
|
|
const userId = data.id ?? data.user_id ?? ""
|
|
const emailVerified = data.is_email_verified ?? data.email_verified ?? false
|
|
|
|
return {
|
|
id: userId,
|
|
user_id: userId,
|
|
email: data.email,
|
|
username: data.username,
|
|
display_name: data.display_name,
|
|
is_email_verified: emailVerified,
|
|
email_verified: emailVerified,
|
|
created_at: data.created_at,
|
|
}
|
|
}
|
|
|
|
// 登录
|
|
export const login = async (data: LoginRequest): Promise<LoginResponse> => {
|
|
const response = await apiClient.post("/auth/login", data)
|
|
return response.data
|
|
}
|
|
|
|
// 刷新 access_token(使用裸 axios 避免拦截器递归)
|
|
export const refreshAccessToken = async (refreshToken: string): Promise<LoginResponse> => {
|
|
const baseURL = apiClient.defaults.baseURL ?? ""
|
|
const response = await axios.post(`${baseURL}/auth/refresh`, {
|
|
refresh_token: refreshToken,
|
|
})
|
|
return response.data
|
|
}
|
|
|
|
// 注册
|
|
export const register = async (data: RegisterRequest): Promise<{ message: string }> => {
|
|
const response = await apiClient.post("/auth/register", data)
|
|
return response.data
|
|
}
|
|
|
|
// 登出
|
|
export const logout = async (): Promise<void> => {
|
|
await apiClient.post("/auth/logout")
|
|
}
|
|
|
|
// 获取当前用户
|
|
export const getCurrentUser = async (): Promise<User> => {
|
|
const response = await apiClient.get<UserResponse>("/auth/me")
|
|
return normalizeUser(response.data)
|
|
}
|
|
|
|
// 请求密码重置
|
|
export const requestPasswordReset = async (email: string): Promise<{ message: string }> => {
|
|
const response = await apiClient.post("/auth/forgot-password", { email })
|
|
return response.data
|
|
}
|
|
|
|
// 重置密码
|
|
export const resetPassword = async (
|
|
token: string,
|
|
newPassword: string,
|
|
): Promise<{ message: string }> => {
|
|
const response = await apiClient.post("/auth/reset-password", {
|
|
token,
|
|
new_password: newPassword,
|
|
})
|
|
return response.data
|
|
}
|
|
|
|
// 验证邮箱
|
|
export const verifyEmail = async (token: string): Promise<{ message: string }> => {
|
|
const response = await apiClient.post("/auth/verify-email", { token })
|
|
return response.data
|
|
}
|
|
|
|
/* ========== 微信登录 ========== */
|
|
|
|
export interface WechatAuthUrlResponse {
|
|
auth_url: string
|
|
state: string
|
|
}
|
|
|
|
export interface WechatCallbackResponse {
|
|
access_token: string
|
|
refresh_token?: string | null
|
|
user_id: string
|
|
display_name: string
|
|
avatar_url: string
|
|
is_new_user: boolean
|
|
binding_complete: boolean
|
|
expires_in: number
|
|
}
|
|
|
|
export interface SendVerificationCodeRequest {
|
|
target: "email" | "phone"
|
|
value: string
|
|
purpose: "bind" | "login" | "reset_password"
|
|
}
|
|
|
|
export interface BindContactRequest {
|
|
target: "email" | "phone"
|
|
value: string
|
|
code: string
|
|
}
|
|
|
|
export interface BindContactResponse {
|
|
message: string
|
|
user: User
|
|
}
|
|
|
|
// 获取微信授权链接
|
|
export const getWechatAuthUrl = async (): Promise<WechatAuthUrlResponse> => {
|
|
const response = await apiClient.get("/auth/wechat/url")
|
|
return response.data
|
|
}
|
|
|
|
// 微信回调登录
|
|
export const wechatCallback = async (
|
|
code: string,
|
|
state: string,
|
|
): Promise<WechatCallbackResponse> => {
|
|
const response = await apiClient.post("/auth/wechat/callback", { code, state })
|
|
return response.data
|
|
}
|
|
|
|
// 发送验证码
|
|
export const sendVerificationCode = async (
|
|
data: SendVerificationCodeRequest,
|
|
): Promise<{ message: string }> => {
|
|
const response = await apiClient.post("/auth/send-verification-code", data)
|
|
return response.data
|
|
}
|
|
|
|
// 绑定联系方式
|
|
export const bindContact = async (data: BindContactRequest): Promise<BindContactResponse> => {
|
|
const response = await apiClient.post("/auth/bind-contact", data)
|
|
return response.data
|
|
}
|