🧭 用 Three.js 给家庭网络做一个 3D 服务拓扑(含公网访问码门户)

💡 「服务部署在哪台主机的哪个端口」这件事,表格记不住、2D 图看不清、力导向图会乱。所以花了一个版本迭代出一个自定义 Three.js 场景:固定的网络层级 + 会转风扇的硬件模型 + 实时指标驱动,再配一套有时效的访问码门户放到公网。

0. 为什么不用现成方案

动手前先排除了四个候选:

方案 结论
3d-force-graph / react-force-graph-3d 生态成熟,但力导向布局会打乱固定网络层级
Grafana Node Graph 适合调用链,端口和主机卡交互展示不自由
Cytoscape.js 2D 图能力强,不是真 3D
NetBox Topology 适配 NetBox 资产模型,部署重,与现有 NetAlertX/8789 栈冲突

我的需求核心是清楚表达层级 + 端口归属,还要保留拖拽旋转、缩放、搜索、点击详情——所以最终用 Three.js 自定义场景,不用通用力导向图。

1. 分层设计

拓扑按真实网络层级组织,而不是随机布局:

1
互联网 → 公网网关 → 主路由 → 主机 → 服务:端口

12 个拓扑节点、56 条服务/端口映射、25 台 LAN 设备。点击节点打开右侧详情,列出全部服务、端口和访问链接;选中主机还会展开 3D 服务轨道,按 Web/API、监控、存储/媒体、自动化/通知分组。

拓扑总览

服务轨道展开

端口锁定与详情

快捷键:/ 搜索、O 服务轨道、E 硬件剖视、D 故障演练、Esc 逐级退出。桌面端斜视 3D 布局,移动端自动切两列纵向布局。

2. 数据链路:浏览器不碰凭据

架构上最重要的一条约束:浏览器只访问同源接口,不接触 Beszel 凭据或数据库文件。

数据链路与公网访问

  • metrics_bridge.py 以只读方式访问 Beszel 的 SQLite,合并 NetAlertX 在线状态和可选的 Kuma 状态页,每 15 秒原子更新 metrics.json
  • server.py 负责静态文件和 8789 同源代理(/api/status、/api/devices),带缓存和后台预热,避免跨域和浏览器私网限制
  • 指标超过 90 秒未更新自动标记 STALE,接口不可用时回退到静态拓扑状态;/api/metrics/health 暴露指标年龄供告警判断

实时感的映射规则:CPU 驱动设备光晕和风扇转速,磁盘 I/O 驱动硬盘灯频率,网络流量驱动连线光粒子密度与速度;NetAlertX 离线的设备熄灯、外壳降透明、连线变灰。

3. 硬件微缩模型:GLB + Draco

路由器、NAS、1U 服务器、塔式 PC、Mac mini、SBC 六类硬件模型,程序化生成,不依赖第三方素材授权:

  • GLB 分 high/low 双档:桌面加载高模,移动端和低配设备自动加载低模;异步替换程序化外壳,加载失败自动回退
  • 机箱带盘位、网口、状态灯、散热格栅和可动画风扇,LED 与风扇持续运行
  • 12 个 GLB + Draco 运行时合计约 1.17 MiB,低于 8 MiB 首屏资源预算
  • Draco 解码器本地化(public/draco/),不依赖 CDN

GLB 硬件细节

硬件爆炸视图

npm run assets:verify 会校验 12 个 GLB、文件头和体积预算——三个 CI workflow 都先跑它,防止资产回归。

4. 公网访问:访问码门户

公网入口用独立 Cloudflare Tunnel(主域名 topology.shallowrecall.top,备用 net3d),两层权限:

  • 访客入口 /access:输入管理员生成的访问码——支持生效/过期时间、最大兑换次数、会话时长、禁用/延期/删除;达到上限后新登录拒绝、已登录会话自然存续
  • 管理入口 /admin:HTTP Basic Auth,支持快捷有效期、搜索、状态筛选、使用进度和移动端卡片布局

访问码门户

管理后台

移动端管理后台

安全要点:

  • 原始访问码只在创建时显示一次,数据库只存 SHA-256 哈希;管理员密码只有 PBKDF2 哈希进环境文件(权限 600)
  • 会话用 HMAC 签名 HttpOnly Cookie,且每次请求都重查数据库状态——禁用访问码会立即注销已有会话
  • /healthz 匿名可访问供 Cloudflare/Uptime Kuma 健康检查;其余页面、静态资源和 /api/* 都要有效会话
  • 密码登录失败有基础限流,访问页与错误日志遮蔽访问码

5. CI/CD:Mock 构建 + self-hosted runner

三个 GitHub Actions workflow,全部先跑 assets:verify 和 metrics:verify:

  • CI:用 VITE_MOCK_API=true 的 Mock 数据构建,并用 Playwright 生成版本截图
  • Deploy:main 更新后由 .55 上的 self-hosted runner(debian-network-topology)构建,只更新 /opt/network-topology-3d/dist——不会覆盖 server.py、Basic Auth 环境文件或访问门户
  • Release:推送 v* tag 时用 Mock 数据生成带截图描述的 GitHub Release

部署面用两个 systemd 服务兜底:主服务 + metrics bridge(notify/watchdog,进程卡死自动重启);bridge 告警接 ntfy,每 6 小时最多重复提醒一次。

6. 故障演练与 AI 助手

按 D 打开演练控制台:选择主机和故障服务注入纯前端合成离线事件,告警链按互联网 → 网关 → 路由 → 主机逐段点亮,故障设备红色呼吸、风扇降速;演练不写数据库、不发真实通知,最近一次演练持久化到浏览器可回放。

页面还内嵌了一个服务端 Agent(SSE 流式 + 工具调用),经 OpenAI-compatible 网关(如 new-api)接入,可查询实时状态、检索拓扑,并联动 3D 场景(选中节点、展开服务轨道、硬件剖视、巡检)。工具仅限白名单,管理操作不开放给模型。

公网 3D 拓扑

移动端拓扑

7. 版本路线

  • v1.0.0-baseline:基础分层拓扑 + 详情面板
  • v1.1.0:程序化硬件模型、节点聚焦镜头、自动巡检、开场动画
  • v1.2:GLB 硬件资产 + 回退机制
  • v1.3:静态拓扑状态回退
  • v1.4:Beszel/NetAlertX 实时遥测桥
  • v1.5(当前):故障告警链、演练恢复、移动端演练

源码与 Release:github.com/ShallowRecall/network-topology-3d(私有仓库)

本文首发于 ShallowRecall,转载请注明出处。