/** * Browser entry point and intentionally small client-side router. * * `/obs/:publicId` is selected before the control application is initialized. * That early split is a security and reliability boundary: OBS sources never * register the control-console PWA or execute authenticated dashboard requests. * All other routes share the session bootstrap and passwordless auth flow. */ import { useCallback, useEffect, useState } from 'react' import { createRoot } from 'react-dom/client' import { ApiError, api, errorMessage, json, normalizeSession } from './api' import { EnrollmentPage, LoginPage } from './auth' import { ComponentsPage, ForbiddenPage, InvitationsPage } from './control' import { Overlay, tokenFromFragment } from './overlay' import { PwaControls, authRoute, cleanupLegacyPwa, initializePwa } from './pwa' import type { Session } from './types' import './style.css' import './control.css' function Redirect({ to }: { to: string }) { useEffect(() => { location.replace(to) }, [to]) return
正在前往云台…
} function NotFoundPage() { return (

404 · LOST IN THE CLOUDS

这里没有组件

地址可能已经失效,或者组件已被所属用户删除。

返回控制台
) } function App() { const [session, setSession] = useState() const [loadError, setLoadError] = useState('') const [online, setOnline] = useState(navigator.onLine) const path = location.pathname.replace(/\/+$/, '') || '/' const refreshSession = useCallback(async () => { setLoadError('') try { const payload = await api('/api/v1/auth/me') setSession(normalizeSession(payload)) } catch (error) { if (error instanceof ApiError && error.status === 401) { setSession({ user: null, setupRequired: false }) return } setLoadError(errorMessage(error, '无法连接认证服务')) } }, []) useEffect(() => { void refreshSession() }, [refreshSession]) useEffect(() => { const wentOnline = () => { setOnline(true) if (loadError) void refreshSession() } const wentOffline = () => setOnline(false) window.addEventListener('online', wentOnline) window.addEventListener('offline', wentOffline) return () => { window.removeEventListener('online', wentOnline) window.removeEventListener('offline', wentOffline) } }, [loadError, refreshSession]) useEffect(() => { const expired = () => { setSession({ user: null, setupRequired: false }) if (location.pathname.startsWith('/control')) location.assign('/control/login') } window.addEventListener('lxc:session-expired', expired) return () => window.removeEventListener('lxc:session-expired', expired) }, []) if (loadError) { const offline = !online return (

{offline ? 'OFFLINE SHELL' : 'CONNECTION ERROR'}

{offline ? '控制台目前处于离线状态' : '云台暂时无法连接'}

{offline && (

应用外壳已离线打开,但账户、直播源和组件数据不会缓存。联网后即可重新验证会话。

)}
{loadError}
) } if (!session) return
正在验证安全会话…
const logout = async () => { try { await api('/api/v1/auth/logout', json('POST')) } finally { setSession({ user: null, setupRequired: false }) location.assign(authRoute('login')) } } if (path === '/') return if (path === '/login' || path === '/control/login') { if (session.user) return return } if (path === '/setup' || path === '/control/setup') { if (session.user) return if (!session.setupRequired) return return } if (path === '/register' || path === '/control/register') { if (session.user) return return } if (path === '/control') { if (location.pathname === '/control') return if (!session.user) return return } if (path === '/control/invitations') { if (!session.user) return if (session.user.role !== 'system_admin') return return } return } const obsMatch = location.pathname.match(/^\/obs\/([^/]+)\/?$/) const root = createRoot(document.getElementById('root')!) if (obsMatch) { // A short-lived migration only: remove the root-scoped worker from early // development builds so it cannot keep controlling an OBS browser source. cleanupLegacyPwa() let publicId = '' try { publicId = decodeURIComponent(obsMatch[1]) } catch { publicId = '' } root.render() } else { initializePwa() root.render() }