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 { config } from 'dotenv';
|
||||||
|
import { hostname } from 'os';
|
||||||
config();
|
config();
|
||||||
|
|
||||||
|
const generateInstanceId = () => {
|
||||||
|
return process.env.INSTANCE_ID || hostname() || `kord-${Math.random().toString(36).substring(2, 7)}`;
|
||||||
|
};
|
||||||
|
|
||||||
export const env = {
|
export const env = {
|
||||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||||
DISCORD_TOKEN: process.env.DISCORD_TOKEN || '',
|
DISCORD_TOKEN: process.env.DISCORD_TOKEN || '',
|
||||||
|
|
@ -10,4 +15,5 @@ export const env = {
|
||||||
REDIS_PORT: parseInt(process.env.REDIS_PORT || '6379', 10),
|
REDIS_PORT: parseInt(process.env.REDIS_PORT || '6379', 10),
|
||||||
VOICE_WAITING_ROOM_ID: process.env.VOICE_WAITING_ROOM_ID || '',
|
VOICE_WAITING_ROOM_ID: process.env.VOICE_WAITING_ROOM_ID || '',
|
||||||
VOICE_CATEGORY_ID: process.env.VOICE_CATEGORY_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 { VoiceService } from '../services/VoiceService';
|
||||||
import { PresenceService } from '../services/PresenceService';
|
import { PresenceService } from '../services/PresenceService';
|
||||||
import { auditLogService } from '../services/AuditLogService';
|
import { auditLogService } from '../services/AuditLogService';
|
||||||
|
import { redis } from '../cache';
|
||||||
|
import { env } from '../config/env';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: Events.ClientReady,
|
name: Events.ClientReady,
|
||||||
|
|
@ -16,9 +18,17 @@ export default {
|
||||||
PresenceService.startActivePresence(client);
|
PresenceService.startActivePresence(client);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
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());
|
const commandsData = Array.from(client.commands.values()).map(c => c.data.toJSON());
|
||||||
await client.application?.commands.set(commandsData);
|
await client.application?.commands.set(commandsData);
|
||||||
logger.info(`Successfully registered ${commandsData.length} global application commands.`);
|
logger.info(`Successfully registered ${commandsData.length} global application commands.`);
|
||||||
|
} else {
|
||||||
|
logger.info('Global commands registration skipped (already handled by another instance).');
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to register global commands', e);
|
logger.error('Failed to register global commands', e);
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +38,7 @@ export default {
|
||||||
category: 'BOOT',
|
category: 'BOOT',
|
||||||
severity: 'INFO',
|
severity: 'INFO',
|
||||||
title: 'Bot Online',
|
title: 'Bot Online',
|
||||||
description: `Kord has successfully started or reconnected.`
|
description: `Kord instance **[${env.INSTANCE_ID}]** has successfully started or reconnected.`
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Guild, EmbedBuilder, TextChannel, Colors } from 'discord.js';
|
import { Guild, EmbedBuilder, TextChannel, Colors } from 'discord.js';
|
||||||
import { prisma } from '../database';
|
import { prisma } from '../database';
|
||||||
|
import { env } from '../config/env';
|
||||||
|
|
||||||
export type AuditSeverity = 'INFO' | 'WARN' | 'ERROR';
|
export type AuditSeverity = 'INFO' | 'WARN' | 'ERROR';
|
||||||
export type AuditCategory = 'SYSTEM' | 'BOOT' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC';
|
export type AuditCategory = 'SYSTEM' | 'BOOT' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC';
|
||||||
|
|
@ -47,7 +48,7 @@ export class AuditLogService {
|
||||||
.setDescription(`**${payload.title}**\n\n${payload.description}`)
|
.setDescription(`**${payload.title}**\n\n${payload.description}`)
|
||||||
.setColor(color)
|
.setColor(color)
|
||||||
.setTimestamp()
|
.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) {
|
if (payload.fields && payload.fields.length > 0) {
|
||||||
embed.addFields(payload.fields);
|
embed.addFields(payload.fields);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue