import type { SiteListItem } from "@umt/contracts"; import { useFeeds } from "~/api/feeds.ts"; import { accessSummary, useSiteRequestStats, useSiteUserData } from "~/api/sites.ts"; import { DetailSection } from "~/components/console/detail-sheet.tsx"; import { ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; import { KvList } from "~/components/console/kv-list.tsx"; import { StatTile } from "~/components/console/stat-tile.tsx"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { relTime } from "~/components/workbench/release-fields.ts"; import { ACCOUNT_OVERALL, AUTH_KIND_LABEL, bytes, dataAccount, hasLogin, healthLine, healthMessage, kindLabel, overallStatus, primaryAccount, rateLimitText, ratioText, siteCan, } from "./site-fields.ts"; import { UserDataChart } from "./user-data-chart.tsx"; const CAP_LABEL = [ ["search", "搜索"], ["list", "列表抓取"], ["rss", "RSS"], ["detail", "详情页"], ["login", "登录"], ["userinfo", "账号数据"], ] as const; /** 可用的获取方式:"搜索 · 列表抓取 · RSS" */ export function capabilityText(site: Pick): string { const on = CAP_LABEL.filter(([c]) => siteCan(site, c)).map(([, l]) => l); return on.length ? on.join(" · ") : "—"; } /** 站点详情"概览":账号数据磁贴 + 30 天趋势 + 状态;没有登录的站点(公开 BT、自定义 RSS 源)只看 RSS */ export function SiteOverview({ site }: { site: SiteListItem }) { return hasLogin(site) ? : ; } function FeedSiteOverview({ site }: { site: SiteListItem }) { const feeds = useFeeds({ siteId: site.id }); const stats = useSiteRequestStats(site.id); const items = feeds.data?.items ?? []; const enabled = items.filter((f) => f.enabled); const failing = enabled.filter((f) => f.healthStatus === "failing" || f.healthStatus === "circuit_open").length; const lastOk = items .map((f) => f.lastSuccessAt) .filter((x): x is string => !!x) .sort() .at(-1); return (
); } function AccountSiteOverview({ site }: { site: SiteListItem }) { const dAcc = dataAccount(site.accounts); const acc = primaryAccount(site.accounts); const u = dAcc?.userData ?? null; const history = useSiteUserData(site.id, dAcc?.id, 30); const stats = useSiteRequestStats(site.id); const st = ACCOUNT_OVERALL[overallStatus(site)]; const h = healthLine(site.lastHealth, site.lastHealthAt); return (

近 30 天

{dAcc ? `账号 ${dAcc.label} · 数据 ${relTime(dAcc.userDataAt)}` : "没有账号数据"}
{history.isPending && dAcc ? ( ) : history.error ? ( void history.refetch()} /> ) : ( )}
{st.label} }, { label: "凭证", value: acc ? AUTH_KIND_LABEL[acc.authKind] : "未配置" }, { label: "用户名", value: u?.username ?? acc?.loginName }, { label: "上次登录", value: relTime(acc?.lastLoginAt) }, ...(acc?.lastError && acc.status !== "active" ? [{ label: "最近错误", value: {acc.lastError.message} }] : []), ]} /> {h.text} {healthMessage(site.lastHealth) ? ( {healthMessage(site.lastHealth)} ) : null} ), }, { label: "抓取", value: site.snapshotEnabled ? `${site.lastSnapshotAt ? relTime(site.lastSnapshotAt) : "还没抓取过"} · ${site.snapshotIntervalSec ? `每 ${Math.round(site.snapshotIntervalSec / 60)} 分钟` : "默认周期"}` : "未开启", }, { label: "限流", value: rateLimitText(site.rateLimit) }, { label: "近 24 小时请求", value: stats.data ? ( {stats.data.last24h} 次 {accessSummary(stats.data)} ) : ( "—" ), }, { label: "代理", value: site.proxy ? "使用" : "不使用" }, ...(site.kind && site.kind !== "pt" ? [{ label: "类型", value: kindLabel(site.kind) }] : []), { label: "获取方式", value: capabilityText(site) }, ]} />
); }