diff --git a/examples/plugin/example/main.go b/examples/plugin/example/main.go new file mode 100644 index 0000000..09fe2ca --- /dev/null +++ b/examples/plugin/example/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "github.com/darkit/goproxy/examples/plugin" +) + +func main() { + // 创建上下文 + ctx := context.Background() + + // 获取插件目录 + pluginsDir := filepath.Join("..", "plugins") + if _, err := os.Stat(pluginsDir); os.IsNotExist(err) { + if err := os.MkdirAll(pluginsDir, 0o755); err != nil { + fmt.Printf("创建插件目录失败: %v\n", err) + os.Exit(1) + } + } + + // 创建插件管理器 + pm := plugin.NewPluginManager(pluginsDir) + + // 检查是否支持动态加载插件 + if pm.IsDynamicLoadingSupported() { + fmt.Println("当前系统支持动态加载插件") + } else { + fmt.Println("当前系统不支持动态加载插件,将使用静态插件") + + // 在不支持动态加载的平台上注册内置插件 + // 这里需要导入并注册所有内置插件 + // 例如:pm.RegisterPlugin(logger.Plugin) + } + + // 加载插件 + if err := pm.LoadPlugins(); err != nil { + fmt.Printf("加载插件失败: %v\n", err) + os.Exit(1) + } + + // 初始化所有插件 + if err := pm.InitPlugins(ctx); err != nil { + fmt.Printf("初始化插件失败: %v\n", err) + os.Exit(1) + } + + // 启动所有插件 + if err := pm.StartPlugins(ctx); err != nil { + fmt.Printf("启动插件失败: %v\n", err) + os.Exit(1) + } + + // 打印所有插件信息 + fmt.Println("\n=== 所有已加载的插件 ===") + for _, info := range pm.GetPluginInfos() { + fmt.Printf("插件名称: %s\n", info.Name) + fmt.Printf("版本: %s\n", info.Version) + fmt.Printf("描述: %s\n", info.Description) + fmt.Printf("作者: %s\n", info.Author) + fmt.Printf("类型: %s\n", info.Type) + fmt.Printf("启用状态: %v\n", info.Enabled) + fmt.Println("---") + } + + // 按类型获取插件 + fmt.Println("\n=== 按类型获取插件 ===") + + // 获取所有存储类型插件 + storagePlugins := pm.GetPluginsByType(plugin.PluginTypeStorage) + fmt.Printf("存储插件数量: %d\n", len(storagePlugins)) + for _, p := range storagePlugins { + fmt.Printf("存储插件: %s (版本: %s)\n", p.Name(), p.Version()) + } + + // 获取所有工具类型插件 + utilsPlugins := pm.GetPluginsByType(plugin.PluginTypeUtils) + fmt.Printf("工具插件数量: %d\n", len(utilsPlugins)) + for _, p := range utilsPlugins { + fmt.Printf("工具插件: %s (版本: %s)\n", p.Name(), p.Version()) + } + + // 在生命周期的不同阶段按类型初始化和启动插件 + fmt.Println("\n=== 按类型初始化和启动插件 ===") + + // 例如,在应用程序启动阶段只初始化工具插件 + if err := pm.InitPluginsByType(ctx, plugin.PluginTypeUtils); err != nil { + fmt.Printf("初始化工具插件失败: %v\n", err) + } else { + fmt.Println("工具插件初始化成功") + } + + // 在需要存储功能时初始化存储插件 + if err := pm.InitPluginsByType(ctx, plugin.PluginTypeStorage); err != nil { + fmt.Printf("初始化存储插件失败: %v\n", err) + } else { + fmt.Println("存储插件初始化成功") + } + + // 使用特定类型的插件示例 + fmt.Println("\n=== 使用特定类型的插件示例 ===") + fmt.Println("注意:这部分代码在真实环境中需要使用类型断言或接口来处理") + fmt.Println("在实际应用中,应该为每种类型的插件定义专门的接口") + + // 按类型停止插件 + fmt.Println("\n=== 按类型停止插件 ===") + + // 首先停止存储插件 + if err := pm.StopPluginsByType(ctx, plugin.PluginTypeStorage); err != nil { + fmt.Printf("停止存储插件失败: %v\n", err) + } else { + fmt.Println("存储插件已停止") + } + + // 然后停止工具插件 + if err := pm.StopPluginsByType(ctx, plugin.PluginTypeUtils); err != nil { + fmt.Printf("停止工具插件失败: %v\n", err) + } else { + fmt.Println("工具插件已停止") + } + + // 确保所有剩余插件都被停止 + if err := pm.StopPlugins(ctx); err != nil { + fmt.Printf("停止剩余插件失败: %v\n", err) + os.Exit(1) + } + + fmt.Println("\n所有插件已停止") +} diff --git a/examples/plugin/interface.go b/examples/plugin/interface.go index 1fa5079..3dcc5e9 100644 --- a/examples/plugin/interface.go +++ b/examples/plugin/interface.go @@ -1,5 +1,19 @@ package plugin +// PluginType 插件类型 +type PluginType string + +// 预定义的插件类型 +const ( + PluginTypeGeneral PluginType = "general" // 通用插件 + PluginTypeStorage PluginType = "storage" // 存储插件 + PluginTypeSecurity PluginType = "security" // 安全插件 + PluginTypeUI PluginType = "ui" // 用户界面插件 + PluginTypeNetwork PluginType = "network" // 网络插件 + PluginTypeUtils PluginType = "utils" // 工具插件 + // 可以根据需求添加更多插件类型 +) + // IPlugin 插件接口 // 这个文件定义了所有插件必须实现的接口 // 注意:这个文件应该与实际插件代码一起编译 @@ -12,6 +26,8 @@ type IPlugin interface { Description() string // Author 插件作者 Author() string + // Type 插件类型 + Type() PluginType // Init 初始化插件 Init(config map[string]interface{}) error // Start 启动插件 @@ -31,16 +47,18 @@ type BasePlugin struct { version string description string author string + pluginType PluginType enabled bool } // NewBasePlugin 创建一个基本插件 -func NewBasePlugin(name, version, description, author string) *BasePlugin { +func NewBasePlugin(name, version, description, author string, pluginType PluginType) *BasePlugin { return &BasePlugin{ name: name, version: version, description: description, author: author, + pluginType: pluginType, enabled: true, } } @@ -65,6 +83,11 @@ func (p *BasePlugin) Author() string { return p.author } +// Type 获取插件类型 +func (p *BasePlugin) Type() PluginType { + return p.pluginType +} + // IsEnabled 插件是否启用 func (p *BasePlugin) IsEnabled() bool { return p.enabled diff --git a/examples/plugin/manager/plugin_manager.go b/examples/plugin/manager/plugin_manager.go index 82f983d..7acbd1a 100644 --- a/examples/plugin/manager/plugin_manager.go +++ b/examples/plugin/manager/plugin_manager.go @@ -17,6 +17,7 @@ type LogPlugin struct { author string enabled bool level string + pluginType plugin.PluginType } // 创建内置日志插件 @@ -26,6 +27,7 @@ func NewLogPlugin() *LogPlugin { version: "1.0.0", description: "内置日志插件,在不支持动态加载插件的平台上使用", author: "开发者", + pluginType: plugin.PluginTypeUtils, // 设置为工具类插件 enabled: true, level: "info", } @@ -48,6 +50,11 @@ func (p *LogPlugin) Author() string { return p.author } +// Type 实现插件类型方法 +func (p *LogPlugin) Type() plugin.PluginType { + return p.pluginType +} + func (p *LogPlugin) IsEnabled() bool { return p.enabled } @@ -130,8 +137,8 @@ func main() { if !info.Enabled { status = "禁用" } - fmt.Printf("[%d] %s (v%s) - %s [%s]\n作者: %s\n", - i, info.Name, info.Version, info.Description, status, info.Author) + fmt.Printf("[%d] %s (v%s) - %s [%s]\n作者: %s\n类型: %s\n", + i, info.Name, info.Version, info.Description, status, info.Author, info.Type) // 打印插件配置 if len(info.Config) > 0 { @@ -159,6 +166,17 @@ func main() { } } + // 按类型获取和使用插件 + fmt.Println("\n按类型获取插件:") + utilsPlugins := pm.GetPluginsByType(plugin.PluginTypeUtils) + fmt.Printf("找到 %d 个工具类插件\n", len(utilsPlugins)) + for _, p := range utilsPlugins { + fmt.Printf("工具类插件: %s\n", p.Name()) + if logPlugin, ok := p.(*LogPlugin); ok { + logPlugin.Log("通过类型获取到的日志插件记录消息") + } + } + // 停止插件 fmt.Println("\n正在停止插件...") if err := pm.StopPlugins(ctx); err != nil { diff --git a/examples/plugin/plugin.go b/examples/plugin/plugin.go index 6c366bf..fccb6f0 100644 --- a/examples/plugin/plugin.go +++ b/examples/plugin/plugin.go @@ -23,6 +23,8 @@ type Plugin interface { Description() string // Author 插件作者 Author() string + // Type 插件类型 + Type() PluginType // Init 初始化插件 Init(ctx context.Context, config map[string]interface{}) error // Start 启动插件 @@ -41,6 +43,7 @@ type PluginInfo struct { Version string `json:"version"` Description string `json:"description"` Author string `json:"author"` + Type PluginType `json:"type"` Enabled bool `json:"enabled"` Config map[string]interface{} `json:"config,omitempty"` } @@ -49,6 +52,7 @@ type PluginInfo struct { type PluginManager struct { pluginsDir string plugins map[string]Plugin + pluginsByType map[PluginType]map[string]Plugin configs map[string]map[string]interface{} mu sync.RWMutex dynamicLoadingSupported bool // 是否支持动态加载插件 @@ -62,6 +66,7 @@ func NewPluginManager(pluginsDir string) *PluginManager { return &PluginManager{ pluginsDir: pluginsDir, plugins: make(map[string]Plugin), + pluginsByType: make(map[PluginType]map[string]Plugin), configs: make(map[string]map[string]interface{}), dynamicLoadingSupported: dynamicLoadingSupported, } @@ -156,6 +161,7 @@ func (pm *PluginManager) savePluginConfigs() error { config = make(map[string]interface{}) } config["enabled"] = plugin.IsEnabled() + config["type"] = string(plugin.Type()) // 保存插件类型 configs[name] = config } pm.mu.RUnlock() @@ -213,10 +219,25 @@ func (pm *PluginManager) loadPlugin(path string) error { plugin.SetEnabled(true) // 默认启用 } - pm.plugins[plugin.Name()] = plugin + // 注册插件 + pm.registerPlugin(plugin) return nil } +// registerPlugin 注册插件到插件管理器 +func (pm *PluginManager) registerPlugin(plugin Plugin) { + pluginType := plugin.Type() + + // 将插件添加到按名称索引的映射 + pm.plugins[plugin.Name()] = plugin + + // 将插件添加到按类型索引的映射 + if pm.pluginsByType[pluginType] == nil { + pm.pluginsByType[pluginType] = make(map[string]Plugin) + } + pm.pluginsByType[pluginType][plugin.Name()] = plugin +} + // RegisterPlugin 注册内置插件 // 用于在不支持动态加载的平台上注册插件 func (pm *PluginManager) RegisterPlugin(plugin Plugin) error { @@ -238,7 +259,8 @@ func (pm *PluginManager) RegisterPlugin(plugin Plugin) error { plugin.SetEnabled(true) // 默认启用 } - pm.plugins[plugin.Name()] = plugin + // 注册插件 + pm.registerPlugin(plugin) return nil } @@ -251,6 +273,36 @@ func (pm *PluginManager) GetPlugin(name string) (Plugin, bool) { return plugin, exists } +// GetPluginsByType 按类型获取插件 +func (pm *PluginManager) GetPluginsByType(pluginType PluginType) []Plugin { + pm.mu.RLock() + defer pm.mu.RUnlock() + + plugins := make([]Plugin, 0) + if typePlugins, exists := pm.pluginsByType[pluginType]; exists { + for _, plugin := range typePlugins { + if plugin.IsEnabled() { + plugins = append(plugins, plugin) + } + } + } + return plugins +} + +// GetAllPluginsByType 获取所有指定类型的插件,无论是否启用 +func (pm *PluginManager) GetAllPluginsByType(pluginType PluginType) []Plugin { + pm.mu.RLock() + defer pm.mu.RUnlock() + + plugins := make([]Plugin, 0) + if typePlugins, exists := pm.pluginsByType[pluginType]; exists { + for _, plugin := range typePlugins { + plugins = append(plugins, plugin) + } + } + return plugins +} + // GetAllPlugins 获取所有插件 func (pm *PluginManager) GetAllPlugins() []Plugin { pm.mu.RLock() @@ -275,6 +327,7 @@ func (pm *PluginManager) GetPluginInfos() []PluginInfo { Version: plugin.Version(), Description: plugin.Description(), Author: plugin.Author(), + Type: plugin.Type(), Enabled: plugin.IsEnabled(), Config: pm.configs[name], } @@ -283,6 +336,29 @@ func (pm *PluginManager) GetPluginInfos() []PluginInfo { return infos } +// GetPluginInfosByType 按类型获取插件信息 +func (pm *PluginManager) GetPluginInfosByType(pluginType PluginType) []PluginInfo { + pm.mu.RLock() + defer pm.mu.RUnlock() + + infos := make([]PluginInfo, 0) + if typePlugins, exists := pm.pluginsByType[pluginType]; exists { + for name, plugin := range typePlugins { + info := PluginInfo{ + Name: plugin.Name(), + Version: plugin.Version(), + Description: plugin.Description(), + Author: plugin.Author(), + Type: plugin.Type(), + Enabled: plugin.IsEnabled(), + Config: pm.configs[name], + } + infos = append(infos, info) + } + } + return infos +} + // EnablePlugin 启用插件 func (pm *PluginManager) EnablePlugin(name string) error { pm.mu.Lock() @@ -345,6 +421,26 @@ func (pm *PluginManager) InitPlugins(ctx context.Context) error { return nil } +// InitPluginsByType 初始化指定类型的所有插件 +func (pm *PluginManager) InitPluginsByType(ctx context.Context, pluginType PluginType) error { + pm.mu.RLock() + defer pm.mu.RUnlock() + + if typePlugins, exists := pm.pluginsByType[pluginType]; exists { + for name, plugin := range typePlugins { + if !plugin.IsEnabled() { + continue + } + + config := pm.configs[name] + if err := plugin.Init(ctx, config); err != nil { + return fmt.Errorf("初始化插件 %s 失败: %v", name, err) + } + } + } + return nil +} + // StartPlugins 启动所有插件 func (pm *PluginManager) StartPlugins(ctx context.Context) error { pm.mu.RLock() @@ -362,6 +458,25 @@ func (pm *PluginManager) StartPlugins(ctx context.Context) error { return nil } +// StartPluginsByType 启动指定类型的所有插件 +func (pm *PluginManager) StartPluginsByType(ctx context.Context, pluginType PluginType) error { + pm.mu.RLock() + defer pm.mu.RUnlock() + + if typePlugins, exists := pm.pluginsByType[pluginType]; exists { + for name, plugin := range typePlugins { + if !plugin.IsEnabled() { + continue + } + + if err := plugin.Start(ctx); err != nil { + return fmt.Errorf("启动插件 %s 失败: %v", name, err) + } + } + } + return nil +} + // StopPlugins 停止所有插件 func (pm *PluginManager) StopPlugins(ctx context.Context) error { pm.mu.RLock() @@ -379,6 +494,25 @@ func (pm *PluginManager) StopPlugins(ctx context.Context) error { return nil } +// StopPluginsByType 停止指定类型的所有插件 +func (pm *PluginManager) StopPluginsByType(ctx context.Context, pluginType PluginType) error { + pm.mu.RLock() + defer pm.mu.RUnlock() + + if typePlugins, exists := pm.pluginsByType[pluginType]; exists { + for name, plugin := range typePlugins { + if !plugin.IsEnabled() { + continue + } + + if err := plugin.Stop(ctx); err != nil { + return fmt.Errorf("停止插件 %s 失败: %v", name, err) + } + } + } + return nil +} + // SetPluginConfig 设置插件配置 func (pm *PluginManager) SetPluginConfig(name string, config map[string]interface{}) error { pm.mu.Lock() diff --git a/examples/plugin/plugins/logger/logger_plugin.go b/examples/plugin/plugins/logger/logger_plugin.go index 99af421..42f017b 100644 --- a/examples/plugin/plugins/logger/logger_plugin.go +++ b/examples/plugin/plugins/logger/logger_plugin.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "log" "os" @@ -27,11 +28,12 @@ var Plugin = &LoggerPlugin{ "1.0.0", "简单的日志记录插件", "开发者", + plugin.PluginTypeUtils, // 设置插件类型为工具插件 ), } // Init 初始化插件 -func (p *LoggerPlugin) Init(config map[string]interface{}) error { +func (p *LoggerPlugin) Init(ctx context.Context, config map[string]interface{}) error { p.config = config // 获取日志文件路径 @@ -63,7 +65,7 @@ func (p *LoggerPlugin) Init(config map[string]interface{}) error { } // Start 启动插件 -func (p *LoggerPlugin) Start() error { +func (p *LoggerPlugin) Start(ctx context.Context) error { if p.logger == nil { return fmt.Errorf("插件未初始化") } @@ -74,7 +76,7 @@ func (p *LoggerPlugin) Start() error { } // Stop 停止插件 -func (p *LoggerPlugin) Stop() error { +func (p *LoggerPlugin) Stop(ctx context.Context) error { if p.logger != nil { p.logger.Println("日志插件正在停止") } diff --git a/examples/plugin/plugins/stats/stats_plugin.go b/examples/plugin/plugins/stats/stats_plugin.go index 3508eed..a51cfb7 100644 --- a/examples/plugin/plugins/stats/stats_plugin.go +++ b/examples/plugin/plugins/stats/stats_plugin.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "sync" "time" @@ -27,13 +28,14 @@ var Plugin = &StatsPlugin{ "1.0.0", "系统运行时统计插件", "开发者", + plugin.PluginTypeUtils, // 设置为工具类插件 ), stats: make(map[string]int64), tickerStop: make(chan bool), } // Init 初始化插件 -func (p *StatsPlugin) Init(config map[string]interface{}) error { +func (p *StatsPlugin) Init(ctx context.Context, config map[string]interface{}) error { p.config = config // 初始化统计指标 @@ -49,7 +51,7 @@ func (p *StatsPlugin) Init(config map[string]interface{}) error { } // Start 启动插件 -func (p *StatsPlugin) Start() error { +func (p *StatsPlugin) Start(ctx context.Context) error { p.startTime = time.Now() // 启动定时统计任务 @@ -70,6 +72,9 @@ func (p *StatsPlugin) Start() error { case <-p.tickerStop: p.ticker.Stop() return + case <-ctx.Done(): + p.ticker.Stop() + return } } }() @@ -79,7 +84,7 @@ func (p *StatsPlugin) Start() error { } // Stop 停止插件 -func (p *StatsPlugin) Stop() error { +func (p *StatsPlugin) Stop(ctx context.Context) error { if p.ticker != nil { p.tickerStop <- true } diff --git a/examples/plugin/plugins/storage/storage_plugin.go b/examples/plugin/plugins/storage/storage_plugin.go new file mode 100644 index 0000000..786a200 --- /dev/null +++ b/examples/plugin/plugins/storage/storage_plugin.go @@ -0,0 +1,138 @@ +package main + +import ( + "context" + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/darkit/goproxy/examples/plugin" +) + +// StoragePlugin 存储插件 +// 提供简单的文件存储功能 +type StoragePlugin struct { + *plugin.BasePlugin // 嵌入基本插件结构 + storageDir string // 存储目录 + config map[string]interface{} // 配置 + mu sync.RWMutex // 读写锁 +} + +// Plugin 导出的插件变量 +// 注意:变量名必须是Plugin,大小写敏感 +var Plugin = &StoragePlugin{ + BasePlugin: plugin.NewBasePlugin( + "StoragePlugin", + "1.0.0", + "简单的文件存储插件", + "开发者", + plugin.PluginTypeStorage, // 设置插件类型为存储插件 + ), +} + +// Init 初始化插件 +func (p *StoragePlugin) Init(ctx context.Context, config map[string]interface{}) error { + p.config = config + + // 获取存储目录路径 + storageDir, ok := config["storage_dir"].(string) + if !ok { + // 使用默认路径 + storageDir = "storage" + } + + // 确保存储目录存在 + if err := os.MkdirAll(storageDir, 0o755); err != nil { + return fmt.Errorf("创建存储目录失败: %v", err) + } + + p.storageDir = storageDir + fmt.Println("存储插件初始化完成,存储目录:", storageDir) + + return nil +} + +// Start 启动插件 +func (p *StoragePlugin) Start(ctx context.Context) error { + if p.storageDir == "" { + return fmt.Errorf("插件未初始化") + } + + fmt.Println("存储插件已启动") + return nil +} + +// Stop 停止插件 +func (p *StoragePlugin) Stop(ctx context.Context) error { + fmt.Println("存储插件已停止") + return nil +} + +// SaveFile 保存文件 +func (p *StoragePlugin) SaveFile(filename string, data []byte) error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.storageDir == "" { + return fmt.Errorf("插件未初始化") + } + + filePath := filepath.Join(p.storageDir, filename) + return os.WriteFile(filePath, data, 0o644) +} + +// LoadFile 加载文件 +func (p *StoragePlugin) LoadFile(filename string) ([]byte, error) { + p.mu.RLock() + defer p.mu.RUnlock() + + if p.storageDir == "" { + return nil, fmt.Errorf("插件未初始化") + } + + filePath := filepath.Join(p.storageDir, filename) + return os.ReadFile(filePath) +} + +// DeleteFile 删除文件 +func (p *StoragePlugin) DeleteFile(filename string) error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.storageDir == "" { + return fmt.Errorf("插件未初始化") + } + + filePath := filepath.Join(p.storageDir, filename) + return os.Remove(filePath) +} + +// ListFiles 列出所有文件 +func (p *StoragePlugin) ListFiles() ([]string, error) { + p.mu.RLock() + defer p.mu.RUnlock() + + if p.storageDir == "" { + return nil, fmt.Errorf("插件未初始化") + } + + var files []string + entries, err := os.ReadDir(p.storageDir) + if err != nil { + return nil, err + } + + for _, entry := range entries { + if !entry.IsDir() { + files = append(files, entry.Name()) + } + } + + return files, nil +} + +// main 函数是必须的,但不会被调用 +func main() { + // 不会被执行,仅用于编译插件 +} diff --git a/internal/proxy/delegate.go b/internal/proxy/delegate.go index 30d01de..91c6860 100644 --- a/internal/proxy/delegate.go +++ b/internal/proxy/delegate.go @@ -84,10 +84,14 @@ func (d *DefaultDelegate) ParentProxy(req *http.Request) (*url.URL, error) { } // WebSocketSendMessage websocket发送消息拦截 -func (h *DefaultDelegate) WebSocketSendMessage(ctx *Context, messageType *int, payload *[]byte) {} +func (h *DefaultDelegate) WebSocketSendMessage(ctx *Context, messageType *int, payload *[]byte) { + // 默认实现不做任何处理 +} // WebSocketReceiveMessage websocket接收消息拦截 -func (h *DefaultDelegate) WebSocketReceiveMessage(ctx *Context, messageType *int, payload *[]byte) {} +func (h *DefaultDelegate) WebSocketReceiveMessage(ctx *Context, messageType *int, payload *[]byte) { + // 默认实现不做任何处理 +} // ErrorLog 错误日志 func (d *DefaultDelegate) ErrorLog(err error) {