Remove embedded auth overlay from app pages
This commit is contained in:
parent
a69804a163
commit
6fb245194e
@ -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 = "") {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 = "") {
|
||||
|
||||
@ -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", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user