Update project files
This commit is contained in:
95
src/context/AuthContext.jsx
Normal file
95
src/context/AuthContext.jsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import { createContext, useContext, useEffect, useState, useCallback } from 'react'
|
||||
import { authApi, getStoredSession, profileApi, saveSession } from '../api/client.js'
|
||||
|
||||
const AuthContext = createContext(null)
|
||||
const SITE_ADMIN_ROLES = new Set(['Site Admin', 'Admin'])
|
||||
|
||||
export function AuthProvider({ children }) {
|
||||
const [session, setSession] = useState(null)
|
||||
const [initializing, setInitializing] = useState(true)
|
||||
|
||||
const setCurrentUserProfile = useCallback((profile) => {
|
||||
setSession(current => {
|
||||
if (!current) return current
|
||||
const updated = { ...current, user: profile }
|
||||
saveSession(updated)
|
||||
return updated
|
||||
})
|
||||
}, [])
|
||||
|
||||
const refreshProfile = useCallback(async () => {
|
||||
const currentSession = await getStoredSession()
|
||||
if (!currentSession) return null
|
||||
const profile = await profileApi.getProfile()
|
||||
setCurrentUserProfile(profile)
|
||||
return profile
|
||||
}, [setCurrentUserProfile])
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
|
||||
async function syncStoredSession() {
|
||||
const existingSession = await getStoredSession()
|
||||
if (!existingSession) {
|
||||
if (!cancelled) setInitializing(false)
|
||||
return
|
||||
}
|
||||
if (!cancelled) setSession(existingSession)
|
||||
try {
|
||||
const profile = await profileApi.getProfile()
|
||||
if (!cancelled) {
|
||||
const updated = { ...existingSession, user: profile }
|
||||
await saveSession(updated)
|
||||
setSession(updated)
|
||||
}
|
||||
} catch {
|
||||
// session may be expired — client will clear it on next 401
|
||||
} finally {
|
||||
if (!cancelled) setInitializing(false)
|
||||
}
|
||||
}
|
||||
|
||||
syncStoredSession()
|
||||
return () => { cancelled = true }
|
||||
}, [])
|
||||
|
||||
const login = useCallback(async (payload) => {
|
||||
const data = await authApi.login(payload)
|
||||
const newSession = { accessToken: data.accessToken, refreshToken: data.refreshToken, user: data.user ?? null }
|
||||
setSession(newSession)
|
||||
return data
|
||||
}, [])
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
await authApi.logout()
|
||||
setSession(null)
|
||||
}, [])
|
||||
|
||||
const register = useCallback(async (payload) => {
|
||||
return authApi.register(payload)
|
||||
}, [])
|
||||
|
||||
const user = session?.user ?? null
|
||||
const userRoles = Array.isArray(user?.roles) ? user.roles : []
|
||||
|
||||
const value = {
|
||||
session,
|
||||
user,
|
||||
isAuthenticated: Boolean(session?.accessToken),
|
||||
isSiteAdmin: userRoles.some(role => SITE_ADMIN_ROLES.has(role)),
|
||||
initializing,
|
||||
login,
|
||||
register,
|
||||
logout,
|
||||
refreshProfile,
|
||||
setCurrentUserProfile,
|
||||
}
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const value = useContext(AuthContext)
|
||||
if (!value) throw new Error('useAuth must be used inside an AuthProvider.')
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user