mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 07:36:38 +08:00
215 lines
7.9 KiB
Plaintext
215 lines
7.9 KiB
Plaintext
Based on my thorough exploration, here's a comprehensive analysis of the node API and data access layer:
|
||
|
||
---
|
||
|
||
## 节点API和数据访问层完整分析
|
||
|
||
### 1. 节点CRUD相关Repository方法
|
||
|
||
#### 核心方法 (`go-backend/internal/store/repo/`)
|
||
|
||
| 方法 | 文件位置 | 功能 |
|
||
|------|----------|------|
|
||
| `CreateNode()` | `repository_mutations.go:199` | 创建节点,处理 serverIP/serverIPV4/serverIPV6 |
|
||
| `UpdateNode()` | `repository_mutations.go:241` | 更新节点,包含所有IP字段 |
|
||
| `DeleteNodeCascade()` | `repository_mutations.go:299` | 级联删除节点(关联 ForwardPort、ChainTunnel、FederationTunnelBinding) |
|
||
| `GetNodeByID()` | `repository.go:555` | 通过ID获取节点 |
|
||
| `GetNodeBySecret()` | `repository.go:540` | 通过Secret获取节点 |
|
||
| `GetNodeStatusFields()` | `repository_mutations.go:229` | 获取节点状态字段 |
|
||
| `GetNodeSecret()` | `repository_mutations.go:263` | 获取节点密钥 |
|
||
| `GetNodeRemoteFields()` | `repository_mutations.go:317` | 获取远程节点字段 |
|
||
| `GetNodePortRange()` | `repository_mutations.go:336` | 获取节点端口范围 |
|
||
| `UpdateNodeOrder()` | `repository_mutations.go:287` | 更新节点排序 |
|
||
| `UpdateNodeOnline()` | `repository.go:570` | 更新节点在线状态 |
|
||
| `UpdateNodeStatus()` | `repository.go:580` | 更新节点状态 |
|
||
| `ListNodes()` | `repository.go:625` | 列出所有节点(返回JSON map格式) |
|
||
| `GetNodeRecord()` | `repository_control.go:131` | 获取 NodeRecord 视图对象 |
|
||
| `CreateRemoteNode()` | `repository_federation.go:266` | 创建远程节点(联邦场景) |
|
||
| `exportNodes()` | `repository.go:1660` | 导出节点数据(用于备份) |
|
||
| `importNodes()` | `repository.go:2016` | 导入节点数据(用于恢复) |
|
||
|
||
---
|
||
|
||
### 2. 节点数据结构定义
|
||
|
||
#### GORM模型 (`model.Node` - 数据库存储)
|
||
**文件:** `/go-backend/internal/store/model/model.go:59-82`
|
||
```go
|
||
type Node struct {
|
||
ID int64 `gorm:"primaryKey;autoIncrement"`
|
||
Name string `gorm:"type:varchar(100);not null"`
|
||
Secret string `gorm:"type:varchar(100);not null"`
|
||
ServerIP string `gorm:"column:server_ip;type:varchar(100);not null"`
|
||
ServerIPV4 sql.NullString `gorm:"column:server_ip_v4;type:varchar(100)"` // 注意:V大写
|
||
ServerIPV6 sql.NullString `gorm:"column:server_ip_v6;type:varchar(100)"`
|
||
// ... 其他字段
|
||
}
|
||
```
|
||
|
||
#### 控制平面视图 (`model.NodeRecord`)
|
||
**文件:** `/go-backend/internal/store/model/model.go:515-531`
|
||
```go
|
||
type NodeRecord struct {
|
||
ID int64
|
||
Name string
|
||
ServerIP string
|
||
ServerIPv4 string // 注意:v小写
|
||
ServerIPv6 string
|
||
Status int
|
||
PortRange string
|
||
// ... 其他字段
|
||
}
|
||
```
|
||
|
||
#### JSON导出格式 (`model.NodeBackup`)
|
||
**文件:** `/go-backend/internal/store/model/model.go:333-356`
|
||
```go
|
||
type NodeBackup struct {
|
||
ID int64 `json:"id"`
|
||
Name string `json:"name"`
|
||
ServerIP string `json:"serverIp"`
|
||
ServerIPv4 string `json:"serverIpV4,omitempty"`
|
||
ServerIPv6 string `json:"serverIpV6,omitempty"`
|
||
// ... 其他字段
|
||
}
|
||
```
|
||
|
||
#### Handler层别名
|
||
**文件:** `/go-backend/internal/http/handler/control_plane.go:25`
|
||
```go
|
||
type nodeRecord = model.NodeRecord // 类型别名
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 节点数据返回给前端的格式
|
||
|
||
#### ListNodes API (`repository.go:625-652`)
|
||
```go
|
||
// 返回 map[string]interface{} 格式
|
||
items = append(items, map[string]interface{}{
|
||
"id": n.ID, "inx": n.Inx, "name": n.Name,
|
||
"ip": n.ServerIP, "serverIp": n.ServerIP, // ip和serverIp都返回
|
||
"serverIpV4": nullableString(n.ServerIPV4),
|
||
"serverIpV6": nullableString(n.ServerIPV6),
|
||
"port": n.Port,
|
||
"tcpListenAddr": n.TCPListenAddr,
|
||
"udpListenAddr": n.UDPListenAddr,
|
||
"version": nullableString(n.Version),
|
||
"http": n.HTTP, "tls": n.TLS, "socks": n.Socks,
|
||
"status": n.Status, "isRemote": n.IsRemote,
|
||
// ...
|
||
})
|
||
```
|
||
|
||
#### API端点
|
||
| 端点 | Handler | 功能 |
|
||
|------|---------|------|
|
||
| `POST /api/v1/node/list` | `h.nodeList` | 列出节点 |
|
||
| `POST /api/v1/node/create` | `h.nodeCreate` | 创建节点 |
|
||
| `POST /api/v1/node/update` | `h.nodeUpdate` | 更新节点 |
|
||
| `POST /api/v1/node/delete` | `h.nodeDelete` | 删除节点 |
|
||
|
||
---
|
||
|
||
### 4. ServerIP/ServerIPV4/ServerIPV6 字段处理逻辑
|
||
|
||
#### 字段命名约定
|
||
| 层级 | 字段名 | 数据库列名 |
|
||
|------|--------|-----------|
|
||
| `model.Node` | `ServerIPV4`, `ServerIPV6` | `server_ip_v4`, `server_ip_v6` |
|
||
| `model.NodeRecord` | `ServerIPv4`, `ServerIPv6` | - (内存结构) |
|
||
| `model.NodeBackup` | `ServerIPv4`, `ServerIPv6` | - (JSON) |
|
||
| 前端JSON | `serverIpV4`, `serverIpV6` | - |
|
||
|
||
#### 模型转换函数 (`repository_control.go:161-201`)
|
||
```go
|
||
func nodeRecordFromModel(n *model.Node) *model.NodeRecord {
|
||
rec := &model.NodeRecord{
|
||
ID: n.ID,
|
||
Name: n.Name,
|
||
ServerIP: n.ServerIP,
|
||
// ...
|
||
}
|
||
if n.ServerIPV4.Valid {
|
||
rec.ServerIPv4 = strings.TrimSpace(n.ServerIPV4.String)
|
||
}
|
||
if n.ServerIPV6.Valid {
|
||
rec.ServerIPv6 = strings.TrimSpace(n.ServerIPV6.String)
|
||
}
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### Dual-Stack辅助函数 (`mutations.go:2715-2772`)
|
||
```go
|
||
// 判断节点是否支持IPv4
|
||
func nodeSupportsV4(node *nodeRecord) bool {
|
||
if strings.TrimSpace(node.ServerIPv4) != "" { return true }
|
||
if strings.TrimSpace(node.ServerIPv6) != "" { return false }
|
||
// 回退到 ServerIP 判断
|
||
legacy := strings.Trim(strings.TrimSpace(node.ServerIP), "[]")
|
||
if ip := net.ParseIP(legacy); ip != nil {
|
||
return ip.To4() != nil
|
||
}
|
||
return true
|
||
}
|
||
|
||
// 选择IPv4地址(优先显式字段,回退ServerIP)
|
||
func pickNodeAddressV4(node *nodeRecord) string {
|
||
if v := strings.TrimSpace(node.ServerIPv4); v != "" { return v }
|
||
return strings.TrimSpace(node.ServerIP)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5. 扩展节点IP字段需要改动的位置
|
||
|
||
如果要添加新的IP字段(如 `ServerIPV4Alt`),需要修改以下位置:
|
||
|
||
#### A. 模型层 (`go-backend/internal/store/model/model.go`)
|
||
1. **`model.Node`** - 添加 GORM 字段定义(约第64行)
|
||
2. **`model.NodeRecord`** - 添加视图字段(约第520行)
|
||
3. **`model.NodeBackup`** - 添加JSON字段(约第338行)
|
||
|
||
#### B. Repository层 (`go-backend/internal/store/repo/`)
|
||
1. **`repository_mutations.go`**
|
||
- `CreateNode()` 参数和赋值(约第199-227行)
|
||
- `UpdateNode()` 参数和Updates map(约第241-261行)
|
||
|
||
2. **`repository_control.go`**
|
||
- `nodeRecordFromModel()` 转换逻辑(约第161-201行)
|
||
|
||
3. **`repository_federation.go`**
|
||
- `CreateRemoteNode()` 参数和赋值(约第266-294行)
|
||
|
||
4. **`repository.go`**
|
||
- `ListNodes()` JSON返回格式(约第625-652行)
|
||
- `exportNodes()` 导出逻辑(约第1660-1700行)
|
||
- `importNodes()` 导入逻辑(约第2016-2057行)
|
||
- `prepareSQLiteLegacyColumns()` 迁移逻辑(约第263行)
|
||
|
||
#### C. Handler层 (`go-backend/internal/http/handler/`)
|
||
1. **`mutations.go`**
|
||
- `nodeCreate()` 请求参数解析(约第234-279行)
|
||
- `nodeUpdate()` 请求参数解析(约第281-336行)
|
||
- `nodeSupportsV4/V6()` 函数逻辑(约第2715-2772行)
|
||
- `pickNodeAddressV4/V6()` 函数逻辑
|
||
|
||
#### D. 测试层 (`go-backend/tests/contract/`)
|
||
所有 contract test 中的 `INSERT INTO node` 语句需要更新字段列表
|
||
|
||
---
|
||
|
||
### 关键文件汇总
|
||
|
||
| 文件 | 用途 |
|
||
|------|------|
|
||
| `/go-backend/internal/store/model/model.go` | 数据模型定义 |
|
||
| `/go-backend/internal/store/repo/repository_mutations.go` | CRUD操作实现 |
|
||
| `/go-backend/internal/store/repo/repository_control.go` | 控制平面查询+模型转换 |
|
||
| `/go-backend/internal/store/repo/repository_federation.go` | 联邦节点操作 |
|
||
| `/go-backend/internal/store/repo/repository.go` | 核心查询+导出导入 |
|
||
| `/go-backend/internal/http/handler/mutations.go` | HTTP API处理器 |
|
||
| `/go-backend/internal/http/handler/control_plane.go` | 控制平面API | |