// screens-admin.jsx — Users, Tokens, Containers, Cluster (graph), Settings function _normalizeNode(n, x, y) { return { id: n.id || n.hostname || n.name || 'node', role: n.role || 'worker', status: n.status || (n.online ? 'online' : 'offline'), ip: n.ip || n.ip_address || '—', version: n.version || '—', uptime: n.uptime || '—', cpu: n.cpu || n.cpu_percent || 0, mem: n.mem || n.memory_used || n.memory_used_gb || 0, memTotal: n.memTotal || n.mem_total || n.memory_total || n.memory_total_gb || 0, gpus: n.gpus || (n.gpu_count ? Array(n.gpu_count).fill('GPU') : []), devices: n.devices || n.capture_devices || [], x, y, }; } function Users() { const { USERS } = window.ZAMPP_DATA; const [tab, setTab] = React.useState("users"); return (

Users & Groups

User
Role
Groups
Last active
{USERS.length === 0 && (
No users found
)} {USERS.map(u => (
{u.initials || '??'}
{u.name}
@{u.username}
{u.role}
{(u.groups || []).map(g => {g})}
{u.lastSeen}
))}
); } function Tokens() { const [burned, setBurned] = React.useState(14340); const [rate, setRate] = React.useState(2.4); const [showCalc, setShowCalc] = React.useState(false); React.useEffect(() => { const i = setInterval(() => { setBurned(b => b + Math.floor(Math.random() * 8) + 1); setRate(r => Math.max(0.8, Math.min(8, r + (Math.random() - 0.5) * 0.4))); }, 800); return () => clearInterval(i); }, []); const burnSpark = React.useMemo(() => Array.from({ length: 40 }, (_, i) => 100 + i * 8 + Math.sin(i * 0.7) * 12), []); const competitorSpark = React.useMemo(() => Array.from({ length: 40 }, (_, i) => 200 + i * 22 + Math.cos(i * 0.5) * 30), []); const yourCostSpark = React.useMemo(() => Array.from({ length: 40 }, () => 1), []); const [events, setEvents] = React.useState([ { t: "21:14:02", action: "preview thumbnail generated", cost: 4 }, { t: "21:14:01", action: "user clicked play", cost: 12 }, { t: "21:13:58", action: "API health check", cost: 8 }, { t: "21:13:54", action: "asset metadata read", cost: 2 }, { t: "21:13:51", action: "session token refreshed", cost: 18 }, { t: "21:13:47", action: "scrubbed timeline 1 frame", cost: 6 }, { t: "21:13:42", action: "took a deep breath near the API", cost: 24 }, ]); React.useEffect(() => { const actions = [ "preview thumbnail generated", "user clicked play", "API health check", "scrubbed timeline 1 frame", "asset metadata read", "session token refreshed", "checked job queue", "rendered a tooltip", "loaded sidebar icon", "blinked", "made eye contact with the cluster", "opened a modal (twice)", "asset list pagination request", "thought about a comment", "moved cursor near 'Save'", ]; const i = setInterval(() => { const now = new Date(); const t = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}`; const a = actions[Math.floor(Math.random() * actions.length)]; const c = Math.floor(Math.random() * 28) + 1; setEvents(ev => [{ t, action: a, cost: c }, ...ev].slice(0, 12)); }, 1600); return () => clearInterval(i); }, []); const tiers = [ { name: "Starter", desc: "For \"evaluation only\" — definitely not production", price: "$2,400", per: "/ month", tokens: "100k tokens", popular: false, color: "#6B7280" }, { name: "Broadcast", desc: "Most teams. Most pain.", price: "$28,000", per: "/ month", tokens: "1.5M tokens", popular: true, color: "#5B7CFA" }, { name: "Enterprise", desc: "If you have to ask, you can't afford it (but you'll ask anyway)", price: "$call us", per: "and bring lawyers", tokens: "∞ tokens", popular: false, color: "#B57CFA" }, ]; return (

Tokens

Token-metered pricing parody · You actually pay $0.00
SATIRE
TOKENS BURNED THIS SESSION
🔥 {burned.toLocaleString()}
↑ {rate.toFixed(1)}k/sec burning since you logged in
WHAT YOU ACTUALLY PAY
$0 .00
Dragonflight is self-hosted. The tokens above are imaginary.
Imagine them as a stress test for your sanity.
HOURLY BURN — DRAGONFLIGHT vs. THE OTHER GUYS
i < 20 ? 1 : 1), color: "#2DD4A8" }, ]} />
Competitor: $1,247/hr and rising
Dragonflight: $0.00/hr forever
LIVE BILLING EVENTS
{events.map((e, i) => (
{e.t} {e.action} +{e.cost} tk
))}
PRICING TIERS WE DIDN'T COPY
{tiers.map(t => (
{t.popular && MOST PAIN}
{t.name}
{t.desc}
{t.price} {t.per}
{t.tokens}
))}
{showCalc && setShowCalc(false)} />}
Disclaimer: No actual tokens were billed in the making of this page. Wild Dragon's broadcast platform is self-hosted infrastructure. Any resemblance to per-API-call broadcast token economies, living or dead, is satirical and protected as commentary. If you came here looking for actual API tokens, that page no longer exists — service credentials are managed through the cluster's own JWT issuer (see Settings → AMPP Integration).
); } function ChartLine({ series }) { const w = 600, h = 140; return ( {series.map((s, si) => { const max = Math.max(...series.flatMap(x => x.data), 1); const pts = s.data.map((d, i) => { const x = (i / (s.data.length - 1)) * w; const y = h - (d / max) * (h - 10) - 4; return `${x},${y}`; }).join(" "); const area = `0,${h} ${pts} ${w},${h}`; return ( ); })} ); } function CostCalculator({ onClose }) { const [users, setUsers] = React.useState(12); const [assets, setAssets] = React.useState(500); const [clicks, setClicks] = React.useState(2000); const cost = users * 240 + assets * 8 + clicks * 0.12; return (
e.stopPropagation()}>
Token Cost Calculator
What it would cost on AMPP-style pricing
You would be paying
${cost.toLocaleString("en-US", { maximumFractionDigits: 0 })}/ month
Your actual Dragonflight cost: $0.00. You're welcome.
); } function CalcSlider({ label, value, onChange, min, max, step = 1, unit }) { return (
{label} {value.toLocaleString()}{unit}
onChange(Number(e.target.value))} style={{ width: "100%", accentColor: "var(--accent)" }} />
); } function Containers() { const [containers, setContainers] = React.useState(null); function load() { setContainers(null); window.ZAMPP_API.fetch('/cluster/containers') .then(data => setContainers(Array.isArray(data) ? data : (data.containers || []))) .catch(() => setContainers([])); } React.useEffect(() => { load(); }, []); const running = (containers || []).filter(c => c.state === 'running').length; return (

Containers

Docker Compose services across the cluster
{containers !== null && containers.length > 0 && (
{running} / {containers.length} running
)}
{containers === null && (
Loading…
)} {containers !== null && containers.length === 0 && (
🐳
No container data available
Container metrics endpoint not yet wired
)} {containers !== null && containers.length > 0 && (
Container
Image
State
CPU
Memory
Ports
{containers.map(c => (
{c.name}
up {c.uptime}
{c.image}
RUNNING {c.healthy && healthy}
{(c.cpu || 0).toFixed(1)}%
{c.mem} MB
{c.ports}
))}
)}
); } function Cluster() { const rawNodes = window.ZAMPP_DATA.NODES; const nodesArr = Array.isArray(rawNodes) ? rawNodes : (rawNodes?.nodes || []); const NODES = React.useMemo(() => { if (!nodesArr.length) return []; const primaryRaw = nodesArr.find(n => n.role === 'primary') || nodesArr[0]; const others = nodesArr.filter(n => n !== primaryRaw); const primary = _normalizeNode(primaryRaw, 0.5, 0.46); const positioned = others.map((n, i) => { const angle = others.length <= 1 ? Math.PI / 2 : (i / others.length) * 2 * Math.PI - Math.PI / 2; return _normalizeNode(n, 0.5 + 0.32 * Math.cos(angle), 0.46 + 0.35 * Math.sin(angle)); }); return [primary, ...positioned]; }, []); const [hovered, setHovered] = React.useState(null); const [selected, setSelected] = React.useState(NODES[0] || null); const W = 720, H = 460; if (!NODES.length) { return (

Cluster

No cluster nodes available
); } const primary = NODES.find(n => n.role === 'primary') || NODES[0]; const edges = NODES.filter(n => n.id !== primary.id).map(n => ({ from: primary, to: n, alive: n.status === 'online', })); const sel = selected || NODES[0]; return (

Cluster

{NODES.filter(n => n.status === "online").length} of {NODES.length} nodes online
Live
Nodes
{NODES.length}
Avg CPU
{Math.round(NODES.reduce((a, n) => a + (n.cpu || 0), 0) / NODES.length)} %
GPUs
{NODES.reduce((a, n) => a + n.gpus.length, 0)}
Avg Memory
{Math.round(NODES.reduce((a, n) => a + (n.mem || 0), 0) / NODES.length)} GB
Topology
{edges.map((e, i) => { const x1 = e.from.x * W, y1 = e.from.y * H; const x2 = e.to.x * W, y2 = e.to.y * H; return ( {e.alive && ( )} ); })} {NODES.map(n => { const cx = n.x * W, cy = n.y * H; const isSelected = sel && sel.id === n.id; const color = n.status === "online" ? "var(--success)" : "var(--text-4)"; return ( setHovered(n.id)} onMouseLeave={() => setHovered(null)} onClick={() => setSelected(n)}> {n.status === "online" && ( )} {n.role === "primary" && } {n.role !== "primary" && {n.role[0].toUpperCase()}} {n.id} {n.ip} ); })}
{sel && (
{sel.id} {sel.role}
{sel.status}} />
{sel.cpu}%
} /> {sel.memTotal > 0 && (
{sel.mem} / {sel.memTotal} GB
} /> )} {sel.gpus.length > 0 && (
GPUs ({sel.gpus.length})
{sel.gpus.map((g, i) => (
{g}
))}
)} {sel.devices && sel.devices.length > 0 && (
Capture devices
{sel.devices.map((d, i) => (
{d}
))}
)}
{sel.role !== "primary" && }
)}
); } function DetailRow({ k, v, mono }) { return (
{k} {v}
); } function Settings() { const [s3, setS3] = React.useState({ s3_endpoint: '', s3_bucket: '', s3_access_key: '', s3_secret_key: '', s3_region: 'us-east-1' }); const [s3Loading, setS3Loading] = React.useState(true); const [s3Saving, setS3Saving] = React.useState(false); const [s3Testing, setS3Testing] = React.useState(false); const [s3Msg, setS3Msg] = React.useState(null); const [secretExists, setSecretExists] = React.useState(false); React.useEffect(() => { window.ZAMPP_API.fetch('/settings/s3') .then(data => { setS3({ s3_endpoint: data.s3_endpoint || '', s3_bucket: data.s3_bucket || '', s3_access_key: data.s3_access_key || '', s3_secret_key: '', s3_region: data.s3_region || 'us-east-1' }); setSecretExists(!!data.s3_secret_key_exists); setS3Loading(false); }) .catch(() => setS3Loading(false)); }, []); const saveS3 = () => { setS3Saving(true); setS3Msg(null); window.ZAMPP_API.fetch('/settings/s3', { method: 'PUT', body: JSON.stringify(s3) }) .then(() => { setS3Saving(false); setS3Msg({ ok: true, text: 'Saved and applied.' }); if (s3.s3_secret_key) setSecretExists(true); }) .catch(e => { setS3Saving(false); setS3Msg({ ok: false, text: e.message }); }); }; const testS3 = () => { setS3Testing(true); setS3Msg(null); window.ZAMPP_API.fetch('/settings/s3/test', { method: 'POST', body: JSON.stringify(s3) }) .then(r => { setS3Testing(false); setS3Msg({ ok: r.ok !== false, text: r.message || 'Connection OK' }); }) .catch(e => { setS3Testing(false); setS3Msg({ ok: false, text: e.message }); }); }; return (

Settings

System configuration · changes apply without restart
connected : not configured} > {s3Loading ? (
Loading…
) : (<> setS3(p => ({...p, s3_endpoint: e.target.value}))} placeholder="https://s3.example.com" />
setS3(p => ({...p, s3_region: e.target.value}))} placeholder="us-east-1" /> setS3(p => ({...p, s3_bucket: e.target.value}))} placeholder="my-bucket" />
setS3(p => ({...p, s3_access_key: e.target.value}))} placeholder="Access key ID" /> setS3(p => ({...p, s3_secret_key: e.target.value}))} placeholder={secretExists ? '(saved — type to replace)' : 'Secret key'} /> {s3Msg && (
{s3Msg.text}
)}
)}
); } function SField({ label, children }) { return (
{children}
); } function SettingsCard({ icon, title, sub, tag, children }) { return (
{title}
{sub}
{tag}
{children}
); } Object.assign(window, { Users, Tokens, Containers, Cluster, Settings });