Files
planet/frontend/public/earth/js/oobe.js
linkong 37e92e7572
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
release: bump version to 0.64.0
2026-05-21 03:46:02 +08:00

127 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const EARTH_OOBE_STATUS_API = "/api/v1/earth/oobe-status";
const EARTH_OOBE_SKIP_KEY = "planet-earth-oobe-skip-until-v1";
const EARTH_OOBE_SKIP_MS = 6 * 60 * 60 * 1000;
function getAuthToken() {
try {
const raw = window.localStorage?.getItem("auth-storage");
if (!raw) return "";
const parsed = JSON.parse(raw);
return parsed?.state?.token || parsed?.token || "";
} catch {
return "";
}
}
function isTemporarilySkipped() {
try {
const value = Number(window.localStorage?.getItem(EARTH_OOBE_SKIP_KEY) || 0);
return Number.isFinite(value) && value > Date.now();
} catch {
return false;
}
}
function markTemporarilySkipped() {
try {
window.localStorage?.setItem(EARTH_OOBE_SKIP_KEY, String(Date.now() + EARTH_OOBE_SKIP_MS));
} catch {
// Local storage is only a soft "remind later" hint.
}
}
function escapeAttribute(value = "") {
return String(value)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
async function fetchOobeStatus() {
const token = getAuthToken();
const headers = token ? { Authorization: `Bearer ${token}` } : {};
const response = await fetch(EARTH_OOBE_STATUS_API, { cache: "no-store", headers });
if (!response.ok) throw new Error(`Earth OOBE status request failed: ${response.status}`);
return response.json();
}
function statusSteps(status) {
return [
{ label: "后端服务在线", done: true },
{ label: "登录控制台", done: Boolean(status.authenticated), warn: !status.authenticated },
{ label: "配置或确认数据源", done: Number(status.datasource_count || 0) > 0 || Number(status.custom_config_count || 0) > 0 },
{ label: "触发首次采集", done: Boolean(status.has_collected_data), warn: !status.has_collected_data },
{ label: "回到 Earth 查看结果", done: Boolean(status.ready) },
];
}
function renderOobe(status) {
const authenticated = Boolean(status.authenticated);
const primaryHref = authenticated ? status.datasources_url || "/datasources" : status.login_url || "/login?next=/datasources";
const primaryText = authenticated ? "去采集数据" : "登录并采集数据";
const secondaryHref = status.collection_url || "/collection-management";
const subtitle = authenticated
? "当前还没有检测到可展示的数据,可以直接进入后台触发首次采集。"
: "登录控制台并完成首次采集后Earth 将显示实时数据层。";
const steps = statusSteps(status)
.map((step, index) => {
const state = step.done ? "done" : step.warn ? "warn" : "pending";
return `
<li class="earth-oobe__step earth-oobe__step--${state}" style="--step-index: ${index}">
<span class="earth-oobe__step-dot" aria-hidden="true"></span>
<span>${step.label}</span>
</li>
`;
})
.join("");
const root = document.createElement("div");
root.className = "earth-oobe";
root.setAttribute("role", "dialog");
root.setAttribute("aria-modal", "true");
root.setAttribute("aria-labelledby", "earth-oobe-title");
root.innerHTML = `
<div class="earth-oobe__scrim" data-oobe-close></div>
<section class="earth-oobe__panel">
<div class="earth-oobe__scan" aria-hidden="true"></div>
<button type="button" class="earth-oobe__close" aria-label="先浏览 Earth" data-oobe-close>×</button>
<div class="earth-oobe__eyebrow">SYSTEM INITIALIZATION</div>
<h2 id="earth-oobe-title">欢迎使用智能星球计划</h2>
<p class="earth-oobe__subtitle">${subtitle}</p>
<ul class="earth-oobe__steps">${steps}</ul>
<div class="earth-oobe__stats">
<span><strong>${Number(status.current_record_count || 0)}</strong> 当前记录</span>
<span><strong>${Number(status.tv_source_count || 0)}</strong> 直播源</span>
<span><strong>${Number(status.active_datasource_count || 0)}</strong> 活跃数据源</span>
</div>
<div class="earth-oobe__actions">
<a class="earth-oobe__primary" href="${escapeAttribute(primaryHref)}">${primaryText}</a>
${authenticated ? `<a class="earth-oobe__secondary" href="${escapeAttribute(secondaryHref)}">进入采集管理</a>` : ""}
<button type="button" class="earth-oobe__ghost" data-oobe-close>先浏览 Earth</button>
</div>
</section>
`;
root.querySelectorAll("[data-oobe-close]").forEach((element) => {
element.addEventListener("click", () => {
markTemporarilySkipped();
root.classList.add("earth-oobe--closing");
window.setTimeout(() => root.remove(), 220);
});
});
return root;
}
export async function initEarthOobe() {
try {
const status = await fetchOobeStatus();
if (status?.ready || isTemporarilySkipped()) return;
document.body.appendChild(renderOobe(status));
} catch (error) {
console.warn("Earth OOBE status unavailable.", error);
}
}