diff --git a/internal/server/router/handler/interfaces.go b/internal/server/router/handler/interfaces.go index dc8771d..c26bfed 100644 --- a/internal/server/router/handler/interfaces.go +++ b/internal/server/router/handler/interfaces.go @@ -47,6 +47,8 @@ type ServerInterface interface { StopClientLogStream(sessionID string) // 插件状态查询 GetClientPluginStatus(clientID string) ([]protocol.PluginStatusEntry, error) + // 插件规则管理 + StartPluginRule(clientID string, rule protocol.ProxyRule) error } // ConfigField 配置字段 diff --git a/internal/server/router/handler/store.go b/internal/server/router/handler/store.go index 991dc28..10a7e9b 100644 --- a/internal/server/router/handler/store.go +++ b/internal/server/router/handler/store.go @@ -7,8 +7,8 @@ import ( "github.com/gin-gonic/gin" "github.com/gotunnel/internal/server/db" - // removed router import "github.com/gotunnel/internal/server/router/dto" + "github.com/gotunnel/pkg/protocol" ) // StoreHandler 插件商店处理器 @@ -192,9 +192,26 @@ func (h *StoreHandler) Install(c *gin.Context) { h.app.GetClientStore().UpdateClient(dbClient) } + // 启动服务端监听器(让外部用户可以通过 RemotePort 访问插件) + if req.RemotePort > 0 { + pluginRule := protocol.ProxyRule{ + Name: req.PluginName, + Type: req.PluginName, // 使用插件名作为类型,让 isClientPlugin 识别 + RemotePort: req.RemotePort, + Enabled: boolPtr(true), + } + // 启动监听器(忽略错误,可能端口已被占用) + h.app.GetServer().StartPluginRule(req.ClientID, pluginRule) + } + Success(c, gin.H{ "status": "ok", "plugin": req.PluginName, "client": req.ClientID, }) } + +// boolPtr 返回 bool 值的指针 +func boolPtr(b bool) *bool { + return &b +} diff --git a/internal/server/tunnel/server.go b/internal/server/tunnel/server.go index b1080e4..781ae91 100644 --- a/internal/server/tunnel/server.go +++ b/internal/server/tunnel/server.go @@ -1227,6 +1227,15 @@ func (s *Server) pushClientInstalledPlugins(cs *ClientSession, alreadyPushed map if err := s.InstallJSPluginToClient(cs.ID, req); err != nil { log.Printf("[Server] Failed to restore plugin %s: %v", cp.Name, err) + } else if cp.RemotePort > 0 { + // 安装成功后启动服务端监听器 + pluginRule := protocol.ProxyRule{ + Name: cp.Name, + Type: cp.Name, + RemotePort: cp.RemotePort, + Enabled: boolPtr(true), + } + s.startClientPluginListener(cs, pluginRule) } } } @@ -1342,6 +1351,30 @@ func (s *Server) sendClientPluginStop(session *yamux.Session, pluginName, ruleNa return nil } +// StartPluginRule 为客户端插件启动服务端监听器 +func (s *Server) StartPluginRule(clientID string, rule protocol.ProxyRule) error { + s.mu.RLock() + cs, ok := s.clients[clientID] + s.mu.RUnlock() + + if !ok { + return fmt.Errorf("client %s not found or not online", clientID) + } + + // 检查端口是否已被占用 + cs.mu.Lock() + _, exists := cs.Listeners[rule.RemotePort] + cs.mu.Unlock() + if exists { + // 端口已在监听,无需重复启动 + return nil + } + + // 启动插件监听器 + s.startClientPluginListener(cs, rule) + return nil +} + // RestartClientPlugin 重启客户端 JS 插件 func (s *Server) RestartClientPlugin(clientID, pluginName, ruleName string) error { s.mu.RLock() @@ -1612,3 +1645,8 @@ func (s *Server) StopClientLogStream(sessionID string) { s.logSessions.RemoveSession(sessionID) } + +// boolPtr 返回 bool 值的指针 +func boolPtr(b bool) *bool { + return &b +}