Remove embedded auth overlay from app pages

This commit is contained in:
Codex 2026-05-14 20:38:31 +08:00
parent a69804a163
commit 6fb245194e
5 changed files with 21 additions and 72 deletions

View File

@ -1,7 +1,4 @@
const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1"; const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1";
const authGate = document.querySelector("#auth-gate");
const authPassword = document.querySelector("#auth-password");
const authMessage = document.querySelector("#auth-message");
const form = document.querySelector("#collect-form"); const form = document.querySelector("#collect-form");
const input = document.querySelector("#program-name"); const input = document.querySelector("#program-name");
const button = document.querySelector("#collect-button"); const button = document.querySelector("#collect-button");
@ -2135,11 +2132,10 @@ async function ensureAccessAuth() {
const response = await fetch("/api/auth/status", { headers: authHeaders() }); const response = await fetch("/api/auth/status", { headers: authHeaders() });
const payload = await response.json(); const payload = await response.json();
if (!payload.enabled || payload.authorized) { if (!payload.enabled || payload.authorized) {
hideAuthGate();
return true; return true;
} }
} catch {} } catch {}
showAuthGate(""); redirectToLogin();
return false; return false;
} }
@ -2151,24 +2147,12 @@ function authHeaders() {
function handleAuthFailure(response, payload) { function handleAuthFailure(response, payload) {
if (response.status !== 401 || !payload?.requires_auth) return false; if (response.status !== 401 || !payload?.requires_auth) return false;
localStorage.removeItem(HOTNESS_AUTH_TOKEN_KEY); localStorage.removeItem(HOTNESS_AUTH_TOKEN_KEY);
showAuthGate(payload.error || "需要输入访问密码"); redirectToLogin();
return true; return true;
} }
function showAuthGate(message = "") { function redirectToLogin() {
if (!authGate) return; window.location.href = "/";
authGate.hidden = false;
setAuthMessage(message);
requestAnimationFrame(() => authPassword?.focus());
}
function hideAuthGate() {
if (authGate) authGate.hidden = true;
setAuthMessage("");
}
function setAuthMessage(message) {
if (authMessage) authMessage.textContent = message || "";
} }
function setBusy(isBusy, text = "") { function setBusy(isBusy, text = "") {

View File

@ -8,15 +8,6 @@
<link rel="stylesheet" href="/rankings.css"> <link rel="stylesheet" href="/rankings.css">
</head> </head>
<body> <body>
<section id="auth-gate" class="auth-gate" hidden>
<form id="auth-form" class="auth-card" action="/auth/login" method="post">
<div class="auth-title">输入访问密码</div>
<p>云端部署时需要先验证,验证后这台设备会记住登录状态。</p>
<input id="auth-password" name="password" type="password" autocomplete="current-password" placeholder="访问密码" required>
<button id="auth-submit" type="submit">进入系统</button>
<div id="auth-message" class="auth-message" aria-live="polite"></div>
</form>
</section>
<nav class="app-nav" aria-label="桌面 App 导航"> <nav class="app-nav" aria-label="桌面 App 导航">
<a class="app-nav-brand" href="#desktop-dashboard">节目热度采集</a> <a class="app-nav-brand" href="#desktop-dashboard">节目热度采集</a>
<span id="app-version-badge" class="app-version-badge">桌面开发版</span> <span id="app-version-badge" class="app-version-badge">桌面开发版</span>

View File

@ -9,15 +9,6 @@
<link rel="stylesheet" href="/mobile.css"> <link rel="stylesheet" href="/mobile.css">
</head> </head>
<body> <body>
<section id="auth-gate" class="auth-gate" hidden>
<form id="auth-form" class="auth-card" action="/auth/login" method="post">
<div class="auth-title">输入访问密码</div>
<p>云端使用时需要先验证,验证后这台手机会记住登录状态。</p>
<input id="auth-password" name="password" type="password" autocomplete="current-password" placeholder="访问密码" required>
<button id="auth-submit" type="submit">进入手机版</button>
<div id="auth-message" class="auth-message" aria-live="polite"></div>
</form>
</section>
<main class="mobile-shell"> <main class="mobile-shell">
<header class="mobile-header"> <header class="mobile-header">
<div> <div>

View File

@ -1,7 +1,4 @@
const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1"; const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1";
const authGate = document.querySelector("#auth-gate");
const authPassword = document.querySelector("#auth-password");
const authMessage = document.querySelector("#auth-message");
const form = document.querySelector("#collect-form"); const form = document.querySelector("#collect-form");
const input = document.querySelector("#program-name"); const input = document.querySelector("#program-name");
const button = document.querySelector("#collect-button"); const button = document.querySelector("#collect-button");
@ -729,13 +726,12 @@ async function ensureAccessAuth() {
const response = await fetch(apiUrl("/api/auth/status"), { headers: authHeaders() }); const response = await fetch(apiUrl("/api/auth/status"), { headers: authHeaders() });
const payload = await response.json(); const payload = await response.json();
if (!payload.enabled || payload.authorized) { if (!payload.enabled || payload.authorized) {
hideAuthGate();
return true; return true;
} }
} catch { } catch {
return true; return true;
} }
showAuthGate(""); redirectToLogin();
return false; return false;
} }
@ -747,24 +743,12 @@ function authHeaders() {
function handleAuthFailure(response, payload) { function handleAuthFailure(response, payload) {
if (response.status !== 401 || !payload?.requires_auth) return false; if (response.status !== 401 || !payload?.requires_auth) return false;
localStorage.removeItem(HOTNESS_AUTH_TOKEN_KEY); localStorage.removeItem(HOTNESS_AUTH_TOKEN_KEY);
showAuthGate(payload.error || "需要输入访问密码"); redirectToLogin();
return true; return true;
} }
function showAuthGate(message = "") { function redirectToLogin() {
if (!authGate) return; window.location.href = "/mobile.html";
authGate.hidden = false;
setAuthMessage(message);
requestAnimationFrame(() => authPassword?.focus());
}
function hideAuthGate() {
if (authGate) authGate.hidden = true;
setAuthMessage("");
}
function setAuthMessage(message) {
if (authMessage) authMessage.textContent = message || "";
} }
function setBusy(isBusy, text = "") { function setBusy(isBusy, text = "") {

View File

@ -33,17 +33,17 @@ test("server renders a standalone password page before protected app pages", ()
assert.match(server, /输入访问密码/); assert.match(server, /输入访问密码/);
}); });
test("desktop page has a password gate and sends auth token with API calls", () => { test("desktop app page has no embedded password overlay after server-side auth", () => {
assert.match(desktopHtml, /id="auth-gate"/); assert.doesNotMatch(desktopHtml, /id="auth-gate"/);
assert.match(desktopHtml, /action="\/auth\/login"/); assert.doesNotMatch(desktopHtml, /id="auth-form"/);
assert.match(desktopHtml, /method="post"/); assert.doesNotMatch(desktopHtml, /id="auth-password"/);
assert.match(desktopHtml, /name="password"/); assert.match(desktopHtml, /id="collect-form"/);
assert.match(desktopHtml, /id="auth-password"/); assert.match(desktopHtml, /采集一次/);
assert.match(desktopJs, /HOTNESS_AUTH_TOKEN_KEY/); assert.match(desktopJs, /HOTNESS_AUTH_TOKEN_KEY/);
assert.match(desktopJs, /ensureAccessAuth/); assert.match(desktopJs, /ensureAccessAuth/);
assert.match(desktopJs, /authHeaders/); assert.match(desktopJs, /authHeaders/);
assert.match(desktopJs, /redirectToLogin/);
assert.match(desktopJs, /x-hotness-auth-token/i); assert.match(desktopJs, /x-hotness-auth-token/i);
assert.match(desktopCss, /\.auth-gate/);
}); });
test("desktop login form is not blocked by JavaScript", () => { test("desktop login form is not blocked by JavaScript", () => {
@ -60,17 +60,16 @@ test("desktop can finish login from a redirected access token", () => {
assert.match(desktopJs, /history\.replaceState/); assert.match(desktopJs, /history\.replaceState/);
}); });
test("mobile page has the same password gate for cloud use", () => { test("mobile app page has no embedded password overlay after server-side auth", () => {
assert.match(mobileHtml, /id="auth-gate"/); assert.doesNotMatch(mobileHtml, /id="auth-gate"/);
assert.match(mobileHtml, /action="\/auth\/login"/); assert.doesNotMatch(mobileHtml, /id="auth-form"/);
assert.match(mobileHtml, /method="post"/); assert.doesNotMatch(mobileHtml, /id="auth-password"/);
assert.match(mobileHtml, /name="password"/); assert.match(mobileHtml, /id="collect-form"/);
assert.match(mobileHtml, /id="auth-password"/);
assert.match(mobileJs, /HOTNESS_AUTH_TOKEN_KEY/); assert.match(mobileJs, /HOTNESS_AUTH_TOKEN_KEY/);
assert.match(mobileJs, /ensureAccessAuth/); assert.match(mobileJs, /ensureAccessAuth/);
assert.match(mobileJs, /authHeaders/); assert.match(mobileJs, /authHeaders/);
assert.match(mobileJs, /redirectToLogin/);
assert.match(mobileJs, /x-hotness-auth-token/i); assert.match(mobileJs, /x-hotness-auth-token/i);
assert.match(mobileCss, /\.auth-gate/);
}); });
test("mobile login form is not blocked by JavaScript", () => { test("mobile login form is not blocked by JavaScript", () => {