poixpixel-discord-bot/src/commands/util/reconnect.ts

203 lines
5.7 KiB
TypeScript

import {
CommandInteraction,
PermissionsBitField,
SlashCommandBuilder,
} from 'discord.js';
import { SubcommandCommand } from '../../types/CommandTypes.js';
import { loadConfig } from '../../util/configLoader.js';
import {
initializeDatabaseConnection,
ensureDbInitialized,
} from '../../db/db.js';
import { isRedisConnected } from '../../db/redis.js';
import {
NotificationType,
notifyManagers,
} from '../../util/notificationHandler.js';
const command: SubcommandCommand = {
data: new SlashCommandBuilder()
.setName('reconnect')
.setDescription('(Manager Only) Force reconnection to database or Redis')
.addSubcommand((subcommand) =>
subcommand
.setName('database')
.setDescription('(Manager Only) Force reconnection to the database'),
)
.addSubcommand((subcommand) =>
subcommand
.setName('redis')
.setDescription('(Manager Only) Force reconnection to Redis cache'),
)
.addSubcommand((subcommand) =>
subcommand
.setName('status')
.setDescription(
'(Manager Only) Check connection status of database and Redis',
),
),
execute: async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const config = loadConfig();
const managerRoleId = config.roles.staffRoles.find(
(role) => role.name === 'Manager',
)?.roleId;
const member = await interaction.guild?.members.fetch(interaction.user.id);
const hasManagerRole = member?.roles.cache.has(managerRoleId || '');
if (
!hasManagerRole &&
!interaction.memberPermissions?.has(
PermissionsBitField.Flags.Administrator,
)
) {
await interaction.reply({
content:
'You do not have permission to use this command. This command is restricted to users with the Manager role.',
flags: ['Ephemeral'],
});
return;
}
const subcommand = interaction.options.getSubcommand();
await interaction.deferReply({ flags: ['Ephemeral'] });
try {
if (subcommand === 'database') {
await handleDatabaseReconnect(interaction);
} else if (subcommand === 'redis') {
await handleRedisReconnect(interaction);
} else if (subcommand === 'status') {
await handleStatusCheck(interaction);
}
} catch (error) {
console.error(`Error in reconnect command (${subcommand}):`, error);
await interaction.editReply({
content: `An error occurred while processing the reconnect command: \`${error}\``,
});
}
},
};
/**
* Handle database reconnection
*/
async function handleDatabaseReconnect(interaction: CommandInteraction) {
await interaction.editReply('Attempting to reconnect to the database...');
try {
const success = await initializeDatabaseConnection();
if (success) {
await interaction.editReply(
'✅ **Database reconnection successful!** All database functions should now be operational.',
);
notifyManagers(
interaction.client,
NotificationType.DATABASE_CONNECTION_RESTORED,
`Database connection manually restored by ${interaction.user.tag}`,
);
} else {
await interaction.editReply(
'❌ **Database reconnection failed.** Check the logs for more details.',
);
}
} catch (error) {
console.error('Error reconnecting to database:', error);
await interaction.editReply(
`❌ **Database reconnection failed with error:** \`${error}\``,
);
}
}
/**
* Handle Redis reconnection
*/
async function handleRedisReconnect(interaction: CommandInteraction) {
await interaction.editReply('Attempting to reconnect to Redis...');
try {
const redisModule = await import('../../db/redis.js');
await redisModule.ensureRedisConnection();
const isConnected = redisModule.isRedisConnected();
if (isConnected) {
await interaction.editReply(
'✅ **Redis reconnection successful!** Cache functionality is now available.',
);
notifyManagers(
interaction.client,
NotificationType.REDIS_CONNECTION_RESTORED,
`Redis connection manually restored by ${interaction.user.tag}`,
);
} else {
await interaction.editReply(
'❌ **Redis reconnection failed.** The bot will continue to function without caching capabilities.',
);
}
} catch (error) {
console.error('Error reconnecting to Redis:', error);
await interaction.editReply(
`❌ **Redis reconnection failed with error:** \`${error}\``,
);
}
}
/**
* Handle status check for both services
*/
async function handleStatusCheck(interaction: any) {
await interaction.editReply('Checking connection status...');
try {
const dbStatus = await (async () => {
try {
await ensureDbInitialized();
return true;
} catch {
return false;
}
})();
const redisStatus = isRedisConnected();
const statusEmbed = {
title: '🔌 Service Connection Status',
fields: [
{
name: 'Database',
value: dbStatus ? '✅ Connected' : '❌ Disconnected',
inline: true,
},
{
name: 'Redis Cache',
value: redisStatus
? '✅ Connected'
: '⚠️ Disconnected (caching disabled)',
inline: true,
},
],
color:
dbStatus && redisStatus ? 0x00ff00 : dbStatus ? 0xffaa00 : 0xff0000,
timestamp: new Date().toISOString(),
};
await interaction.editReply({ content: '', embeds: [statusEmbed] });
} catch (error) {
console.error('Error checking connection status:', error);
await interaction.editReply(
`❌ **Error checking connection status:** \`${error}\``,
);
}
}
export default command;