NetNav|把整个局域网装进一个浏览器标签页
为什么要做这个
家里跑着一台常年不关机的小主机,上面挂着 NAS、几台虚拟机、路由器后台,还有一堆自己搭的服务。用久了,有几个点越来越别扭:
- 内网服务越来越多,IP 和端口全靠脑子记,过两周就忘,每次都得翻备忘录或者重新扫一遍端口;
- 现成的导航页(Homepage、Dashy、Heimdall 这些)都很漂亮,但它们只解决「书签」这一件事——天气、备忘、API 余额这种高频信息,还是没地方放;
- 想看一眼某台虚拟机是不是还开着,得专门打开虚拟化平台的管理后台,登录、等待、再点进去,就为了看一个状态灯;
- 书签、密钥、内网拓扑这些东西,放在别人家的服务器上总觉得不踏实,数据我想 100% 留在本地。
现成的都不满足,那就自己写一个。目标很明确:一个跑在局域网里、用浏览器打开就能用的导航首页,它不只是书签墙,而是整个内网的控制面板。
于是有了 NetNav:前端 React + Vite,后端一个 Express 单文件服务,数据全部落在本地 JSON 里,部署在任何一台常开的小主机上,全家设备打开浏览器就能用。
长什么样
整体走毛玻璃(glassmorphism)路线:半透明面板 + 背景模糊 + 细腻的动态渐变背景,深浅两套主题 1 秒内热切换。主题切换时背景渐变会跟着明暗自动配对,不会出现在暗色模式下还留着一套刺眼亮背景的情况。

主区域是书签卡片网格,每张卡片会尝试自动抓取站点图标;右上角那条「顶栏」,把所有小组件收在一起,不占正文空间。下面是同一套界面的暗色形态:

功能
顶栏:可拖拽排序的功能区
顶栏是整套系统最灵活的部分。每个模块都是一个独立小组件,可以在设置里勾选显示、拖拽调顺序,顺序存在浏览器本地,换设备不会互相打架。
默认模块依次是:时钟、API 余额、天气、备忘录、文件、压缩、虚拟机监控、提醒事项、设置。

天气
城市可以手动配置,面板展示实时温度、体感温度、湿度、风速和天气描述,未来几天的天气也在同一屏里列出来。数据由服务端拉取并缓存,避免每次开页面都去打一次外部接口。

备忘录
不需要笔记软件那套重型同步体系,这里只解决两件事:记下来 和 看得见。支持多条目、时间戳、完成划线,内容以纯文本存在本地 JSON 里;因为全家访问的是同一个内网地址,天然就是「跨设备同步」。

API 余额
接入大模型 API 之后,最大的焦虑是「这个月额度还剩多少」。这个面板直接查余额,把总余额、已用、剩余额度摊开显示,并附一条按每日消耗推算的本地费用估算——跑长任务之前瞄一眼,心里踏实。密钥只写在服务端配置,前端拿不到。

文件与压缩
临时传个文件到内网、或者把几张图打个包,不用再开 FTP 客户端。文件上传后走 /api/files 管理,支持下载和删除;压缩模块直接在页面上把选中的文件打包,少了一次「下下来再传上去」的往返。
提醒事项
到期提醒,和备忘录分开——备忘是「记下来」,提醒是「到时候叫我一声」。
虚拟化平台监控
内网跑着一套虚拟化平台,里面有几台开发测试用的虚拟机。这个面板通过平台 API 读取每台 VM 的状态,在线的带绿色状态点,点击可以直接开机、关机、重启。自打有了它,半夜想重启个服务再也不用爬起来开电脑——手机浏览器打开导航页就能操作。
连接信息(地址、账号、密码)只写在服务端配置里,前端只能拿到状态和发起操作。

设置
设置面板主要承担三件事:顶栏模块的显示与排序、主题切换(亮 / 暗 / 跟随背景)、以及数据的一键导出与导入(整份书签和配置导出成一个 JSON)。加上右键快捷操作和背景图自定义这些小功能,日常使用基本不用再碰配置文件。

移动端
整套布局是响应式的,手机上同样能用。把导航页设成浏览器主页之后,手机解锁 → 打开浏览器 → 所有内网服务都在手边,当遥控器用。

