Skip to content

Commit 2c121c2

Browse files
committed
feat: 服务管理部分功能点实现
1 parent 43bee40 commit 2c121c2

File tree

16 files changed

+1359
-97
lines changed

16 files changed

+1359
-97
lines changed

internal/api/dashboard.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ func (h *DashboardHandler) HandleGetTunnelStats(c *gin.Context) {
167167
Error int64 `json:"error"`
168168
Offline int64 `json:"offline"`
169169
TotalEndpoints int64 `json:"total_endpoints"`
170+
TotalServices int64 `json:"total_services"`
170171
}
171172

172173
// 使用原生 SQL 查询统计数据
@@ -199,6 +200,16 @@ func (h *DashboardHandler) HandleGetTunnelStats(c *gin.Context) {
199200
return
200201
}
201202

203+
// 获取服务总数
204+
err = h.dashboardService.DB().Raw("SELECT COUNT(*) FROM services").Scan(&stats.TotalServices).Error
205+
if err != nil {
206+
c.JSON(http.StatusInternalServerError, gin.H{
207+
"success": false,
208+
"error": "获取服务总数失败: " + err.Error(),
209+
})
210+
return
211+
}
212+
202213
c.JSON(http.StatusOK, gin.H{
203214
"success": true,
204215
"data": stats,

internal/api/services.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package api
2+
3+
import (
4+
"NodePassDash/internal/services"
5+
"net/http"
6+
7+
"github.com/gin-gonic/gin"
8+
)
9+
10+
// ServicesHandler 服务处理器
11+
type ServicesHandler struct {
12+
servicesService *services.ServiceImpl
13+
}
14+
15+
// NewServicesHandler 创建服务处理器
16+
func NewServicesHandler(servicesService *services.ServiceImpl) *ServicesHandler {
17+
return &ServicesHandler{servicesService: servicesService}
18+
}
19+
20+
// SetupServicesRoutes 设置服务相关路由
21+
func SetupServicesRoutes(rg *gin.RouterGroup, servicesService *services.ServiceImpl) {
22+
// 创建ServicesHandler实例
23+
servicesHandler := NewServicesHandler(servicesService)
24+
25+
// 服务相关路由
26+
rg.GET("/services", servicesHandler.GetServices)
27+
rg.GET("/services/:sid/:type", servicesHandler.GetServiceByID)
28+
rg.GET("/services/available-instances", servicesHandler.GetAvailableInstances)
29+
rg.POST("/services/assemble", servicesHandler.AssembleService)
30+
}
31+
32+
// GetServices 获取所有服务
33+
func (h *ServicesHandler) GetServices(c *gin.Context) {
34+
serviceList, err := h.servicesService.GetServices()
35+
if err != nil {
36+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
37+
return
38+
}
39+
40+
response := services.ServiceResponse{
41+
Success: true,
42+
Services: serviceList,
43+
}
44+
45+
c.JSON(http.StatusOK, response)
46+
}
47+
48+
// GetServiceByID 根据 SID 和 Type 获取单个服务
49+
func (h *ServicesHandler) GetServiceByID(c *gin.Context) {
50+
sid := c.Param("sid")
51+
serviceType := c.Param("type")
52+
53+
service, err := h.servicesService.GetServiceByID(sid, serviceType)
54+
if err != nil {
55+
c.JSON(http.StatusNotFound, gin.H{"error": "服务不存在"})
56+
return
57+
}
58+
59+
response := services.ServiceResponse{
60+
Success: true,
61+
Service: service,
62+
}
63+
64+
c.JSON(http.StatusOK, response)
65+
}
66+
67+
// GetAvailableInstances 获取可用实例
68+
func (h *ServicesHandler) GetAvailableInstances(c *gin.Context) {
69+
instances, err := h.servicesService.GetAvailableInstances()
70+
if err != nil {
71+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
72+
return
73+
}
74+
75+
response := services.AvailableInstancesResponse{
76+
Success: true,
77+
Instances: instances,
78+
}
79+
80+
c.JSON(http.StatusOK, response)
81+
}
82+
83+
// AssembleService 组装服务
84+
func (h *ServicesHandler) AssembleService(c *gin.Context) {
85+
var req services.AssembleServiceRequest
86+
if err := c.ShouldBindJSON(&req); err != nil {
87+
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误: " + err.Error()})
88+
return
89+
}
90+
91+
if err := h.servicesService.AssembleService(&req); err != nil {
92+
c.JSON(http.StatusInternalServerError, gin.H{"error": "组装服务失败: " + err.Error()})
93+
return
94+
}
95+
96+
c.JSON(http.StatusOK, gin.H{
97+
"success": true,
98+
"message": "组装服务成功",
99+
})
100+
}

internal/db/db.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import (
1414
"sync"
1515
"time"
1616

17+
_ "github.com/mattn/go-sqlite3"
1718
"gorm.io/driver/sqlite"
1819
"gorm.io/gorm"
1920
"gorm.io/gorm/logger"
20-
_ "github.com/mattn/go-sqlite3"
2121
)
2222

2323
var (
@@ -193,6 +193,9 @@ func QuickInitSchema(db *gorm.DB) error {
193193
&models.TrafficHourlySummary{},
194194
&models.DashboardTrafficSummary{},
195195
&models.ServiceHistory{},
196+
197+
// 服务管理表
198+
&models.Services{},
196199
)
197200
}
198201

@@ -220,6 +223,9 @@ func StandardMigrate(db *gorm.DB) error {
220223
&models.TrafficHourlySummary{},
221224
&models.DashboardTrafficSummary{},
222225
&models.ServiceHistory{},
226+
227+
// 服务管理表
228+
&models.Services{},
223229
)
224230
}
225231

internal/models/models.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,29 @@ type TunnelRecycle struct {
143143
DeletedAt time.Time `json:"deletedAt" gorm:"autoCreateTime;column:deleted_at"`
144144
}
145145

146+
type Services struct {
147+
Sid string `json:"sid" gorm:"type:text;primaryKey;column:sid"`
148+
Type string `json:"type" gorm:"type:text;primaryKey;column:type"`
149+
Alias *string `json:"alias,omitempty" gorm:"type:text;column:alias"`
150+
ServerInstanceId *string `json:"serverInstanceId,omitempty" gorm:"type:text;column:server_instance_id"`
151+
ClientInstanceId *string `json:"clientInstanceId,omitempty" gorm:"type:text;column:client_instance_id"`
152+
ServerEndpointId *int64 `json:"serverEndpointId,omitempty" gorm:"type:int;column:server_endpoint_id"`
153+
ClientEndpointId *int64 `json:"clientEndpointId,omitempty" gorm:"type:int;column:server_endpoint_id"`
154+
TunnelPort *int64 `json:"tunnelPort,omitempty" gorm:"type:int;column:tunnel_port"`
155+
TunnelEndpointName *string `json:"tunnelEndpointName,omitempty" gorm:"type:text;column:tunnel_endpoint_name"`
156+
EntrancePort *int64 `json:"entrancePort,omitempty" gorm:"type:int;column:entrance_port"`
157+
EntranceHost *string `json:"entranceHost,omitempty" gorm:"type:text;column:entrance_host"`
158+
ExitPort *int64 `json:"exitPort,omitempty" gorm:"type:int;column:exit_port"`
159+
ExitHost *string `json:"exitHost,omitempty" gorm:"type:text;column:exit_host"`
160+
CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime;column:created_at"`
161+
UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime;column:updated_at"`
162+
}
163+
164+
// TableName 设置表名
165+
func (Services) TableName() string {
166+
return "services"
167+
}
168+
146169
// TableName 设置表名
147170
func (TunnelRecycle) TableName() string {
148171
return "tunnel_recycle"

internal/router/router.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import (
55
"NodePassDash/internal/auth"
66
"NodePassDash/internal/dashboard"
77
"NodePassDash/internal/endpoint"
8+
"NodePassDash/internal/group"
89
"NodePassDash/internal/metrics"
10+
"NodePassDash/internal/services"
911
"NodePassDash/internal/sse"
10-
"NodePassDash/internal/group"
1112
"NodePassDash/internal/tunnel"
1213
"NodePassDash/internal/websocket"
1314
"fmt"
@@ -50,6 +51,7 @@ func setupAPIRoutes(r *gin.Engine, db *gorm.DB, sseService *sse.Service, sseMana
5051
endpointService := endpoint.NewService(db)
5152
tunnelService := tunnel.NewService(db)
5253
groupService := group.NewService(db)
54+
servicesService := services.NewService(db)
5355
dashboardService := dashboard.NewService(db)
5456

5557
// 创建 Metrics 系统相关的处理器
@@ -65,6 +67,7 @@ func setupAPIRoutes(r *gin.Engine, db *gorm.DB, sseService *sse.Service, sseMana
6567
api.SetupDashboardRoutes(apiGroup, dashboardService)
6668
api.SetupDataRoutes(apiGroup, db, sseManager, endpointService, tunnelService)
6769
api.SetupGroupRoutes(apiGroup, groupService)
70+
api.SetupServicesRoutes(apiGroup, servicesService)
6871
api.SetupVersionRoutes(apiGroup, version)
6972
api.SetupDebugRoutes(apiGroup)
7073
}
@@ -74,22 +77,22 @@ func setupAPIRoutes(r *gin.Engine, db *gorm.DB, sseService *sse.Service, sseMana
7477
func docsProxyHandler(c *gin.Context) {
7578
// 获取路径参数
7679
path := c.Param("path")
77-
80+
7881
// 构建目标 URL
7982
targetURL := fmt.Sprintf("https://raw.githubusercontent.com%s", path)
80-
83+
8184
// 创建 HTTP 客户端
8285
client := &http.Client{
8386
Timeout: 30 * time.Second,
8487
}
85-
88+
8689
// 创建请求
8790
req, err := http.NewRequest(c.Request.Method, targetURL, c.Request.Body)
8891
if err != nil {
8992
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建请求失败"})
9093
return
9194
}
92-
95+
9396
// 复制请求头(排除某些不需要的头)
9497
for name, values := range c.Request.Header {
9598
if !shouldSkipHeader(name) {
@@ -98,15 +101,15 @@ func docsProxyHandler(c *gin.Context) {
98101
}
99102
}
100103
}
101-
104+
102105
// 发送请求
103106
resp, err := client.Do(req)
104107
if err != nil {
105108
c.JSON(http.StatusBadGateway, gin.H{"error": "代理请求失败"})
106109
return
107110
}
108111
defer resp.Body.Close()
109-
112+
110113
// 复制响应头
111114
for name, values := range resp.Header {
112115
if !shouldSkipHeader(name) {
@@ -115,10 +118,10 @@ func docsProxyHandler(c *gin.Context) {
115118
}
116119
}
117120
}
118-
121+
119122
// 设置状态码
120123
c.Status(resp.StatusCode)
121-
124+
122125
// 复制响应体
123126
_, err = io.Copy(c.Writer, resp.Body)
124127
if err != nil {
@@ -139,7 +142,7 @@ func shouldSkipHeader(name string) bool {
139142
"Transfer-Encoding",
140143
"Upgrade",
141144
}
142-
145+
143146
for _, skip := range skipHeaders {
144147
if strings.EqualFold(name, skip) {
145148
return true

0 commit comments

Comments
 (0)