mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-19 16:38:31 +00:00
- 主题: sakura×frost 玻璃拟态 (theme.css) + SCSS 变量重映射 - 侧栏: 玻璃侧栏 246px + 渐变品牌区 + 面包屑导航 - 状态页: 玻璃 KPI 卡 + 远程节点/本地服务卡片网格 - 集群页: 英雄玻璃卡 + 横向环拓扑链 + 待办命令/活跃拓扑/日志区 - 令牌环: 新增 lastSync 上次同步时间替代周期计数 - 模拟 frps: frps2/frps3 容器 + test-forward.sh 全链路验证脚本 - 修复: BinaryPath 空导致 worker 不启动, 撤销仅撤第一个 link, 任务复活风暴 (published 追踪)
16 KiB
16 KiB
webui4frpc 开发计划
webui4frpc —— 独立于 frp 源码的可视化 frpc 控制器(单二进制,零 frp 代码依赖,frpc 运行时一键从官方 Releases 下载或手动指定)。
目标
用画布方式把「本地转发项」连到「远程服务器」,自动为每台远程服务器生成 frpc 配置并拉起独立 worker 进程,避免运维反复手写 frpc 配置。
已完成(M0:骨架与核心)
- 独立仓库:module
webui4frpc,仅依赖modernc.org/sqlite,零 frp 源码引用 - 目录结构:cmd / internal/{store,render,process,httpapi,install} + web(Vue3 + VueFlow + Element Plus)
- 画布模型:local / remote / link 多对多连线,节点可编辑、可添加
- 渲染器:tcp/udp/http/https → frpc JSON 配置(http 用 customDomains=
<name>.local) - worker 进程管理:spawn/stop/restart、日志轮转、崩溃自愈(指数退避)
- 一键安装 frpc(GitHub Releases)+ 手动指定路径
- REST API:status/canvas/settings/remotes/profiles/binary
- 三页 UI:状态(默认)/ 连接配置 / 设置
- 状态页:远程节点状态框 + 本地服务转发表(每 5s 轮询),节点可增删改启停
- 画布可用性检查:端口冲突 / http 域名唯一性,标红 + 弹窗提示,保存前拦截
- 演示环境:3 本地 × 3 远程多对多全连通(tcp + http 转发实测返回数据)
- 后端单元测试: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
已知问题(审查发现,待修复)
设置保存失效(P0)已修复:server.go的NewServeMux现在对PUT /api/manager/settings分发到handleSettingsPut,新增TestSettingsPutRoundTrip回归测试,前端 SettingsView 保存生效。dev proxy 端口不一致已修复:web/vite.config.tsproxy 指向127.0.0.1:7500(可用VITE_PROXY_TARGET覆盖),与后端默认一致。- 冗余依赖:
vue-router在 package.json 依赖中但未被使用(App.vue 以视图 ref 手动切页,无路由)。 - README 笔误:架构图写
cmds/webui4frpc,实际目录为cmd/webui4frpc。
里程碑
M1 高级传输参数(P0,建议优先)
目标:让常用 frpc 能力可配置,保持画布简洁(折叠高级区)。
- Local 增加高级字段:useEncryption / useCompression / bandwidthLimit / poolCount / metadatas / annotations(渲染器 + 编辑表单 + 折叠 UI)
- Remote 增加 transport:protocol(tcp/quic/kcp/websocket)、tls.enable、poolCount
- 渲染器补齐 transport 段输出;增加 render 单测覆盖新字段
- 高级字段随 canvas API 持久化(store 表扩展 + 迁移)
M2 HTTP/HTTPS 完善
- local http/https 增加可编辑域名字段(customDomains / subdomain)
- locations(路径路由)多配置
- httpHeaderRewrite / hostHeaderRewrite / basicAuth(站点访问认证)
- frps vhostHTTPPort 的显示与状态映射(状态页展示)
M3 负载均衡与健康检查
- 模型扩展:Local 增加 lbGroup / lbGroupKey / healthCheck* 字段,Remote 增加 adminAddr / adminPort / adminUser / adminPassword(local frpc admin API)
- 渲染器:proxy 输出 loadBalancer + healthCheck 段;worker 配置输出 webServer 段(frpc >= 0.52 支持)
- 状态页:按 admin API GET /api/status 拉取真实 per-proxy 状态(running / check failed / wait start / start error / new / closed),未启用 admin 时回退日志推断
- 前端:LocalNode/RemoteNode 表单 + StatusView per-proxy 状态与 LB 组徽标(vue-tsc 通过)
- CLUSTER 页:展示负载均衡组(group)+ 健康检查状态(e2e 验证:backend 停止→check failed 剔除,恢复→自动收回)
M4 更多代理类型
- tcpmux(HTTP/2 多路复用)
- stcp / sudp(点对点加密穿透)
- xtcp(P2P,需 kcp)
- 按类型动态显示字段的表单
M5 运维与发布
- 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):令牌按固定顺序传递;每个节点记录前驱/后继。
令牌环协议(单轮:边传播边同步,收敛快半轮)
- 令牌环行时,各节点收到令牌后同时做两件事:
- 透传/同步:采纳令牌已携带的完整集群信息(其它节点此前追加的节点表、负载、拓扑、日志增量),更新自己的完整集群视图。
- 追加/顺带更新:把自己的信息(地址、负载指标、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 做添加节点,哪个节点负责把新节点并入集群(临近节点处理):
- 临近节点(如 B)收到新节点(C)的加入请求后,修改转发链与拓扑:把 C 插入自己后面(B 的后继=C),自己的原后继(A)移到 C 后面(C 的后继=A),形成
A→B→C→A。 - B 将令牌交给 C(而非原后继 A)——C 作为 B 的后继参与环;C 处理令牌后继续传给自己的后继 A,形成完整的 join 语义。
- 随令牌轮转,全节点收敛到含 C 的一致拓扑。
- 临近节点(如 B)收到新节点(C)的加入请求后,修改转发链与拓扑:把 C 插入自己后面(B 的后继=C),自己的原后继(A)移到 C 后面(C 的后继=A),形成
- 移除节点(removenode):借用现有任务发布基础设施——在某节点 webui 发起删除节点,发布
node.remove命令进令牌;令牌传递到被移除节点自身时,该节点执行自移除:- 将自己负责的转发任务重新追加到待分配任务(自然的
owner 离线重挂语义), - 修改拓扑与转发链、移除自身(拓扑去掉自己的项、环去掉自己、后继衔接),
- 令牌传递给自身原本的下一家。
- 将自己负责的转发任务重新追加到待分配任务(自然的
- 脱离集群后的本地视图:被移除节点脱离后,自身内部维持的拓扑只保留本地 local(localOnly 不进集群的转发),完全取消一切集群远程转发(删掉由集群分发的转发、停其 worker)。
故障自愈(传递令牌间隙判定邻居离线)
- 邻居离线判定仅在传递令牌的间隙发生:上家传递令牌给下家后,若在预期窗口内未收到回执,则判定下家离线 → 修改集群状态、移除该节点、将其负责的任务重新追加为待办,令牌传给再下家。
节点消亡(防脑裂最强兜底)
- 任何节点若超时收不到令牌(超过既定周期的 N 倍),直接自杀退出:不探测上家、不生成新令牌、不做任何协商。这是防止集群脑裂(split-brain)的最后防线——宁可单一节点自毁,不让两个各自为政的残环长期并存。
实现里程碑(待按上述设计重建)
- 数据面:Ring State(节点表/转发链/leader/轮次延迟/负载/待办任务)+ 日志(追加式 + 水位)
- 令牌环传输:HTTP POST /cluster/token + 回执;单轮一周期(信息追加与同步同步完成)
- leader:创建集群者为首 leader,StartRing 首令牌 + 轮次延迟测量 + 令牌丢失重发(LossTimeout)
- 新转发任务:POST /cluster/task 挂 pending → 令牌携带 → 负载最低者摘取 → 持久化 + 拉 worker(e2e 验证)
- 增量日志同步:LogForwardAdd/LogNodeJoin 等事件随令牌 delta 传播(单轮内完成同步),各节点重放收敛(e2e 验证)
- 拓扑回写:摘取后 state 写入令牌,全网一致(e2e topology=[t1,owner])
- 新节点加入:POST /cluster/join → 插入环(后继) → AdoptState(e2e A+B 环)
- 离线处理:令牌超时 → 改集群状态 → 邻居转发转任务重挂(forwardToNext 已实现,待 e2e)
- leader 监控:上家邻居心跳探测 → 崩溃提升(WatchLeader 已实现,待 e2e)
- 前端:集群页展示环拓扑/令牌轮次/负载/任务摘取;设置页二进制缓存状态
M7 运维与发布(原 M5)
- git init + 首次 commit + .gitignore
- 一键构建脚本 / GitHub Actions release / 配置导入导出 / 日志查看页
建议推进顺序
- 修复「设置保存失效」——给
PUT /api/manager/settings补注册(server.go 方法分发 + httpapi 单测) - 对齐 dev proxy 端口——vite.config.ts → 7500(支持 VITE_PROXY_TARGET 覆盖)
- M1 高级传输参数(store 加列 → render 输出 → 前端折叠表单 → 单测回归)
- M3 负载均衡与健康检查(模型 → 渲染 → admin API 状态 → 集群页)— 进行中
- 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 原则:高级字段折叠式 / 按类型动态显示,保画布简洁