@@ -3,7 +3,6 @@ package api
33import (
44 "database/sql"
55 "encoding/json"
6- "fmt"
76 "net/http"
87 "time"
98
@@ -21,36 +20,13 @@ func NewDataHandler(db *sql.DB, mgr *sse.Manager) *DataHandler {
2120 return & DataHandler {db : db , sseManager : mgr }
2221}
2322
24- // EndpointExport 导出端点结构
23+ // EndpointExport 导出端点结构(简化版,仅包含基本配置信息)
2524type EndpointExport struct {
26- Name string `json:"name"`
27- URL string `json:"url"`
28- APIPath string `json:"apiPath"`
29- APIKey string `json:"apiKey"`
30- Status string `json:"status"`
31- Color string `json:"color,omitempty"`
32- Tunnels []TunnelExport `json:"tunnels,omitempty"`
33- }
34-
35- // TunnelExport 导出隧道结构
36- type TunnelExport struct {
37- Name string `json:"name"`
38- Mode string `json:"mode"`
39- Status string `json:"status"`
40- TunnelAddress string `json:"tunnelAddress"`
41- TunnelPort string `json:"tunnelPort"`
42- TargetAddress string `json:"targetAddress"`
43- TargetPort string `json:"targetPort"`
44- TLSMode string `json:"tlsMode"`
45- CertPath string `json:"certPath,omitempty"`
46- KeyPath string `json:"keyPath,omitempty"`
47- LogLevel string `json:"logLevel"`
48- CommandLine string `json:"commandLine"`
49- InstanceID string `json:"instanceId,omitempty"`
50- TCPRx string `json:"tcpRx,omitempty"`
51- TCPTx string `json:"tcpTx,omitempty"`
52- UDPRx string `json:"udpRx,omitempty"`
53- UDPTx string `json:"udpTx,omitempty"`
25+ Name string `json:"name"`
26+ URL string `json:"url"`
27+ APIPath string `json:"apiPath"`
28+ APIKey string `json:"apiKey"`
29+ Color string `json:"color,omitempty"`
5430}
5531
5632// ---------- 导出 ----------
@@ -60,8 +36,8 @@ func (h *DataHandler) HandleExport(w http.ResponseWriter, r *http.Request) {
6036 return
6137 }
6238
63- // 查询端点
64- rows , err := h .db .Query (`SELECT id, name, url, apiPath, apiKey, status, color FROM "Endpoint" ORDER BY id` )
39+ // 查询端点(仅导出基本配置信息,不包括状态和隧道信息)
40+ rows , err := h .db .Query (`SELECT name, url, apiPath, apiKey, COALESCE(color, '') as color FROM "Endpoint" ORDER BY id` )
6541 if err != nil {
6642 log .Errorf ("export query endpoints: %v" , err )
6743 http .Error (w , "export failed" , http .StatusInternalServerError )
@@ -71,59 +47,23 @@ func (h *DataHandler) HandleExport(w http.ResponseWriter, r *http.Request) {
7147
7248 var endpoints []EndpointExport
7349 for rows .Next () {
74- var epID int64
7550 var ep EndpointExport
76- if err := rows .Scan (& epID , & ep .Name , & ep .URL , & ep .APIPath , & ep .APIKey , & ep . Status , & ep .Color ); err != nil {
51+ if err := rows .Scan (& ep .Name , & ep .URL , & ep .APIPath , & ep .APIKey , & ep .Color ); err != nil {
7752 continue
7853 }
79- // 查询该端点隧道
80- tRows , err := h .db .Query (`SELECT name, mode, status, tunnelAddress, tunnelPort, targetAddress, targetPort, tlsMode, certPath, keyPath, logLevel, commandLine, instanceId, tcpRx, tcpTx, udpRx, udpTx FROM "Tunnel" WHERE endpointId = ?` , epID )
81- if err == nil {
82- for tRows .Next () {
83- var t TunnelExport
84- var tcpRx , tcpTx , udpRx , udpTx sql.NullInt64
85- var instanceNS sql.NullString
86- var certNS , keyNS sql.NullString
87- if err := tRows .Scan (& t .Name , & t .Mode , & t .Status , & t .TunnelAddress , & t .TunnelPort , & t .TargetAddress , & t .TargetPort , & t .TLSMode , & certNS , & keyNS , & t .LogLevel , & t .CommandLine , & instanceNS , & tcpRx , & tcpTx , & udpRx , & udpTx ); err == nil {
88- if certNS .Valid {
89- t .CertPath = certNS .String
90- }
91- if keyNS .Valid {
92- t .KeyPath = keyNS .String
93- }
94- if instanceNS .Valid {
95- t .InstanceID = instanceNS .String
96- }
97- if tcpRx .Valid {
98- t .TCPRx = fmt .Sprintf ("%d" , tcpRx .Int64 )
99- }
100- if tcpTx .Valid {
101- t .TCPTx = fmt .Sprintf ("%d" , tcpTx .Int64 )
102- }
103- if udpRx .Valid {
104- t .UDPRx = fmt .Sprintf ("%d" , udpRx .Int64 )
105- }
106- if udpTx .Valid {
107- t .UDPTx = fmt .Sprintf ("%d" , udpTx .Int64 )
108- }
109- ep .Tunnels = append (ep .Tunnels , t )
110- }
111- }
112- tRows .Close ()
113- }
11454 endpoints = append (endpoints , ep )
11555 }
11656
11757 payload := map [string ]interface {}{
118- "version" : "1 .0" ,
58+ "version" : "2 .0" , // 更新版本号以表示新的简化格式
11959 "timestamp" : time .Now ().Format (time .RFC3339 ),
12060 "data" : map [string ]interface {}{
12161 "endpoints" : endpoints ,
12262 },
12363 }
12464
12565 w .Header ().Set ("Content-Type" , "application/json" )
126- w .Header ().Set ("Content-Disposition" , "attachment; filename=nodepass-data .json" )
66+ w .Header ().Set ("Content-Disposition" , "attachment; filename=nodepass-endpoints .json" )
12767 json .NewEncoder (w ).Encode (payload )
12868}
12969
@@ -147,13 +87,23 @@ func (h *DataHandler) HandleImport(w http.ResponseWriter, r *http.Request) {
14787 }
14888
14989 var skippedEndpoints int
150- var importedTunnels int
90+ var importedEndpoints int
15191
15292 tx , err := h .db .Begin ()
15393 if err != nil {
15494 http .Error (w , "db error" , http .StatusInternalServerError )
15595 return
15696 }
97+ defer tx .Rollback ()
98+
99+ // 存储新创建的端点信息,用于后续启动SSE
100+ var newEndpoints []struct {
101+ ID int64
102+ URL string
103+ APIPath string
104+ APIKey string
105+ }
106+
157107 for _ , ep := range importData .Data .Endpoints {
158108 var exists bool
159109 if err := tx .QueryRow (`SELECT EXISTS(SELECT 1 FROM "Endpoint" WHERE url = ? AND apiPath = ?)` , ep .URL , ep .APIPath ).Scan (& exists ); err != nil {
@@ -163,29 +113,59 @@ func (h *DataHandler) HandleImport(w http.ResponseWriter, r *http.Request) {
163113 skippedEndpoints ++
164114 continue
165115 }
166- res , err := tx .Exec (`INSERT INTO "Endpoint" (name, url, apiPath, apiKey, status, color, tunnelCount, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)` , ep .Name , ep .URL , ep .APIPath , ep .APIKey , ep .Status , ep .Color )
116+
117+ // 插入端点,设置默认状态为 OFFLINE
118+ result , err := tx .Exec (`INSERT INTO "Endpoint" (name, url, apiPath, apiKey, status, color, tunnelCount, createdAt, updatedAt) VALUES (?, ?, ?, ?, 'OFFLINE', ?, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)` ,
119+ ep .Name , ep .URL , ep .APIPath , ep .APIKey , ep .Color )
167120 if err != nil {
121+ log .Errorf ("insert endpoint failed: %v" , err )
168122 continue
169123 }
170- epID , _ := res .LastInsertId ()
171- for _ , t := range ep .Tunnels {
172- _ , _ = tx .Exec (`INSERT INTO "Tunnel" (name, mode, status, tunnelAddress, tunnelPort, targetAddress, targetPort, tlsMode, certPath, keyPath, logLevel, commandLine, instanceId, tcpRx, tcpTx, udpRx, udpTx, endpointId, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)` ,
173- t .Name , t .Mode , t .Status , t .TunnelAddress , t .TunnelPort , t .TargetAddress , t .TargetPort , t .TLSMode , t .CertPath , t .KeyPath , t .LogLevel , t .CommandLine , t .InstanceID , t .TCPRx , t .TCPTx , t .UDPRx , t .UDPTx , epID )
174- importedTunnels ++
124+
125+ // 获取新创建的端点ID
126+ endpointID , err := result .LastInsertId ()
127+ if err != nil {
128+ log .Errorf ("get last insert id failed: %v" , err )
129+ continue
175130 }
131+
132+ // 保存端点信息用于后续启动SSE
133+ newEndpoints = append (newEndpoints , struct {
134+ ID int64
135+ URL string
136+ APIPath string
137+ APIKey string
138+ }{
139+ ID : endpointID ,
140+ URL : ep .URL ,
141+ APIPath : ep .APIPath ,
142+ APIKey : ep .APIKey ,
143+ })
144+
145+ importedEndpoints ++
146+ }
147+
148+ if err := tx .Commit (); err != nil {
149+ http .Error (w , "commit failed" , http .StatusInternalServerError )
150+ return
176151 }
177- tx .Commit ()
178152
179- // 重置 SSE
153+ // 为每个新导入的端点启动SSE监听(学习HandleCreateEndpoint的逻辑)
180154 if h .sseManager != nil {
181- h .sseManager .Close ()
182- h .sseManager .InitializeSystem ()
155+ for _ , ep := range newEndpoints {
156+ go func (endpointID int64 , url , apiPath , apiKey string ) {
157+ log .Infof ("[Master-%v] 导入成功,准备启动 SSE 监听" , endpointID )
158+ if err := h .sseManager .ConnectEndpoint (endpointID , url , apiPath , apiKey ); err != nil {
159+ log .Errorf ("[Master-%v] 启动 SSE 监听失败: %v" , endpointID , err )
160+ }
161+ }(ep .ID , ep .URL , ep .APIPath , ep .APIKey )
162+ }
183163 }
184164
185165 json .NewEncoder (w ).Encode (map [string ]interface {}{
186- "success" : true ,
187- "message" : "数据导入成功 " ,
188- "skippedEndpoints " : skippedEndpoints ,
189- "tunnels " : importedTunnels ,
166+ "success" : true ,
167+ "message" : "端点配置导入成功 " ,
168+ "importedEndpoints " : importedEndpoints ,
169+ "skippedEndpoints " : skippedEndpoints ,
190170 })
191171}
0 commit comments