mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
fix: harden upgrade restart recovery
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-dialog v-model="visible" max-width="520">
|
||||
<v-dialog v-model="visible" max-width="520" :persistent="blockingRecovery || restarting">
|
||||
<v-card>
|
||||
<v-card-title class="upgrade-recovery-title">
|
||||
<span>{{ t('core.common.upgradeRecovery.title') }}</span>
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" :disabled="restarting" @click="dismiss">
|
||||
<v-btn v-if="!blockingRecovery" variant="text" :disabled="restarting" @click="dismiss">
|
||||
{{ t('core.common.upgradeRecovery.laterButton') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@@ -64,11 +64,16 @@ type StartTimeData = {
|
||||
start_time?: number | string | null;
|
||||
};
|
||||
|
||||
type RecoveryEventDetail = VersionData & {
|
||||
blocking?: boolean;
|
||||
};
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const visible = ref(false);
|
||||
const restarting = ref(false);
|
||||
const blockingRecovery = ref(false);
|
||||
const statusMessage = ref('');
|
||||
const coreVersion = ref('');
|
||||
const dashboardVersion = ref('');
|
||||
@@ -147,6 +152,7 @@ function clearRestartTimer() {
|
||||
function dismiss() {
|
||||
sessionStorage.setItem(getDismissKey(), '1');
|
||||
sessionStorage.removeItem(UPGRADE_RECOVERY_TOKEN_KEY);
|
||||
blockingRecovery.value = false;
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
@@ -196,7 +202,7 @@ async function restartCore() {
|
||||
}
|
||||
}
|
||||
|
||||
async function showRecoveryDialog(versionData: VersionData) {
|
||||
async function showRecoveryDialog(versionData: VersionData, blocking = false) {
|
||||
if (visible.value || restarting.value) {
|
||||
return;
|
||||
}
|
||||
@@ -206,17 +212,18 @@ async function showRecoveryDialog(versionData: VersionData) {
|
||||
|
||||
coreVersion.value = displayVersion(versionData.version);
|
||||
dashboardVersion.value = displayVersion(versionData.dashboard_version);
|
||||
if (sessionStorage.getItem(getDismissKey())) {
|
||||
if (!blocking && sessionStorage.getItem(getDismissKey())) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockingRecovery.value = blocking;
|
||||
initialStartTime.value = await fetchLegacyStartTime().catch(() => null);
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
function handleRecoveryEvent(event: Event) {
|
||||
const versionData = (event as CustomEvent<VersionData>).detail || {};
|
||||
void showRecoveryDialog(versionData);
|
||||
const versionData = (event as CustomEvent<RecoveryEventDetail>).detail || {};
|
||||
void showRecoveryDialog(versionData, !!versionData.blocking);
|
||||
}
|
||||
|
||||
async function detectUpgradeMismatch() {
|
||||
|
||||
@@ -60,6 +60,7 @@ let restartCompleted = ref(false);
|
||||
let restartReloadCountdown = ref(3);
|
||||
let restartReloadTimer: ReturnType<typeof setInterval> | null = null;
|
||||
const RESTART_FEEDBACK_DELAY_SECONDS = 3;
|
||||
const RESTART_START_TIME_POLL_INTERVAL_MS = 2000;
|
||||
type DownloadStageStatus = "pending" | "running" | "done" | "error";
|
||||
type DownloadStage = {
|
||||
status: DownloadStageStatus;
|
||||
@@ -606,6 +607,7 @@ function showRestartCompleted() {
|
||||
if (restartCompleted.value) {
|
||||
return;
|
||||
}
|
||||
stopUpdateProgressPolling();
|
||||
stopRestartReloadTimer();
|
||||
restartWaiting.value = false;
|
||||
restartCompleted.value = true;
|
||||
@@ -626,20 +628,29 @@ function showRestartCompleted() {
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function waitForAstrBotRestart(initialStartTime: number | string | null) {
|
||||
if (restartWaiting.value || restartCompleted.value) {
|
||||
function waitForAstrBotRestart(
|
||||
initialStartTime: number | string | null,
|
||||
showWaiting = true,
|
||||
) {
|
||||
if (restartCompleted.value) {
|
||||
return;
|
||||
}
|
||||
stopRestartPolling();
|
||||
restartWaiting.value = true;
|
||||
if (showWaiting && !restartWaiting.value) {
|
||||
restartWaiting.value = true;
|
||||
restartStartTime.value = initialStartTime;
|
||||
updateProgress.value = {
|
||||
...updateProgress.value,
|
||||
stage: "restart",
|
||||
status: "success",
|
||||
message: t("core.header.updateDialog.progress.restarting"),
|
||||
overall_percent: 100,
|
||||
};
|
||||
}
|
||||
if (restartPollTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
restartStartTime.value = initialStartTime;
|
||||
updateProgress.value = {
|
||||
...updateProgress.value,
|
||||
stage: "restart",
|
||||
status: "success",
|
||||
message: t("core.header.updateDialog.progress.restarting"),
|
||||
overall_percent: 100,
|
||||
};
|
||||
|
||||
const poll = async () => {
|
||||
try {
|
||||
@@ -657,9 +668,10 @@ function waitForAstrBotRestart(initialStartTime: number | string | null) {
|
||||
}
|
||||
};
|
||||
|
||||
void poll();
|
||||
restartPollTimer = setInterval(() => {
|
||||
void poll();
|
||||
}, 1000);
|
||||
}, RESTART_START_TIME_POLL_INTERVAL_MS);
|
||||
}
|
||||
|
||||
function applyUpdateProgress(payload: UpdateProgress) {
|
||||
@@ -686,6 +698,9 @@ function applyUpdateProgress(payload: UpdateProgress) {
|
||||
if (payload.status === "success" || payload.status === "error") {
|
||||
stopUpdateProgressPolling();
|
||||
}
|
||||
if (payload.status === "error") {
|
||||
stopRestartPolling();
|
||||
}
|
||||
if (payload.status === "success") {
|
||||
waitForAstrBotRestart(restartStartTime.value);
|
||||
}
|
||||
@@ -714,7 +729,10 @@ async function switchVersion(targetVersion: string) {
|
||||
typeof crypto !== "undefined" && "randomUUID" in crypto
|
||||
? crypto.randomUUID()
|
||||
: `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
let initialStartTime: number | string | null = null;
|
||||
let initialStartTime: number | string | null = commonStore.getStartTime();
|
||||
if (initialStartTime === -1) {
|
||||
initialStartTime = null;
|
||||
}
|
||||
updateProgress.value = {
|
||||
...createEmptyUpdateProgress(),
|
||||
id: progressId,
|
||||
@@ -726,12 +744,15 @@ async function switchVersion(targetVersion: string) {
|
||||
updateStatus.value = t("core.header.updateDialog.status.switching");
|
||||
installLoading.value = true;
|
||||
|
||||
try {
|
||||
initialStartTime = await fetchAstrBotStartTime();
|
||||
} catch (_error) {
|
||||
initialStartTime = commonStore.getStartTime();
|
||||
if (initialStartTime === null) {
|
||||
try {
|
||||
initialStartTime = await fetchAstrBotStartTime();
|
||||
} catch (_error) {
|
||||
initialStartTime = null;
|
||||
}
|
||||
}
|
||||
restartStartTime.value = initialStartTime;
|
||||
waitForAstrBotRestart(initialStartTime, false);
|
||||
startUpdateProgressPolling(progressId);
|
||||
|
||||
updatesApi
|
||||
@@ -744,6 +765,7 @@ async function switchVersion(targetVersion: string) {
|
||||
updateStatus.value = res.data.message || "";
|
||||
if (res.data.status === "error") {
|
||||
stopUpdateProgressPolling();
|
||||
stopRestartPolling();
|
||||
updateProgress.value = {
|
||||
...updateProgress.value,
|
||||
status: "error",
|
||||
@@ -756,6 +778,12 @@ async function switchVersion(targetVersion: string) {
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
stopUpdateProgressPolling();
|
||||
if (!err?.response && restartPollTimer) {
|
||||
waitForAstrBotRestart(restartStartTime.value);
|
||||
updateStatus.value = t("core.header.updateDialog.progress.restarting");
|
||||
return;
|
||||
}
|
||||
stopRestartPolling();
|
||||
updateStatus.value = err;
|
||||
updateProgress.value = {
|
||||
...updateProgress.value,
|
||||
|
||||
@@ -104,6 +104,7 @@ export const useAuthStore = defineStore("auth", {
|
||||
detail: {
|
||||
version: versionData.version,
|
||||
dashboard_version: versionData.dashboard_version,
|
||||
blocking: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user