feat: cluster reliability (leader failover, crash rejoin, key exchange) + auth/users + canvas/forwards enhancements + comprehensive README + API docs

- Cluster: forwardToNext offline detection (leader+non-leader), WatchLeader 1s heartbeat fallback, 409 for standalone nodes, Node.NodeKey key exchange via token ring, ClusterPeers persistence + auto-rejoin, Forward delegates to forwardToNext (bugfix)
- Auth: Basic Auth (flag-creds fast path) + bcrypt users (admin/viewer) + Bearer API keys (read/write/admin scope)
- Frontend: UsersView (accounts+API keys), ClusterView (ring/nodeKey/tasks/topology/log), StatusView (group management, per-proxy status), CanvasView (edge toggle/group), PortEdge (disabled/group labels)
- API: handlers split (canvas/forwards/users/logs), canvas export/import, forwards group start/stop/assign/delete, cluster endpoints
- Docs: comprehensive README rewrite (all flags/APIs/auth/cluster), docs/cluster-api.md (cluster management API reference)
- Deploy: run-cluster.sh now 4-node ring + 1 isolated standalone, test-forward.sh updated for 4 nodes
- Removed plan.md (design notes consolidated into README + API docs)
This commit is contained in:
2026-08-19 21:09:24 +08:00
parent b518a13446
commit eda9bb9597
55 changed files with 5462 additions and 793 deletions

283
web/public/favicon.svg Normal file
View File

