feat: Add instance ID and implement Redis lock for global command registration to support multi-instance deployments.
This commit is contained in:
parent
031a8b3146
commit
5e41bea74e
|
|
@ -1,6 +1,11 @@
|
|||
import { config } from 'dotenv';
|
||||
import { hostname } from 'os';
|
||||
config();
|
||||
|
||||
const generateInstanceId = () => {
|
||||
return process.env.INSTANCE_ID || hostname() || `kord-${Math.random().toString(36).substring(2, 7)}`;
|
||||
};
|
||||
|
||||
export const env = {
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
DISCORD_TOKEN: process.env.DISCORD_TOKEN || '',
|
||||
|
|
@ -10,4 +15,5 @@ export const env = {
|
|||
REDIS_PORT: parseInt(process.env.REDIS_PORT || '6379', 10),
|
||||
VOICE_WAITING_ROOM_ID: process.env.VOICE_WAITING_ROOM_ID || '',
|
||||
VOICE_CATEGORY_ID: process.env.VOICE_CATEGORY_ID || '',
|
||||
INSTANCE_ID: generateInstanceId(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { InviteService } from '../services/InviteService';
|
|||
import { VoiceService } from '../services/VoiceService';
|
||||
import { PresenceService } from '../services/PresenceService';
|
||||
import { auditLogService } from '../services/AuditLogService';
|
||||
import { redis } from '../cache';
|
||||
import { env } from '../config/env';
|
||||
|
||||
export default {
|
||||
name: Events.ClientReady,
|
||||
|
|
@ -16,9 +18,17 @@ export default {
|
|||
PresenceService.startActivePresence(client);
|
||||
|
||||
try {
|
||||
const commandsData = Array.from(client.commands.values()).map(c => c.data.toJSON());
|
||||
await client.application?.commands.set(commandsData);
|
||||
logger.info(`Successfully registered ${commandsData.length} global application commands.`);
|
||||
const lockKey = 'commands:sync:lock';
|
||||
// EX 300 = 5 minutes lock. Only one instance needs to do this per boot cycle.
|
||||
const acquired = await redis.set(lockKey, '1', 'EX', 300, 'NX');
|
||||
|
||||
if (acquired) {
|
||||
const commandsData = Array.from(client.commands.values()).map(c => c.data.toJSON());
|
||||
await client.application?.commands.set(commandsData);
|
||||
logger.info(`Successfully registered ${commandsData.length} global application commands.`);
|
||||
} else {
|
||||
logger.info('Global commands registration skipped (already handled by another instance).');
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('Failed to register global commands', e);
|
||||
}
|
||||
|
|
@ -28,7 +38,7 @@ export default {
|
|||
category: 'BOOT',
|
||||
severity: 'INFO',
|
||||
title: 'Bot Online',
|
||||
description: `Kord has successfully started or reconnected.`
|
||||
description: `Kord instance **[${env.INSTANCE_ID}]** has successfully started or reconnected.`
|
||||
}).catch(() => {});
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Guild, EmbedBuilder, TextChannel, Colors } from 'discord.js';
|
||||
import { prisma } from '../database';
|
||||
import { env } from '../config/env';
|
||||
|
||||
export type AuditSeverity = 'INFO' | 'WARN' | 'ERROR';
|
||||
export type AuditCategory = 'SYSTEM' | 'BOOT' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC';
|
||||
|
|
@ -47,7 +48,7 @@ export class AuditLogService {
|
|||
.setDescription(`**${payload.title}**\n\n${payload.description}`)
|
||||
.setColor(color)
|
||||
.setTimestamp()
|
||||
.setFooter({ text: `${icon} ${payload.severity} · Kord System` });
|
||||
.setFooter({ text: `${icon} ${payload.severity} · Kord System [${env.INSTANCE_ID}]` });
|
||||
|
||||
if (payload.fields && payload.fields.length > 0) {
|
||||
embed.addFields(payload.fields);
|
||||
|
|
|
|||
Loading…
Reference in New Issue