diff --git a/services/arbiter-3.0/src/routes/admin/discord-audit.js b/services/arbiter-3.0/src/routes/admin/discord-audit.js index f767313..45a5760 100644 --- a/services/arbiter-3.0/src/routes/admin/discord-audit.js +++ b/services/arbiter-3.0/src/routes/admin/discord-audit.js @@ -9,6 +9,124 @@ const express = require('express'); const router = express.Router(); +/** + * GET /admin/discord + * Main Discord audit dashboard + */ +router.get('/', async (req, res) => { + try { + const client = req.app.locals.client; + const guildId = process.env.GUILD_ID; + + if (!client || !client.isReady()) { + return res.render('admin/discord/index', { + title: 'Discord', + error: 'Discord client not ready', + data: null + }); + } + + const guild = client.guilds.cache.get(guildId); + if (!guild) { + return res.render('admin/discord/index', { + title: 'Discord', + error: 'Guild not found', + data: null + }); + } + + // Fetch fresh data + await guild.channels.fetch(); + await guild.roles.fetch(); + + // Build channel structure + const channels = guild.channels.cache.map(ch => ({ + id: ch.id, + name: ch.name, + type: ch.type, + typeName: getChannelTypeName(ch.type), + parentId: ch.parentId, + position: ch.position, + nsfw: ch.nsfw || false, + topic: ch.topic || null, + permissionOverwrites: ch.permissionOverwrites?.cache.map(p => ({ + id: p.id, + type: p.type, + allow: p.allow.bitfield.toString(), + deny: p.deny.bitfield.toString() + })) || [] + })).sort((a, b) => a.position - b.position); + + // Build role structure with permission overwrites lookup + const roles = guild.roles.cache.map(r => ({ + id: r.id, + name: r.name, + color: r.hexColor, + position: r.position, + permissions: r.permissions.bitfield.toString(), + mentionable: r.mentionable, + managed: r.managed, + memberCount: r.members.size + })).sort((a, b) => b.position - a.position); + + // Categories with their children + const categories = channels + .filter(ch => ch.type === 4) + .map(cat => ({ + ...cat, + children: channels.filter(ch => ch.parentId === cat.id) + })); + + // Orphan channels + const orphanChannels = channels.filter(ch => !ch.parentId && ch.type !== 4); + + // Server info + const serverInfo = { + id: guild.id, + name: guild.name, + memberCount: guild.memberCount, + ownerId: guild.ownerId, + createdAt: guild.createdAt, + icon: guild.iconURL(), + features: guild.features + }; + + // Health checks + const healthChecks = { + orphanChannels: orphanChannels.length, + emptyRoles: roles.filter(r => r.memberCount === 0 && !r.managed && r.name !== '@everyone').length, + botRoles: roles.filter(r => r.managed).length + }; + + res.render('admin/discord/index', { + title: 'Discord', + error: null, + data: { + server: serverInfo, + categories, + orphanChannels, + allChannels: channels, + roles, + healthChecks, + summary: { + totalChannels: channels.length, + totalRoles: roles.length, + categoryCount: categories.length, + orphanCount: orphanChannels.length + } + } + }); + + } catch (error) { + console.error('Discord dashboard error:', error); + res.render('admin/discord/index', { + title: 'Discord', + error: error.message, + data: null + }); + } +}); + /** * GET /admin/discord/audit * Full Discord server audit - channels, roles, members diff --git a/services/arbiter-3.0/src/views/admin/discord/index.ejs b/services/arbiter-3.0/src/views/admin/discord/index.ejs new file mode 100644 index 0000000..6da77ab --- /dev/null +++ b/services/arbiter-3.0/src/views/admin/discord/index.ejs @@ -0,0 +1,467 @@ +<% if (error) { %> +
+ Channels without a parent category +
++ Non-bot roles with no members +
++ Managed by Discord integrations +
+