48 lines
1.3 KiB
TypeScript
48 lines
1.3 KiB
TypeScript
import { useEffect, useState, createContext, useContext } from 'react'
|
|
import type { User } from '../types'
|
|
|
|
const SHARED_TIMEOUT_MS = 10 * 60 * 1000
|
|
|
|
function isSharedDevice() {
|
|
return document.cookie.split(';').some(c => c.trim() === 'hnf_shared_device=1')
|
|
}
|
|
|
|
|
|
interface AuthCtx { user: User }
|
|
const Ctx = createContext<AuthCtx | null>(null)
|
|
|
|
export function useAuth() {
|
|
const ctx = useContext(Ctx)
|
|
if (!ctx) throw new Error('useAuth must be used inside AuthGate')
|
|
return ctx
|
|
}
|
|
|
|
export default function AuthGate({ children }: { children: React.ReactNode }) {
|
|
const [user, setUser] = useState<User | null>(null)
|
|
|
|
useEffect(() => {
|
|
fetch('/reports/api/auth/verify?app=reports', { credentials: 'include' })
|
|
.then(r => {
|
|
if (!r.ok) {
|
|
;(window.top ?? window).location.href = '/login'
|
|
return null
|
|
}
|
|
return r.json()
|
|
})
|
|
.then(data => { if (data) setUser(data) })
|
|
.catch(() => { ;(window.top ?? window).location.href = '/login' })
|
|
}, [])
|
|
|
|
if (!user) {
|
|
return (
|
|
<div style={{
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
height: '100vh', fontFamily: 'sans-serif', color: '#6b7280',
|
|
}}>
|
|
Loading…
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return <Ctx.Provider value={{ user }}>{children}</Ctx.Provider>
|
|
}
|