Files
Nexumi/apps/webui/src/lib/discord-guild-resources.ts
smueller 211403d54d Enhance guild management features in the owner panel
- Updated the guilds API to support searching by both guild name and ID, improving user experience.
- Increased the number of guilds retrieved from the database from 100 to 200 for better visibility.
- Implemented invite creation functionality, allowing owners to generate invites directly from the guild management interface.
- Enhanced the UI to display guild names, icons, and member counts, providing a more informative overview.
- Updated localization files to reflect new search capabilities and invite-related messages in both English and German.
2026-07-24 10:59:07 +02:00

93 lines
2.8 KiB
TypeScript

import type { DiscordChannelOption, DiscordRoleOption } from '@nexumi/shared';
import { DASHBOARD_CHANNEL_TYPES } from './discord-channel-types';
import { env } from './env';
import { redis } from './redis';
const DISCORD_API_BASE = 'https://discord.com/api/v10';
const CACHE_TTL_SECONDS = 60;
export async function discordBotFetch<T>(
path: string,
init?: { method?: string; body?: unknown }
): Promise<T> {
const method = init?.method ?? 'GET';
const response = await fetch(`${DISCORD_API_BASE}${path}`, {
method,
headers: {
Authorization: `Bot ${env.BOT_TOKEN}`,
...(init?.body !== undefined ? { 'Content-Type': 'application/json' } : {})
},
body: init?.body !== undefined ? JSON.stringify(init.body) : undefined,
cache: 'no-store'
});
if (!response.ok) {
throw new Error(`Discord API request to ${path} failed with status ${response.status}`);
}
if (response.status === 204) {
return undefined as T;
}
return (await response.json()) as T;
}
interface DiscordChannelRest {
id: string;
name: string;
type: number;
parent_id?: string | null;
position?: number;
}
interface DiscordRoleRest {
id: string;
name: string;
color: number;
position: number;
managed?: boolean;
}
export async function listGuildChannelsForDashboard(guildId: string): Promise<DiscordChannelOption[]> {
const cacheKey = `dashboard:guild:${guildId}:channels:v2`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached) as DiscordChannelOption[];
}
const channels = await discordBotFetch<DiscordChannelRest[]>(`/guilds/${guildId}/channels`);
const options = channels
.filter((channel) => DASHBOARD_CHANNEL_TYPES.has(channel.type))
.map((channel) => ({
id: channel.id,
name: channel.name,
type: channel.type,
parentId: channel.parent_id ?? null
}))
.sort((a, b) => a.name.localeCompare(b.name));
await redis.set(cacheKey, JSON.stringify(options), 'EX', CACHE_TTL_SECONDS);
return options;
}
export async function listGuildRolesForDashboard(guildId: string): Promise<DiscordRoleOption[]> {
const cacheKey = `dashboard:guild:${guildId}:roles`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached) as DiscordRoleOption[];
}
const roles = await discordBotFetch<DiscordRoleRest[]>(`/guilds/${guildId}/roles`);
const options = roles
.filter((role) => role.name !== '@everyone')
.map((role) => ({
id: role.id,
name: role.name,
color: role.color,
position: role.position
}))
.sort((a, b) => b.position - a.position);
await redis.set(cacheKey, JSON.stringify(options), 'EX', CACHE_TTL_SECONDS);
return options;
}
export { CHANNEL_TYPES, channelPrefix, type ChannelKind } from './discord-channel-types';