77 lines
1.9 KiB
TypeScript
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
|
|
}
|