From 8d075ac401881899e161420c812e44d5957b08d6 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 14 May 2026 19:48:44 +0800 Subject: [PATCH] Add direct login button handler --- public/app.js | 14 ++++++++++++++ public/mobile.js | 14 ++++++++++++++ test/access-password.test.js | 6 ++++++ 3 files changed, 34 insertions(+) diff --git a/public/app.js b/public/app.js index 101dea3..8ea401d 100644 --- a/public/app.js +++ b/public/app.js @@ -2,6 +2,7 @@ const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1"; const authGate = document.querySelector("#auth-gate"); const authForm = document.querySelector("#auth-form"); const authPassword = document.querySelector("#auth-password"); +const authSubmit = document.querySelector("#auth-submit"); const authMessage = document.querySelector("#auth-message"); const form = document.querySelector("#collect-form"); const input = document.querySelector("#program-name"); @@ -110,12 +111,18 @@ let resolveTimer = 0; let resolveRequestId = 0; let temporaryQueryItems = []; let appStarted = false; +let authSubmitting = false; authForm?.addEventListener("submit", async (event) => { event.preventDefault(); await submitAccessPassword(); }); +authSubmit?.addEventListener("click", async (event) => { + event.preventDefault(); + await submitAccessPassword(); +}); + for (const [platform, element] of Object.entries(urlInputs)) { element.addEventListener("input", () => { dirtyUrlInputs.add(platform); @@ -2138,11 +2145,14 @@ async function ensureAccessAuth() { } async function submitAccessPassword() { + if (authSubmitting) return; const password = authPassword?.value || ""; if (!password.trim()) { showAuthGate("请输入访问密码"); return; } + authSubmitting = true; + if (authSubmit) authSubmit.disabled = true; setAuthMessage("正在验证..."); try { const response = await fetch("/api/auth/login", { @@ -2154,10 +2164,14 @@ async function submitAccessPassword() { if (!response.ok) throw new Error(payload.error || "访问密码不正确"); if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token); if (authPassword) authPassword.value = ""; + setAuthMessage("登录成功,正在进入..."); hideAuthGate(); startApp(); } catch (error) { showAuthGate(error.message || "访问密码不正确"); + } finally { + authSubmitting = false; + if (authSubmit) authSubmit.disabled = false; } } diff --git a/public/mobile.js b/public/mobile.js index 964438b..9e7fbbc 100644 --- a/public/mobile.js +++ b/public/mobile.js @@ -2,6 +2,7 @@ const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1"; const authGate = document.querySelector("#auth-gate"); const authForm = document.querySelector("#auth-form"); const authPassword = document.querySelector("#auth-password"); +const authSubmit = document.querySelector("#auth-submit"); const authMessage = document.querySelector("#auth-message"); const form = document.querySelector("#collect-form"); const input = document.querySelector("#program-name"); @@ -62,12 +63,18 @@ let activeName = ""; let dirtyUrlInputs = new Set(); let deferredInstallPrompt = null; let appStarted = false; +let authSubmitting = false; authForm?.addEventListener("submit", async (event) => { event.preventDefault(); await submitAccessPassword(); }); +authSubmit?.addEventListener("click", async (event) => { + event.preventDefault(); + await submitAccessPassword(); +}); + for (const [platform, element] of Object.entries(urlInputs)) { element.addEventListener("input", () => { dirtyUrlInputs.add(platform); @@ -734,11 +741,14 @@ async function ensureAccessAuth() { } async function submitAccessPassword() { + if (authSubmitting) return; const password = authPassword?.value || ""; if (!password.trim()) { showAuthGate("请输入访问密码"); return; } + authSubmitting = true; + if (authSubmit) authSubmit.disabled = true; setAuthMessage("正在验证..."); try { const response = await fetch(apiUrl("/api/auth/login"), { @@ -750,10 +760,14 @@ async function submitAccessPassword() { if (!response.ok) throw new Error(payload.error || "访问密码不正确"); if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token); if (authPassword) authPassword.value = ""; + setAuthMessage("登录成功,正在进入..."); hideAuthGate(); await startApp(); } catch (error) { showAuthGate(error.message || "访问密码不正确"); + } finally { + authSubmitting = false; + if (authSubmit) authSubmit.disabled = false; } } diff --git a/test/access-password.test.js b/test/access-password.test.js index c3e6d34..71c7d04 100644 --- a/test/access-password.test.js +++ b/test/access-password.test.js @@ -31,10 +31,13 @@ test("desktop page has a password gate and sends auth token with API calls", () test("desktop login submit is bound before the rest of the app can fail", () => { const authBinding = desktopJs.indexOf('authForm?.addEventListener("submit"'); + const authClickBinding = desktopJs.indexOf('authSubmit?.addEventListener("click"'); const collectBinding = desktopJs.indexOf('form.addEventListener("submit"'); assert.ok(authBinding > -1, "auth submit binding should exist"); + assert.ok(authClickBinding > -1, "auth button click binding should exist"); assert.ok(collectBinding > -1, "collect submit binding should exist"); assert.ok(authBinding < collectBinding, "auth binding must run before normal app bindings"); + assert.ok(authClickBinding < collectBinding, "auth click binding must run before normal app bindings"); }); test("mobile page has the same password gate for cloud use", () => { @@ -49,8 +52,11 @@ test("mobile page has the same password gate for cloud use", () => { test("mobile login submit is bound before normal capture events", () => { const authBinding = mobileJs.indexOf('authForm?.addEventListener("submit"'); + const authClickBinding = mobileJs.indexOf('authSubmit?.addEventListener("click"'); const collectBinding = mobileJs.indexOf('form.addEventListener("submit"'); assert.ok(authBinding > -1, "auth submit binding should exist"); + assert.ok(authClickBinding > -1, "auth button click binding should exist"); assert.ok(collectBinding > -1, "collect submit binding should exist"); assert.ok(authBinding < collectBinding, "auth binding must run before normal app bindings"); + assert.ok(authClickBinding < collectBinding, "auth click binding must run before normal app bindings"); });