Files
xiaoxia-saas/apps/web/src/hooks/useAuth.ts
T
xiaoxia 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
refactor(#783): 统一API文件命名风格为kebab-case (#788)
2026-07-23 22:34:56 +08:00

143 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 认证相关 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,
})
}