3862996045
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Waiting to run
CI/CD Pipeline / PR Build Web Image (push) Waiting to run
CI/CD Pipeline / PR Build Worker Image (push) Waiting to run
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Waiting to run
CI/CD Pipeline / Build Production Web Image (push) Waiting to run
CI/CD Pipeline / Build Production Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
143 lines
4.1 KiB
TypeScript
143 lines
4.1 KiB
TypeScript
/**
|
||
* 认证相关 Hooks
|
||
*/
|
||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||
import { useNavigate } from "react-router-dom"
|
||
import * as authApi from "@/api/auth"
|
||
import { useAuthStore } from "@/store/authStore"
|
||
|
||
// 登录 Hook
|
||
export const useLogin = () => {
|
||
const navigate = useNavigate()
|
||
const setAuth = useAuthStore((state) => state.setAuth)
|
||
|
||
const mutation = useMutation({
|
||
mutationFn: authApi.login,
|
||
})
|
||
|
||
// 手动处理成功后的逻辑
|
||
const login = async (credentials: Parameters<typeof authApi.login>[0]) => {
|
||
const data = await mutation.mutateAsync(credentials)
|
||
const refreshToken = data.refresh_token ?? null
|
||
|
||
// 先存 token 到 localStorage,确保后续请求拦截器能取到
|
||
// (apiClient 拦截器从 localStorage 读 access_token)
|
||
localStorage.setItem("access_token", data.access_token)
|
||
if (refreshToken) {
|
||
localStorage.setItem("refresh_token", refreshToken)
|
||
}
|
||
|
||
// 再获取用户信息(这时候请求带 Authorization header)
|
||
const user = await authApi.getCurrentUser()
|
||
setAuth(user, data.access_token, refreshToken)
|
||
|
||
// 跳转到登录前页面或首页
|
||
const redirect = localStorage.getItem("login_redirect") || "/"
|
||
localStorage.removeItem("login_redirect")
|
||
navigate(redirect, { replace: true })
|
||
return data
|
||
}
|
||
|
||
return { ...mutation, mutateAsync: login }
|
||
}
|
||
|
||
// 微信登录 Hook(用于回调后处理登录状态)
|
||
export const useWechatCallback = () => {
|
||
const navigate = useNavigate()
|
||
const setAuth = useAuthStore((state) => state.setAuth)
|
||
const setUser = useAuthStore((state) => state.setUser)
|
||
|
||
const mutation = useMutation({
|
||
mutationFn: ({ code, state }: { code: string; state: string }) =>
|
||
authApi.wechatCallback(code, state),
|
||
})
|
||
|
||
const handleCallback = async (code: string, state: string) => {
|
||
// 校验 state
|
||
const savedState = localStorage.getItem("wechat_state")
|
||
if (!savedState || savedState !== state) {
|
||
throw new Error("安全校验失败")
|
||
}
|
||
localStorage.removeItem("wechat_state")
|
||
|
||
const result = await mutation.mutateAsync({ code, state })
|
||
|
||
// 先存 token 到 localStorage,确保后续请求拦截器能取到
|
||
// (apiClient 拦截器从 localStorage 读 access_token)
|
||
localStorage.setItem("access_token", result.access_token)
|
||
if (result.refresh_token) {
|
||
localStorage.setItem("refresh_token", result.refresh_token)
|
||
}
|
||
|
||
// 再获取用户信息(这时候请求带 Authorization header)
|
||
const user = await authApi.getCurrentUser()
|
||
setAuth(user, result.access_token, result.refresh_token)
|
||
|
||
return { ...result, user }
|
||
}
|
||
|
||
// 绑定成功后跳转
|
||
const finishLogin = () => {
|
||
const redirect = localStorage.getItem("login_redirect") || "/"
|
||
localStorage.removeItem("login_redirect")
|
||
navigate(redirect, { replace: true })
|
||
}
|
||
|
||
return { ...mutation, handleCallback, finishLogin, setUser }
|
||
}
|
||
|
||
// 注册 Hook
|
||
export const useRegister = () => {
|
||
const navigate = useNavigate()
|
||
|
||
const mutation = useMutation({
|
||
mutationFn: authApi.register,
|
||
})
|
||
|
||
const register = async (data: Parameters<typeof authApi.register>[0]) => {
|
||
const result = await mutation.mutateAsync(data)
|
||
navigate("/login", {
|
||
state: { message: "注册成功!请查收验证邮件。" },
|
||
})
|
||
return result
|
||
}
|
||
|
||
return { ...mutation, mutateAsync: register }
|
||
}
|
||
|
||
// 登出 Hook
|
||
export const useLogout = () => {
|
||
const navigate = useNavigate()
|
||
const clearAuth = useAuthStore((state) => state.clearAuth)
|
||
const queryClient = useQueryClient()
|
||
|
||
const mutation = useMutation({
|
||
mutationFn: authApi.logout,
|
||
})
|
||
|
||
const logout = async () => {
|
||
try {
|
||
await mutation.mutateAsync()
|
||
} catch (error) {
|
||
// 即使登出失败也清除本地状态
|
||
} finally {
|
||
clearAuth()
|
||
queryClient.clear()
|
||
navigate("/")
|
||
}
|
||
}
|
||
|
||
return { ...mutation, mutateAsync: logout }
|
||
}
|
||
|
||
// 获取当前用户 Hook
|
||
export const useCurrentUser = () => {
|
||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||
|
||
return useQuery({
|
||
queryKey: ["currentUser"],
|
||
queryFn: authApi.getCurrentUser,
|
||
enabled: isAuthenticated,
|
||
})
|
||
}
|