Improve installer extraction, login failures, and upload handling

This commit is contained in:
2026-07-27 00:17:30 +08:00
parent 97ea6fb7aa
commit 73555cd04c
14 changed files with 199 additions and 31 deletions
@@ -67,6 +67,7 @@ const router = useRouter();
const currentPath = computed(() => normalizeAdminPath(route.path));
const loading = ref(false);
const loginPending = ref(false);
const captchaPending = ref(false);
const toast = ref<ToastState | null>(null);
const autoRefreshPaused = ref(false);
const databaseFormEditing = ref(false);
@@ -75,6 +76,7 @@ let refreshTimer: number | undefined;
let systemRefreshTimer: number | undefined;
let toastTimer: number | undefined;
let events: EventSource | null = null;
let captchaRequestSerial = 0;
const authStore = createAuthStore();
const dashboardStore = createDashboardStore();
@@ -491,7 +493,26 @@ function isAuthError(raw: string, message: string) {
}
async function loadCaptcha() {
captcha.value = await adminFetch<Captcha>("/api/admin/auth/captcha", {}, { timeoutMs: 5000 });
const serial = ++captchaRequestSerial;
captchaPending.value = true;
try {
const next = await adminFetch<Captcha>("/api/admin/auth/captcha", {}, { timeoutMs: 5000 });
if (serial === captchaRequestSerial) {
captcha.value = next;
loginForm.captcha = "";
}
} finally {
if (serial === captchaRequestSerial) captchaPending.value = false;
}
}
async function refreshCaptcha() {
try {
await loadCaptcha();
} catch {
captcha.value = null;
setToast("验证码加载失败,请检查服务端连接", "error");
}
}
async function loadAuthBootstrap() {
@@ -511,7 +532,7 @@ async function login() {
const data = await adminFetch<{ csrfToken: string }>("/api/admin/auth/login", {
method: "POST",
body: JSON.stringify({ ...loginForm, captchaId: captcha.value?.captchaId }),
}, { timeoutMs: 8000 });
}, { timeoutMs: 12000 });
csrf.value = data.csrfToken;
sessionStorage.setItem("ymhut.csrf", csrf.value);
localStorage.removeItem("ymhut.csrf");
@@ -520,8 +541,7 @@ async function login() {
} catch (error) {
const message = toChineseError(error instanceof Error ? error.message : String(error));
setToast(message, "error");
loginForm.captcha = "";
void loadCaptcha().catch(() => {
await loadCaptcha().catch(() => {
captcha.value = null;
});
} finally {
@@ -1742,9 +1762,9 @@ function connectAdminEvents() {
验证码
<div class="captcha-row">
<input v-model="loginForm.captcha" :disabled="loginPending" autocomplete="off" />
<button class="captcha-button" type="button" title="刷新验证码" :disabled="loginPending" @click="loadCaptcha">
<button class="captcha-button" type="button" title="刷新验证码" :disabled="loginPending || captchaPending" @click="refreshCaptcha">
<img v-if="captcha?.image" :src="captcha.image" alt="验证码" />
<span v-else>刷新</span>
<span v-else>{{ captchaPending ? "加载中" : "刷新" }}</span>
</button>
</div>
</label>
@@ -32,6 +32,9 @@ const exactMessages: Record<string, string> = {
const codeMessages: Record<string, string> = {
UNAUTHORIZED: "需要登录后继续操作",
LOGIN_FAILED: "登录失败,请检查密码和验证码",
LOGIN_LOCKED: "登录失败次数过多,请 5 分钟后重试",
CAPTCHA_INVALID: "验证码错误或已过期,请输入新的验证码",
CREDENTIALS_INVALID: "账号或密码不正确",
LOGIN_TIMEOUT: "登录校验超时,请稍后重试",
PASSWORD_CHANGE_FAILED: "密码修改失败",
INVALID_PAYLOAD: "提交内容格式不正确",
@@ -50,6 +53,7 @@ const codeMessages: Record<string, string> = {
UPLOAD_STORAGE_FAILED: "服务端无法保存上传文件",
MANIFEST_UPDATE_FAILED: "发布包已回滚,更新清单写入失败",
PACKAGE_UPLOAD_FAILED: "发布包上传失败",
UPLOAD_INTERRUPTED: "上传连接已中断,请保持页面打开后重试",
SOURCE_SAVE_FAILED: "接口源保存失败",
CHECK_FAILED: "接口健康检测失败",
SYNC_FAILED: "同步操作失败",