怎么搭的
技术栈很常规,没有花活:
| 层 | 选型 |
|---|---|
| 前端 | React 18 + Vite |
| 状态 | Zustand(带持久化) |
| 后端 | Node.js + Express(单文件) |
| 数据 | JSON 文件直读直写 |
| 样式 | 手写 CSS + 毛玻璃动效 |
架构上只有一个原则:一个端口搞定所有事。前端构建产物 dist/ 和 /api 接口由同一个 Express 服务托管,部署时直接 node server/index.js,不需要配反向代理,也不需要管跨域。
每个监控类模块都套用同一种结构:后端一个 /status 接口去轮询目标服务,一个 /config 接口保存连接信息(密码只存后端、不回传前端),前端小组件定时拉取刷新。所以后来加虚拟机监控的时候,几乎是照着模板抄一遍就长出来了。
踩坑记录
挑几个印象最深的记录一下:
1. Node 没有热重载
后端是单文件 Express,改完 server/index.js 必须手动重启才生效。更烦的是端口释放后有 TIME_WAIT,立刻重启会报 EADDRINUSE,得等一会儿才能重新绑定。后来固定成「改完必重启、重启前先确认端口放掉」的习惯,才算不踩。
2. 内网自签证书
虚拟化平台的 API 走 HTTPS,但证书是自签的、CN 还对不上内网 IP。Node 侧直接请求会校验失败,而且是静默失败——没有任何报错,只是数据一直拿不到。最后在服务端显式关掉证书校验(局域网内自用的妥协)。放到公网的话,请务必换成正规证书。
3. 密钥不能回传前端
最初图省事,把配置的账号密码一起返给前端表单回填,后来意识到只要开个开发者工具就全看见了。改成 /config 只写不读:密码能从前端存进去,但永远读不出来,前端只拿「是否已配置」这个状态。
4. 截图要脱敏
这篇文章的配图是真实界面,但里面的书签、密钥、内网地址不能露。做法是把 data.json 临时换成一份脱敏样例数据、截完图再还原,还原后还做了字节级比对确认一致。中间 notes.json 被脱敏残留污染过一次,从备份恢复才干净——备份这一步不能省。
5. 文章目录(TOC)一直不生成
主题用 tocbot 在前端生成目录,它要求标题必须带 id 锚点。而 Halo 渲染 markdown 产出的 <h2> 默认是没有 id 的,所以目录容器永远是空的。解法是在 markdown 里直接写带 id 的 HTML 标题,例如 <h2 id="sec-2">长什么样</h2>——Halo 会原样保留这些 HTML 标签和属性。
6. 正文渲染成了 markdown 源码
这个坑最折腾。页面渲染的是快照里的 content(HTML),不是 raw。如果通过 API 更新文章时只传 raw 而不传渲染好的 content,页面就会把 markdown 源码原样显示出来——图片全都变成  的裸文本,表格变成 |---|。
正确的做法是:本地先把 markdown 渲染成 HTML,然后 raw(编辑用源)和 content(渲染后的 HTML)一起提交,再重新发布同步快照。而且验证时不能只数 /upload/*.jpg 的出现次数——源码里也有这些字符串,要数 <img> 标签才算数。
接口一览
后端挂在 http://<主机>:3000/api 下,主要接口如下:
| 用途 | 路径 | 说明 |
|---|---|---|
| 书签数据 | GET / PUT /api/data |
整份书签配置的读取与写回 |
| 备忘录 | GET / PUT /api/notes |
纯文本,落本地 JSON |
| 提醒事项 | GET / POST /api/reminders、DELETE /api/reminders/:id |
增删查 |
| 文件 | GET / POST /api/files、GET /api/files/:id/download、DELETE /api/files/:id |
上传、下载、删除 |
| 天气 | GET /api/weather |
服务端拉取并缓存 |
| 热点新闻 | GET /api/hot-news |
顶栏信息流 |
| 虚拟化平台 | GET /api/pve/status、GET /api/pve/vms、POST /api/pve/vm-action |
状态、列表、开关机重启 |
| 平台配置 | GET / POST /api/pve/config |
密码只写不读 |
| 备份与恢复 | POST /api/backup、GET /api/backups、POST /api/restore/:filename |
本地备份 |
| WebDAV 备份 | POST /api/backup-webdav、GET /api/backups-webdav、POST /api/restore-webdav |
异地备份 |
| 版本与更新 | GET /api/version、GET /api/download-update |
应用内检查更新 |
| 站点图标 | GET /api/favicon、GET /api/favicon-cached |
抓取并缓存 favicon |
| 背景图 | POST / DELETE /api/background-image |
自定义背景 |
| 健康检查 | GET /api/health |
探活 |
安全设计
- 服务只绑定内网,不暴露公网端口;
- 所有密钥 / 密码只存服务端本地配置,前端一律不返回明文;
- 整份数据支持一键导出,方便迁移与容灾;
- 需要远程访问时走 WireGuard 回家,而不是把端口映射出去。
项目地址
https://gitea.zwbcc.cn/zwbpc/netnav
纯自用 + 兴趣项目,界面和数据都还在持续迭代。下一步想做的是把 NAS 存储水位、路由带宽也接进监控,以及多用户与基础权限。如果你也在捣鼓内网服务,欢迎交流。
记录于 2026-09-09,更新于 2026-09-10。