docs: 更新 README 与 cluster-api — per-forward 模型/网络负载/审计CSV/三级角色/session登录

README:
- 特性: per-forward 独立进程、NIC 负载采样、冲突检查、Del 键删除
- 认证: session-cookie 登录 + 三级角色 (superadmin/admin/viewer)
- 审计: 4 个 CSV 导出端点说明
- 构建: dist 不再进 git (编译前需 cp -r)
- 数据目录: 配置/日志路径改为 per-forward 键式
- 架构: 更新模块说明 (worker_key/session/audit)
- API 表: 新增 login/logout + 审计导出 4 端点
- gofmt: store.go 注释对齐

docs/cluster-api.md:
- §9 审计 CSV 导出: 四个端点用法、示例行、公式注入防御说明
This commit is contained in:
JianFeeeee
2026-08-24 23:52:16 +08:00
parent f90627f542
commit 94b6396738
3 changed files with 107 additions and 24 deletions

View File

@ -3,6 +3,7 @@
所有接口前缀 `/api/manager`,使用 Basic Auth 认证(`-user` / `-password` 启动参数)。
权限分级:
- **read**viewer + read-scope API key + admin 均可访问
- **write**write-scope API key + admin启动参数的 Basic Auth 凭证解析为 admin
@ -17,6 +18,7 @@
**权限**read
**响应**
```json
{
"selfId": "node-a:7500",
@ -55,8 +57,9 @@
```
**字段说明**
| 字段 | 说明 |
|---|---|
| --- | --- |
| `selfId` | 本节点 ID`host:port` |
| `leaderId` | 当前 leader 节点 ID |
| `cycle` | 令牌环当前轮次(每完成一圈 +1 |
@ -85,11 +88,13 @@
**响应**:同 `GET /cluster/ring` 的快照
**错误**
| 状态码 | 说明 |
|---|---|
| 409 | 已是多人环成员,需先脱离集群 |
**示例**
```bash
curl -X POST -u admin:admin123 http://localhost:7501/api/manager/cluster/create
```
@ -107,27 +112,31 @@ curl -X POST -u admin:admin123 http://localhost:7501/api/manager/cluster/create
**前置条件**:本节点不能已是多人环成员(已是成员返回 409
**请求体**
```json
{
"addr": "192.168.1.10:7500",
"joinKey": "ed67ca6b181b3cf2..."
}
```
| 字段 | 说明 |
|---|---|
| --- | --- |
| `addr` | 对端节点的真实可路由地址IP:port 或域名:port |
| `joinKey` | 对端节点的 nodeKey从对端的集群页或 `GET /cluster/ring` 获取) |
**响应**:同 `GET /cluster/ring` 的快照
**错误**
| 状态码 | 说明 |
|---|---|
| --- | --- |
| 400 | `addr``joinKey` 为空 |
| 409 | 已是多人环成员 |
| 502 | 对端不可达或加入失败(密钥错误返回 403、网络超时等 |
**示例**
```bash
curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ring \
-H 'Content-Type: application/json' \
@ -147,6 +156,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
**权限**write
**请求体**`JoinInfo`
```json
{
"id": "node-b:7500",
@ -158,6 +168,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
```
**响应**
```json
{
"state": { /* 完整 State环状态 */ }
@ -165,8 +176,9 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
```
**错误**
| 状态码 | 说明 |
|---|---|
| --- | --- |
| 400 | JSON 解析失败 |
| 403 | `joinKey` 为空或不匹配本节点的 nodeKey |
@ -181,6 +193,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
**权限**write
**请求体**
```json
{
"local": { "name": "web", "port": 8080, "type": "tcp" },
@ -190,6 +203,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
```
**响应**
```json
{
"task": {
@ -203,6 +217,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
```
任务随令牌环行,负载最低的节点摘取后:
1. 持久化 local/remote/link 到本地 store
2. 拉起 frpc worker
3. 写入拓扑(`topology[].owner = 摘取节点`
@ -215,6 +230,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
### `POST /cluster/node-remove`
发布 `node.remove` 命令到令牌。令牌传递到被移除节点自身时,该节点执行自移除:
1. 将自己负责的转发任务重新追加为待办
2. 修改拓扑与转发链、移除自身
3. 令牌传递给自身原本的下一家
@ -223,11 +239,13 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
**权限**write
**请求体**
```json
{ "id": "node-c:7500" }
```
**响应**
```json
{
"task": {
@ -239,6 +257,7 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
```
被移除节点脱离集群后:
- `nodeKey` 清空(不再有效)
- `ClusterPeers` 清空(重启不自动重联)
- 本地画布仅保留 localOnly 转发
@ -256,7 +275,9 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
**两种模式**
#### 令牌传递(有 body
请求体为 `cluster.Token` JSON。本节点
1. `OnToken`:采纳令牌中的集群状态 → 运行命令 → 追加自身信息
2. leader → `Send`bump cycle + 转发);非 leader → `Forward`(转发)
3. 异步转发,立即返回 200 + 更新后的 token
@ -264,12 +285,14 @@ curl -X POST -u admin:admin123 http://localhost:7502/api/manager/cluster/join-ri
**响应**:更新后的 `Token` JSON
#### 心跳探测(空 body
leader 的上邻居每秒探测 leader 存活。空 body → 心跳。
- leader 在线且为多人环 → `200 OK`
- leader 已重启/脱离standalone ≤1 节点)→ `409 Conflict`(上邻居感知 leader 已退出 → 晋升自身为新 leader + StartRing
**错误**
| 状态码 | 说明 |
|---|---|
| 409 | standalone 节点拒绝心跳leader 已退出,触发 failover |
@ -285,6 +308,7 @@ leader 的上邻居每秒探测 leader 存活。空 body → 心跳。
**权限**read
**响应**
```json
{
"nodes": [
@ -307,6 +331,7 @@ leader 的上邻居每秒探测 leader 存活。空 body → 心跳。
**权限**writeGET 为 readPOST 需 write
**请求体**
```json
{ "keep": 3 }
```
@ -322,6 +347,7 @@ leader 的上邻居每秒探测 leader 存活。空 body → 心跳。
**权限**read
**响应**
```json
{
"node": "node-a:7500",
@ -355,9 +381,53 @@ leader 的上邻居每秒探测 leader 存活。空 body → 心跳。
## 容错行为
| 场景 | 检测机制 | 恢复动作 |
|---|---|---|
| --- | --- | --- |
| 非leader节点宕机 | `forwardToNext` 发送失败 → MarkOffline → OfflineReassign | 环跳过死亡节点,任务重挂为待办 |
| leader 宕机(发送时) | `forwardToNext` 发送给 leader 失败 → MarkOffline | 上邻居晋升为新 leader + StartRing |
| leader 宕机(收到令牌后) | `WatchLeader` 心跳失败/409 → MarkOffline | 上邻居晋升 + StartRing兜底 |
| leader 快速重启 | 心跳得到 409standalone | 上邻居晋升 + StartRing |
| 令牌丢失 | `WatchTokenLoss` 超时LossTimeout | leader 重发令牌 |
---
## 9. 审计 CSV 导出read 级)
四个只读导出端点,供审计/合规场景下载证据表格。全部返回 `text/csv; charset=utf-8` + `Content-Disposition: attachment`RFC4180 转义ISO8601 UTC 时间戳。`= + @ - tab` 开头的单元格自动加 `'` 前缀防御表格软件公式注入。
### `GET /audit/users.csv`
账号清单(密码哈希永不导出)。
```csv
id,username,role,enabled,system,created_at,last_login_at
1,jianf,admin,true,true,2026-08-23T03:18:03Z,2026-08-24T14:00:00Z
```
### `GET /audit/apikeys.csv`
API Key 清单(仅展示前缀 `w4f_xxxxxxxx…`,明文不可逆)。
```csv
id,key_prefix,owner,label,scope,created_at,last_used_at,expires_at
1,w4f_5hIp…,jianf,ci-key,read,2026-08-24T14:47:54Z,,
```
### `GET /audit/cluster-log.csv`
本节点的令牌环操作日志时间线(转发创建/撤销、节点加入离开、leader 变更、任务认领)。`detail` 列为人读摘要,`data_json` 为无损原始载荷。
```csv
seq,time_utc,node,kind,detail,data_json
1,2026-08-24T14:51:21Z,192.168.2.60:7500,node.join,"192.168.2.60:7500 @ 192.168.2.60:7500","{""addr"":""192.168.2.60:7500"",""node"":""192.168.2.60:7500""}"
```
### `GET /audit/worker-logs.csv`
全环 fan-out 收集每节点 frpc worker 日志,**逐行展开**(一行 = 一条日志行),便于在表格软件中按 worker_state 过滤或按 remote 分组统计。不可达节点生成 `ERROR` 行。
```csv
node,worker_key,remote,worker_state,log_line
192.168.2.30:7500,homeagent_device~aliyun-frps~9891,aliyun-frps,running,"564 [I] [client/control.go:168] [...] start proxy success"
```
> 注意:操作日志与 worker 日志均为内存/文件态,不持久化历史——审计建议定期拉取归档。