import { useEffect, useState } from 'react'
import { useQueryClient } from '@tanstack/react-query'
import { useAuthStore } from './store/auth'
import { useWsStore } from './store/ws'
import { connectWs, disconnectWs } from './api/websocket'
import { useStatus } from './api/queries'
import LoginPage from './components/layout/LoginPage'
import Nav from './components/layout/Nav'
import Dashboard from './tabs/dashboard/Dashboard'
import RequestLog from './tabs/requests/RequestLog'
import Settings from './tabs/settings/Settings'
import Backends from './tabs/backends/Backends'
import Keys from './tabs/keys/Keys'
import Models from './tabs/models/Models'
import Audit from './tabs/audit/Audit'
import TrafficView from './tabs/traffic/TrafficView'
import UptimeView from './tabs/uptime/UptimeView'
type Tab = 'dashboard' | 'requests' | 'settings' | 'backends' | 'keys' | 'models' | 'audit' | 'traffic' | 'uptime'
export default function App() {
const token = useAuthStore((s) => s.token)
const login = useAuthStore((s) => s.login)
const lastEvent = useWsStore((s) => s.lastEvent)
const qc = useQueryClient()
const [activeTab, setActiveTab] = useState<Tab>('dashboard')
const [bootstrapping, setBootstrapping] = useState(true)
const { data: status } = useStatus(!!token)
// On mount: if ?token= is in the URL and no token is stored, validate and log in.
useEffect(() => {
const params = new URLSearchParams(location.search)
const urlToken = params.get('token')
if (urlToken && !token) {
fetch('/admin/api/metrics', {
headers: { Authorization: `Bearer ${urlToken}` },
})
.then((res) => {
if (res.ok) {
login(urlToken)
history.replaceState(null, '', location.pathname)
}
})
.catch(() => {})
.finally(() => setBootstrapping(false))
} else {
setBootstrapping(false)
}
}, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentionally runs once on mount
useEffect(() => {
if (token) {
connectWs()
} else {
disconnectWs()
}
}, [token])
// Default to the Settings tab on first load when nothing is configured.
useEffect(() => {
if (token && status && !status.configured) {
setActiveTab('settings')
}
}, [status?.configured, token]) // eslint-disable-line react-hooks/exhaustive-deps -- run when status first arrives
// Invalidate query cache on relevant WS events.
useEffect(() => {
if (!lastEvent) return
if (lastEvent.type === 'metrics_snapshot') {
qc.setQueryData(['metrics'], lastEvent.data)
} else if (lastEvent.type === 'backend_health_changed') {
qc.invalidateQueries({ queryKey: ['uptime'] })
}
}, [lastEvent, qc])
if (bootstrapping) return null
if (!token) return <LoginPage />
return (
<div>
<Nav activeTab={activeTab} onTabChange={setActiveTab} />
<div className="tab-content">
{activeTab === 'dashboard' && <Dashboard />}
{activeTab === 'requests' && <RequestLog />}
{activeTab === 'settings' && <Settings configured={status?.configured ?? true} />}
{activeTab === 'backends' && <Backends />}
{activeTab === 'keys' && <Keys />}
{activeTab === 'models' && <Models />}
{activeTab === 'audit' && <Audit />}
{activeTab === 'traffic' && <TrafficView />}
{activeTab === 'uptime' && <UptimeView />}
</div>
</div>
)
}