"""系统状态页面的 HTML 渲染。""" from __future__ import annotations from html import escape from ..schemas.system import SystemStatus def _fmt_bytes(n: int) -> str: x = float(n) for unit in ("B", "KiB", "MiB", "GiB", "TiB", "PiB"): if abs(x) < 1024.0 or unit == "PiB": return f"{int(x):,} B" if unit == "B" else f"{x:,.1f} {unit}" x /= 1024.0 return f"{x:,.1f} PiB" def _fmt_duration(seconds: float) -> str: s = int(seconds) d, s = divmod(s, 86400) h, s = divmod(s, 3600) m, s = divmod(s, 60) parts: list[str] = [] if d: parts.append(f"{d}天") if h or d: parts.append(f"{h}小时") if m or h or d: parts.append(f"{m}分") parts.append(f"{s}秒") return " ".join(parts) def _bar(pct: float) -> str: """带颜色阈值的进度条:>85% 红,>60% 黄,否则绿。""" pct = max(0.0, min(100.0, pct)) color = "#e53935" if pct >= 85 else "#fb8c00" if pct >= 60 else "#43a047" return ( '
' f'
' f'{pct:.1f}%' '
' ) _CSS = """ :root { color-scheme: light dark; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; max-width: 880px; margin: 2em auto; padding: 0 1em; line-height: 1.5; } h1 { margin-bottom: 0.1em; } .sub { color: #777; margin-top: 0; font-size: 0.95em; } .card { border: 1px solid #ddd; border-radius: 8px; padding: 1em 1.2em; margin: 1em 0; background: rgba(0,0,0,0.02); } .card h2 { margin: 0 0 0.6em; font-size: 1.1em; } .row { display: grid; grid-template-columns: 160px 1fr; gap: 0.25em 1em; align-items: center; padding: 0.15em 0; } .row .k { color: #666; } .bar { position: relative; background: #e6e6e6; border-radius: 4px; height: 18px; width: 100%; overflow: hidden; } .bar .fill { height: 100%; transition: width 0.3s; } .bar .pct { position: absolute; top: 0; left: 0; width: 100%; height: 100%; text-align: center; font-size: 12px; line-height: 18px; color: #fff; mix-blend-mode: difference; } table.disks { border-collapse: collapse; width: 100%; } table.disks th, table.disks td { padding: 0.4em 0.6em; text-align: left; border-bottom: 1px solid #eee; vertical-align: middle; } table.disks th { font-weight: 600; color: #555; font-size: 0.9em; } .foot { color: #888; font-size: 0.85em; margin-top: 2em; text-align: center; } a.json { font-size: 0.85em; color: #1565c0; text-decoration: none; } @media (prefers-color-scheme: dark) { body { background: #1a1a1a; color: #e0e0e0; } .card { background: rgba(255,255,255,0.04); border-color: #333; } .bar { background: #333; } table.disks th, table.disks td { border-bottom-color: #2a2a2a; } } """ def render(status: SystemStatus) -> str: rows = "".join( "" f"{escape(d.mountpoint)}" f"{escape(d.device)}" f"{escape(d.fstype)}" f"{_fmt_bytes(d.used)} / {_fmt_bytes(d.total)}" f"{_fmt_bytes(d.free)}" f"{_bar(d.percent)}" "" for d in status.disks ) return f""" 系统状态 — {escape(status.hostname)}

系统状态

主机 {escape(status.hostname)} · 运行 {_fmt_duration(status.uptime_seconds)} · 查看 JSON

CPU

逻辑核心数
{status.cpu_count}
使用率
{_bar(status.cpu_percent)}

内存

总量
{_fmt_bytes(status.memory.total)}
已用
{_fmt_bytes(status.memory.used)}
可用
{_fmt_bytes(status.memory.available)}
使用率
{_bar(status.memory.percent)}

磁盘

{rows}
挂载点设备文件系统 已用 / 总量剩余使用率

zikai file service · 数据来源 psutil

"""