import { useState, useEffect, useCallback } from 'react' export type Profile = { id: number email: string | null display_name: string | null is_guest: boolean } type AuthState = { profile: Profile | null; token: string | null; loading: boolean } export function useAuth() { const [state, setState] = useState({ profile: null, token: null, loading: true }) const validate = useCallback(async (token: string) => { const r = await fetch('/api/auth/me', { headers: { Authorization: `Bearer ${token}` }, }) if (!r.ok) throw new Error('invalid session') const profile: Profile = await r.json() return profile }, []) useEffect(() => { // Check URL for ?session= from magic link redirect const params = new URLSearchParams(window.location.search) const urlToken = params.get('session') if (urlToken) { localStorage.setItem('session', urlToken) window.history.replaceState({}, '', window.location.pathname) } const token = urlToken || localStorage.getItem('session') if (!token) { setState({ profile: null, token: null, loading: false }) return } validate(token) .then(profile => setState({ profile, token, loading: false })) .catch(() => { localStorage.removeItem('session') setState({ profile: null, token: null, loading: false }) }) }, [validate]) const logout = useCallback(async () => { const token = localStorage.getItem('session') if (token) { await fetch('/api/auth/session', { method: 'DELETE', headers: { Authorization: `Bearer ${token}` }, }).catch(() => {}) localStorage.removeItem('session') } setState({ profile: null, token: null, loading: false }) }, []) // Used by guest login to inject a session without a round-trip const setAuth = useCallback((token: string, profile: Profile) => { localStorage.setItem('session', token) setState({ profile, token, loading: false }) }, []) return { ...state, logout, setAuth } }