Files
ai-safety-console-web/src/auth/AuthProvider.tsx
T
baozaotumao2025 1b90e552a5 first commit
2026-07-18 21:10:39 +08:00

64 lines
2.1 KiB
TypeScript

import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import type { LoginInput, User } from '../schemas/auth'
import { services } from '../services'
import { session } from './session'
import { activity } from './activity'
import { refreshAuth } from '../api'
type AuthContextValue = {
user: User | null
authenticated: boolean
loading: boolean
login: (input: LoginInput) => Promise<void>
logout: () => Promise<void>
}
const AuthContext = createContext<AuthContextValue | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const queryClient = useQueryClient()
const [token, setToken] = useState(() => session.get())
useEffect(() => session.subscribe(() => setToken(session.get())), [])
useEffect(() => token ? activity.start(() => { void refreshAuth().catch(() => undefined) }) : undefined, [token])
const me = useQuery({
queryKey: ['auth', 'me'],
queryFn: services.auth.me,
enabled: Boolean(token),
retry: false,
})
const value = useMemo<AuthContextValue>(
() => ({
user: me.data || null,
authenticated: Boolean(token && me.data),
loading: Boolean(token) && me.isPending,
login: async (input) => {
session.set(await services.auth.login(input))
activity.record(true)
await queryClient.invalidateQueries({ queryKey: ['auth', 'me'] })
},
logout: async () => {
try {
await services.auth.logout()
} finally {
session.clear()
activity.clear()
queryClient.clear()
}
},
}),
[me.data, me.isPending, queryClient, token],
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
// Provider and hook intentionally share the private context.
// eslint-disable-next-line react-refresh/only-export-components
export function useAuth() {
const value = useContext(AuthContext)
if (!value) throw new Error('useAuth must be used inside AuthProvider')
return value
}