From 24b3b47ccd94ba21300739ab4c13f282b9855235 Mon Sep 17 00:00:00 2001 From: Flik Date: Sun, 11 Jan 2026 11:43:07 +0800 Subject: [PATCH] 1 --- internal/client/tunnel/client.go | 61 ++++++++++++++++---- internal/server/app/dist/.gitkeep | 2 - internal/server/router/handler/client.go | 8 +-- internal/server/router/handler/interfaces.go | 8 +-- internal/server/router/handler/store.go | 3 + internal/server/tunnel/server.go | 20 +++++-- pkg/protocol/message.go | 20 ++++--- scripts/build.ps1 | 1 + 8 files changed, 89 insertions(+), 34 deletions(-) delete mode 100644 internal/server/app/dist/.gitkeep diff --git a/internal/client/tunnel/client.go b/internal/client/tunnel/client.go index 3cc428f..2b1e37d 100644 --- a/internal/client/tunnel/client.go +++ b/internal/client/tunnel/client.go @@ -525,13 +525,24 @@ func (c *Client) handleClientPluginConn(stream net.Conn, msg *protocol.Message) return } - key := req.PluginName + ":" + req.RuleName c.pluginMu.RLock() - handler, ok := c.runningPlugins[key] + var handler plugin.ClientPlugin + var ok bool + + // 优先使用 PluginID 查找 + if req.PluginID != "" { + handler, ok = c.runningPlugins[req.PluginID] + } + + // 如果没找到,回退到 pluginName:ruleName + if !ok { + key := req.PluginName + ":" + req.RuleName + handler, ok = c.runningPlugins[key] + } c.pluginMu.RUnlock() if !ok { - c.logWarnf("[Client] Plugin %s not running", key) + c.logWarnf("[Client] Plugin %s (ID: %s) not running", req.PluginName, req.PluginID) stream.Close() return } @@ -696,10 +707,25 @@ func (c *Client) handleClientPluginStop(stream net.Conn, msg *protocol.Message) return } - key := req.PluginName + ":" + req.RuleName - c.pluginMu.Lock() - handler, ok := c.runningPlugins[key] + var handler plugin.ClientPlugin + var key string + var ok bool + + // 优先使用 PluginID 查找 + if req.PluginID != "" { + handler, ok = c.runningPlugins[req.PluginID] + if ok { + key = req.PluginID + } + } + + // 如果没找到,回退到 pluginName:ruleName + if !ok { + key = req.PluginName + ":" + req.RuleName + handler, ok = c.runningPlugins[key] + } + if ok { if err := handler.Stop(); err != nil { c.logErrorf("[Client] Plugin %s stop error: %v", key, err) @@ -754,13 +780,28 @@ func (c *Client) handlePluginConfigUpdate(stream net.Conn, msg *protocol.Message return } - key := req.PluginName + ":" + req.RuleName - c.logf("[Client] Config update for plugin %s", key) - c.pluginMu.RLock() - handler, ok := c.runningPlugins[key] + var handler plugin.ClientPlugin + var key string + var ok bool + + // 优先使用 PluginID 查找 + if req.PluginID != "" { + handler, ok = c.runningPlugins[req.PluginID] + if ok { + key = req.PluginID + } + } + + // 如果没找到,回退到 pluginName:ruleName + if !ok { + key = req.PluginName + ":" + req.RuleName + handler, ok = c.runningPlugins[key] + } c.pluginMu.RUnlock() + c.logf("[Client] Config update for plugin %s", key) + if !ok { c.sendPluginConfigUpdateResult(stream, req.PluginName, req.RuleName, false, "plugin not running") return diff --git a/internal/server/app/dist/.gitkeep b/internal/server/app/dist/.gitkeep deleted file mode 100644 index 51c003d..0000000 --- a/internal/server/app/dist/.gitkeep +++ /dev/null @@ -1,2 +0,0 @@ -# This file ensures the dist directory exists for go:embed -# The actual frontend files are built during CI/CD diff --git a/internal/server/router/handler/client.go b/internal/server/router/handler/client.go index e2419df..581b721 100644 --- a/internal/server/router/handler/client.go +++ b/internal/server/router/handler/client.go @@ -372,17 +372,17 @@ func (h *ClientHandler) PluginAction(c *gin.Context) { switch action { case "start": - err = h.app.GetServer().StartClientPlugin(clientID, pluginName, req.RuleName) + err = h.app.GetServer().StartClientPlugin(clientID, pluginID, pluginName, req.RuleName) case "stop": - err = h.app.GetServer().StopClientPlugin(clientID, pluginName, req.RuleName) + err = h.app.GetServer().StopClientPlugin(clientID, pluginID, pluginName, req.RuleName) case "restart": - err = h.app.GetServer().RestartClientPlugin(clientID, pluginName, req.RuleName) + err = h.app.GetServer().RestartClientPlugin(clientID, pluginID, pluginName, req.RuleName) case "config": if req.Config == nil { BadRequest(c, "config required") return } - err = h.app.GetServer().UpdateClientPluginConfig(clientID, pluginName, req.RuleName, req.Config, req.Restart) + err = h.app.GetServer().UpdateClientPluginConfig(clientID, pluginID, pluginName, req.RuleName, req.Config, req.Restart) case "delete": err = h.deleteClientPlugin(clientID, pluginID) default: diff --git a/internal/server/router/handler/interfaces.go b/internal/server/router/handler/interfaces.go index ed62c86..6bf83db 100644 --- a/internal/server/router/handler/interfaces.go +++ b/internal/server/router/handler/interfaces.go @@ -37,10 +37,10 @@ type ServerInterface interface { SyncPluginConfigToClient(clientID string, pluginName string, config map[string]string) error InstallJSPluginToClient(clientID string, req JSPluginInstallRequest) error RestartClient(clientID string) error - StartClientPlugin(clientID, pluginName, ruleName string) error - StopClientPlugin(clientID, pluginName, ruleName string) error - RestartClientPlugin(clientID, pluginName, ruleName string) error - UpdateClientPluginConfig(clientID, pluginName, ruleName string, config map[string]string, restart bool) error + StartClientPlugin(clientID, pluginID, pluginName, ruleName string) error + StopClientPlugin(clientID, pluginID, pluginName, ruleName string) error + RestartClientPlugin(clientID, pluginID, pluginName, ruleName string) error + UpdateClientPluginConfig(clientID, pluginID, pluginName, ruleName string, config map[string]string, restart bool) error SendUpdateToClient(clientID, downloadURL string) error // 日志流 StartClientLogStream(clientID, sessionID string, lines int, follow bool, level string) (<-chan protocol.LogEntry, error) diff --git a/internal/server/router/handler/store.go b/internal/server/router/handler/store.go index 51a652b..9d9b301 100644 --- a/internal/server/router/handler/store.go +++ b/internal/server/router/handler/store.go @@ -235,6 +235,7 @@ func (h *StoreHandler) Install(c *gin.Context) { dbClient.Rules[i].Type = req.PluginName dbClient.Rules[i].RemotePort = req.RemotePort dbClient.Rules[i].Enabled = boolPtr(true) + dbClient.Rules[i].PluginID = pluginID dbClient.Rules[i].AuthEnabled = req.AuthEnabled dbClient.Rules[i].AuthUsername = req.AuthUsername dbClient.Rules[i].AuthPassword = req.AuthPassword @@ -250,6 +251,7 @@ func (h *StoreHandler) Install(c *gin.Context) { Type: req.PluginName, RemotePort: req.RemotePort, Enabled: boolPtr(true), + PluginID: pluginID, AuthEnabled: req.AuthEnabled, AuthUsername: req.AuthUsername, AuthPassword: req.AuthPassword, @@ -268,6 +270,7 @@ func (h *StoreHandler) Install(c *gin.Context) { Type: req.PluginName, // 使用插件名作为类型,让 isClientPlugin 识别 RemotePort: req.RemotePort, Enabled: boolPtr(true), + PluginID: pluginID, AuthEnabled: req.AuthEnabled, AuthUsername: req.AuthUsername, AuthPassword: req.AuthPassword, diff --git a/internal/server/tunnel/server.go b/internal/server/tunnel/server.go index 31ecf8a..2e4d3f7 100644 --- a/internal/server/tunnel/server.go +++ b/internal/server/tunnel/server.go @@ -1126,14 +1126,18 @@ func (s *Server) acceptClientPluginConns(cs *ClientSession, ln net.Listener, rul func (s *Server) handleClientPluginConn(cs *ClientSession, conn net.Conn, rule protocol.ProxyRule) { defer conn.Close() + log.Printf("[Server] handleClientPluginConn: plugin=%s, auth=%v", rule.Type, rule.AuthEnabled) + // 如果启用了 HTTP Basic Auth,先进行认证 var bufferedData []byte if rule.AuthEnabled { authenticated, data := s.checkHTTPBasicAuth(conn, rule.AuthUsername, rule.AuthPassword) if !authenticated { + log.Printf("[Server] Auth failed for plugin %s", rule.Type) return } bufferedData = data + log.Printf("[Server] Auth success, buffered %d bytes", len(bufferedData)) } stream, err := cs.Session.Open() @@ -1144,6 +1148,7 @@ func (s *Server) handleClientPluginConn(cs *ClientSession, conn net.Conn, rule p defer stream.Close() req := protocol.ClientPluginConnRequest{ + PluginID: rule.PluginID, PluginName: rule.Type, RuleName: rule.Name, } @@ -1331,6 +1336,7 @@ func (s *Server) pushClientInstalledPlugins(cs *ClientSession, alreadyPushed map Type: cp.Name, RemotePort: cp.RemotePort, Enabled: boolPtr(true), + PluginID: cp.ID, AuthEnabled: cp.AuthEnabled, AuthUsername: cp.AuthUsername, AuthPassword: cp.AuthPassword, @@ -1386,7 +1392,7 @@ func (s *Server) RestartClient(clientID string) error { } // StartClientPlugin 启动客户端插件 -func (s *Server) StartClientPlugin(clientID, pluginName, ruleName string) error { +func (s *Server) StartClientPlugin(clientID, pluginID, pluginName, ruleName string) error { s.mu.RLock() _, ok := s.clients[clientID] s.mu.RUnlock() @@ -1400,7 +1406,7 @@ func (s *Server) StartClientPlugin(clientID, pluginName, ruleName string) error } // StopClientPlugin 停止客户端插件 -func (s *Server) StopClientPlugin(clientID, pluginName, ruleName string) error { +func (s *Server) StopClientPlugin(clientID, pluginID, pluginName, ruleName string) error { s.mu.RLock() cs, ok := s.clients[clientID] s.mu.RUnlock() @@ -1409,11 +1415,11 @@ func (s *Server) StopClientPlugin(clientID, pluginName, ruleName string) error { return fmt.Errorf("client %s not found or not online", clientID) } - return s.sendClientPluginStop(cs.Session, pluginName, ruleName) + return s.sendClientPluginStop(cs.Session, pluginID, pluginName, ruleName) } // sendClientPluginStop 发送客户端插件停止命令 -func (s *Server) sendClientPluginStop(session *yamux.Session, pluginName, ruleName string) error { +func (s *Server) sendClientPluginStop(session *yamux.Session, pluginID, pluginName, ruleName string) error { stream, err := session.Open() if err != nil { return err @@ -1421,6 +1427,7 @@ func (s *Server) sendClientPluginStop(session *yamux.Session, pluginName, ruleNa defer stream.Close() req := protocol.ClientPluginStopRequest{ + PluginID: pluginID, PluginName: pluginName, RuleName: ruleName, } @@ -1566,7 +1573,7 @@ func (s *Server) ProxyPluginAPIRequest(clientID string, req protocol.PluginAPIRe } // RestartClientPlugin 重启客户端 JS 插件 -func (s *Server) RestartClientPlugin(clientID, pluginName, ruleName string) error { +func (s *Server) RestartClientPlugin(clientID, pluginID, pluginName, ruleName string) error { s.mu.RLock() _, ok := s.clients[clientID] s.mu.RUnlock() @@ -1670,7 +1677,7 @@ func (s *Server) sendJSPluginRestart(session *yamux.Session, pluginName, ruleNam } // UpdateClientPluginConfig 更新客户端插件配置 -func (s *Server) UpdateClientPluginConfig(clientID, pluginName, ruleName string, config map[string]string, restart bool) error { +func (s *Server) UpdateClientPluginConfig(clientID, pluginID, pluginName, ruleName string, config map[string]string, restart bool) error { s.mu.RLock() cs, ok := s.clients[clientID] s.mu.RUnlock() @@ -1687,6 +1694,7 @@ func (s *Server) UpdateClientPluginConfig(clientID, pluginName, ruleName string, defer stream.Close() req := protocol.PluginConfigUpdateRequest{ + PluginID: pluginID, PluginName: pluginName, RuleName: ruleName, Config: config, diff --git a/pkg/protocol/message.go b/pkg/protocol/message.go index 02bd91c..91af99d 100644 --- a/pkg/protocol/message.go +++ b/pkg/protocol/message.go @@ -101,6 +101,7 @@ type ProxyRule struct { RemotePort int `json:"remote_port" yaml:"remote_port"` // 服务端监听端口 Enabled *bool `json:"enabled,omitempty" yaml:"enabled"` // 是否启用,默认为 true // Plugin 支持字段 + PluginID string `json:"plugin_id,omitempty" yaml:"plugin_id"` // 插件实例ID PluginName string `json:"plugin_name,omitempty" yaml:"plugin_name"` PluginVersion string `json:"plugin_version,omitempty" yaml:"plugin_version"` PluginConfig map[string]string `json:"plugin_config,omitempty" yaml:"plugin_config"` @@ -218,8 +219,9 @@ type ClientPluginStartRequest struct { // ClientPluginStopRequest 停止客户端插件请求 type ClientPluginStopRequest struct { - PluginName string `json:"plugin_name"` // 插件名称 - RuleName string `json:"rule_name"` // 规则名称 + PluginID string `json:"plugin_id,omitempty"` // 插件ID(优先使用) + PluginName string `json:"plugin_name"` // 插件名称 + RuleName string `json:"rule_name"` // 规则名称 } // ClientPluginStatusResponse 客户端插件状态响应 @@ -233,8 +235,9 @@ type ClientPluginStatusResponse struct { // ClientPluginConnRequest 客户端插件连接请求 type ClientPluginConnRequest struct { - PluginName string `json:"plugin_name"` // 插件名称 - RuleName string `json:"rule_name"` // 规则名称 + PluginID string `json:"plugin_id,omitempty"` // 插件ID(优先使用) + PluginName string `json:"plugin_name"` // 插件名称 + RuleName string `json:"rule_name"` // 规则名称 } // PluginStatusEntry 单个插件状态 @@ -280,10 +283,11 @@ type ClientRestartResponse struct { // PluginConfigUpdateRequest 插件配置更新请求 type PluginConfigUpdateRequest struct { - PluginName string `json:"plugin_name"` // 插件名称 - RuleName string `json:"rule_name"` // 规则名称 - Config map[string]string `json:"config"` // 新配置 - Restart bool `json:"restart"` // 是否重启插件 + PluginID string `json:"plugin_id,omitempty"` // 插件ID(优先使用) + PluginName string `json:"plugin_name"` // 插件名称 + RuleName string `json:"rule_name"` // 规则名称 + Config map[string]string `json:"config"` // 新配置 + Restart bool `json:"restart"` // 是否重启插件 } // PluginConfigUpdateResponse 插件配置更新响应 diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 6c95d15..2ff0fc8 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -37,6 +37,7 @@ $Platforms = @( @{OS="darwin"; Arch="amd64"}, @{OS="darwin"; Arch="arm64"} ) +) # 颜色输出函数 function Write-Info {