Files
brainwave/frontend/src/contexts/AuthContext.tsx
T

95 lines
2.8 KiB
TypeScript

import { createContext, useContext, useState, useEffect, useCallback } from 'react'
import type { ReactNode } from 'react'
import { apiFetch, setToken, clearToken, isAuthenticated } from '../utils/api'
export interface User {
id: number
email: string
displayName: string
role: 'user' | 'admin'
}
interface AuthContextValue {
user: User | null
loading: boolean
needsSetup: boolean
setUser: (user: User | null) => void
login: (email: string, password: string) => Promise<void>
register: (email: string, password: string, displayName: string) => Promise<string>
logout: () => void
refreshUser: () => Promise<void>
}
const AuthContext = createContext<AuthContextValue | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
const [needsSetup, setNeedsSetup] = useState(false)
const refreshUser = useCallback(async () => {
try {
// Always check setup status first
const setupStatus = await apiFetch<{ needsSetup: boolean }>('/api/setup/status')
if (setupStatus.needsSetup) {
setNeedsSetup(true)
setUser(null)
setLoading(false)
return
}
setNeedsSetup(false)
if (!isAuthenticated()) {
setUser(null)
setLoading(false)
return
}
const data = await apiFetch<{ id: number; email: string; displayName: string; role: 'user' | 'admin' }>('/api/auth/me')
setUser({ id: data.id, email: data.email, displayName: data.displayName, role: data.role ?? 'user' })
} catch {
clearToken()
setUser(null)
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
void refreshUser()
}, [refreshUser])
const login = useCallback(async (email: string, password: string) => {
const data = await apiFetch<{ token: string; user: User }>('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
})
setToken(data.token)
setUser({ ...data.user, role: data.user.role ?? 'user' })
}, [])
const register = useCallback(async (email: string, password: string, displayName: string): Promise<string> => {
const data = await apiFetch<{ message: string }>('/api/auth/register', {
method: 'POST',
body: JSON.stringify({ email, password, displayName }),
})
return data.message
}, [])
const logout = useCallback(() => {
clearToken()
setUser(null)
}, [])
return (
<AuthContext.Provider value={{ user, loading, needsSetup, setUser, login, register, logout, refreshUser }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth must be used within an AuthProvider')
return ctx
}