项目:nas-console | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
这是全套文档(00–20 共 21 篇)的最后一篇,承担三个收尾职能。其一,术语表:本套文档与源码注释在长期写作中形成了一批「行话」——有的来自项目自身的命名(如「白名单分组」「浏览器巡检」),有的是领域通用词在本项目语境下的具体含义(如「探针」「翻转」),有的是文档写作中为表达方便而固化的短语(如「字段存在性即权限」「隐藏≠删除」)。术语表把它们集中定义,每条给出定义、在源码中的对应物与首次深入展开的篇目,避免同一概念在不同篇目间出现口径漂移。其二,源码文件与篇目的反向索引:以文件为行、篇目为列,回答「我要改这个文件,应该先读哪几篇」。其三,按任务的阅读路径:以四个高频任务(加监控类型、修 SSE 断流、公网上线检查、排查探针不可达)为线索,给出「从哪篇的哪节读起、按什么顺序动手」的导航。
先说明这套文档的总体分工,作为所有导航的坐标系:00–06 是「系统层」(总览、架构、页面、接口、数据、流程、部署),任何新接手者按序读完即可建立全貌;07–15 是「深读层」(模块、安全、测试、性能、扩展、FAQ、债务、配置、接口参考),按需查阅;16–20 是「考古与索引层」(决策记录、依赖全景、代码走读、数据契约示例、本篇)。所有篇目的开头都有元信息行,结论一律标注源码依据,无法从源码确认的事项统一标「⚠️ 待确认」——这套纪律本篇同样遵守。
探针(probe)。本项目的核心概念,指 server/ 下的常驻 Node 进程及其容器形态 nas-console-probe。它不是网络工程里「探针报文」的狭义含义,而是「巡检引擎 + API 服务 + 静态托管 + 出站客户端」四角色合一的单进程(01 篇第 1 节)。源码对应:server/src/ 全部十个文件;容器定义:两个 Dockerfile 与两份 compose。深入阅读:07 篇、16 篇 ADR-002。
巡检(check / 巡查)。探针按周期对每个服务发起的周期性探测动作。每次巡检产生一个 CheckResult(up/down + 延迟 + 可选 error/证书天数),写入 checks 表并触发告警与推送链。默认周期 30 秒(可按服务覆盖,强制下限 15 秒),单次检查超时 8 秒。源码对应:probe.ts 的 apply/checkAndRecord/checkHttp/checkTcp。深入阅读:18 篇第 1 节(逐段走读)。
首检补跑(runInitial)。引擎启动或清单热更新后,不等第一个周期、立即以并发窗 4 补跑一轮全量检查的机制——保证面板打开时就有数据,也让 PUT /api/services 落盘后新服务立刻可检。源码对应:probe.ts 第 54-68 行;并发窗与防重入语义见 18 篇第 1.3 节。
翻转(flip / 翻转事件 / ProbeEvent)。某服务本次巡检结果与上一条记录状态不同的时刻(up 变 down 或 down 变 up)。翻转是三类下游的共同源头:events 表入库、SSE event 帧、告警恢复判定。注意「首轮检查只建立基线、不产生翻转」(服务端与浏览器降级路径两侧语义一致)。源码对应:store.ts 的 record() 判定段。深入阅读:18 篇第 2.2 节。
双模式(probe 模式 / browser 模式 / 降级模式)。前端健康数据的三态:pending(挂载初态)、probe(探针可达,数据以服务端为权威)、browser(探针不可达,回退 v0.2 的浏览器端 no-cors 探测 + localStorage 累积)。「降级模式」是 browser 模式的同义词,强调它是刻意保留而非残留。模式在页面加载时探测一次并锁定,运行期不自动切换——修复探针后必须刷新页面。源码对应:use-health.ts 的 source 状态;health.ts/health-history.ts 为降级路径本体。深入阅读:05 篇第 2 节、16 篇 ADR-006。
同形结构(DayBucket / RecentSample)。服务端查询返回与前端本地历史刻意保持一致的数据形状(按天桶四字段、48h 短键采样),使 applyServerSnapshot 能整体替换本地状态而 UI 无感切换。源码对应:health-history.ts 与 store.ts 的两个返回结构。深入阅读:16 篇 ADR-006。
SSE(Server-Sent Events)。探针向浏览器单向推送的原生 HTTP 长连接协议,替代 WebSocket 的决策理由见 16 篇 ADR-005。源码对应:sse.ts(服务端)与 src/api/probe.ts 的 subscribeProbeEvents(客户端)。帧格式与原始流示例见 19 篇第 4 节。
三层容错(status 单播 / batch 兜底 / latest 轮询)。实时链路的递进设计:status 事件每次检查完成即广播(秒级实时);batch 全量快照 30 秒一拍(丢帧后最多 30 秒自愈);latest 轮询默认 60 秒一拍(SSE 完全失效时的降级通道)。外加 25 秒注释行心跳与 x-accel-buffering: no 头对抗中间层断连与攒包。设计立场是「不信任长连接,实时性是尽力而为的增强属性」。源码对应:sse.ts 全文、server.ts 的 batchTimer、use-health.ts 的 pollTimer。深入阅读:01 篇第 5.1 节、10 篇第 2 节、18 篇第 3 节。
心跳(: ping)。SSE 规范中冒号开头的注释行,浏览器不派发事件,但连接上有字节流动——喂饱所有基于「空闲即断」逻辑的中间设备。25 秒间隔、惰性启动、unref() 不阻塞退出。源码对应:sse.ts 第 46-52 行。
公开面(public 面)。匿名访客可达的界面与数据:首页 /、详情 /projects/:id、状态页 /status、/api/public/* 两个端点与 /portal-covers/* 封面托管。与之相对的是鉴权面(管理区 /console/* 与全部非公开 API)。VPS nginx 层的 basic auth 放行规则(/status 与 /api/public/ 豁免)是公开面的外层边界。深入阅读:08 篇。
脱敏 / 字段级脱敏 / 字段存在性即权限。公开快照 /api/public/status 的核心机制:所有分组都出现(名字/状态/延迟/45 天可用率),但 url/tags/error/profile 四个字段只对白名单分组下发——前端以 s.url 字段的存在与否判定能否成项目卡、能否进详情页,权限判定收敛为一次字段判空,且连失败原因 error 都不对非白名单分组下发(「error 里可能含 host:port」)。源码对应:server.ts 的 publicStatus() 第 341-381 行。深入阅读:08 篇第 3 节、16 篇 ADR-010。
白名单分组(portal.publicGroups)。config.json portal 段的分组名数组,缺省「应用站点/文档与统计」。匹配是字符串精确相等,作用是分组级(白名单分组内全部服务公开,无法只公开某个服务)——因此「白名单分组里只放公网服务」是运维纪律而非代码保证。源码对应:server.ts 第 375 行的 publicGroups.includes(s.group)。深入阅读:14 篇第 1.1 节。
裸奔模式(未启用 auth)。config.json 的 auth 段为空/格式不符时,authGuard 恒放行,管理 API 完全无鉴权——仅限本地开发的模式,启动日志有 ⚠️ 警告但代码不强制阻断。完整暴露面清单与后果评估见 08 篇第 6 节;上线检查清单第一项就是确认 auth 段非空。
HMAC 会话 / session-secret。自研无状态会话:Cookie 值为 base64url({u,exp}) 与其 HMAC-SHA256 签名的拼接,算法硬编码(无 Header 段,免疫 alg:none 类攻击);签名密钥 32 字节 hex 首启生成落盘 data/session-secret(0600)——泄露该文件等于可伪造任意会话,删除或替换它再重启是「强制全员重新登录」的唯一手段(改密码不使已签发会话失效)。源码对应:auth.ts 全文。深入阅读:08 篇第 2 节。
限流共享键(反代后失真)。登录限流以 req.socket.remoteAddress 为键,生产链路后所有外部来源共享同一键(frpc/docker 网关地址)——「5 次失败锁 10 分钟」实际是全局限额且可被恶意锁死管理员。源码对应:auth.ts 的 fails Map 与 server.ts 第 116 行传参。深入阅读:08 篇第 2.3 节。
权威副本(探针侧)与编译期基线(前端侧)。服务清单的双层权威模型:src/config/services.ts 的 19 项内置清单随构建进 dist,是「代码即基线」;探针 config.json 的 services 段是运行时权威,由前端每 60 秒全量 PUT 同步。两者通过 upsert 语义衔接:已存在 id 的手工 check 配置被保护,元数据以传入为准。深入阅读:16 篇 ADR-007、04 篇第 1.4 节(段所有权)。
隐藏≠删除(prune=false)。前端「隐藏」只是给服务打 hidden 标记:不参与巡检、不出现在公开快照,但配置原样保留在 config.json,随时可恢复;清单同步固定不删除(prune 关闭),探针侧多出的服务保留在尾部。源码对应:config.ts 的 SyncOptions 与 server.ts 的 activeServices()。
原子写(tmp + rename)。config.json 的一切物理写入都走「先写 .tmp 再 renameSync 替换」,进程崩溃不会留下半个 JSON。衍生部署约束:rename 在单文件 bind mount 上会 EBUSY,故 NAS compose 改用目录挂载。源码对应:config.ts 第 16-21 行;docker-compose.nas.yml 第 24 行注释。
段所有权。config.json 五段的写入口分界:services 段可被前端 PUT 热更新(落盘后 engine.apply 立即重排);alerts/auth/portal/onePanel 四段只能手工编辑且重启进程才生效——「改什么、怎么生效」的速查表见 14 篇第 8 节。
WAL(Write-Ahead Logging)。SQLite 的日志模式,写不阻塞读,代价是 probe.db 旁多出 -wal/-shm 两个伴随文件——备份不能只拷主文件(热拷有丢尾风险,应停容器或用 .backup)。源码对应:store.ts 构造函数第一行 pragma。深入阅读:04 篇第 2 节、10 篇第 1 节。
有界状态哲学。全项目任何一个会增长的容器都配了裁剪规则或上限(checks 90 天、本地历史 90 天/48h/240 点/100 条、请求体 1MB/5MB、关键词读取 256KB);唯一的例外是服务端 events 表无裁剪(与设计稿不符,13 篇问题二)。深入阅读:04 篇第 5 节。
孤儿文件。上传成功但未随档案 PUT 保存的封面/截图文件——占用 covers/ 但不被任何 profile 引用,也不会被自动清理(上传与档案保存是两个独立动作)。
frp 链路。生产访问链路「浏览器 → VPS nginx(TLS + basic auth)→ frps :5950 → NAS frpc → 容器 :5950」的简称。仓库内没有 frps/frpc/nginx 的配置文件(位于 VPS 与 NAS 自有目录,⚠️ 待确认各自参数),但探针与前端的行为都被这条链路塑形:端口 5950 的选取、SSE 心跳与 no-buffering 头、限流键失真都源于此。深入阅读:01 篇第 1 节、14 篇第 5 节。
单容器双角色。一个 Node 进程同时承担静态托管与 API/SSE 服务(外加巡检与出站集成),对外表现为单容器单端口。它带来的同源性(免 CORS、免版本协商)是整个接口设计的前提。深入阅读:16 篇 ADR-002。
两种 Docker 路径。Dockerfile(全量构建:前端 + 探针一锅出,上下文是父目录)与 Dockerfile.nas(NAS 轻量:仅探针,dist 由宿主机挂载,deps 阶段在 NAS 平台拉 better-sqlite3 预编译二进制)——日常更新走后者。深入阅读:06 篇第 5 节、14 篇第 3-4 节。
SPA fallback。静态托管对「不存在的路径」回落 index.html(200 + text/html),前端路由接手渲染 404 视图。它的坑:dev 代理漏配时 API 请求拿到 HTML——「请求成功但 JSON 解析炸」的元凶。源码对应:server.ts 的 serveStatic()。
resolve-only。检查器(checkHttp/checkTcp)的 Promise 设计:参数列表里没有 reject,一切异常(超时/拒连/DNS 失败/响应中断)都 resolve 成 down + error 文案——引擎主循环永远拿得到结果,未处理 rejection 理论为零。源码对应:probe.ts 第 131、199 行。深入阅读:16 篇 ADR-011、18 篇第 1.6 节。
「安静即健康」日志模型。探针正常运转时几乎不输出日志(无访问日志、无巡检日志)——「日志没动静」不能推断「没在干活」,确认巡检活体要看 /api/health/latest 的 checkedAt、SQLite mtime、状态页时间戳三处。全部日志语句清单见 12 篇第 1 节。
下表以「改动这个文件时先读什么」为出发点组织。每行列出文件、职责一句话、以及最相关的篇目(加粗为必读)。
服务端(server/src/,10 个文件)
| 文件 | 职责 | 相关篇目 |
|---|---|---|
index.ts | 进程入口:环境变量、装配、优雅停机 | 06 篇第 2 节、14 篇第 2 节、07 篇第 2 节 |
server.ts | 路由分发、鉴权门接线、静态托管、公开快照组装 | 15 篇、03 篇、08 篇第 3 节、19 篇全篇 |
probe.ts | 巡检引擎:调度、http/tcp 探测、证书采集 | 18 篇第 1 节(走读)、05 篇第 1 节、07 篇第 3 节 |
store.ts | SQLite 三表:写入/查询/裁剪 | 18 篇第 2 节、04 篇第 2 节、10 篇第 1 节 |
sse.ts | SSE Hub:客户端管理、三类广播、心跳 | 18 篇第 3 节、15 篇第 2 节、01 篇第 5.1 节 |
alerts.ts | 告警:防抖/抑制/安静时段、三渠道投递 | 07 篇第 6 节、11 篇 SOP 五、12 篇 Q10-Q12 |
auth.ts | 鉴权:登录/会话/限流/守卫 | 08 篇第 2 节、05 篇第 3 节 |
config.ts | 配置读写(原子写)、清单 upsert、profile 净化 | 14 篇第 1 节、04 篇第 1 节、16 篇 ADR-007/012 |
1panel.ts | 1Panel v2 签名客户端 | 08 篇第 4 节、15 篇第 4 节、11 篇 SOP 四 |
types.ts | 全部共享类型(前后端契约的权威) | 04 篇、19 篇第 8 节 |
前端(src/,按目录)
| 文件/目录 | 职责 | 相关篇目 |
|---|---|---|
App.tsx / main.tsx | Provider 编排、路由表、RequireAuth | 02 篇第 1-2 节 |
api/probe.ts | 探针客户端(getJson/SSE 封装/清单同步) | 05 篇第 2、5 节、19 篇第 2-4 节 |
api/use-health.ts | 双模式健康 Hook | 05 篇第 2 节、16 篇 ADR-006 |
api/health.ts / health-history.ts | 浏览器降级探测与本地历史 store | 05 篇第 2 节、02 篇第 5 节 |
api/umami.ts | Umami 浏览器直连客户端 | 03 篇第 5 节、16 篇 ADR-008 |
api/host.ts / public-status.ts | 1Panel 代理客户端 / 公开快照类型镜像 | 19 篇第 5-6 节、15 篇第 1.4-1.5 节 |
config/services.ts / identity.ts | 内置清单基线 / 名片常量 | 04 篇第 1.4 节、16 篇 ADR-007、00 篇第 5 节 |
pages/(6 个页面) | 路由级页面组件 | 02 篇第 3 节(逐页说明) |
components/(15 个组件) | 登录页、状态页、抽屉、上传等 | 02 篇第 3-4 节、07 篇第 12-14 节 |
charts/(8 个文件) | recharts 封装 + 手写 Sparkline | 07 篇第 15 节、09 篇 P0 清单 |
theme/ / settings/ / hooks/ / layouts/ | 主题、偏好 Context、通用 Hook、布局 | 02 篇第 5-6 节 |
根目录与部署文件
| 文件 | 相关篇目 |
|---|---|
Dockerfile / compose.yml | 06 篇第 5 节、14 篇第 3 节、13 篇问题三(react-ui 遗留) |
Dockerfile.nas / docker-compose.nas.yml | 14 篇第 3.2/4 节、06 篇第 5 节 |
config.example.json / auth.admin.json(不引用其内容) | 14 篇第 1 节、13 篇问题一、08 篇 |
docs/v03-probe-design.md | 16 篇(多数 ADR 的原始出处)、00 篇第 6 节 |
vite.config.ts / 两个 tsconfig | 06 篇第 2 节、09 篇第 2.1 节、17 篇第 2 节 |
server/scripts/hash-password.mjs / scripts/make-icons.mjs | 14 篇第 6 节 |
先读 11 篇 SOP 一(完整步骤表与陷阱提醒,含「要不要新增类型」的前置判断),配合 16 篇 ADR-011 理解 resolve-only 约束(新检查器绝不 reject),动手时对照 18 篇第 1 节的 checkTcp 走读作为代码模板。验证阶段用 06 篇第 6 节的冒烟清单;若新类型需要容器内系统二进制(如 ping),回读 14 篇第 3 节评估镜像变更。全程遵守 11 篇第 0 节的四条「项目宪法」(契约三处对齐、分层不越级、鉴权面默认收紧、dev 代理同步)。
排障入口是 12 篇 Q2(现象→根因→处置)与第 3 节排查决策树的对应分支;理解机制必读 01 篇第 5.1 节(三层容错的选型推理)与 18 篇第 3 节(sse.ts 逐段走读:x-accel-buffering、心跳、连接摘除)。协议细节与原始帧流查 19 篇第 4 节,成本结构查 10 篇第 2 节(三层各自的存在意义——不建议砍层)。涉及 nginx/frp 参数时读 14 篇第 5 节(⚠️ 具体参数需登 VPS 核实)。若要给新数据类型走 SSE,先读 11 篇 SOP 六。
按序读四份材料:06 篇第 6.1 节(上线检查清单,第一项就是 config.json 的 auth 段)、08 篇全文(威胁模型 → 自研鉴权 → 公开脱敏 → 裸奔面清单 → 输入防御,第 8 节有按风险排序的隐患汇总)、14 篇第 8 节(改什么、怎么生效——上线前逐项核对配置)、12 篇第 4 节(兜底手段清单,出事时的处置层级)。部署形态选择回读 06 篇第 5 节与 14 篇第 4 节(host 网络的两条衍生语义)。已知但可接受的残余风险(Cookie 无 Secure、限流共享键)见 13 篇路线图,不要在上线日顺手修——按路线图的 v1.1 批次做。
从 12 篇 Q1 入手(三个最常见根因:端口混淆、frp 链路断、页面未刷新),配合第 3 节决策树把问题切到容器层或链路层;容器层看 12 篇第 1 节日志清单与 06 篇第 7 节速查表,链路层看 12 篇 Q8(frp 分段验证)。理解「为什么修复后要刷新页面」读 16 篇 ADR-006(模式锁定设计);理解降级模式期间数据口径的变化读 05 篇第 2 节与 04 篇第 4.2 节(浏览器侧历史的三态流转)。
按序读 06 篇第 2-3 节(本地启动与配置准备)→ 14 篇第 3-4 节(两种构建路径与 compose 差异)→ 14 篇第 7 节(数据文件清单:probe.db/session-secret/covers 的可再生物与不可再生物分界)→ 12 篇 Q4/Q5(重建后手工 check 与白名单的恢复动作)。若在迁移中遇到容器反复重启,直接查 12 篇 Q6。
| 篇 | 标题 | 一句话定位 |
|---|---|---|
| 00 | 项目总览 | 是什么、用什么造、东西在哪 |
| 01 | 系统架构 | 如何组织、为什么这样组织 |
| 02 | 页面设计 | 前端图纸:路由、组件树、逐页设计 |
| 03 | 接口设计 | 端点总表与设计约定 |
| 04 | 数据模型 | 三处持久化与运行时数据的形状 |
| 05 | 核心流程 | 五条端到端链路的时序图 |
| 06 | 部署运行 | 本地开发、两种容器部署、运维例程 |
| 07 | 模块详解 | 函数级模块索引 |
| 08 | 安全与权限设计 | 威胁模型、鉴权、脱敏、输入防御 |
| 09 | 测试与质量保障 | 无测试的证据链与补测路线图 |
| 10 | 性能设计与优化 | 量级预算与优化建议 |
| 11 | 扩展开发指南 | 六个扩展场景 SOP |
| 12 | FAQ 与故障排查 | 日志清单、FAQ、决策树、兜底手段 |
| 13 | 技术债务与演进建议 | 五项债务与版本路线图 |
| 14 | 配置与环境详解 | 逐字段配置手册 |
| 15 | 接口全量参考 | 端点详表、SSE 专节、存储键全表 |
| 16 | 决策记录(ADR) | 12 条关键决策的背景/备选/证据/后果 |
| 17 | 依赖全景 | 前后端依赖逐项解析与健康度评估 |
| 18 | 代码走读实录 | probe/store/sse 三文件逐段带码走读 |
| 19 | 数据契约与示例集 | 核心端点的完整请求/响应/错误/curl 示例 |
| 20 | 术语表与文档索引 | 本篇 |
最后给三条使用建议,作为全套文档的结束语。第一,把它当索引而非教科书:21 篇之间刻意做了分工与互引,重读全本不如按第 3 节的任务路径或第 2 节的反向索引精准定位——每篇开头与结尾的引用句就是为了这种跳读设计的。第二,用行号锚点时以函数名为准:全部行号是 2026-10-06 对 v0.3.0 源码的快照,重构后行号会漂移,但函数名与注释语义相对稳定。第三,文档与代码的分歧以代码为准并回写文档:本套文档在写作中核实出两处与早期表述有出入的细节(/api/public/metrics 502 实际透传 panelMsg,见 19 篇第 6.2 节;/api/events 线格式为蛇形 service_id,见 19 篇第 2.3 节),均已标注——接手者若再发现类似分歧,正确的动作是修文档、修代码其一,而不是让两边各说各话。源码是唯一的真相,文档是通向真相的地图。
全篇引用的篇目均位于同一目录(nas-console 文档集);项目源码位于仓库根 /nas-console(前端 src/、服务端 server/)。若这是你读的第一篇——建议先回 00-项目总览,再按第 3 节的任务路径取用其余各篇。