Fix ranking auth during cloud login
This commit is contained in:
parent
8d075ac401
commit
b42059f4c8
@ -2163,6 +2163,7 @@ async function submitAccessPassword() {
|
|||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
if (!response.ok) throw new Error(payload.error || "访问密码不正确");
|
if (!response.ok) throw new Error(payload.error || "访问密码不正确");
|
||||||
if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token);
|
if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token);
|
||||||
|
window.dispatchEvent(new CustomEvent("hotness:auth-updated"));
|
||||||
if (authPassword) authPassword.value = "";
|
if (authPassword) authPassword.value = "";
|
||||||
setAuthMessage("登录成功,正在进入...");
|
setAuthMessage("登录成功,正在进入...");
|
||||||
hideAuthGate();
|
hideAuthGate();
|
||||||
|
|||||||
@ -759,6 +759,7 @@ async function submitAccessPassword() {
|
|||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
if (!response.ok) throw new Error(payload.error || "访问密码不正确");
|
if (!response.ok) throw new Error(payload.error || "访问密码不正确");
|
||||||
if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token);
|
if (payload.token) localStorage.setItem(HOTNESS_AUTH_TOKEN_KEY, payload.token);
|
||||||
|
window.dispatchEvent(new CustomEvent("hotness:auth-updated"));
|
||||||
if (authPassword) authPassword.value = "";
|
if (authPassword) authPassword.value = "";
|
||||||
setAuthMessage("登录成功,正在进入...");
|
setAuthMessage("登录成功,正在进入...");
|
||||||
hideAuthGate();
|
hideAuthGate();
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
const HOTNESS_AUTH_TOKEN_KEY = "video-hotness-auth-token-v1";
|
||||||
const PLATFORM_LABELS = { tencent: "腾讯视频", youku: "优酷", iqiyi: "爱奇艺", mgtv: "芒果TV" };
|
const PLATFORM_LABELS = { tencent: "腾讯视频", youku: "优酷", iqiyi: "爱奇艺", mgtv: "芒果TV" };
|
||||||
const TYPE_LABELS = { animation: "动画", education: "早教", song: "儿歌", toy: "玩具", movie: "电影", other: "其他" };
|
const TYPE_LABELS = { animation: "动画", education: "早教", song: "儿歌", toy: "玩具", movie: "电影", other: "其他" };
|
||||||
const SOURCE_LABELS = { new: "新片", recommend: "推荐", rank: "榜单", hot: "热播", channel: "频道" };
|
const SOURCE_LABELS = { new: "新片", recommend: "推荐", rank: "榜单", hot: "热播", channel: "频道" };
|
||||||
@ -21,19 +22,26 @@ const state = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const root = document.querySelector("#ranking-radar");
|
const root = document.querySelector("#ranking-radar");
|
||||||
if (root) init();
|
if (root) {
|
||||||
|
window.addEventListener("hotness:auth-updated", () => init());
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
render();
|
render();
|
||||||
const [defaults, latest] = await Promise.all([
|
try {
|
||||||
apiGet("/api/rankings/default-sources"),
|
const [defaults, latest] = await Promise.all([
|
||||||
apiGet("/api/kids-trends/latest"),
|
apiGet("/api/rankings/default-sources"),
|
||||||
refreshPrograms(),
|
apiGet("/api/kids-trends/latest"),
|
||||||
]);
|
refreshPrograms(),
|
||||||
state.defaults = defaults.sources || [];
|
]);
|
||||||
if (latest.trend?.results?.length) {
|
state.defaults = defaults.sources || [];
|
||||||
state.trendResults = latest.trend.results || [];
|
if (latest.trend?.results?.length) {
|
||||||
state.message = `已恢复上次上新趋势:${formatTime(latest.trend.captured_at)},采集 ${latest.trend.collected_count || state.trendResults.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();
|
render();
|
||||||
}
|
}
|
||||||
@ -386,25 +394,34 @@ function options(map, selected = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function apiGet(path) {
|
async function apiGet(path) {
|
||||||
const response = await fetch(path);
|
const response = await fetch(path, { headers: authHeaders() });
|
||||||
return parseApiResponse(response);
|
return parseApiResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apiPost(path, payload) {
|
async function apiPost(path, payload) {
|
||||||
const response = await fetch(path, {
|
const response = await fetch(path, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json", ...authHeaders() },
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
return parseApiResponse(response);
|
return parseApiResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseApiResponse(response) {
|
async function parseApiResponse(response) {
|
||||||
const data = await response.json();
|
const data = await response.json().catch(() => ({}));
|
||||||
if (!response.ok) throw new Error(data.error || "request failed");
|
if (!response.ok) {
|
||||||
|
const error = new Error(data.error || "request failed");
|
||||||
|
error.requiresAuth = Boolean(data.requires_auth || response.status === 401);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function authHeaders() {
|
||||||
|
const token = localStorage.getItem(HOTNESS_AUTH_TOKEN_KEY) || "";
|
||||||
|
return token ? { "x-hotness-auth-token": token } : {};
|
||||||
|
}
|
||||||
|
|
||||||
function formatTime(value) {
|
function formatTime(value) {
|
||||||
if (!value) return "";
|
if (!value) return "";
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
|
|||||||
@ -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 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 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 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", () => {
|
test("server supports optional shared access password authentication", () => {
|
||||||
assert.match(server, /HOTNESS_ACCESS_PASSWORD/);
|
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(authBinding < collectBinding, "auth binding must run before normal app bindings");
|
||||||
assert.ok(authClickBinding < collectBinding, "auth click 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/);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user