用 Three.js 给家庭网络做一个 3D 服务拓扑(含公网访问码门户)
🧭 用 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.jsonserver.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


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 场景(选中节点、展开服务轨道、硬件剖视、巡检)。工具仅限白名单,管理操作不开放给模型。


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,转载请注明出处。