@ -0,0 +1,283 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" width="500" height="500" shape-rendering="crispEdges">
<defs>
<!-- 主字母像素块(黑色) -->
<rect id="p" width="12" height="12" fill="#000000" />
<!-- frpc 小像素块(蓝色) -->
<rect id="q" width="5" height="5" fill="#2563eb" />
<!-- 装饰小方块(浅灰) -->
<rect id="d" width="8" height="8" fill="#94a3b8" />
<!-- 小十字星装饰 -->
<rect id="s" width="6" height="6" fill="#94a3b8" />
</defs>
<!-- 圆角白色背景(模拟 App 图标外框) -->
<rect width="500" height="500" rx="40" fill="#ffffff" stroke="#e2e8f0" stroke-width="4" />
<!-- ========== 主字母整体居中(横向间距更合理) ========== -->
<g transform="translate(70, 160)">
<!-- ===== W基于原始结构高度约 150px ===== -->
<g transform="translate(0, 0)">
<!-- 左竖线占2列 -->
<use href="#p" x="0" y="0" />
<use href="#p" x="12" y="0" />
<use href="#p" x="0" y="12" />
<use href="#p" x="12" y="12" />
<use href="#p" x="0" y="24" />
<use href="#p" x="12" y="24" />
<use href="#p" x="0" y="36" />
<use href="#p" x="12" y="36" />
<use href="#p" x="0" y="48" />
<use href="#p" x="12" y="48" />
<use href="#p" x="0" y="60" />
<use href="#p" x="12" y="60" />
<use href="#p" x="0" y="72" />
<use href="#p" x="12" y="72" />
<use href="#p" x="0" y="84" />
<use href="#p" x="12" y="84" />
<use href="#p" x="0" y="96" />
<use href="#p" x="12" y="96" />
<use href="#p" x="0" y="108" />
<use href="#p" x="12" y="108" />
<use href="#p" x="0" y="120" />
<use href="#p" x="12" y="120" />
<use href="#p" x="0" y="132" />
<use href="#p" x="12" y="132" />
<!-- 右竖线占2列 -->
<use href="#p" x="84" y="0" />
<use href="#p" x="96" y="0" />
<use href="#p" x="84" y="12" />
<use href="#p" x="96" y="12" />
<use href="#p" x="84" y="24" />
<use href="#p" x="96" y="24" />
<use href="#p" x="84" y="36" />
<use href="#p" x="96" y="36" />
<use href="#p" x="84" y="48" />
<use href="#p" x="96" y="48" />
<use href="#p" x="84" y="60" />
<use href="#p" x="96" y="60" />
<use href="#p" x="84" y="72" />
<use href="#p" x="96" y="72" />
<use href="#p" x="84" y="84" />
<use href="#p" x="96" y="84" />
<use href="#p" x="84" y="96" />
<use href="#p" x="96" y="96" />
<use href="#p" x="84" y="108" />
<use href="#p" x="96" y="108" />
<use href="#p" x="84" y="120" />
<use href="#p" x="96" y="120" />
<use href="#p" x="84" y="132" />
<use href="#p" x="96" y="132" />
<!-- 中间 V 形(从 y=60 开始) -->
<use href="#p" x="24" y="60" />
<use href="#p" x="48" y="60" />
<use href="#p" x="72" y="60" />
<use href="#p" x="24" y="72" />
<use href="#p" x="48" y="72" />
<use href="#p" x="72" y="72" />
<use href="#p" x="24" y="84" />
<use href="#p" x="48" y="84" />
<use href="#p" x="72" y="84" />
<use href="#p" x="24" y="96" />
<use href="#p" x="48" y="96" />
<use href="#p" x="72" y="96" />
<!-- 底部汇合 -->
<use href="#p" x="36" y="108" />
<use href="#p" x="60" y="108" />
<use href="#p" x="36" y="120" />
<use href="#p" x="60" y="120" />
<use href="#p" x="36" y="132" />
<use href="#p" x="60" y="132" />
<use href="#p" x="48" y="132" />
</g>
<!-- ===== U高度与 W 一致,内部空间增大) ===== -->
<g transform="translate(124, 0)">
<!-- 左竖线占2列 -->
<use href="#p" x="0" y="0" />
<use href="#p" x="12" y="0" />
<use href="#p" x="0" y="12" />
<use href="#p" x="12" y="12" />
<use href="#p" x="0" y="24" />
<use href="#p" x="12" y="24" />
<use href="#p" x="0" y="36" />
<use href="#p" x="12" y="36" />
<use href="#p" x="0" y="48" />
<use href="#p" x="12" y="48" />
<use href="#p" x="0" y="60" />
<use href="#p" x="12" y="60" />
<use href="#p" x="0" y="72" />
<use href="#p" x="12" y="72" />
<use href="#p" x="0" y="84" />
<use href="#p" x="12" y="84" />
<use href="#p" x="0" y="96" />
<use href="#p" x="12" y="96" />
<use href="#p" x="0" y="108" />
<use href="#p" x="12" y="108" />
<use href="#p" x="0" y="120" />
<use href="#p" x="12" y="120" />
<use href="#p" x="0" y="132" />
<use href="#p" x="12" y="132" />
<!-- 右竖线占2列 -->
<use href="#p" x="84" y="0" />
<use href="#p" x="96" y="0" />
<use href="#p" x="84" y="12" />
<use href="#p" x="96" y="12" />
<use href="#p" x="84" y="24" />
<use href="#p" x="96" y="24" />
<use href="#p" x="84" y="36" />
<use href="#p" x="96" y="36" />
<use href="#p" x="84" y="48" />
<use href="#p" x="96" y="48" />
<use href="#p" x="84" y="60" />
<use href="#p" x="96" y="60" />
<use href="#p" x="84" y="72" />
<use href="#p" x="96" y="72" />
<use href="#p" x="84" y="84" />
<use href="#p" x="96" y="84" />
<use href="#p" x="84" y="96" />
<use href="#p" x="96" y="96" />
<use href="#p" x="84" y="108" />
<use href="#p" x="96" y="108" />
<use href="#p" x="84" y="120" />
<use href="#p" x="96" y="120" />
<use href="#p" x="84" y="132" />
<use href="#p" x="96" y="132" />
<!-- 底部横线 -->
<use href="#p" x="0" y="132" />
<use href="#p" x="12" y="132" />
<use href="#p" x="24" y="132" />
<use href="#p" x="36" y="132" />
<use href="#p" x="48" y="132" />
<use href="#p" x="60" y="132" />
<use href="#p" x="72" y="132" />
<use href="#p" x="84" y="132" />
<use href="#p" x="96" y="132" />
<!-- ===== U 内部:竖向排列的 frpc蓝色5×5 像素) ===== -->
<g transform="translate(32, 18)">
<!-- f -->
<use href="#q" x="0" y="0" />
<use href="#q" x="0" y="6" />
<use href="#q" x="0" y="12" />
<use href="#q" x="0" y="18" />
<use href="#q" x="6" y="0" />
<use href="#q" x="12" y="0" />
<use href="#q" x="18" y="0" />
<use href="#q" x="6" y="12" />
<use href="#q" x="12" y="12" />
<!-- r -->
<use href="#q" x="0" y="28" />
<use href="#q" x="0" y="34" />
<use href="#q" x="0" y="40" />
<use href="#q" x="0" y="46" />
<use href="#q" x="6" y="28" />
<use href="#q" x="12" y="28" />
<use href="#q" x="18" y="28" />
<use href="#q" x="6" y="40" />
<use href="#q" x="12" y="40" />
<!-- p -->
<use href="#q" x="0" y="56" />
<use href="#q" x="0" y="62" />
<use href="#q" x="0" y="68" />
<use href="#q" x="0" y="74" />
<use href="#q" x="0" y="80" />
<use href="#q" x="6" y="56" />
<use href="#q" x="12" y="56" />
<use href="#q" x="18" y="56" />
<use href="#q" x="18" y="62" />
<use href="#q" x="18" y="68" />
<use href="#q" x="6" y="74" />
<use href="#q" x="12" y="74" />
<use href="#q" x="18" y="74" />
<!-- c -->
<use href="#q" x="6" y="90" />
<use href="#q" x="12" y="90" />
<use href="#q" x="18" y="90" />
<use href="#q" x="0" y="96" />
<use href="#q" x="0" y="102" />
<use href="#q" x="6" y="108" />
<use href="#q" x="12" y="108" />
<use href="#q" x="18" y="108" />
</g>
</g>
<!-- ===== I中间竖线 2 列宽,高度一致) ===== -->
<g transform="translate(248, 0)">
<!-- 顶部横线占6列 -->
<use href="#p" x="0" y="0" />
<use href="#p" x="12" y="0" />
<use href="#p" x="24" y="0" />
<use href="#p" x="36" y="0" />
<use href="#p" x="48" y="0" />
<use href="#p" x="60" y="0" />
<!-- 中间竖线2 列宽) -->
<use href="#p" x="24" y="12" />
<use href="#p" x="36" y="12" />
<use href="#p" x="24" y="24" />
<use href="#p" x="36" y="24" />
<use href="#p" x="24" y="36" />
<use href="#p" x="36" y="36" />
<use href="#p" x="24" y="48" />
<use href="#p" x="36" y="48" />
<use href="#p" x="24" y="60" />
<use href="#p" x="36" y="60" />
<use href="#p" x="24" y="72" />
<use href="#p" x="36" y="72" />
<use href="#p" x="24" y="84" />
<use href="#p" x="36" y="84" />
<use href="#p" x="24" y="96" />
<use href="#p" x="36" y="96" />
<use href="#p" x="24" y="108" />
<use href="#p" x="36" y="108" />
<use href="#p" x="24" y="120" />
<use href="#p" x="36" y="120" />
<!-- 底部横线占6列 -->
<use href="#p" x="0" y="132" />
<use href="#p" x="12" y="132" />
<use href="#p" x="24" y="132" />
<use href="#p" x="36" y="132" />
<use href="#p" x="48" y="132" />
<use href="#p" x="60" y="132" />
</g>
</g>
<!-- ========== 外围装饰元素(像素风,增加层次) ========== -->
<!-- 左上角小十字星 -->
<g transform="translate(40, 40)">
<use href="#s" x="0" y="0" />
<use href="#s" x="0" y="12" />
<use href="#s" x="6" y="6" />
<use href="#s" x="12" y="0" />
<use href="#s" x="12" y="12" />
</g>
<!-- 右上角小方块 -->
<use href="#d" x="440" y="40" />
<!-- 左下角小方块 -->
<use href="#d" x="40" y="440" />
<!-- 右下角小十字星 -->
<g transform="translate(436, 436)">
<use href="#s" x="0" y="0" />
<use href="#s" x="0" y="12" />
<use href="#s" x="6" y="6" />
<use href="#s" x="12" y="0" />
<use href="#s" x="12" y="12" />
</g>
<!-- 三个随机黑色装饰点更大一点12×12 -->
<rect x="90" y="380" width="12" height="12" fill="#000000" />
<rect x="420" y="140" width="12" height="12" fill="#000000" />
<rect x="240" y="60" width="12" height="12" fill="#000000" />
<!-- 额外小点缀:蓝色小点,呼应 frpc 颜色 -->
<rect x="180" y="400" width="6" height="6" fill="#2563eb" />
<rect x="370" y="80" width="6" height="6" fill="#2563eb" />
</svg>

After

Width:  |  Height:  |  Size: 10 KiB