mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-20 00:47:57 +00:00
- 主题: sakura×frost 玻璃拟态 (theme.css) + SCSS 变量重映射 - 侧栏: 玻璃侧栏 246px + 渐变品牌区 + 面包屑导航 - 状态页: 玻璃 KPI 卡 + 远程节点/本地服务卡片网格 - 集群页: 英雄玻璃卡 + 横向环拓扑链 + 待办命令/活跃拓扑/日志区 - 令牌环: 新增 lastSync 上次同步时间替代周期计数 - 模拟 frps: frps2/frps3 容器 + test-forward.sh 全链路验证脚本 - 修复: BinaryPath 空导致 worker 不启动, 撤销仅撤第一个 link, 任务复活风暴 (published 追踪)
194 lines
16 KiB
Markdown
194 lines
16 KiB
Markdown
# webui4frpc 开发计划
|
||
|
||
> webui4frpc —— 独立于 frp 源码的可视化 frpc 控制器(单二进制,零 frp 代码依赖,frpc 运行时一键从官方 Releases 下载或手动指定)。
|
||
|
||
## 目标
|
||
|
||
用画布方式把「本地转发项」连到「远程服务器」,自动为每台远程服务器生成 frpc 配置并拉起独立 worker 进程,避免运维反复手写 frpc 配置。
|
||
|
||
## 已完成(M0:骨架与核心)
|
||
|
||
- [x] 独立仓库:module `webui4frpc`,仅依赖 `modernc.org/sqlite`,零 frp 源码引用
|
||
- [x] 目录结构:cmd / internal/{store,render,process,httpapi,install} + web(Vue3 + VueFlow + Element Plus)
|
||
- [x] 画布模型:local / remote / link 多对多连线,节点可编辑、可添加
|
||
- [x] 渲染器:tcp/udp/http/https → frpc JSON 配置(http 用 customDomains=`<name>.local`)
|
||
- [x] worker 进程管理:spawn/stop/restart、日志轮转、崩溃自愈(指数退避)
|
||
- [x] 一键安装 frpc(GitHub Releases)+ 手动指定路径
|
||
- [x] REST API:status/canvas/settings/remotes/profiles/binary
|
||
- [x] 三页 UI:状态(默认)/ 连接配置 / 设置
|
||
- [x] 状态页:远程节点状态框 + 本地服务转发表(每 5s 轮询),节点可增删改启停
|
||
- [x] 画布可用性检查:端口冲突 / http 域名唯一性,标红 + 弹窗提示,保存前拦截
|
||
- [x] 演示环境:3 本地 × 3 远程多对多全连通(tcp + http 转发实测返回数据)
|
||
- [x] 后端单元测试:store / render / process / httpapi / install
|
||
|
||
## 项目现状核对(代码审查记录 2024-08 单 commit c193688)
|
||
|
||
- 后端入口 `cmd/webui4frpc/main.go`:flag 解析(-addr/-user/-password/-workdir/-bin)→ 建数据目录 → `store.New` → `process.NewManager`(BinaryPath/AutoRestart/RestartInterval 动态闭包,AutoStartProfiles 启动已启用 worker)→ `httpapi.NewServeMux`(healthz 免认证,其余 Basic Auth)→ SIGINT/SIGTERM 优雅退出(15s 超时 + StopAll)
|
||
- 核心数据流:PUT `/api/manager/canvas` → store 全量 upsert/删除 + 事务替换 links → `SyncWorkers` 重启受影响 worker → 每台远程独立 `frpc -c configs/<name>.json` 进程
|
||
- 渲染器当前能力:tcp/udp 用 remotePort,http/https 用 customDomains(默认 `<name>.local`),重名服务自动加端口后缀;transport 段仅有 protocol 字段且从未填充
|
||
- 前端:App.vue 三页手动切换(状态/连接配置/设置),CanvasView/VueFlow 画布 + StatusView 5s 轮询 + SettingsView
|
||
|
||
## 已知问题(审查发现,待修复)
|
||
|
||
1. ~~设置保存失效(P0)~~ **已修复**:`server.go` 的 `NewServeMux` 现在对 `PUT /api/manager/settings` 分发到 `handleSettingsPut`,新增 `TestSettingsPutRoundTrip` 回归测试,前端 SettingsView 保存生效。
|
||
2. ~~dev proxy 端口不一致~~ **已修复**:`web/vite.config.ts` proxy 指向 `127.0.0.1:7500`(可用 `VITE_PROXY_TARGET` 覆盖),与后端默认一致。
|
||
3. **冗余依赖**:`vue-router` 在 package.json 依赖中但未被使用(App.vue 以视图 ref 手动切页,无路由)。
|
||
4. **README 笔误**:架构图写 `cmds/webui4frpc`,实际目录为 `cmd/webui4frpc`。
|
||
|
||
## 里程碑
|
||
|
||
### M1 高级传输参数(P0,建议优先)
|
||
|
||
目标:让常用 frpc 能力可配置,保持画布简洁(折叠高级区)。
|
||
|
||
- [x] Local 增加高级字段:useEncryption / useCompression / bandwidthLimit / poolCount / metadatas / annotations(渲染器 + 编辑表单 + 折叠 UI)
|
||
- [x] Remote 增加 transport:protocol(tcp/quic/kcp/websocket)、tls.enable、poolCount
|
||
- [x] 渲染器补齐 transport 段输出;增加 render 单测覆盖新字段
|
||
- [x] 高级字段随 canvas API 持久化(store 表扩展 + 迁移)
|
||
|
||
### M2 HTTP/HTTPS 完善
|
||
|
||
- [x] local http/https 增加可编辑域名字段(customDomains / subdomain)
|
||
- [x] locations(路径路由)多配置
|
||
- [x] httpHeaderRewrite / hostHeaderRewrite / basicAuth(站点访问认证)
|
||
- [x] frps vhostHTTPPort 的显示与状态映射(状态页展示)
|
||
|
||
### M3 负载均衡与健康检查
|
||
|
||
- [x] 模型扩展:Local 增加 lbGroup / lbGroupKey / healthCheck* 字段,Remote 增加 adminAddr / adminPort / adminUser / adminPassword(local frpc admin API)
|
||
- [x] 渲染器:proxy 输出 loadBalancer + healthCheck 段;worker 配置输出 webServer 段(frpc >= 0.52 支持)
|
||
- [x] 状态页:按 admin API GET /api/status 拉取真实 per-proxy 状态(running / check failed / wait start / start error / new / closed),未启用 admin 时回退日志推断
|
||
- [x] 前端:LocalNode/RemoteNode 表单 + StatusView per-proxy 状态与 LB 组徽标(vue-tsc 通过)
|
||
- [x] CLUSTER 页:展示负载均衡组(group)+ 健康检查状态(e2e 验证:backend 停止→check failed 剔除,恢复→自动收回)
|
||
|
||
### M4 更多代理类型
|
||
|
||
- [ ] tcpmux(HTTP/2 多路复用)
|
||
- [ ] stcp / sudp(点对点加密穿透)
|
||
- [ ] xtcp(P2P,需 kcp)
|
||
- [ ] 按类型动态显示字段的表单
|
||
|
||
### M5 运维与发布
|
||
|
||
- [x] git init + 首次 commit + .gitignore(排除 node_modules / web/dist;.pi/ 本地代理状态已忽略)
|
||
- [ ] 一键构建脚本(web→dist→embed→单二进制)
|
||
- [ ] GitHub Actions:release 构建(linux/windows/mac)
|
||
- [ ] 配置导出/导入(备份)
|
||
- [ ] 日志查看页(worker 日志 tail UI)
|
||
|
||
> 注:发布类条目已并入 **M7 运维与发布(原 M5)**,本节 M5 保留初始清单。
|
||
>
|
||
### M6 集群(令牌环网拓扑)
|
||
|
||
> **权威设计(用户约定,必须固化)**:集群节点间通过**令牌环网(token ring)**通信,单轮为一个周期。
|
||
|
||
#### 拓扑与角色
|
||
|
||
- 集群 = 若干 webui4frpc 节点组成的内网协作网络;逻辑上各节点**平等**,均持有**完整集群信息**(节点表、转发链、各自配置)与完整 webui。
|
||
- **leader**:唯一特殊角色,负责启动时发送第一个令牌;默认**创建集群的节点**为初始 leader。
|
||
- **转发链(ring)**:令牌按固定顺序传递;每个节点记录前驱/后继。
|
||
|
||
#### 令牌环协议(单轮:边传播边同步,收敛快半轮)
|
||
|
||
- **令牌环行时,各节点收到令牌后同时做两件事**:
|
||
1. **透传/同步**:采纳令牌已携带的完整集群信息(其它节点此前追加的节点表、负载、拓扑、日志增量),更新自己的完整集群视图。
|
||
2. **追加/顺带更新**:把自己的信息(地址、负载指标、frpc 缓存、转发能力、本机产生的命令)追加进令牌。
|
||
- 因每经过一个节点就立即传播当前已知的最新状态,第一个节点的信息在**第二个节点**就被同步到,全网收敛只需**约半圈**——无需分两轮——第一轮即同步,全网收敛只需约半圈。
|
||
- 令牌同一时刻只有一个;由"收到令牌的节点处理后交给下家"驱动,无定时重启、无竞态。
|
||
- **节奏控制(并行计时器 + 同步操作,非硬性 sleep)**:令牌到达时节点做自己的操作(摘取/追加/日志同步/差异预计算),**同时起一个独立的节奏计时器**。操作完成后若计时器未走完则等计时器(保持环的稳定节奏);计时器先到而操作未完则等操作完成(操作皆轻量,且大部分耗时操作如差异计算在令牌到来前已完成)。故转发时机 = max(操作完成, 节奏计时器),两者并行不互斥。
|
||
- **令牌带时间戳,只取最新**:每个令牌携带发起时间戳(`SentAt`);节点收到令牌时比较时间戳——若令牌比本机最近记录的旧,说明是**重发残留的旧令牌**,直接丢弃。此机制彻底消除"重发时旧令牌可能还活着"的担忧,即使重发,旧令牌也会被下游识别过期而消失,环上始终只有一个有效令牌。
|
||
- 新转发任务:需要建立转发时,**把任务附加在令牌中**(而非直接指派);因每节点都持完整集群信息,由**负载最低的节点**(**内存使用率 + 网络使用率共同判断**)自行摘取并创建转发。
|
||
- **负载摘取**:令牌传递到某节点时,若该节点在上一轮被判定为负载最低,则**先主动摘取任务**,随后在令牌中更新自身信息。
|
||
|
||
#### 容错与邻居离线
|
||
|
||
- 令牌传递超时(未收到回执)→ 判定邻居离线。因每节点持完整集群信息,当前节点可**自动修改集群状态**,并把邻居负责的转发**作为新任务追加到令牌**,再传给下一节点。
|
||
- leader 检测令牌丢失:发出令牌后超过 **(轮次延迟 / 2 + 20ms)** 未回传 → 判定令牌丢失(可能某节点接令后崩溃),**重发令牌**。
|
||
- leader 每轮探测后更新一次轮次延迟。
|
||
|
||
#### leader 补充/监控
|
||
|
||
- leader 受其上家邻居监控:二者**交换心跳包**(因节点都持完整信息,容易做到)。
|
||
- 上家邻居探测到 leader 崩溃 → **自身成为新 leader**。
|
||
|
||
#### 新节点加入
|
||
|
||
- 每个节点都有 webui;用户登录到哪个节点的 webui,就由**该节点**执行新节点加入:令牌发到自己时,**先转发给新节点**,并把新节点加入令牌中的集群信息,**更新转发链**:新节点放在自己后面,自己原本的后继放在新节点之后。
|
||
|
||
#### 增量日志同步(本机事件 → 令牌 → 全网一致)
|
||
|
||
- 每个节点维护一份**集群事件日志(operation log)**:本机发生的变更(创建/删除转发、节点加入/离开、负载变化、leader 变更)以**递增序号**追加为本机日志条目。
|
||
- 令牌携带**本机日志增量**(同步与追加在同一轮):号码 > 上一周期已同步水位(`lastSyncedSeq`)的条目,随令牌环行时让其它节点**按序追加重放到本地日志**,从而实现全网**增量日志同步**。
|
||
- 日志条目不可变(追加式);节点收到增量后校验序号连续性(缺失则请求补齐),因每个节点持有完整集群信息,可从其它节点补拉。
|
||
- 集群状态(拓扑/任务/leader)可**由日志重放得到**:新节点加入时,通过令牌/邻居拉取完整日志并重放,即可获得与其它节点一致的完整视图(满足"每个节点都掌握完整集群信息")。
|
||
- 该机制与"令牌承载中间配置文件/转发拓扑"正交共存:令牌同时携带〔拓扑快照〕与〔日志增量〕,快照用于即时校验,日志用于一致性追补。
|
||
|
||
#### 任务撤销(复用任务发布通道,撤销语义)
|
||
|
||
- 撤销一个转发 = **发布一个"撤销任务"到令牌**:与任务发布走同一基础通道(round-1 注入、round-2 执行),只是 Type 为撤销、载荷为〔任务 ID + 该转发中间配置〕。
|
||
- 令牌环行时,**持有该转发的节点**(其 owner 记录在拓扑中)收到撤销任务后:取消本地 frpc worker → 从活跃拓扑移除该转发 → 记 `forward.remove` 增量日志,全网随之下一次轮次收敛一致。
|
||
- 撤销任务幂等:若转发已不在拓扑(已被撤销/离线重挂),收到撤销仅记日志、不报错。
|
||
- localOnly 的本地转发撤销不发布到令牌,直接本机取消(因本机即 owner,仅本节点可见)。
|
||
|
||
#### 画布差异判断(diff)与命令追加
|
||
|
||
- **在哪个节点修改 webui 画布,就由该节点进行差异判断**:将该节点画布的「期望状态」与当前集群拓扑(该节点持有的完整视图)比对。
|
||
- 差异结果转为**一批转发/撤销命令,追加到令牌任务**:
|
||
- 画布有、拓扑无 → 追加**新增转发命令**(任务+中间配置)→ 后续由负载最低者摘取创建。
|
||
- 画布无、拓扑有 → 追加**撤销转发命令**(撤任务+转发的中间配置)→ 持有者收到后取消 worker 并移出拓扑。
|
||
- 画布即唯一编辑入口:编辑节点只发命令,不直接改其它节点;拓扑随令牌轮次收敛全网一致。
|
||
- **其它节点根据集群拓扑生成本地画布存储**:非编辑节点收到令牌同步的完整拓扑后,把拓扑中的转发(locals/remotes/links)重建为本地画布视图,叠加本节点的 localOnly 项。故所有节点的画布展示一致(都源自拓扑),编辑只在入口节点生效。
|
||
|
||
#### 增删节点
|
||
|
||
- **加入节点(addnode)**:维持原设计——在**哪个节点的 webui 做添加节点,哪个节点负责把新节点并入集群**(临近节点处理):
|
||
1. 临近节点(如 B)收到新节点(C)的加入请求后,**修改转发链与拓扑**:把 C 插入自己后面(B 的后继=C),自己的原后继(A)移到 C 后面(C 的后继=A),形成 `A→B→C→A`。
|
||
2. **B 将令牌交给 C**(而非原后继 A)——C 作为 B 的后继参与环;C 处理令牌后继续传给自己的后继 A,**形成完整的 join 语义**。
|
||
3. 随令牌轮转,全节点收敛到含 C 的一致拓扑。
|
||
- **移除节点(removenode)**:**借用现有任务发布基础设施**——在某节点 webui 发起删除节点,发布 `node.remove` 命令进令牌;令牌传递到**被移除节点自身**时,该节点执行自移除:
|
||
1. 将自己负责的转发任务**重新追加到待分配任务**(自然的 `owner 离线重挂` 语义),
|
||
2. **修改拓扑与转发链、移除自身**(拓扑去掉自己的项、环去掉自己、后继衔接),
|
||
3. 令牌传递给自身**原本的下一家**。
|
||
- **脱离集群后的本地视图**:被移除节点脱离后,自身内部维持的拓扑**只保留本地 local(localOnly 不进集群的转发)**,**完全取消一切集群远程转发**(删掉由集群分发的转发、停其 worker)。
|
||
|
||
#### 故障自愈(传递令牌间隙判定邻居离线)
|
||
|
||
- 邻居离线判定**仅在传递令牌的间隙**发生:上家传递令牌给下家后,若在预期窗口内未收到回执,则判定下家离线 → **修改集群状态、移除该节点、将其负责的任务重新追加为待办**,令牌传给再下家。
|
||
|
||
#### 节点消亡(防脑裂最强兜底)
|
||
|
||
- 任何节点若**超时收不到令牌**(超过既定周期的 N 倍),**直接自杀退出**:不探测上家、不生成新令牌、不做任何协商。这是防止集群脑裂(split-brain)的最后防线——宁可单一节点自毁,不让两个各自为政的残环长期并存。
|
||
|
||
#### 实现里程碑(待按上述设计重建)
|
||
|
||
- [x] 数据面:Ring State(节点表/转发链/leader/轮次延迟/负载/待办任务)+ 日志(追加式 + 水位)
|
||
- [x] 令牌环传输:HTTP POST /cluster/token + 回执;单轮一周期(信息追加与同步同步完成)
|
||
- [x] leader:创建集群者为首 leader,StartRing 首令牌 + 轮次延迟测量 + 令牌丢失重发(LossTimeout)
|
||
- [x] 新转发任务:POST /cluster/task 挂 pending → 令牌携带 → 负载最低者摘取 → 持久化 + 拉 worker(e2e 验证)
|
||
- [x] 增量日志同步:LogForwardAdd/LogNodeJoin 等事件随令牌 delta 传播(单轮内完成同步),各节点重放收敛(e2e 验证)
|
||
- [x] 拓扑回写:摘取后 state 写入令牌,全网一致(e2e topology=[t1,owner])
|
||
- [x] 新节点加入:POST /cluster/join → 插入环(后继) → AdoptState(e2e A+B 环)
|
||
- [ ] 离线处理:令牌超时 → 改集群状态 → 邻居转发转任务重挂(forwardToNext 已实现,待 e2e)
|
||
- [ ] leader 监控:上家邻居心跳探测 → 崩溃提升(WatchLeader 已实现,待 e2e)
|
||
- [ ] 前端:集群页展示环拓扑/令牌轮次/负载/任务摘取;设置页二进制缓存状态
|
||
|
||
### M7 运维与发布(原 M5)
|
||
|
||
- [x] git init + 首次 commit + .gitignore
|
||
- [ ] 一键构建脚本 / GitHub Actions release / 配置导入导出 / 日志查看页
|
||
|
||
## 建议推进顺序
|
||
|
||
1. [x] 修复「设置保存失效」——给 `PUT /api/manager/settings` 补注册(server.go 方法分发 + httpapi 单测)
|
||
2. [x] 对齐 dev proxy 端口——vite.config.ts → 7500(支持 VITE_PROXY_TARGET 覆盖)
|
||
3. M1 高级传输参数(store 加列 → render 输出 → 前端折叠表单 → 单测回归)
|
||
4. M3 负载均衡与健康检查(模型 → 渲染 → admin API 状态 → 集群页)— 进行中
|
||
5. M6 集群内 frpc 二进制分发(节点间优先 → 外部 URL 兜底 → 新节点自动传输)
|
||
|
||
## 审查结论摘要(详见 FRPC_FEATURES_AUDIT.md)
|
||
|
||
- 已支持:tcp / http(customDomains)、token 认证、多对多、worker 管理
|
||
- 未支持:stcp/sudp/xtcp/tcpmux、OIDC/TLS 证书、加密/压缩/限速/连接池、LB/健康检查、http 路由高级、插件/虚拟 IP
|
||
- 优先级:P0 高级传输参数 → P1 域名字段+transport → P2 LB/健康检查 → P3 低频项
|
||
- UI 原则:高级字段折叠式 / 按类型动态显示,保画布简洁
|