Discord admin page: servers, radio channels, radio bots (server-26#213 item 2)
Frontend, built from design-canvas/discord-system-mockup.html ("DRB admin"):
- Settings > Discord (/settings/discord, platform admin only; the tab is
hidden from org owners because every route behind it is admin-only).
KPIs + tabs:
* Radio channels: every discord_streams doc, desired -> actual, the node's
real error, red after 2 min desired-on without connecting. Stop
(POST /discord/guilds/{id}/stop), Start for a stopped channel and
"+ Add radio channel" (POST /discord/streams), with the invite links a
"no bot in this server" refusal carries.
* Servers: linked servers plus servers seen running radio while unlinked
(acting for DISCORD_UNLINKED_GUILD_ORG_ID); Link / Change org
(PUT /discord/guilds/{id}/link).
* Radio bots: valid / check_error, guild membership with a per-server
invite link for each server the bot is missing from, lease (node,
since, releasing), fan-out, Refresh (POST /tokens/{id}/refresh),
Remove, Release all leases (POST /tokens/flush), add with optional BYO org.
* How it works.
- /tokens now renders the same radio-bot table (new token fields + refresh).
- Left out, no backend yet: alert routes, incident forum/roles/plan columns,
per-channel talkgroup chips, "Generate link code" (item 1).
Backend (routers/discord.py):
- GET /discord/admin/overview (platform admin): guilds, streams with node
and bot names, orgs, systems, limits. No token strings, no Discord user
ids, no linking-admin uid.
- PUT /discord/guilds/{id}/link: digits-only id, optional guild_name label,
and re-linking a server to a different org stops its live channels
(the reconciler would otherwise keep re-placing them on the old org's
nodes, using its stored org_id).
- POST /discord/streams: guild_id/channel_id must be digits (the web form
types them in; the bot always sent snowflakes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e574bd924a
commit
d3277c13e1
@@ -1,5 +1,6 @@
|
||||
import { auth } from "@/lib/firebase";
|
||||
import type { AreaContext, CameraProviderAdmin, CamerasResponse, Ny511Response, TalkgroupPending } from "@/lib/types";
|
||||
import type { DiscordAdminOverview, DiscordStream, RadioBot, RadioBotAddResult } from "@/lib/discord";
|
||||
|
||||
const BASE = process.env.NEXT_PUBLIC_C2_URL ?? "http://localhost:8000";
|
||||
|
||||
@@ -55,12 +56,39 @@ export const c2api = {
|
||||
deleteSystem: (id: string) =>
|
||||
request(`/systems/${id}`, { method: "DELETE" }),
|
||||
|
||||
// Tokens
|
||||
// Tokens (radio bots) — every route is platform-admin only (server-26#204 item 6)
|
||||
getTokens: () => request<unknown[]>("/tokens"),
|
||||
addToken: (body: { name: string; token: string }) =>
|
||||
request("/tokens", { method: "POST", body: JSON.stringify(body) }),
|
||||
addToken: (body: { name: string; token: string; org_id?: string | null }) =>
|
||||
request<RadioBotAddResult>("/tokens", { method: "POST", body: JSON.stringify(body) }),
|
||||
deleteToken: (id: string) =>
|
||||
request(`/tokens/${id}`, { method: "DELETE" }),
|
||||
/** Re-check one radio bot with Discord: token valid? which servers is it in? */
|
||||
refreshToken: (id: string) =>
|
||||
request<RadioBot>(`/tokens/${id}/refresh`, { method: "POST" }),
|
||||
/** Admin last resort: force-release every radio-bot lease. */
|
||||
flushTokens: () =>
|
||||
request<{ released: number }>("/tokens/flush", { method: "POST" }),
|
||||
|
||||
// Discord radio channels — web admin page (server-26#213 item 2)
|
||||
getDiscordAdmin: () => request<DiscordAdminOverview>("/discord/admin/overview"),
|
||||
linkDiscordGuild: (guildId: string, body: { org_id: string; guild_name?: string }) =>
|
||||
request<{ ok: boolean; stopped: string[] }>(`/discord/guilds/${encodeURIComponent(guildId)}/link`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
startDiscordStream: (body: {
|
||||
guild_id: string;
|
||||
channel_id: string;
|
||||
system_id: string;
|
||||
mode: "persistent" | "adhoc";
|
||||
channel_name?: string;
|
||||
preferred_token_id?: string;
|
||||
}) => request<DiscordStream>("/discord/streams", { method: "POST", body: JSON.stringify(body) }),
|
||||
stopDiscordStream: (guildId: string, channelId: string) =>
|
||||
request<{ stopped: string[] }>(`/discord/guilds/${encodeURIComponent(guildId)}/stop`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ channel_id: channelId, reason: "stopped from web admin" }),
|
||||
}),
|
||||
|
||||
// Node approval
|
||||
approveNode: (id: string) =>
|
||||
|
||||
Reference in New Issue
Block a user