import { api, stats } from "../api.js"; import { emptyChart, mixedChart } from "../charts.js"; import { emptyState, sectionHeader } from "../components.js"; import { escapeHtml, formatCount, formatDateTime, formatNumber, } from "../format.js"; function taskRows(tasks) { const entries = Object.entries(tasks || {}); if (!entries.length) return emptyState("暂无任务记录", "运行一次同步、快照或发现后会写入最近结果。"); return `
${entries.map(([name, record]) => { const summary = record.summary && typeof record.summary === "object" ? Object.entries(record.summary) .filter(([key]) => ["added", "removed", "candidates", "notified", "ok", "failed", "total", "completed", "backup_error"].includes(key)) .map(([key, value]) => `${key}=${value}`).join(" ") : ""; return ``; }).join("")}
任务结果完成时间摘要 / 错误
${escapeHtml(name)} ${record.ok ? "成功" : `失败`} ${escapeHtml(formatDateTime(record.finished_at))} ${escapeHtml(summary || record.error || "—")}
`; } export async function renderSystem(root) { const [system, visitStats] = await Promise.all([ api("/api/system"), stats({ days: 30 }).catch(() => null), ]); const health = system.snapshot_health || { total: 0, ok: 0, failed: 0 }; const counts = system.counts || {}; const next = system.next_runs || {}; const visitExtra = visitStats ? `近 ${visitStats.days} 天 · ${visitStats.pending ? "含未落库缓冲" : "已落库"}` : ""; const visitPanel = visitStats ? `
${sectionHeader("访问统计", visitExtra)}
今日 PV${formatNumber(visitStats.today_pv)}
今日 UV${formatNumber(visitStats.today_uv)}
${visitStats.days} 天 PV${formatNumber(visitStats.total_pv)}
${visitStats.days} 天 UV${formatNumber(visitStats.total_uv)}

柱状为每日 PV,折线为每日 UV(按客户端 IP 去重)。数据来自前端埋点,先落内存、每分钟批量入库;仅统计页面访问,不含 API 与静态资源请求。

${(visitStats.top_pages || []).length ? `
${visitStats.top_pages.map((page) => ``).join("")}
页面PVUV
${escapeHtml(page.path)} ${formatNumber(page.pv)} ${formatNumber(page.uv)}
` : `

暂无访问记录。

`}
` : ""; root.innerHTML = `

系统状态

数据 ${escapeHtml(system.data_date || "—")} · 快照 ${formatCount(system.snapshot_days)} 天

采集成功${formatCount(health.ok)}
采集失败${formatCount(health.failed)}
在库${formatCount(counts.active)}
已失效${formatCount(counts.removed)}
已移出库${formatCount(counts.dropped)}
${visitPanel}
${sectionHeader("最近任务")} ${taskRows(system.tasks)}
${sectionHeader("预计下次运行")}
咻咻满粉丝数更新${running.fans ? "运行中" : escapeHtml(formatDateTime(next.fans))}
视频数据快照 + UP 信息${running.snapshot ? "运行中" : escapeHtml(formatDateTime(next.snapshot))}
新视频搜索${running.discover || running.discovery ? "运行中" : escapeHtml(formatDateTime(next.discover))}

进行中显示「运行中」;空闲时按该任务上次完成时间推算下次运行,不按墙钟抢跑。

${sectionHeader("授权与备份")}
登录态${system.auth.ok === "1" ? "正常" : "异常"}
登录信息${escapeHtml(system.auth.message || "—")}
最近检查${escapeHtml(formatDateTime(system.auth.last_check))}
最近备份${system.backup.ok === "1" ? "成功" : "未完成"}
备份错误${escapeHtml(system.backup.error || "—")}
Telegram${system.telegram_enabled ? "已启用" : "未启用"}
${(system.errors || []).length ? `
${sectionHeader("最近错误")} ${system.errors.map((error) => `
${escapeHtml(error.task)} ${escapeHtml(error.error || "未知错误")}
`).join("")}
` : ""}
${sectionHeader("UP 黑名单")}

只影响未来搜索发现;已入库视频继续追踪。移除请使用 CLI 或数据库。

${(system.blacklist || []).length ? `
${system.blacklist.map((item) => ``).join("")}
mid昵称原因在库视频
${item.mid} ${escapeHtml(item.name || "—")} ${escapeHtml(item.reason || "—")} ${formatNumber(item.video_count || 0)}
` : emptyState("黑名单为空")}
`; const visitChart = root.querySelector("#visit-chart"); if (visitChart) { if (visitStats && (visitStats.total_pv || visitStats.total_uv)) { mixedChart(visitChart, { dates: visitStats.series.dates, bars: visitStats.series.pv, line: visitStats.series.uv, barName: "PV", lineName: "UV", }); } else { emptyChart(visitChart, "暂无访问数据"); } } }