Fix ranking auth during cloud login

This commit is contained in:
Codex 2026-05-14 19:59:17 +08:00
parent 8d075ac401
commit b42059f4c8
4 changed files with 46 additions and 14 deletions

View File

@ -2163,6 +2163,7 @@ async function submitAccessPassword() {
const payload = await response.json();
if (!response.ok) throw new Error(payload.error || "访问密码不正确");
if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token);
window.dispatchEvent(new CustomEvent("hotness:auth-updated"));
if (authPassword) authPassword.value = "";
setAuthMessage("登录成功,正在进入...");
hideAuthGate();

View File

@ -759,6 +759,7 @@ async function submitAccessPassword() {
const payload = await response.json();
if (!response.ok) throw new Error(payload.error || "访问密码不正确");
if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token);
window.dispatchEvent(new CustomEvent("hotness:auth-updated"));
if (authPassword) authPassword.value = "";
setAuthMessage("登录成功,正在进入...");
hideAuthGate();

View File

@ -1,3 +1,4 @@
const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1";
const PLATFORM_LABELS = { tencent: "腾讯视频", youku: "优酷", iqiyi: "爱奇艺", mgtv: "芒果TV" };
const TYPE_LABELS = { animation: "动画", education: "早教", song: "儿歌", toy: "玩具", movie: "电影", other: "其他" };
const SOURCE_LABELS = { new: "新片", recommend: "推荐", rank: "榜单", hot: "热播", channel: "频道" };
@ -21,19 +22,26 @@ const state = {
};
const root = document.querySelector("#ranking-radar");
if (root) init();
if (root) {
window.addEventListener("hotness:auth-updated", () => init());
init();
}
async function init() {
render();
const [defaults, latest] = await Promise.all([
apiGet("/api/rankings/default-sources"),
apiGet("/api/kids-trends/latest"),
refreshPrograms(),
]);
state.defaults = defaults.sources || [];
if (latest.trend?.results?.length) {
state.trendResults = latest.trend.results || [];
state.message = `已恢复上次上新趋势:${formatTime(latest.trend.captured_at)},采集 ${latest.trend.collected_count || state.trendResults.length} 个节目`;
try {
const [defaults, latest] = await Promise.all([
apiGet("/api/rankings/default-sources"),
apiGet("/api/kids-trends/latest"),
refreshPrograms(),
]);
state.defaults = defaults.sources || [];
if (latest.trend?.results?.length) {
state.trendResults = latest.trend.results || [];
state.message = `已恢复上次上新趋势:${formatTime(latest.trend.captured_at)},采集 ${latest.trend.collected_count || state.trendResults.length} 个节目`;
}
} catch (error) {
state.message = error.requiresAuth ? "请先输入访问密码" : error.message;
}
render();
}
@ -386,25 +394,34 @@ function options(map, selected = "") {
}
async function apiGet(path) {
const response = await fetch(path);
const response = await fetch(path, { headers: authHeaders() });
return parseApiResponse(response);
}
async function apiPost(path, payload) {
const response = await fetch(path, {
method: "POST",
headers: { "content-type": "application/json" },
headers: { "content-type": "application/json", ...authHeaders() },
body: JSON.stringify(payload),
});
return parseApiResponse(response);
}
async function parseApiResponse(response) {
const data = await response.json();
if (!response.ok) throw new Error(data.error || "request failed");
const data = await response.json().catch(() => ({}));
if (!response.ok) {
const error = new Error(data.error || "request failed");
error.requiresAuth = Boolean(data.requires_auth || response.status === 401);
throw error;
}
return data;
}
function authHeaders() {
const token = localStorage.getItem(HOTNESS_AUTH_TOKEN_KEY) || "";
return token ? { "x-hotness-auth-token": token } : {};
}
function formatTime(value) {
if (!value) return "";
const date = new Date(value);

View File

@ -9,6 +9,7 @@ const desktopCss = await readFile(new URL("../public/styles.css", import.meta.ur
const mobileHtml = await readFile(new URL("../public/mobile.html", import.meta.url), "utf8");
const mobileJs = await readFile(new URL("../public/mobile.js", import.meta.url), "utf8");
const mobileCss = await readFile(new URL("../public/mobile.css", import.meta.url), "utf8");
const rankingsJs = await readFile(new URL("../public/rankings.js", import.meta.url), "utf8");
test("server supports optional shared access password authentication", () => {
assert.match(server, /HOTNESS_ACCESS_PASSWORD/);
@ -60,3 +61,15 @@ test("mobile login submit is bound before normal capture events", () => {
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("ranking radar requests respect the shared cloud login token", () => {
assert.match(rankingsJs, /HOTNESS_AUTH_TOKEN_KEY/);
assert.match(rankingsJs, /authHeaders/);
assert.match(rankingsJs, /x-hotness-auth-token/i);
assert.match(rankingsJs, /requires_auth/);
assert.match(rankingsJs, /hotness:auth-updated/);
});
test("desktop login notifies secondary modules after auth succeeds", () => {
assert.match(desktopJs, /hotness:auth-updated/);
});