Files
affiliate_dash/frontend/src/store/auth.tsx
T
2026-07-20 14:58:57 +08:00

77 lines
1.9 KiB
TypeScript

import {
createContext,
useCallback,
useContext,
useMemo,
useState,
type ReactNode,
} from 'react'
import type { User } from '../types'
import { authApi } from '../api'
interface AuthState {
token: string | null
user: User | null
login: (username: string, password: string) => Promise<void>
logout: () => void
refreshProfile: () => Promise<void>
isAdmin: boolean
}
const AuthContext = createContext<AuthState | null>(null)
function loadUser(): User | null {
try {
const raw = localStorage.getItem('user')
return raw ? (JSON.parse(raw) as User) : null
} catch {
return null
}
}
export function AuthProvider({ children }: { children: ReactNode }) {
const [token, setToken] = useState<string | null>(() => localStorage.getItem('token'))
const [user, setUser] = useState<User | null>(() => loadUser())
const login = useCallback(async (username: string, password: string) => {
const result = await authApi.login(username, password)
localStorage.setItem('token', result.token)
localStorage.setItem('user', JSON.stringify(result.user))
setToken(result.token)
setUser(result.user)
}, [])
const logout = useCallback(() => {
localStorage.removeItem('token')
localStorage.removeItem('user')
setToken(null)
setUser(null)
}, [])
const refreshProfile = useCallback(async () => {
const profile = await authApi.profile()
localStorage.setItem('user', JSON.stringify(profile))
setUser(profile)
}, [])
const value = useMemo(
() => ({
token,
user,
login,
logout,
refreshProfile,
isAdmin: user?.role === 'admin',
}),
[token, user, login, logout, refreshProfile],
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function useAuth() {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth must be used within AuthProvider')
return ctx
}