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

View File

@ -8,15 +8,6 @@
<link rel="stylesheet" href="/rankings.css">
</head>
<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 导航">
<a class="app-nav-brand" href="#desktop-dashboard">节目热度采集</a>
<span id="app-version-badge" class="app-version-badge">桌面开发版</span>

View File

@ -9,15 +9,6 @@
<link rel="stylesheet" href="/mobile.css">
</head>
<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">
<header class="mobile-header">
<div>

View File

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

View File

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