Skip to content

Commit 1d1cc06

Browse files
committed
fix: tunnel sorts supports by services sorts
1 parent b738319 commit 1d1cc06

File tree

8 files changed

+258
-29
lines changed

8 files changed

+258
-29
lines changed

internal/api/endpoint.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,11 @@ func buildTunnelFromInstance(endpointID int64, inst nodepass.InstanceResult) *mo
10801080
tunnel.Tags = inst.Meta.Tags
10811081
tunnel.Peer = inst.Meta.Peer
10821082

1083+
// 同步设置 service_sid 字段
1084+
if tunnel.Peer != nil && tunnel.Peer.SID != nil {
1085+
tunnel.ServiceSID = tunnel.Peer.SID
1086+
}
1087+
10831088
if tunnel.Mode == nil {
10841089
tunnel.Mode = (*models.TunnelMode)(inst.Mode)
10851090
}

internal/models/models.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ type Tunnel struct {
103103

104104
Sorts int64 `json:"sorts" gorm:"type:int;column:sorts;default:0"`
105105

106+
// Service关联ID - 用于快速查询和排序,避免解析peer JSON字段
107+
ServiceSID *string `json:"serviceSid,omitempty" gorm:"type:text;index;column:service_sid"`
108+
106109
CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime;index;column:created_at"`
107110
UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime;column:updated_at"`
108111
LastEventTime NullTime `json:"lastEventTime,omitempty" gorm:"column:last_event_time;type:datetime"`

internal/nodepass/parse.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,15 @@ func TunnelToMap(tunnel *models.Tunnel) map[string]interface{} {
348348
if peerJSON, err := json.Marshal(tunnel.Peer); err == nil {
349349
updates["peer"] = string(peerJSON)
350350
}
351+
// 同步更新 service_sid 字段,用于快速查询和排序
352+
if tunnel.Peer.SID != nil && *tunnel.Peer.SID != "" {
353+
updates["service_sid"] = *tunnel.Peer.SID
354+
} else {
355+
updates["service_sid"] = nil
356+
}
357+
} else {
358+
// peer 为 nil 时,清空 service_sid
359+
updates["service_sid"] = nil
351360
}
352361
if tunnel.ConfigLine != nil {
353362
updates["config_line"] = tunnel.ConfigLine

internal/services/service.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,12 @@ func (s *ServiceImpl) AssembleService(req *AssembleServiceRequest) error {
109109
return fmt.Errorf("更新客户端实例peer信息失败: %w", err)
110110
}
111111

112-
// 更新数据库中客户端隧道的 peer 字段
112+
// 更新数据库中客户端隧道的 peer 字段和 service_sid
113113
clientTunnel.Peer = peer
114+
clientTunnel.ServiceSID = &req.Sid
114115
if err := s.db.Model(&models.Tunnel{}).
115116
Where("instance_id = ?", req.ClientInstanceId).
116-
Select("peer").
117+
Select("peer", "service_sid").
117118
Updates(&clientTunnel).Error; err != nil {
118119
return fmt.Errorf("更新客户端隧道peer字段失败: %w", err)
119120
}
@@ -124,11 +125,12 @@ func (s *ServiceImpl) AssembleService(req *AssembleServiceRequest) error {
124125
return fmt.Errorf("更新服务端实例peer信息失败: %w", err)
125126
}
126127

127-
// 更新数据库中服务端隧道的 peer 字段
128+
// 更新数据库中服务端隧道的 peer 字段和 service_sid
128129
serverTunnel.Peer = peer
130+
serverTunnel.ServiceSID = &req.Sid
129131
if err := s.db.Model(&models.Tunnel{}).
130132
Where("instance_id = ?", *req.ServerInstanceId).
131-
Select("peer").
133+
Select("peer", "service_sid").
132134
Updates(serverTunnel).Error; err != nil {
133135
return fmt.Errorf("更新服务端隧道peer字段失败: %w", err)
134136
}
@@ -303,12 +305,19 @@ func (s *ServiceImpl) RenameService(sid, newName string) error {
303305
return fmt.Errorf("更新客户端实例peer信息失败: %w", err)
304306
}
305307

306-
// 更新数据库中客户端隧道的 peer 字段
308+
// 更新数据库中客户端隧道的 peer 字段和 service_sid
307309
// 使用 Updates 而不是 Update,以正确触发 JSON 序列化
308310
if peerJSON, err := json.Marshal(peer); err == nil {
311+
updates := map[string]interface{}{
312+
"peer": string(peerJSON),
313+
}
314+
// 同步更新 service_sid
315+
if peer.SID != nil {
316+
updates["service_sid"] = *peer.SID
317+
}
309318
if err := s.db.Model(&models.Tunnel{}).
310319
Where("instance_id = ?", *service.ClientInstanceId).
311-
Updates(map[string]interface{}{"peer": string(peerJSON)}).Error; err != nil {
320+
Updates(updates).Error; err != nil {
312321
return fmt.Errorf("更新客户端隧道peer字段失败: %w", err)
313322
}
314323
}
@@ -321,11 +330,18 @@ func (s *ServiceImpl) RenameService(sid, newName string) error {
321330
return fmt.Errorf("更新服务端实例peer信息失败: %w", err)
322331
}
323332

324-
// 更新数据库中服务端隧道的 peer 字段
333+
// 更新数据库中服务端隧道的 peer 字段和 service_sid
325334
if peerJSON, err := json.Marshal(peer); err == nil {
335+
updates := map[string]interface{}{
336+
"peer": string(peerJSON),
337+
}
338+
// 同步更新 service_sid
339+
if peer.SID != nil {
340+
updates["service_sid"] = *peer.SID
341+
}
326342
if err := s.db.Model(&models.Tunnel{}).
327343
Where("instance_id = ?", *service.ServerInstanceId).
328-
Updates(map[string]interface{}{"peer": string(peerJSON)}).Error; err != nil {
344+
Updates(updates).Error; err != nil {
329345
return fmt.Errorf("更新服务端隧道peer字段失败: %w", err)
330346
}
331347
}
@@ -362,12 +378,15 @@ func (s *ServiceImpl) DissolveService(sid string) error {
362378
return fmt.Errorf("清空客户端实例peer信息失败: %w", err)
363379
}
364380

365-
// 清空数据库中客户端隧道的 peer 字段
381+
// 清空数据库中客户端隧道的 peer 字段和 service_sid
366382
// 使用 Updates 而不是 Update,以正确触发 JSON 序列化
367383
if peerJSON, err := json.Marshal(emptyPeer); err == nil {
368384
if err := s.db.Model(&models.Tunnel{}).
369385
Where("instance_id = ?", *service.ClientInstanceId).
370-
Updates(map[string]interface{}{"peer": string(peerJSON)}).Error; err != nil {
386+
Updates(map[string]interface{}{
387+
"peer": string(peerJSON),
388+
"service_sid": nil, // 清空 service_sid
389+
}).Error; err != nil {
371390
return fmt.Errorf("清空客户端隧道peer字段失败: %w", err)
372391
}
373392
}
@@ -379,12 +398,15 @@ func (s *ServiceImpl) DissolveService(sid string) error {
379398
return fmt.Errorf("清空服务端实例peer信息失败: %w", err)
380399
}
381400

382-
// 清空数据库中服务端隧道的 peer 字段
401+
// 清空数据库中服务端隧道的 peer 字段和 service_sid
383402
// 使用 Updates 而不是 Update,以正确触发 JSON 序列化
384403
if peerJSON, err := json.Marshal(emptyPeer); err == nil {
385404
if err := s.db.Model(&models.Tunnel{}).
386405
Where("instance_id = ?", *service.ServerInstanceId).
387-
Updates(map[string]interface{}{"peer": string(peerJSON)}).Error; err != nil {
406+
Updates(map[string]interface{}{
407+
"peer": string(peerJSON),
408+
"service_sid": nil, // 清空 service_sid
409+
}).Error; err != nil {
388410
return fmt.Errorf("清空服务端隧道peer字段失败: %w", err)
389411
}
390412
}

internal/sse/service.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ func buildTunnel(payload SSEResp) *models.Tunnel {
287287
tunnel.Tags = payload.Instance.Meta.Tags
288288
tunnel.Peer = payload.Instance.Meta.Peer
289289

290+
// 同步设置 service_sid 字段
291+
if tunnel.Peer != nil && tunnel.Peer.SID != nil && *tunnel.Peer.SID != "" {
292+
tunnel.ServiceSID = tunnel.Peer.SID
293+
}
294+
290295
if tunnel.Mode == nil {
291296
tunnel.Mode = (*models.TunnelMode)(payload.Instance.Mode)
292297
}
@@ -317,6 +322,7 @@ func (s *Service) handleCreateEvent(payload SSEResp) {
317322
"tunnel_address", "tunnel_port", "target_address", "target_port",
318323
"tls_mode", "log_level", "command_line", "password", "cert_path", "key_path",
319324
"min", "max", "mode", "read", "rate", "restart", "last_event_time", "updated_at",
325+
"peer", "service_sid", // 添加 peer 和 service_sid 字段同步
320326
}),
321327
}).Create(&tunnel).Error; err != nil {
322328
log.Errorf("[Master-%d]创建/更新隧道记录 %s 失败: %v", payload.EndpointID, payload.Instance.ID, err)

internal/tunnel/service.go

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,28 +2472,37 @@ func (s *Service) GetTunnelsWithPagination(params TunnelQueryParams) (*TunnelLis
24722472

24732473
// 构建排序
24742474
var orderClause string
2475+
needServicesJoin := params.SortBy == "services"
2476+
2477+
if needServicesJoin {
2478+
// 修改baseQuery以包含LEFT JOIN services表
2479+
baseQuery = `FROM tunnels t
2480+
LEFT JOIN services s ON t.service_sid = s.sid`
2481+
}
2482+
24752483
if params.SortBy != "" {
24762484
switch params.SortBy {
2477-
case "sorts":
2478-
orderClause = fmt.Sprintf(" ORDER BY t.sorts %s, t.id DESC", params.SortOrder)
24792485
case "id":
24802486
orderClause = fmt.Sprintf(" ORDER BY t.id %s", params.SortOrder)
2487+
case "sorts":
2488+
orderClause = fmt.Sprintf(" ORDER BY t.sorts %s, t.id DESC", params.SortOrder)
24812489
case "name":
2482-
orderClause = fmt.Sprintf(" ORDER BY t.name %s, t.sorts DESC, t.id DESC", params.SortOrder)
2483-
case "created_at":
2484-
orderClause = fmt.Sprintf(" ORDER BY t.created_at %s, t.sorts DESC, t.id DESC", params.SortOrder)
2490+
orderClause = fmt.Sprintf(" ORDER BY t.name %s, t.id DESC", params.SortOrder)
24852491
case "status":
2486-
orderClause = fmt.Sprintf(" ORDER BY t.status %s, t.sorts DESC, t.id DESC", params.SortOrder)
2487-
case "tunnelAddress":
2488-
orderClause = fmt.Sprintf(" ORDER BY t.tunnel_address %s, t.tunnel_port %s, t.sorts DESC, t.id DESC", params.SortOrder, params.SortOrder)
2489-
case "targetAddress":
2490-
orderClause = fmt.Sprintf(" ORDER BY t.target_address %s, t.target_port %s, t.sorts DESC, t.id DESC", params.SortOrder, params.SortOrder)
2492+
orderClause = fmt.Sprintf(" ORDER BY t.status %s, t.id DESC", params.SortOrder)
24912493
case "type":
2492-
orderClause = fmt.Sprintf(" ORDER BY t.type %s, t.sorts DESC, t.id DESC", params.SortOrder)
2493-
case "endpoint":
2494-
orderClause = fmt.Sprintf(" ORDER BY e.name %s, t.sorts DESC, t.id DESC", params.SortOrder)
2495-
case "updated_at":
2496-
orderClause = fmt.Sprintf(" ORDER BY t.updated_at %s, t.sorts DESC, t.id DESC", params.SortOrder)
2494+
orderClause = fmt.Sprintf(" ORDER BY t.type %s, t.id DESC", params.SortOrder)
2495+
case "endpoint_id":
2496+
orderClause = fmt.Sprintf(" ORDER BY t.endpoint_id %s, t.id DESC", params.SortOrder)
2497+
case "services":
2498+
// 按关联的服务的sorts字段排序
2499+
// NULL值(没有关联服务的tunnel)排在最后
2500+
// SQLite不支持NULLS LAST,使用CASE实现
2501+
if params.SortOrder == "ASC" {
2502+
orderClause = " ORDER BY CASE WHEN s.sorts IS NULL THEN 1 ELSE 0 END, s.sorts ASC"
2503+
} else {
2504+
orderClause = " ORDER BY CASE WHEN s.sorts IS NULL THEN 1 ELSE 0 END, s.sorts DESC"
2505+
}
24972506
default:
24982507
orderClause = " ORDER BY t.sorts DESC, t.id DESC"
24992508
}
@@ -2516,6 +2525,8 @@ func (s *Service) GetTunnelsWithPagination(params TunnelQueryParams) (*TunnelLis
25162525
t.tcp_tx + t.udp_tx as total_tx
25172526
`
25182527

2528+
// todo
2529+
25192530
// 执行主查询
25202531
query := selectFields + baseQuery + whereClause + orderClause + limitClause
25212532
rows, err := sqlDB.Query(query, args...)
@@ -2855,6 +2866,44 @@ func (s *Service) getTunnelsByIDs(tunnelIDs []int) ([]models.Tunnel, error) {
28552866
return tunnels, nil
28562867
}
28572868

2869+
// MigrateServiceSID 数据迁移:从 peer JSON 字段提取 sid 并填充到 service_sid 字段
2870+
// 这是一个一次性迁移函数,用于填充新添加的 service_sid 字段
2871+
func (s *Service) MigrateServiceSID() (int64, error) {
2872+
log.Infof("[Migration] 开始迁移 service_sid 字段...")
2873+
2874+
var tunnels []models.Tunnel
2875+
// 只查询有 peer 数据的隧道
2876+
err := s.db.Where("peer IS NOT NULL AND peer != ''").Find(&tunnels).Error
2877+
if err != nil {
2878+
return 0, fmt.Errorf("查询隧道失败: %v", err)
2879+
}
2880+
2881+
log.Infof("[Migration] 找到 %d 条需要迁移的隧道记录", len(tunnels))
2882+
2883+
var updatedCount int64
2884+
for _, tunnel := range tunnels {
2885+
// 如果 peer 存在且有 SID,则更新 service_sid
2886+
if tunnel.Peer != nil && tunnel.Peer.SID != nil && *tunnel.Peer.SID != "" {
2887+
result := s.db.Model(&models.Tunnel{}).
2888+
Where("id = ?", tunnel.ID).
2889+
Update("service_sid", *tunnel.Peer.SID)
2890+
2891+
if result.Error != nil {
2892+
log.Warnf("[Migration] 更新隧道 %d 的 service_sid 失败: %v", tunnel.ID, result.Error)
2893+
continue
2894+
}
2895+
2896+
if result.RowsAffected > 0 {
2897+
updatedCount++
2898+
log.Debugf("[Migration] 隧道 %d: service_sid = %s", tunnel.ID, *tunnel.Peer.SID)
2899+
}
2900+
}
2901+
}
2902+
2903+
log.Infof("[Migration] service_sid 迁移完成,成功更新 %d 条记录", updatedCount)
2904+
return updatedCount, nil
2905+
}
2906+
28582907
// QuickCreateTunnelDirectURL 根据完整 URL 快速创建隧道实例,直接传递URL给NodePass API
28592908
// 这个方法避免了URL解析->重新组装的过程,提高性能并减少错误风险
28602909
func (s *Service) QuickCreateTunnelDirectURL(endpointID int64, rawURL string, name string, timeout time.Duration) error {

0 commit comments

Comments
 (0)