import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AdminSetupRequest, LoginRequest, LoginResult, PasswordChangeRequest, PasswordChangeResult, SessionInfo, TotpAction, TotpActionResult, TotpStatus, } from "@umt/contracts"; import { ApiRequestError, apiData, apiFetch } from "~/lib/api.ts"; import { qk } from "~/lib/query-keys.ts"; /** 登录态探测(免鉴权,不触发 401 跳转) */ export function useSession() { return useQuery({ queryKey: qk.session, queryFn: ({ signal }) => apiData("/auth/session", { signal, noAuthRedirect: true }), staleTime: 60_000, retry: 1, }); } /** 登录/初始化类请求:返回错误码(由调用方按码分流文案),不跳转 */ async function authPost(path: string, body: unknown): Promise { const r = await apiFetch(path, { method: "POST", body, noAuthRedirect: true }); if (!r.ok) throw new ApiRequestError(r.error.code, r.error.message); return r.data; } export const login = (body: LoginRequest) => authPost("/auth/login", body); export const setupAdmin = (body: AdminSetupRequest) => authPost("/auth/setup", body); export function useLogout() { const qc = useQueryClient(); return useMutation({ mutationFn: () => authPost("/auth/logout", {}), onSettled: () => { qc.clear(); }, }); } export function useChangePassword() { return useMutation({ mutationFn: (body: PasswordChangeRequest) => // 旧密码错误也是 401:留在页面显示字段错误,不当成会话失效跳登录 apiData("/auth/password", { method: "POST", body, noAuthRedirect: true }), }); } export function useTotpStatus(enabled = true) { return useQuery({ queryKey: qk.sub("auth", "totp"), queryFn: ({ signal }) => apiData("/auth/totp", { signal }), enabled, }); } export function useTotpAction() { const qc = useQueryClient(); return useMutation({ mutationFn: (body: TotpAction) => apiData("/auth/totp", { method: "POST", body, noAuthRedirect: true }), onSuccess: () => { qc.invalidateQueries({ queryKey: qk.all("auth") }); }, }); }