Skip to content

Commit 47c1545

Browse files
committed
build: v3.2.0-beta2
1 parent 2c121c2 commit 47c1545

File tree

4 files changed

+232
-2
lines changed

4 files changed

+232
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<img src="docs/nodepassdash-logo.svg" alt="NodePassDash" height="80">
33
</div>
44

5-
![Version](https://img.shields.io/badge/version-3.2.0--beta1-blue.svg)
5+
![Version](https://img.shields.io/badge/version-3.2.0--beta2-blue.svg)
66
![GitHub license](https://img.shields.io/github/license/NodePassProject/NodePassDash)
77

88
NodePassDash是一个现代化的 NodePass 管理界面,基于 Go 后端 + React + Vite、HeroUI 和 TypeScript 构建。提供实时隧道监控、流量统计和端点管理功能。

internal/services/models.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package services
2+
3+
import "time"
4+
5+
// Service 服务响应模型
6+
type Service struct {
7+
Sid string `json:"sid"`
8+
Type string `json:"type"`
9+
Alias *string `json:"alias,omitempty"`
10+
ServerInstanceId *string `json:"serverInstanceId,omitempty"`
11+
ClientInstanceId *string `json:"clientInstanceId,omitempty"`
12+
ServerEndpointId *int64 `json:"serverEndpointId,omitempty"`
13+
ClientEndpointId *int64 `json:"clientEndpointId,omitempty"`
14+
TunnelPort *int64 `json:"tunnelPort,omitempty"`
15+
TunnelEndpointName *string `json:"tunnelEndpointName,omitempty"`
16+
EntrancePort *int64 `json:"entrancePort,omitempty"`
17+
EntranceHost *string `json:"entranceHost,omitempty"`
18+
ExitPort *int64 `json:"exitPort,omitempty"`
19+
ExitHost *string `json:"exitHost,omitempty"`
20+
CreatedAt time.Time `json:"createdAt,omitempty"`
21+
UpdatedAt time.Time `json:"updatedAt,omitempty"`
22+
}
23+
24+
// ServiceResponse API响应
25+
type ServiceResponse struct {
26+
Success bool `json:"success"`
27+
Message string `json:"message,omitempty"`
28+
Error string `json:"error,omitempty"`
29+
Service *Service `json:"service,omitempty"`
30+
Services []*Service `json:"services,omitempty"`
31+
}
32+
33+
// AvailableInstance 可用实例(没有peer或peer.sid的实例)
34+
type AvailableInstance struct {
35+
InstanceId string `json:"instanceId"`
36+
EndpointId int64 `json:"endpointId"`
37+
EndpointName string `json:"endpointName"`
38+
TunnelType string `json:"tunnelType"` // "server" | "client"
39+
Name string `json:"name"`
40+
TunnelAddress string `json:"tunnelAddress"`
41+
TunnelPort string `json:"tunnelPort"`
42+
TargetAddress string `json:"targetAddress"`
43+
TargetPort string `json:"targetPort"`
44+
}
45+
46+
// AvailableInstancesResponse 可用实例响应
47+
type AvailableInstancesResponse struct {
48+
Success bool `json:"success"`
49+
Instances []*AvailableInstance `json:"instances,omitempty"`
50+
Error string `json:"error,omitempty"`
51+
}
52+
53+
// AssembleServiceRequest 组装服务请求
54+
type AssembleServiceRequest struct {
55+
Sid string `json:"sid" binding:"required"`
56+
Name string `json:"name" binding:"required"`
57+
Type string `json:"type" binding:"required"`
58+
ClientInstanceId string `json:"clientInstanceId" binding:"required"`
59+
ServerInstanceId *string `json:"serverInstanceId,omitempty"`
60+
}

internal/services/service.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package services
2+
3+
import (
4+
"NodePassDash/internal/models"
5+
"NodePassDash/internal/nodepass"
6+
"fmt"
7+
8+
"gorm.io/gorm"
9+
)
10+
11+
type ServiceImpl struct {
12+
db *gorm.DB
13+
}
14+
15+
func NewService(db *gorm.DB) *ServiceImpl {
16+
return &ServiceImpl{db: db}
17+
}
18+
19+
// GetServices 获取所有服务
20+
func (s *ServiceImpl) GetServices() ([]*Service, error) {
21+
var modelServices []models.Services
22+
err := s.db.Order("sid, type").Find(&modelServices).Error
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
var services []*Service
28+
for _, modelService := range modelServices {
29+
services = append(services, &Service{
30+
Sid: modelService.Sid,
31+
Type: modelService.Type,
32+
Alias: modelService.Alias,
33+
ServerInstanceId: modelService.ServerInstanceId,
34+
ClientInstanceId: modelService.ClientInstanceId,
35+
ServerEndpointId: modelService.ServerEndpointId,
36+
ClientEndpointId: modelService.ClientEndpointId,
37+
TunnelPort: modelService.TunnelPort,
38+
TunnelEndpointName: modelService.TunnelEndpointName,
39+
EntrancePort: modelService.EntrancePort,
40+
EntranceHost: modelService.EntranceHost,
41+
ExitPort: modelService.ExitPort,
42+
ExitHost: modelService.ExitHost,
43+
CreatedAt: modelService.CreatedAt,
44+
UpdatedAt: modelService.UpdatedAt,
45+
})
46+
}
47+
48+
return services, nil
49+
}
50+
51+
// GetServiceByID 根据 SID 和 Type 获取单个服务
52+
func (s *ServiceImpl) GetServiceByID(sid, serviceType string) (*Service, error) {
53+
var modelService models.Services
54+
err := s.db.Where("sid = ? AND type = ?", sid, serviceType).First(&modelService).Error
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
return &Service{
60+
Sid: modelService.Sid,
61+
Type: modelService.Type,
62+
Alias: modelService.Alias,
63+
ServerInstanceId: modelService.ServerInstanceId,
64+
ClientInstanceId: modelService.ClientInstanceId,
65+
ServerEndpointId: modelService.ServerEndpointId,
66+
ClientEndpointId: modelService.ClientEndpointId,
67+
TunnelPort: modelService.TunnelPort,
68+
TunnelEndpointName: modelService.TunnelEndpointName,
69+
EntrancePort: modelService.EntrancePort,
70+
EntranceHost: modelService.EntranceHost,
71+
ExitPort: modelService.ExitPort,
72+
ExitHost: modelService.ExitHost,
73+
CreatedAt: modelService.CreatedAt,
74+
UpdatedAt: modelService.UpdatedAt,
75+
}, nil
76+
}
77+
78+
// GetAvailableInstances 获取可用实例(没有peer或peer.sid的实例)
79+
func (s *ServiceImpl) GetAvailableInstances() ([]*AvailableInstance, error) {
80+
var tunnels []models.Tunnel
81+
82+
// 查询没有 peer 或 peer.sid 为空的隧道
83+
// peer 字段为 JSON,需要检查是否为 null 或者 sid 字段为空
84+
err := s.db.Where("peer IS NULL OR json_extract(peer, '$.sid') IS NULL OR json_extract(peer, '$.sid') = ''").
85+
Preload("Endpoint").
86+
Find(&tunnels).Error
87+
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
var instances []*AvailableInstance
93+
for _, tunnel := range tunnels {
94+
if tunnel.InstanceID == nil {
95+
continue
96+
}
97+
98+
instances = append(instances, &AvailableInstance{
99+
InstanceId: *tunnel.InstanceID,
100+
EndpointId: tunnel.EndpointID,
101+
EndpointName: tunnel.Endpoint.Name,
102+
TunnelType: string(tunnel.Type),
103+
Name: tunnel.Name,
104+
TunnelAddress: tunnel.TunnelAddress,
105+
TunnelPort: tunnel.TunnelPort,
106+
TargetAddress: tunnel.TargetAddress,
107+
TargetPort: tunnel.TargetPort,
108+
})
109+
}
110+
111+
return instances, nil
112+
}
113+
114+
// AssembleService 组装服务
115+
func (s *ServiceImpl) AssembleService(req *AssembleServiceRequest) error {
116+
// 验证客户端实例是否存在并获取 tunnel 信息
117+
var clientTunnel models.Tunnel
118+
if err := s.db.Where("instance_id = ?", req.ClientInstanceId).First(&clientTunnel).Error; err != nil {
119+
return fmt.Errorf("客户端实例不存在: %w", err)
120+
}
121+
122+
// 如果需要服务端实例,验证是否存在并获取 tunnel 信息
123+
var serverTunnel *models.Tunnel
124+
if req.ServerInstanceId != nil && *req.ServerInstanceId != "" {
125+
serverTunnel = &models.Tunnel{}
126+
if err := s.db.Where("instance_id = ?", *req.ServerInstanceId).First(serverTunnel).Error; err != nil {
127+
return fmt.Errorf("服务端实例不存在: %w", err)
128+
}
129+
}
130+
131+
// 创建 peer 对象
132+
alias := req.Name
133+
peer := &models.Peer{
134+
SID: &req.Sid,
135+
Type: &req.Type,
136+
Alias: &alias,
137+
}
138+
139+
// 调用 nodepass API 更新客户端实例的 peer 信息
140+
if _, err := nodepass.UpdateInstancePeers(clientTunnel.EndpointID, req.ClientInstanceId, peer); err != nil {
141+
return fmt.Errorf("更新客户端实例peer信息失败: %w", err)
142+
}
143+
144+
// 更新数据库中客户端隧道的 peer 字段
145+
clientTunnel.Peer = peer
146+
if err := s.db.Model(&models.Tunnel{}).
147+
Where("instance_id = ?", req.ClientInstanceId).
148+
Select("peer").
149+
Updates(&clientTunnel).Error; err != nil {
150+
return fmt.Errorf("更新客户端隧道peer字段失败: %w", err)
151+
}
152+
153+
// 如果有服务端实例,也调用 nodepass API 更新其 peer 信息
154+
if serverTunnel != nil {
155+
if _, err := nodepass.UpdateInstancePeers(serverTunnel.EndpointID, *req.ServerInstanceId, peer); err != nil {
156+
return fmt.Errorf("更新服务端实例peer信息失败: %w", err)
157+
}
158+
159+
// 更新数据库中服务端隧道的 peer 字段
160+
serverTunnel.Peer = peer
161+
if err := s.db.Model(&models.Tunnel{}).
162+
Where("instance_id = ?", *req.ServerInstanceId).
163+
Select("peer").
164+
Updates(serverTunnel).Error; err != nil {
165+
return fmt.Errorf("更新服务端隧道peer字段失败: %w", err)
166+
}
167+
}
168+
169+
return nil
170+
}

web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "NodePassDash",
33
"private": true,
4-
"version": "3.2.0-beta1",
4+
"version": "3.2.0-beta2",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

0 commit comments

Comments
 (0)