feat: add public h5 dashboard and publish traceability

This commit is contained in:
Wyndham ARR
2026-08-03 13:04:41 +08:00
parent 7e7470821b
commit 2107f00e32
29 changed files with 1047 additions and 71 deletions

View File

@@ -36,10 +36,14 @@
}
async function api(path, options = {}) {
const allowUnauthorized = Boolean(options.allowUnauthorized);
const requestOptions = { ...options };
delete requestOptions.allowUnauthorized;
const headers = new Headers(options.headers || {});
if (options.method && options.method !== "GET") headers.set("X-ARR-CSRF", csrf);
const response = await fetch(path, { ...options, headers, credentials: "same-origin" });
const response = await fetch(path, { ...requestOptions, headers, credentials: "same-origin" });
if (response.status === 401) {
if (allowUnauthorized) return null;
redirectToLogin();
throw new Error(I18N?.errorMessage("SESSION_INVALID", "登录状态已失效") || "登录状态已失效");
}
@@ -130,7 +134,7 @@
async function load() {
const month = $("#h5-month").value || monthNow();
try {
render(await api(`/api/analytics?month=${encodeURIComponent(month)}`));
render(await api(`/api/public/h5/analytics?month=${encodeURIComponent(month)}`));
} catch (error) {
reset(I18N?.t("bi.empty") || "该月份暂无可用看板数据");
notify(error.message);
@@ -145,10 +149,10 @@
async function boot() {
const connection = $("#h5-connection");
try {
const session = await api("/api/session");
csrf = session.csrf_token;
const session = await api("/api/session", { allowUnauthorized: true });
csrf = session?.csrf_token || "";
const logout = $("#h5-logout");
if (session.username) {
if (session?.username) {
logout.hidden = false;
logout.removeAttribute("aria-disabled");
logout.classList.remove("is-busy");
@@ -169,11 +173,14 @@
});
}
} catch (_) {
return;
// Session lookup is optional for the public read-only dashboard.
}
try {
const health = await api("/api/health");
const databaseReady = Boolean(health.database_ready);
const healthResponse = await fetch("/healthz", {
credentials: "same-origin",
cache: "no-store",
});
const databaseReady = healthResponse.ok;
connection.classList.add(databaseReady ? "ready" : "error");
connection.lastChild.textContent = databaseReady
? ""
@@ -190,7 +197,7 @@
connection.setAttribute("aria-label", I18N?.t("status.service_disconnected") || "数据服务未连接");
}
let months = [];
try { months = await api("/api/months"); } catch (_) { /* handled by load */ }
try { months = await api("/api/public/h5/months"); } catch (_) { /* handled by load */ }
const select = $("#h5-month");
select.innerHTML = months.length
? months.map((item) => `<option value="${escapeHtml(item.month_key)}">${escapeHtml(item.month_key)}</option>`).join("")