Add Discord channel status check to server cards

- Checks for 4 channels per server: chat, in-game, forum, voice
- Shows 'All 4 channels configured' or lists missing channels
- Caches Discord channel data for 5 minutes to reduce API calls
This commit is contained in:
Claude
2026-04-09 19:55:41 +00:00
parent cbf5d219fc
commit 081bad1279
2 changed files with 110 additions and 4 deletions

View File

@@ -4,11 +4,94 @@ const db = require('../../database');
const { getMinecraftServers } = require('../../panel/discovery');
const { readServerProperties, writeWhitelistFile } = require('../../panel/files');
const { reloadWhitelistCommand } = require('../../panel/commands');
const { ChannelType } = require('discord.js');
// In-memory cache for RV low-bandwidth operations
let serverCache = { data: null, lastFetch: 0 };
const CACHE_TTL = 60000; // 60 seconds
// Cache for Discord channels (refresh less frequently)
let discordChannelCache = { channels: null, lastFetch: 0 };
const DISCORD_CACHE_TTL = 300000; // 5 minutes
/**
* Get Discord channels from cache or fetch fresh
*/
async function getDiscordChannels(client) {
const now = Date.now();
if (discordChannelCache.channels && (now - discordChannelCache.lastFetch < DISCORD_CACHE_TTL)) {
return discordChannelCache.channels;
}
const guild = client.guilds.cache.get(process.env.GUILD_ID);
if (!guild) return [];
const channels = guild.channels.cache.map(ch => ({
id: ch.id,
name: ch.name,
type: ch.type,
parentId: ch.parentId
}));
discordChannelCache = { channels, lastFetch: now };
return channels;
}
/**
* Check which Discord channels exist for a server
* Returns object with missing channels array
*/
function checkServerChannels(serverName, allChannels) {
// Normalize server name to expected channel format
const baseName = serverName
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '') // Remove special chars except spaces/hyphens
.replace(/\s+/g, '-') // Spaces to hyphens
.replace(/-+/g, '-') // Multiple hyphens to single
.trim();
const expectedChannels = [
{ name: `${baseName}-chat`, type: 'text', label: 'Chat' },
{ name: `${baseName}-in-game`, type: 'text', label: 'In-Game' },
{ name: `${baseName}-forum`, type: 'forum', label: 'Forum' },
{ name: serverName, type: 'voice', label: 'Voice' } // Voice uses display name
];
const missing = [];
const found = [];
for (const expected of expectedChannels) {
let exists = false;
if (expected.type === 'voice') {
// Voice channels match by exact name (case-insensitive)
exists = allChannels.some(ch =>
ch.type === ChannelType.GuildVoice &&
ch.name.toLowerCase() === expected.name.toLowerCase()
);
} else if (expected.type === 'forum') {
exists = allChannels.some(ch =>
ch.type === ChannelType.GuildForum &&
ch.name === expected.name
);
} else {
// Text channels
exists = allChannels.some(ch =>
ch.type === ChannelType.GuildText &&
ch.name === expected.name
);
}
if (exists) {
found.push(expected.label);
} else {
missing.push(expected.label);
}
}
return { missing, found, complete: missing.length === 0 };
}
router.get('/', (req, res) => {
res.render('admin/servers/index', { title: 'Server Matrix' });
});
@@ -38,10 +121,18 @@ router.get('/matrix', async (req, res) => {
return acc;
}, {});
const enrichedServers = serversData.map(srv => ({
...srv,
log: logMap[srv.identifier] || { is_online: false, last_error: 'Never synced' }
}));
// Get Discord channels
const client = req.app.locals.client;
const discordChannels = await getDiscordChannels(client);
const enrichedServers = serversData.map(srv => {
const channelStatus = checkServerChannels(srv.name, discordChannels);
return {
...srv,
log: logMap[srv.identifier] || { is_online: false, last_error: 'Never synced' },
discord: channelStatus
};
});
// Group by Node Location
const txServers = enrichedServers.filter(s => s.node === 'TX1');

View File

@@ -1,6 +1,7 @@
<%
const isOnline = server.log.is_online;
const hasError = !!server.log.last_error;
const discordComplete = server.discord?.complete;
let borderClass = 'border-gray-200 dark:border-gray-700'; // Default / Offline
if (isOnline && !hasError) borderClass = 'border-green-500 shadow-[0_0_10px_rgba(34,197,94,0.2)]';
@@ -37,6 +38,20 @@
</div>
</div>
<!-- Discord Channel Status -->
<div class="mb-4">
<span class="text-gray-500 dark:text-gray-400 block text-xs mb-1">Discord Channels</span>
<% if (discordComplete) { %>
<span class="text-green-600 dark:text-green-400 text-sm font-medium">✅ All 4 channels configured</span>
<% } else if (server.discord?.missing?.length > 0) { %>
<div class="bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 p-2 rounded text-xs">
<strong>Missing:</strong> <%= server.discord.missing.join(', ') %>
</div>
<% } else { %>
<span class="text-gray-500 dark:text-gray-400 text-sm">Unable to check</span>
<% } %>
</div>
<% if (hasError) { %>
<div class="bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 p-2 rounded text-xs mb-4 break-words">
<strong>Error:</strong> <%= server.log.last_error %>