-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
🌐 English · Deutsch
The PDC Redbot Webapp is a SvelteKit application that acts as a Backend-for-Frontend (BFF) in front of a Red-DiscordBot. The browser never talks to the bot directly; instead it calls the SvelteKit server, which holds all secrets and forwards requests to the bot over a token-secured JSON-RPC gateway provided by the companion cog. This page covers the stack, the BFF pattern, the gateway protocol, the request flow and the project structure.
| Layer | Technology |
|---|---|
| Framework | SvelteKit (adapter-node) |
| Language | TypeScript |
| Styling | TailwindCSS (shadcn-svelte tokens) |
| Charts | Chart.js |
| Auth | Discord OAuth2 (server-side) |
| Transport to the bot | JSON-RPC 2.0 over the cog gateway |
| i18n | German + English, full UI translation |
┌──────────────────────────────┐ ┌─────────────────────────────────┐
│ Red-DiscordBot (Python) │ │ Web app (Node / SvelteKit) │
│ │ │ │
│ ┌────────────────────────┐ │ JSON- │ ┌───────────────────────────┐ │
│ │ pdc_webdashboard (cog) │ │ RPC │ │ SvelteKit server (BFF) │ │
│ │ ├─ RPC gateway │◄─┼─2.0────┼─►│ ├─ Discord OAuth2 │ │
│ │ │ (localhost+token) │ │ +token │ │ ├─ session / cookies │ │
│ │ ├─ integration registry│ │ │ │ └─ RPC client │ │
│ │ └─ permission mapper │ │ │ └───────────┬───────────────┘ │
│ └────────────────────────┘ │ │ │ /api/* (BFF) │
│ ┌────────────────────────┐ │ │ ┌───────────▼───────────────┐ │
│ │ third-party cogs │ │ │ │ Svelte SPA (Tailwind) │ │
│ │ (widgets/panels/lists)│ │ │ │ widgets · panels · charts│ │
│ └────────────────────────┘ │ │ └───────────────────────────┘ │
└──────────────────────────────┘ └─────────────────────────────────┘
-
pdc_webdashboardcog (inPDC_Redbot_Cogs) runs inside the bot process and exposes the RPC gateway plus an integration registry where other cogs register widgets, panels and lists. See Cog Integration. - PDC Redbot Webapp (this repo) is the SvelteKit app. Its server is the BFF: it performs Discord OAuth2, holds the session, and is the only client that knows the gateway token. The Svelte SPA talks only to the SvelteKit server.
The single most important rule: all secrets live server-side. They are read at runtime from $env/dynamic/private and never bundled into client code.
-
lib/server/is the server-only zone: env (env.ts), session (session.ts), OAuth2 (auth.ts) and the RPC client (rpc.ts). Nothing here is importable from the browser. - The browser calls the BFF's own
/api/*endpoints (and pageloadfunctions). Those, in turn, call the gateway with the gateway token attached server-side. -
DISCORD_CLIENT_SECRET,GATEWAY_TOKENandSESSION_SECRETtherefore never reach the client — the browser only holds a signed session cookie. See Authentication.
The BFF speaks JSON-RPC 2.0 to the cog. Requests are HTTP POSTs to ${GATEWAY_URL}/rpc with an X-Dashboard-Token header. The cog also defines a WebSocket endpoint at /rpc for bidirectional/streaming use; the current web app uses the HTTP transport.
Each call carries an auth context the gateway re-checks server-side:
The gateway derives the user's Red permission level from auth and enforces the method's required level on every call. See API & Gateway for the method catalog.
Browser ──▶ BFF (SvelteKit server) ──▶ gateway (cog, localhost+token) ──▶ Red bot
│ page load / /api/* fetch │ JSON-RPC + X-Dashboard-Token │ Config / discord.py
│ │ │ permission re-check
◀── HTML / JSON ──────────────────◀── result / error ────────────────◀──
- The browser requests a page or hits a
/api/*endpoint. -
hooks.server.tsreads and validates the signed session cookie (and checks the session epoch, cached ~60 s). - The BFF builds the auth context and calls the gateway over JSON-RPC with the token.
- The cog enforces permissions, runs the method against Red, and returns a result.
- The BFF renders or returns JSON to the browser.
src/
hooks.server.ts Session from cookie + epoch check on every request
lib/server/ Server-ONLY (secrets!):
env.ts $env/dynamic/private, fail-closed on bad SESSION_SECRET
session.ts HMAC-SHA256 signed cookie sessions
auth.ts Discord OAuth2 (authorize URL, code exchange, user fetch)
rpc.ts JSON-RPC client (token header, auth context)
lib/components/ Widget, PanelForm, ListManager, ModuleTabs, CommandPalette
charts/ Chart.js wrappers (Line/Bar/Donut + SeriesToggle)
ui/ shadcn-svelte primitives
lib/i18n/ de + en (full UI translation)
lib/markdown.ts Safe Markdown → HTML renderer (custom pages)
routes/
+page.svelte Landing/overview (public)
commands/ Public command list
guilds/[id]/settings/ Server overview + per-guild bot settings
cogs/ Cog management (cogs/slash/downloader/global)
stats/ Server statistics (needs pdc_webdashboard_stats)
settings/ Global settings + branding + global panels
pages/ p/[slug]/ Custom pages (editor + public view)
announce/ Announcement / embed builder
audit/ system/ Audit log + health/self-update (owner)
auth/… logout OAuth2 flow
api/… BFF endpoints that call the gateway
| Component | Role |
|---|---|
hooks.server.ts |
Validates the session cookie and the session epoch on each request; fail-open if the gateway is offline. |
Widget.svelte |
Renders a cog's read-only widget data (KPI/list/status). |
PanelForm.svelte |
Renders a cog's declarative panel form and submits it. |
ListManager.svelte |
View/add/edit/delete rows for a cog's list contribution. |
ModuleTabs.svelte |
Groups one cog's widgets/panels/lists into a single tabbed module. |
lib/i18n |
German + English message stores. |
lib/markdown.ts |
XSS-safe Markdown renderer for custom pages. |
- Authentication — the security side of the BFF pattern
- API & Gateway — the JSON-RPC method catalog
- Cog Integration — how cogs feed the dashboard
🇬🇧 English
Users
- Getting Started
- Configuration
- Authentication
- Web UI
- Feature Catalog
- Embed Builder
- Public Pages
- Cog Management
- Statistics
- Logs
- Custom Pages
- Deployment
- Self-Update
Developers
{ "jsonrpc": "2.0", "id": 1, "method": "core.guild_detail", "params": { "auth": { "user_id": "123", "guild_id": "456", "locale": "en-US" }, "args": { /* method-specific */ } } }