100 lines
3.7 KiB
TypeScript
100 lines
3.7 KiB
TypeScript
import { createCommand } from "@lib/utils";
|
|
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
|
|
import { UpdateService } from "@/modules/admin/update.service";
|
|
import {
|
|
getCheckingEmbed,
|
|
getNoUpdatesEmbed,
|
|
getUpdatesAvailableMessage,
|
|
getPreparingEmbed,
|
|
getUpdatingEmbed,
|
|
getCancelledEmbed,
|
|
getTimeoutEmbed,
|
|
getErrorEmbed
|
|
} from "@/modules/admin/update.view";
|
|
|
|
export const update = createCommand({
|
|
data: new SlashCommandBuilder()
|
|
.setName("update")
|
|
.setDescription("Check for updates and restart the bot")
|
|
.addBooleanOption(option =>
|
|
option.setName("force")
|
|
.setDescription("Force update even if checks fail (not recommended)")
|
|
.setRequired(false)
|
|
)
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
|
execute: async (interaction) => {
|
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
|
const force = interaction.options.getBoolean("force") || false;
|
|
|
|
try {
|
|
await interaction.editReply({ embeds: [getCheckingEmbed()] });
|
|
|
|
const { hasUpdates, log, branch } = await UpdateService.checkForUpdates();
|
|
|
|
if (!hasUpdates && !force) {
|
|
await interaction.editReply({ embeds: [getNoUpdatesEmbed()] });
|
|
return;
|
|
}
|
|
|
|
const { embeds, components } = getUpdatesAvailableMessage(branch, log, force);
|
|
const response = await interaction.editReply({ embeds, components });
|
|
|
|
try {
|
|
const confirmation = await response.awaitMessageComponent({
|
|
filter: (i) => i.user.id === interaction.user.id,
|
|
componentType: ComponentType.Button,
|
|
time: 30000
|
|
});
|
|
|
|
if (confirmation.customId === "confirm_update") {
|
|
await confirmation.update({
|
|
embeds: [getPreparingEmbed()],
|
|
components: []
|
|
});
|
|
|
|
// 1. Check what the update requires
|
|
const { needsInstall, needsMigrations } = await UpdateService.checkUpdateRequirements(branch);
|
|
|
|
// 2. Prepare context BEFORE update
|
|
await UpdateService.prepareRestartContext({
|
|
channelId: interaction.channelId,
|
|
userId: interaction.user.id,
|
|
timestamp: Date.now(),
|
|
runMigrations: needsMigrations,
|
|
installDependencies: needsInstall
|
|
});
|
|
|
|
// 3. Update UI to "Restarting" state
|
|
await interaction.editReply({ embeds: [getUpdatingEmbed(needsInstall)] });
|
|
|
|
// 4. Perform Update (Danger Zone)
|
|
await UpdateService.performUpdate(branch);
|
|
|
|
// 5. Trigger Restart (if we are still alive)
|
|
await UpdateService.triggerRestart();
|
|
|
|
} else {
|
|
await confirmation.update({
|
|
embeds: [getCancelledEmbed()],
|
|
components: []
|
|
});
|
|
}
|
|
|
|
} catch (e) {
|
|
if (e instanceof Error && e.message.includes("time")) {
|
|
await interaction.editReply({
|
|
embeds: [getTimeoutEmbed()],
|
|
components: []
|
|
});
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error("Update failed:", error);
|
|
await interaction.editReply({ embeds: [getErrorEmbed(error)] });
|
|
}
|
|
}
|
|
});
|