import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react' import { api, ApiError, setCsrf } from '@shared/api/client' import { isRemote, setToken } from '@shared/api/source' import type { LoginResponse, MeDto } from '@shared/api/types' interface Auth { me: MeDto | null ready: boolean login: (username: string, password: string) => Promise /** 连别的后端时用 API 令牌登录(不用 Cookie)。 */ loginWithToken: (token: string) => Promise logout: () => Promise } const AuthContext = createContext(null) export function AuthProvider({ children }: { children: ReactNode }) { const [me, setMe] = useState(null) const [ready, setReady] = useState(false) useEffect(() => { api .get('/api/admin/auth/me') .then((m) => { setCsrf(m.csrf_token ?? null) setMe(m) }) .catch((e: unknown) => { if (!(e instanceof ApiError) || (e.status !== 401 && e.status !== 403)) console.error(e) }) .finally(() => setReady(true)) }, []) const login = useCallback(async (username: string, password: string) => { const r = await api.post('/api/admin/auth/login', { username, password }) setCsrf(r.csrf_token) setMe({ kind: 'user', scope: 'admin', user_id: r.user_id, username: r.username, csrf_token: r.csrf_token }) }, []) const loginWithToken = useCallback(async (token: string) => { setToken(token) try { const m = await api.get('/api/admin/auth/me') setCsrf(m.csrf_token ?? null) setMe(m) } catch (e) { setToken('') throw e } }, []) const logout = useCallback(async () => { try { if (isRemote()) setToken('') else await api.post('/api/admin/auth/logout') } finally { setCsrf(null) setMe(null) } }, []) return {children} } export function useAuth(): Auth { const value = useContext(AuthContext) if (!value) throw new Error('AuthProvider is missing') return value }