11111
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 29s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m3s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 49s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m30s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 46s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m29s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 51s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m44s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m5s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m15s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m35s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m3s
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 29s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m3s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 49s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m30s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 46s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m29s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 51s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m44s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m5s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m15s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m35s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m3s
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/gotunnel/internal/client/tunnel"
|
||||
"github.com/gotunnel/pkg/crypto"
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
"github.com/gotunnel/pkg/plugin/builtin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -30,15 +29,9 @@ func main() {
|
||||
log.Printf("[Client] TLS enabled")
|
||||
}
|
||||
|
||||
// 初始化插件系统
|
||||
// 初始化插件注册表(用于 JS 插件)
|
||||
registry := plugin.NewRegistry()
|
||||
for _, h := range builtin.GetClientPlugins() {
|
||||
if err := registry.RegisterClient(h); err != nil {
|
||||
log.Fatalf("[Plugin] Register error: %v", err)
|
||||
}
|
||||
}
|
||||
client.SetPluginRegistry(registry)
|
||||
log.Printf("[Plugin] Registered %d plugins", len(builtin.GetClientPlugins()))
|
||||
|
||||
client.Run()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/gotunnel/internal/server/tunnel"
|
||||
"github.com/gotunnel/pkg/crypto"
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
"github.com/gotunnel/pkg/plugin/builtin"
|
||||
"github.com/gotunnel/pkg/plugin/sign"
|
||||
)
|
||||
|
||||
@@ -71,14 +70,10 @@ func main() {
|
||||
log.Printf("[Server] TLS enabled")
|
||||
}
|
||||
|
||||
// 初始化插件系统
|
||||
// 初始化插件系统(用于客户端 JS 插件管理)
|
||||
registry := plugin.NewRegistry()
|
||||
if err := registry.RegisterAllServer(builtin.GetServerPlugins()); err != nil {
|
||||
log.Fatalf("[Plugin] Register error: %v", err)
|
||||
}
|
||||
server.SetPluginRegistry(registry)
|
||||
server.SetJSPluginStore(clientStore) // 设置 JS 插件存储,用于客户端重连时恢复插件
|
||||
log.Printf("[Plugin] Registered %d plugins", len(builtin.GetServerPlugins()))
|
||||
|
||||
// 加载 JS 插件配置
|
||||
if len(cfg.JSPlugins) > 0 {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
"github.com/gotunnel/pkg/plugin/builtin"
|
||||
)
|
||||
|
||||
// Manager 客户端 plugin 管理器
|
||||
type Manager struct {
|
||||
registry *plugin.Registry
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewManager 创建客户端 plugin 管理器
|
||||
func NewManager() (*Manager, error) {
|
||||
registry := plugin.NewRegistry()
|
||||
|
||||
m := &Manager{
|
||||
registry: registry,
|
||||
}
|
||||
|
||||
if err := m.registerBuiltins(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// registerBuiltins 注册内置 plugins
|
||||
func (m *Manager) registerBuiltins() error {
|
||||
for _, h := range builtin.GetClientPlugins() {
|
||||
if err := m.registry.RegisterClient(h); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Printf("[Plugin] Registered %d client plugins", len(builtin.GetClientPlugins()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClient 返回客户端插件
|
||||
func (m *Manager) GetClient(name string) (plugin.ClientPlugin, error) {
|
||||
return m.registry.GetClient(name)
|
||||
}
|
||||
|
||||
// GetRegistry 返回插件注册表
|
||||
func (m *Manager) GetRegistry() *plugin.Registry {
|
||||
return m.registry
|
||||
}
|
||||
@@ -509,6 +509,18 @@ func (c *Client) handleJSPluginInstall(stream net.Conn, msg *protocol.Message) {
|
||||
|
||||
log.Printf("[Client] Installing JS plugin: %s", req.PluginName)
|
||||
|
||||
// 如果插件已经在运行,先停止它
|
||||
key := req.PluginName + ":" + req.RuleName
|
||||
c.pluginMu.Lock()
|
||||
if existingHandler, ok := c.runningPlugins[key]; ok {
|
||||
log.Printf("[Client] Stopping existing plugin %s before reinstall", key)
|
||||
if err := existingHandler.Stop(); err != nil {
|
||||
log.Printf("[Client] Stop existing plugin error: %v", err)
|
||||
}
|
||||
delete(c.runningPlugins, key)
|
||||
}
|
||||
c.pluginMu.Unlock()
|
||||
|
||||
// 验证官方签名
|
||||
if err := c.verifyJSPluginSignature(req.PluginName, req.Source, req.Signature); err != nil {
|
||||
log.Printf("[Client] JS plugin %s signature verification failed: %v", req.PluginName, err)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
"github.com/gotunnel/pkg/plugin/builtin"
|
||||
)
|
||||
|
||||
// Manager 服务端 plugin 管理器
|
||||
@@ -22,33 +20,9 @@ func NewManager() (*Manager, error) {
|
||||
registry: registry,
|
||||
}
|
||||
|
||||
if err := m.registerBuiltins(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// registerBuiltins 注册内置 plugins
|
||||
func (m *Manager) registerBuiltins() error {
|
||||
if err := m.registry.RegisterAllServer(builtin.GetServerPlugins()); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, h := range builtin.GetClientPlugins() {
|
||||
if err := m.registry.RegisterClient(h); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Printf("[Plugin] Registered %d server, %d client plugins",
|
||||
len(builtin.GetServerPlugins()), len(builtin.GetClientPlugins()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetServer 返回服务端插件
|
||||
func (m *Manager) GetServer(name string) (plugin.ServerPlugin, error) {
|
||||
return m.registry.GetServer(name)
|
||||
}
|
||||
|
||||
// ListPlugins 返回所有插件
|
||||
func (m *Manager) ListPlugins() []plugin.Info {
|
||||
return m.registry.List()
|
||||
|
||||
@@ -471,21 +471,7 @@ func (s *Server) acceptProxyConns(cs *ClientSession, ln net.Listener, rule proto
|
||||
func (s *Server) acceptProxyServerConns(cs *ClientSession, ln net.Listener, rule protocol.ProxyRule) {
|
||||
dialer := proxy.NewTunnelDialer(cs.Session)
|
||||
|
||||
// 优先使用插件系统
|
||||
if s.pluginRegistry != nil {
|
||||
if handler, err := s.pluginRegistry.GetServer(rule.Type); err == nil {
|
||||
handler.Init(rule.PluginConfig)
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go handler.HandleConn(conn, dialer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 回退到内置 proxy 实现
|
||||
// 使用内置 proxy 实现
|
||||
proxyServer := proxy.NewServer(rule.Type, dialer)
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
@@ -771,14 +757,9 @@ func (s *Server) InstallPluginsToClient(clientID string, plugins []string) error
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// 获取插件信息
|
||||
version := "1.0.0"
|
||||
if handler, err := s.pluginRegistry.GetServer(pluginName); err == nil && handler != nil {
|
||||
version = handler.Metadata().Version
|
||||
}
|
||||
client.Plugins = append(client.Plugins, db.ClientPlugin{
|
||||
Name: pluginName,
|
||||
Version: version,
|
||||
Version: "1.0.0",
|
||||
Enabled: true,
|
||||
})
|
||||
}
|
||||
@@ -893,7 +874,7 @@ func (s *Server) GetPluginConfigSchema(name string) ([]router.ConfigField, error
|
||||
return nil, fmt.Errorf("plugin registry not initialized")
|
||||
}
|
||||
|
||||
handler, err := s.pluginRegistry.GetServer(name)
|
||||
handler, err := s.pluginRegistry.GetClient(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plugin %s not found", name)
|
||||
}
|
||||
@@ -1302,37 +1283,64 @@ func (s *Server) sendClientPluginStop(session *yamux.Session, pluginName, ruleNa
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestartClientPlugin 重启客户端插件
|
||||
// RestartClientPlugin 重启客户端 JS 插件
|
||||
func (s *Server) RestartClientPlugin(clientID, pluginName, ruleName string) error {
|
||||
s.mu.RLock()
|
||||
cs, ok := s.clients[clientID]
|
||||
_, ok := s.clients[clientID]
|
||||
s.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("client %s not found or not online", clientID)
|
||||
}
|
||||
|
||||
// 查找规则(用于内置插件)
|
||||
var rule *protocol.ProxyRule
|
||||
for _, r := range cs.Rules {
|
||||
if r.Name == ruleName && r.Type == pluginName {
|
||||
rule = &r
|
||||
// 重新发送完整的安装请求来重启 JS 插件
|
||||
return s.reinstallJSPlugin(clientID, pluginName, ruleName)
|
||||
}
|
||||
|
||||
// reinstallJSPlugin 重新安装 JS 插件(用于重启)
|
||||
func (s *Server) reinstallJSPlugin(clientID, pluginName, ruleName string) error {
|
||||
// 从数据库获取插件信息
|
||||
if s.jsPluginStore == nil {
|
||||
return fmt.Errorf("JS plugin store not configured")
|
||||
}
|
||||
|
||||
jsPlugin, err := s.jsPluginStore.GetJSPlugin(pluginName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("plugin %s not found: %w", pluginName, err)
|
||||
}
|
||||
|
||||
// 获取客户端的插件配置
|
||||
client, err := s.clientStore.GetClient(clientID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client not found: %w", err)
|
||||
}
|
||||
|
||||
// 合并配置
|
||||
config := jsPlugin.Config
|
||||
if config == nil {
|
||||
config = make(map[string]string)
|
||||
}
|
||||
for _, cp := range client.Plugins {
|
||||
if cp.Name == pluginName {
|
||||
for k, v := range cp.Config {
|
||||
config[k] = v
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 先停止
|
||||
if err := s.sendClientPluginStop(cs.Session, pluginName, ruleName); err != nil {
|
||||
log.Printf("[Server] Stop plugin warning: %v", err)
|
||||
log.Printf("[Server] Reinstalling JS plugin %s to client %s", pluginName, clientID)
|
||||
|
||||
req := router.JSPluginInstallRequest{
|
||||
PluginName: pluginName,
|
||||
Source: jsPlugin.Source,
|
||||
Signature: jsPlugin.Signature,
|
||||
RuleName: ruleName,
|
||||
Config: config,
|
||||
AutoStart: true, // 重启时总是自动启动
|
||||
}
|
||||
|
||||
// 如果找到规则,使用规则重启(内置插件)
|
||||
if rule != nil {
|
||||
return s.sendClientPluginStart(cs.Session, *rule)
|
||||
}
|
||||
|
||||
// 否则发送 JS 插件重启命令
|
||||
return s.sendJSPluginRestart(cs.Session, pluginName, ruleName)
|
||||
return s.InstallJSPluginToClient(clientID, req)
|
||||
}
|
||||
|
||||
// sendJSPluginRestart 发送 JS 插件重启命令
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package builtin
|
||||
|
||||
import "github.com/gotunnel/pkg/plugin"
|
||||
|
||||
var (
|
||||
serverPlugins []plugin.ServerPlugin
|
||||
clientPlugins []plugin.ClientPlugin
|
||||
)
|
||||
|
||||
// RegisterServer 注册服务端插件
|
||||
func RegisterServer(handler plugin.ServerPlugin) {
|
||||
serverPlugins = append(serverPlugins, handler)
|
||||
}
|
||||
|
||||
// RegisterClient 注册客户端插件
|
||||
func RegisterClient(handler plugin.ClientPlugin) {
|
||||
clientPlugins = append(clientPlugins, handler)
|
||||
}
|
||||
|
||||
// GetServerPlugins 返回所有服务端插件
|
||||
func GetServerPlugins() []plugin.ServerPlugin {
|
||||
return serverPlugins
|
||||
}
|
||||
|
||||
// GetClientPlugins 返回所有客户端插件
|
||||
func GetClientPlugins() []plugin.ClientPlugin {
|
||||
return clientPlugins
|
||||
}
|
||||
@@ -6,9 +6,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Registry 管理可用的 plugins
|
||||
// Registry 管理可用的 plugins (仅客户端插件)
|
||||
type Registry struct {
|
||||
serverPlugins map[string]ServerPlugin // 服务端插件
|
||||
clientPlugins map[string]ClientPlugin // 客户端插件
|
||||
enabled map[string]bool // 启用状态
|
||||
mu sync.RWMutex
|
||||
@@ -17,31 +16,11 @@ type Registry struct {
|
||||
// NewRegistry 创建 plugin 注册表
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
serverPlugins: make(map[string]ServerPlugin),
|
||||
clientPlugins: make(map[string]ClientPlugin),
|
||||
enabled: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterServer 注册服务端插件
|
||||
func (r *Registry) RegisterServer(handler ServerPlugin) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
meta := handler.Metadata()
|
||||
if meta.Name == "" {
|
||||
return fmt.Errorf("plugin name cannot be empty")
|
||||
}
|
||||
|
||||
if _, exists := r.serverPlugins[meta.Name]; exists {
|
||||
return fmt.Errorf("plugin %s already registered", meta.Name)
|
||||
}
|
||||
|
||||
r.serverPlugins[meta.Name] = handler
|
||||
r.enabled[meta.Name] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterClient 注册客户端插件
|
||||
func (r *Registry) RegisterClient(handler ClientPlugin) error {
|
||||
r.mu.Lock()
|
||||
@@ -61,20 +40,6 @@ func (r *Registry) RegisterClient(handler ClientPlugin) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetServer 返回服务端插件
|
||||
func (r *Registry) GetServer(name string) (ServerPlugin, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
if handler, ok := r.serverPlugins[name]; ok {
|
||||
if !r.enabled[name] {
|
||||
return nil, fmt.Errorf("plugin %s is disabled", name)
|
||||
}
|
||||
return handler, nil
|
||||
}
|
||||
return nil, fmt.Errorf("plugin %s not found", name)
|
||||
}
|
||||
|
||||
// GetClient 返回客户端插件
|
||||
func (r *Registry) GetClient(name string) (ClientPlugin, error) {
|
||||
r.mu.RLock()
|
||||
@@ -96,14 +61,6 @@ func (r *Registry) List() []Info {
|
||||
|
||||
var plugins []Info
|
||||
|
||||
for name, handler := range r.serverPlugins {
|
||||
plugins = append(plugins, Info{
|
||||
Metadata: handler.Metadata(),
|
||||
Loaded: true,
|
||||
Enabled: r.enabled[name],
|
||||
})
|
||||
}
|
||||
|
||||
for name, handler := range r.clientPlugins {
|
||||
plugins = append(plugins, Info{
|
||||
Metadata: handler.Metadata(),
|
||||
@@ -120,9 +77,8 @@ func (r *Registry) Has(name string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
_, ok1 := r.serverPlugins[name]
|
||||
_, ok2 := r.clientPlugins[name]
|
||||
return ok1 || ok2
|
||||
_, ok := r.clientPlugins[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Close 关闭所有 plugins
|
||||
@@ -131,11 +87,6 @@ func (r *Registry) Close(ctx context.Context) error {
|
||||
defer r.mu.Unlock()
|
||||
|
||||
var lastErr error
|
||||
for name, handler := range r.serverPlugins {
|
||||
if err := handler.Close(); err != nil {
|
||||
lastErr = fmt.Errorf("failed to close plugin %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
for name, handler := range r.clientPlugins {
|
||||
if err := handler.Stop(); err != nil {
|
||||
lastErr = fmt.Errorf("failed to stop client plugin %s: %w", name, err)
|
||||
@@ -171,9 +122,8 @@ func (r *Registry) Disable(name string) error {
|
||||
|
||||
// has 内部检查(无锁)
|
||||
func (r *Registry) has(name string) bool {
|
||||
_, ok1 := r.serverPlugins[name]
|
||||
_, ok2 := r.clientPlugins[name]
|
||||
return ok1 || ok2
|
||||
_, ok := r.clientPlugins[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsEnabled 检查插件是否启用
|
||||
@@ -182,13 +132,3 @@ func (r *Registry) IsEnabled(name string) bool {
|
||||
defer r.mu.RUnlock()
|
||||
return r.enabled[name]
|
||||
}
|
||||
|
||||
// RegisterAllServer 批量注册服务端插件
|
||||
func (r *Registry) RegisterAllServer(handlers []ServerPlugin) error {
|
||||
for _, handler := range handlers {
|
||||
if err := r.RegisterServer(handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
type Side string
|
||||
|
||||
const (
|
||||
SideServer Side = "server"
|
||||
SideClient Side = "client"
|
||||
)
|
||||
|
||||
@@ -100,15 +99,6 @@ type Dialer interface {
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// ServerPlugin 服务端插件接口
|
||||
// 运行在服务端,处理外部连接并通过隧道转发到客户端
|
||||
type ServerPlugin interface {
|
||||
Metadata() Metadata
|
||||
Init(config map[string]string) error
|
||||
HandleConn(conn net.Conn, dialer Dialer) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// ClientPlugin 客户端插件接口
|
||||
// 运行在客户端,提供本地服务
|
||||
type ClientPlugin interface {
|
||||
|
||||
Reference in New Issue
Block a user