Wire up the three uo-link sidecar surfaces that weren't integrated yet. Champion spawns - Ingest champ.update/champ.remove into a new shard_champs table (served from our own store, like online/houses); public /site/champs board with a nav link, live via the existing SSE feed (champ.* added to the public allowlist). Staff write plane (admin + moderator) - kick / ban / unban / broadcast via /admin/shard/*; actor is stamped server-side from the session, never the browser. Sidecar status codes mapped (403 disabled/ protected, 404 unknown, 503/504 transient). admin.audit events are logged and surfaced at /admin/shard/audit. - New admin "In-Game Ops" view (/admin/shard-ops), plus per-account Kick/Ban/Unban on the user-detail and character views (ShardAccountActions, self-gated to staff). Help-page (support) queue - Ingest page.new/updated/closed into a new shard_pages table; respond/close via /admin/shard/pages/*. Champ board and page queue are snapshotted from the sidecar's /champs and /pages on every WS (re)connect (guarded so a failed call never wipes local state). Verified live end-to-end against MariaDB + the Rust sidecar + ServUO; unit tests cover ingest routing (shardIngest.champsPages.test.js). Swagger regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
165 lines
7.4 KiB
JavaScript
165 lines
7.4 KiB
JavaScript
import { Routes, Route, Navigate, Outlet } from 'react-router-dom'
|
|
import { AuthProvider } from './contexts/AuthContext.jsx'
|
|
import { SiteProvider } from './contexts/SiteContext.jsx'
|
|
import MaintenanceGate from './components/MaintenanceGate.jsx'
|
|
import RequireAuth from './components/RequireAuth.jsx'
|
|
import RequirePlayer from './components/RequirePlayer.jsx'
|
|
import RoleGate from './components/RoleGate.jsx'
|
|
|
|
// Public
|
|
import Portal from './routes/public/Portal.jsx'
|
|
import Website from './routes/public/Website.jsx'
|
|
import News from './routes/public/News.jsx'
|
|
import Screenshots from './routes/public/Screenshots.jsx'
|
|
import FiveOnFriday from './routes/public/FiveOnFriday.jsx'
|
|
import Newsletter from './routes/public/Newsletter.jsx'
|
|
import NewsletterIssue from './routes/public/NewsletterIssue.jsx'
|
|
import About from './routes/public/About.jsx'
|
|
import Status from './routes/public/Status.jsx'
|
|
import Shard from './routes/public/Shard.jsx'
|
|
import ShardActivity from './routes/public/ShardActivity.jsx'
|
|
import ChampSpawns from './routes/public/ChampSpawns.jsx'
|
|
import Wiki from './routes/wiki/Wiki.jsx'
|
|
import WikiArticle from './routes/wiki/WikiArticle.jsx'
|
|
import CmsPage from './routes/public/CmsPage.jsx'
|
|
|
|
// Admin
|
|
import AdminLogin from './routes/admin/AdminLogin.jsx'
|
|
import AdminLayout from './routes/admin/AdminLayout.jsx'
|
|
import Dashboard from './routes/admin/views/Dashboard.jsx'
|
|
import PostsAdmin from './routes/admin/views/PostsAdmin.jsx'
|
|
import PagesAdmin from './routes/admin/views/PagesAdmin.jsx'
|
|
import PageBuilder from './routes/admin/views/PageBuilder.jsx'
|
|
import WikiAdmin from './routes/admin/views/WikiAdmin.jsx'
|
|
import HeroEditor from './routes/admin/views/HeroEditor.jsx'
|
|
import SettingsAdmin from './routes/admin/views/SettingsAdmin.jsx'
|
|
import ActivityAdmin from './routes/admin/views/ActivityAdmin.jsx'
|
|
import BotActivityAdmin from './routes/admin/views/BotActivityAdmin.jsx'
|
|
import DiscordBotAdmin from './routes/admin/views/DiscordBotAdmin.jsx'
|
|
import ShardAdmin from './routes/admin/views/ShardAdmin.jsx'
|
|
import ShardOps from './routes/admin/views/ShardOps.jsx'
|
|
import AdminCharacters from './routes/admin/views/AdminCharacters.jsx'
|
|
import AdminCharacter from './routes/admin/views/AdminCharacter.jsx'
|
|
import AuthProvidersAdmin from './routes/admin/views/AuthProvidersAdmin.jsx'
|
|
import UsersAdmin from './routes/admin/views/UsersAdmin.jsx'
|
|
import UserDetail from './routes/admin/views/UserDetail.jsx'
|
|
import AccountAdmin from './routes/admin/views/AccountAdmin.jsx'
|
|
import Moderation from './routes/admin/views/Moderation.jsx'
|
|
import ModerationUser from './routes/admin/views/ModerationUser.jsx'
|
|
|
|
// Player portal
|
|
import PlayerLogin from './routes/player/PlayerLogin.jsx'
|
|
import PlayerRegister from './routes/player/PlayerRegister.jsx'
|
|
import PlayerPortalLayout from './routes/player/PlayerPortalLayout.jsx'
|
|
import PlayerCharacters from './routes/player/PlayerCharacters.jsx'
|
|
import PlayerCharacter from './routes/player/PlayerCharacter.jsx'
|
|
import PlayerAccount from './routes/player/PlayerAccount.jsx'
|
|
|
|
export default function App() {
|
|
return (
|
|
<AuthProvider>
|
|
<SiteProvider>
|
|
<Routes>
|
|
{/* Public site — gated by maintenance mode (admins preview through it) */}
|
|
<Route
|
|
element={
|
|
<MaintenanceGate>
|
|
<Outlet />
|
|
</MaintenanceGate>
|
|
}
|
|
>
|
|
<Route path="/" element={<Portal />} />
|
|
<Route path="/site" element={<Website />} />
|
|
<Route path="/site/news" element={<News />} />
|
|
<Route path="/site/screenshots" element={<Screenshots />} />
|
|
<Route path="/site/five-on-friday" element={<FiveOnFriday />} />
|
|
<Route path="/site/newsletter" element={<Newsletter />} />
|
|
<Route path="/site/newsletter/:id" element={<NewsletterIssue />} />
|
|
<Route path="/site/about" element={<About />} />
|
|
<Route path="/site/status" element={<Status />} />
|
|
<Route path="/site/shard" element={<Shard />} />
|
|
<Route path="/site/shard/activity" element={<ShardActivity />} />
|
|
<Route path="/site/champs" element={<ChampSpawns />} />
|
|
<Route path="/wiki" element={<Wiki />} />
|
|
<Route path="/wiki/:slug" element={<WikiArticle />} />
|
|
{/* CMS pages: top-level /:slug, matched only after the named routes
|
|
above (React Router ranks static routes over this dynamic one). */}
|
|
<Route path="/:slug" element={<CmsPage />} />
|
|
</Route>
|
|
|
|
{/* Draft-preview link (token-gated). Outside the maintenance gate so a
|
|
preview link works regardless of site mode. */}
|
|
<Route path="/preview/:id/:token" element={<CmsPage preview />} />
|
|
|
|
{/* Admin */}
|
|
<Route path="/admin/login" element={<AdminLogin />} />
|
|
<Route
|
|
path="/admin"
|
|
element={
|
|
<RequireAuth>
|
|
<AdminLayout />
|
|
</RequireAuth>
|
|
}
|
|
>
|
|
<Route index element={<Dashboard />} />
|
|
<Route path="posts" element={<PostsAdmin />} />
|
|
<Route path="pages" element={<PagesAdmin />} />
|
|
<Route path="pages/new" element={<PageBuilder />} />
|
|
<Route path="pages/:id" element={<PageBuilder />} />
|
|
<Route path="wiki" element={<WikiAdmin />} />
|
|
<Route path="hero" element={<HeroEditor />} />
|
|
<Route path="settings" element={<SettingsAdmin />} />
|
|
<Route
|
|
path="moderation"
|
|
element={
|
|
<RoleGate roles={['admin', 'moderator']}>
|
|
<Outlet />
|
|
</RoleGate>
|
|
}
|
|
>
|
|
<Route index element={<Moderation />} />
|
|
<Route path="user/:discordId" element={<ModerationUser />} />
|
|
</Route>
|
|
<Route path="activity" element={<ActivityAdmin />} />
|
|
<Route path="bot-activity" element={<BotActivityAdmin />} />
|
|
<Route path="discord-bot" element={<DiscordBotAdmin />} />
|
|
<Route path="shard" element={<ShardAdmin />} />
|
|
<Route
|
|
path="shard-ops"
|
|
element={
|
|
<RoleGate roles={['admin', 'moderator']}>
|
|
<ShardOps />
|
|
</RoleGate>
|
|
}
|
|
/>
|
|
<Route path="characters" element={<AdminCharacters />} />
|
|
<Route path="characters/:serial" element={<AdminCharacter />} />
|
|
<Route path="auth-providers" element={<AuthProvidersAdmin />} />
|
|
<Route path="users" element={<UsersAdmin />} />
|
|
<Route path="users/:id" element={<UserDetail />} />
|
|
<Route path="account" element={<AccountAdmin />} />
|
|
<Route path="*" element={<Navigate to="/admin" replace />} />
|
|
</Route>
|
|
|
|
{/* Player portal */}
|
|
<Route path="/account/login" element={<PlayerLogin />} />
|
|
<Route path="/account/register" element={<PlayerRegister />} />
|
|
<Route
|
|
element={
|
|
<RequirePlayer>
|
|
<PlayerPortalLayout />
|
|
</RequirePlayer>
|
|
}
|
|
>
|
|
<Route path="/player" element={<PlayerCharacters />} />
|
|
<Route path="/player/char/:serial" element={<PlayerCharacter />} />
|
|
<Route path="/account" element={<PlayerAccount />} />
|
|
</Route>
|
|
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</SiteProvider>
|
|
</AuthProvider>
|
|
)
|
|
}
|