Discord: log node-reported stream state; remove a radio channel or a server
- handle_actual logs every node-reported state TRANSITION (not heartbeats) with stream/node/guild/channel/token ids and the node's error text: info for connected/connecting/stopped, warning for error, and a dedicated "radio bot X failed to log in on node N: <reason>" line for login_failed. The node's reason code is now stored in actual.code and shown next to the error on Settings > Discord and the node page. - Remove a radio channel permanently: streams.delete_stream stops it through desired state first (node republished without it, token -> releasing per #216, freed on the node's ack), then deletes the doc in a transaction that refuses if the channel was restarted in between. DELETE /discord/streams/{id} (platform admin) and DELETE /discord/nodes/{node}/streams/{id} (admin or org owner, same org + node only; operators can still stop, not remove). Audited. - Remove a server: DELETE /discord/guilds/{id} (admin) deletes every radio channel in it the same way, then tombstones discord_guilds (org_id None + unlinked_at, #218 semantics, plus removed_at) so it never falls back to DISCORD_UNLINKED_GUILD_ORG_ID; the admin page hides it, and /drb link or the admin link brings it back. Optional leave_radio_bots makes pool bots and the server's org's own BYO bots leave via Discord REST; the DRB server bot stays until kicked, and the UI says so. Audited. - UI: Remove (with confirm) on Radio channels, Servers and the node page list. Refs #213, #221 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fabdf5839e
commit
c92bdfe841
@@ -84,6 +84,21 @@ export const c2api = {
|
||||
channel_name?: string;
|
||||
preferred_token_id?: string;
|
||||
}) => request<DiscordStream>("/discord/streams", { method: "POST", body: JSON.stringify(body) }),
|
||||
/** Permanently remove a radio channel (stopped through desired state first). Admin only. */
|
||||
deleteDiscordStream: (streamId: string) =>
|
||||
request<{ ok: boolean; stream_id: string }>(`/discord/streams/${encodeURIComponent(streamId)}`, {
|
||||
method: "DELETE",
|
||||
}),
|
||||
/** Remove a Discord server from DRB: its radio channels are deleted and it is unlinked with no fallback org. */
|
||||
removeDiscordGuild: (guildId: string, leaveRadioBots: boolean) =>
|
||||
request<{
|
||||
ok: boolean;
|
||||
guild_id: string;
|
||||
streams_removed: string[];
|
||||
radio_bots: { name: string; left: boolean; error: string | null }[];
|
||||
}>(`/discord/guilds/${encodeURIComponent(guildId)}?leave_radio_bots=${leaveRadioBots ? "true" : "false"}`, {
|
||||
method: "DELETE",
|
||||
}),
|
||||
stopDiscordStream: (guildId: string, channelId: string) =>
|
||||
request<{ stopped: string[] }>(`/discord/guilds/${encodeURIComponent(guildId)}/stop`, {
|
||||
method: "POST",
|
||||
@@ -116,6 +131,12 @@ export const c2api = {
|
||||
method: "POST",
|
||||
body: JSON.stringify(streamId ? { stream_id: streamId } : {}),
|
||||
}),
|
||||
/** Permanently remove one radio channel from this node (org owner or admin). */
|
||||
removeNodeDiscordStream: (nodeId: string, streamId: string) =>
|
||||
request<{ ok: boolean; stream_id: string }>(
|
||||
`/discord/nodes/${nodeId}/streams/${encodeURIComponent(streamId)}`,
|
||||
{ method: "DELETE" },
|
||||
),
|
||||
deleteNode: (id: string) =>
|
||||
request(`/nodes/${id}`, { method: "DELETE" }),
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ export interface RadioBotAddResult {
|
||||
export interface DiscordStreamActual {
|
||||
state?: string;
|
||||
error?: string | null;
|
||||
/** The node's reason code on error: login_failed, connect_failed, not_in_guild, bad_channel, no_audio. */
|
||||
code?: string | null;
|
||||
since?: string | null;
|
||||
at?: string | null;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface DiscordStreamView {
|
||||
mode: "persistent" | "adhoc" | null;
|
||||
node_id: string | null;
|
||||
node_name: string | null;
|
||||
actual: { state: StreamState; error: string | null; since?: string; at?: string } | null;
|
||||
actual: { state: StreamState; error: string | null; code?: string | null; since?: string; at?: string } | null;
|
||||
updated_at: string | null;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ export interface DiscordJoinOptions {
|
||||
system_id: string | null;
|
||||
system_name: string | null;
|
||||
can_control: boolean;
|
||||
/** Permanent removal: platform admin or the org owner only (operators can stop, not remove). */
|
||||
can_remove?: boolean;
|
||||
limits: { node_max: number; node_used: number; guild_max: number };
|
||||
guilds: DiscordGuildOption[];
|
||||
streams: DiscordStreamView[];
|
||||
|
||||
Reference in New Issue
Block a user