Add 'frontend/' from commit '63381d9f2a4829e09d9ba6f29608bbaa2eb38601'

git-subtree-dir: frontend
git-subtree-mainline: 79bc04aaa7
git-subtree-split: 63381d9f2a
This commit is contained in:
2026-07-31 23:53:50 +05:00
39 changed files with 4968 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react'
import {
api,
ApiError,
clearApiKey,
getApiKey,
setApiKey,
setUnauthorizedHandler,
} from '@/lib/api'
interface AuthState {
authenticated: boolean
login: (key: string) => Promise<void>
logout: () => void
}
const AuthContext = createContext<AuthState | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [authenticated, setAuthenticated] = useState(() => !!getApiKey())
const logout = useCallback(() => {
clearApiKey()
setAuthenticated(false)
}, [])
// Any 401 from the API layer forces a logout so the login screen reappears.
useEffect(() => {
setUnauthorizedHandler(logout)
}, [logout])
const login = useCallback(async (key: string) => {
setApiKey(key)
try {
await api.ping() // validates the key; throws ApiError(401) if wrong
setAuthenticated(true)
} catch (err) {
clearApiKey()
setAuthenticated(false)
if (err instanceof ApiError && err.status === 401) {
throw new Error('That API key was rejected.')
}
throw err instanceof Error ? err : new Error('Could not reach the API.')
}
}, [])
return <AuthContext value={{ authenticated, login, logout }}>{children}</AuthContext>
}
export function useAuth(): AuthState {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth must be used within AuthProvider')
return ctx
}