diff --git a/db/initialize.ts b/db/initialize.ts index c07e9a1..a9dae8a 100644 --- a/db/initialize.ts +++ b/db/initialize.ts @@ -93,5 +93,20 @@ await dbClient.execute(` `); console.log('Table created'); +// Database sizes view +console.log('Attempting to create view db_size'); +await dbClient.execute(` + CREATE VIEW db_size AS + SELECT + table_name AS "table", + ROUND(((data_length + index_length) / 1024 / 1024), 3) AS "size", + table_rows AS "rows" + FROM information_schema.TABLES + WHERE + table_schema = "${config.db.name}" + AND table_name <> "db_size"; +`); +console.log('View Created'); + await dbClient.close(); console.log('Done!'); diff --git a/db/populateDefaults.ts b/db/populateDefaults.ts index c9efec9..102570e 100644 --- a/db/populateDefaults.ts +++ b/db/populateDefaults.ts @@ -4,6 +4,7 @@ import { dbClient } from '../src/db.ts'; console.log('Attempting to insert default actions into command_cnt'); const actions = [ 'msg-mention', + 'cmd-audit', 'cmd-delete', 'cmd-help', 'cmd-info', diff --git a/src/commands/_index.ts b/src/commands/_index.ts index 3bcdaf2..e701d21 100644 --- a/src/commands/_index.ts +++ b/src/commands/_index.ts @@ -2,6 +2,7 @@ import { Bot, CreateApplicationCommand, log, LT, MakeRequired } from '../../deps import { Command } from '../types/commandTypes.ts'; import utils from '../utils.ts'; +import audit from './audit.ts'; import info from './info.ts'; import help from './help.ts'; import report from './report.ts'; @@ -10,7 +11,7 @@ import deleteCmd from './delete.ts'; import managerJLA from './managerJLA.ts'; import { gameSelectionCommand } from '../buttons/event-creation/step1-gameSelection.ts'; -export const commands: Array = [deleteCmd, info, report, setup, gameSelectionCommand, managerJLA, help]; +export const commands: Array = [deleteCmd, info, report, setup, gameSelectionCommand, managerJLA, help, audit]; export const createSlashCommands = async (bot: Bot) => { const globalCommands: MakeRequired[] = []; diff --git a/src/commands/audit.ts b/src/commands/audit.ts new file mode 100644 index 0000000..a9070c5 --- /dev/null +++ b/src/commands/audit.ts @@ -0,0 +1,88 @@ +import config from '../../config.ts'; +import { ApplicationCommandTypes, ApplicationCommandOptionTypes, Bot, Interaction, InteractionResponseTypes, DiscordEmbedField } from '../../deps.ts'; +import { infoColor2, infoEmbed, isLFGChannel, somethingWentWrong } from '../commandUtils.ts'; +import { dbClient, queries } from '../db.ts'; +import { CommandDetails } from '../types/commandTypes.ts'; +import utils from '../utils.ts'; +import { auditSlashName } from './slashCommandNames.ts'; + +const auditDbName = 'database'; +const auditCustomActivities = 'custom-activities'; +const auditGuildName = 'guilds'; + +const details: CommandDetails = { + name: auditSlashName, + description: `Developer Command for checking in on ${config.name}'s health.`, + type: ApplicationCommandTypes.ChatInput, + defaultMemberPermissions: ['ADMINISTRATOR'], + options: [ + { + name: auditDbName, + type: ApplicationCommandOptionTypes.SubCommand, + description: `Developer Command: Checks ${config.name}'s DB size.`, + }, + { + name: auditCustomActivities, + type: ApplicationCommandOptionTypes.SubCommand, + description: 'Developer Command: Checks for duplicate custom activities.', + }, + { + name: auditGuildName, + type: ApplicationCommandOptionTypes.SubCommand, + description: `Developer Command: Checks in on channel and member counts of guilds that ${config.name} is in.`, + }, + ], +}; + +const execute = async (bot: Bot, interaction: Interaction) => { + if (interaction.member && interaction.guildId && interaction.data?.options?.[0].options) { + dbClient.execute(queries.callIncCnt('cmd-audit')).catch((e) => utils.commonLoggers.dbError('audit.ts@inc', 'call sproc INC_CNT on', e)); + const auditName = interaction.data.options[0].name; + switch (auditName) { + case auditDbName: + // Get DB statistics + const auditQuery = await dbClient.query(`SELECT * FROM db_size;`).catch((e) => utils.commonLoggers.dbError('audit.ts@dbSize', 'query', e)); + + // Turn all tables into embed fields, currently only properly will handle 25 tables, but we'll fix that when group up gets 26 tables + const embedFields: Array = []; + auditQuery.forEach((row: any) => { + embedFields.push({ + name: `${row.table}`, + value: `**Size:** ${row.size} MB +**Rows:** ${row.rows}`, + inline: true, + }); + }); + bot.helpers.sendInteractionResponse( + interaction.id, + interaction.token, + { + type: InteractionResponseTypes.ChannelMessageWithSource, + data: { + flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n), + embeds: [{ + color: infoColor2, + title: 'Database Audit', + description: 'Lists all tables with their current size and row count.', + timestamp: new Date().getTime(), + fields: embedFields.slice(0, 25), + }], + }, + }, + ).catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@dbSize', interaction, e)); + break; + case auditCustomActivities: + case auditGuildName: + default: + somethingWentWrong(bot, interaction, `auditNameNotHandled@${auditName}`); + break; + } + } else { + somethingWentWrong(bot, interaction, 'auditMissingData') + } +}; + +export default { + details, + execute, +}; diff --git a/src/commands/slashCommandNames.ts b/src/commands/slashCommandNames.ts index dbef1c3..06ba76b 100644 --- a/src/commands/slashCommandNames.ts +++ b/src/commands/slashCommandNames.ts @@ -1,7 +1,8 @@ +export const auditSlashName = 'audit'; +export const createEventSlashName = 'create-event'; export const deleteSlashName = 'delete-lfg-channel'; +export const managerJLASlashName = 'event'; export const helpSlashName = 'help'; export const infoSlashName = 'info'; -export const managerJLASlashName = 'event'; export const reportSlashName = 'report'; export const setupSlashName = 'setup'; -export const createEventSlashName = 'create-event';