mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-01 01:10:21 +08:00
fix: prefer v1 auth with legacy recovery fallback
This commit is contained in:
@@ -180,7 +180,7 @@ function typed<T>(response: Promise<unknown>): V1Response<T> {
|
||||
return response as unknown as V1Response<T>;
|
||||
}
|
||||
|
||||
function isLegacyFallbackError(error: unknown): boolean {
|
||||
export function isLegacyFallbackError(error: unknown): boolean {
|
||||
const axiosError = error as {
|
||||
response?: { status?: number; data?: { message?: string } | string };
|
||||
message?: string;
|
||||
@@ -556,25 +556,40 @@ export const providerApi = {
|
||||
|
||||
export const authApi = {
|
||||
login(payload: LoginRequest) {
|
||||
return httpClient.post<ApiEnvelope<any>>('/api/auth/login', payload);
|
||||
return withLegacyFallback<any>(openApiV1.login({ body: payload }), () =>
|
||||
httpClient.post<ApiEnvelope<any>>('/api/auth/login', payload),
|
||||
);
|
||||
},
|
||||
logout() {
|
||||
return httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/logout');
|
||||
return withLegacyFallback<OpenConfig>(openApiV1.logout(), () =>
|
||||
httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/logout'),
|
||||
);
|
||||
},
|
||||
setupStatus() {
|
||||
return httpClient.get<ApiEnvelope<any>>('/api/auth/setup-status');
|
||||
return withLegacyFallback<any>(openApiV1.getAuthSetupStatus(), () =>
|
||||
httpClient.get<ApiEnvelope<any>>('/api/auth/setup-status'),
|
||||
);
|
||||
},
|
||||
setup(payload: SetupAuthRequest) {
|
||||
return httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/setup', payload);
|
||||
return withLegacyFallback<OpenConfig>(openApiV1.setupAuth({ body: payload }), () =>
|
||||
httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/setup', payload),
|
||||
);
|
||||
},
|
||||
setupTotp(payload?: TotpSetupRequest) {
|
||||
return httpClient.post<ApiEnvelope<any>>('/api/auth/totp/setup', payload);
|
||||
return withLegacyFallback<any>(openApiV1.setupTotp({ body: payload }), () =>
|
||||
httpClient.post<ApiEnvelope<any>>('/api/auth/totp/setup', payload),
|
||||
);
|
||||
},
|
||||
recoverTotp() {
|
||||
return httpClient.post<ApiEnvelope<any>>('/api/auth/totp/recovery');
|
||||
return withLegacyFallback<any>(openApiV1.recoverTotp(), () =>
|
||||
httpClient.post<ApiEnvelope<any>>('/api/auth/totp/recovery'),
|
||||
);
|
||||
},
|
||||
updateAccount(payload: UpdateAccountRequest) {
|
||||
return httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/account/edit', payload);
|
||||
return withLegacyFallback<OpenConfig>(
|
||||
openApiV1.updateAuthAccount({ body: payload }),
|
||||
() => httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/account/edit', payload),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -48,10 +48,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import axios, { type AxiosRequestConfig } from 'axios';
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import type { ApiEnvelope, VersionData } from '@/api/v1';
|
||||
import { httpClient } from '@/api/http';
|
||||
import { useI18n } from '@/i18n/composables';
|
||||
|
||||
type StartTimeData = {
|
||||
@@ -59,6 +60,7 @@ type StartTimeData = {
|
||||
};
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const visible = ref(false);
|
||||
const restarting = ref(false);
|
||||
@@ -68,6 +70,8 @@ const dashboardVersion = ref('');
|
||||
const initialStartTime = ref<number | string | null>(null);
|
||||
|
||||
let restartTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let detecting = false;
|
||||
const recoveryClient = axios.create();
|
||||
|
||||
function normalizeVersion(version?: string | null) {
|
||||
return (version || '').trim().replace(/^v/i, '');
|
||||
@@ -102,9 +106,26 @@ function getDismissKey() {
|
||||
return `astrbot-upgrade-recovery-dismissed:${coreVersion.value}:${dashboardVersion.value}`;
|
||||
}
|
||||
|
||||
function recoveryRequestConfig(validateStatus = false): AxiosRequestConfig {
|
||||
const headers: Record<string, string> = {};
|
||||
const token = localStorage.getItem('token');
|
||||
const locale = localStorage.getItem('astrbot-locale');
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
if (locale) {
|
||||
headers['Accept-Language'] = locale;
|
||||
}
|
||||
return {
|
||||
headers,
|
||||
...(validateStatus ? { validateStatus: () => true } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchLegacyStartTime() {
|
||||
const response = await httpClient.get<ApiEnvelope<StartTimeData>>(
|
||||
const response = await recoveryClient.get<ApiEnvelope<StartTimeData>>(
|
||||
'/api/stat/start-time',
|
||||
recoveryRequestConfig(),
|
||||
);
|
||||
return response.data?.data?.start_time ?? null;
|
||||
}
|
||||
@@ -153,7 +174,11 @@ async function restartCore() {
|
||||
try {
|
||||
initialStartTime.value =
|
||||
initialStartTime.value ?? (await fetchLegacyStartTime());
|
||||
await httpClient.post<ApiEnvelope<unknown>>('/api/stat/restart-core');
|
||||
await recoveryClient.post<ApiEnvelope<unknown>>(
|
||||
'/api/stat/restart-core',
|
||||
undefined,
|
||||
recoveryRequestConfig(),
|
||||
);
|
||||
statusMessage.value = t('core.common.upgradeRecovery.waiting');
|
||||
waitForRestart();
|
||||
} catch (_error) {
|
||||
@@ -163,18 +188,22 @@ async function restartCore() {
|
||||
}
|
||||
|
||||
async function detectUpgradeMismatch() {
|
||||
if (detecting || visible.value || restarting.value) {
|
||||
return;
|
||||
}
|
||||
detecting = true;
|
||||
try {
|
||||
const v1Response = await httpClient.get<ApiEnvelope<unknown>>(
|
||||
const v1Response = await recoveryClient.get<ApiEnvelope<unknown>>(
|
||||
'/api/v1/auth/setup-status',
|
||||
{ validateStatus: () => true },
|
||||
recoveryRequestConfig(true),
|
||||
);
|
||||
if (!isMissingApiKeyResponse(v1Response)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const legacyResponse = await httpClient.get<ApiEnvelope<VersionData>>(
|
||||
const legacyResponse = await recoveryClient.get<ApiEnvelope<VersionData>>(
|
||||
'/api/stat/version',
|
||||
{ validateStatus: () => true },
|
||||
recoveryRequestConfig(true),
|
||||
);
|
||||
if (legacyResponse.status === 401 || legacyResponse.status >= 400) {
|
||||
return;
|
||||
@@ -195,6 +224,8 @@ async function detectUpgradeMismatch() {
|
||||
visible.value = true;
|
||||
} catch (_error) {
|
||||
// This recovery dialog is best-effort and should never block the app.
|
||||
} finally {
|
||||
detecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +233,13 @@ onMounted(() => {
|
||||
void detectUpgradeMismatch();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
void detectUpgradeMismatch();
|
||||
},
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearRestartTimer();
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import StyledMenu from "@/components/shared/StyledMenu.vue";
|
||||
import { useLanguageSwitcher } from "@/i18n/composables";
|
||||
import type { Locale } from "@/i18n/types";
|
||||
import AboutPage from "@/views/AboutPage.vue";
|
||||
import { authApi, statsApi, updatesApi } from "@/api/v1";
|
||||
import { authApi, isLegacyFallbackError, statsApi, updatesApi } from "@/api/v1";
|
||||
import { getDesktopRuntimeInfo } from "@/utils/desktopRuntime";
|
||||
|
||||
enableKatex();
|
||||
@@ -479,6 +479,10 @@ function checkUpdate() {
|
||||
: res.data.data.dashboard_has_new_version;
|
||||
})
|
||||
.catch((err) => {
|
||||
if (isLegacyFallbackError(err)) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (err.response && err.response.status == 401) {
|
||||
console.log("401");
|
||||
const authStore = useAuthStore();
|
||||
|
||||
Reference in New Issue
Block a user