diff --git a/cmd/serve.go b/cmd/serve.go index 4619946e..1ab011d3 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -59,7 +59,7 @@ type serve struct { member *member.MemberManagerCtx session *session.SessionManagerCtx webSocket *websocket.WebSocketManagerCtx - plugins *plugins.PluginsManagerCtx + plugins *plugins.ManagerCtx api *api.ApiManagerCtx http *http.HttpManagerCtx } diff --git a/internal/plugins/manager.go b/internal/plugins/manager.go index a2456353..776363bc 100644 --- a/internal/plugins/manager.go +++ b/internal/plugins/manager.go @@ -14,13 +14,13 @@ import ( "gitlab.com/demodesk/neko/server/pkg/types" ) -type PluginsManagerCtx struct { +type ManagerCtx struct { logger zerolog.Logger plugins map[string]types.Plugin } -func New(config *config.Plugins) *PluginsManagerCtx { - manager := &PluginsManagerCtx{ +func New(config *config.Plugins) *ManagerCtx { + manager := &ManagerCtx{ logger: log.With().Str("module", "plugins").Logger(), plugins: map[string]types.Plugin{}, } @@ -33,7 +33,7 @@ func New(config *config.Plugins) *PluginsManagerCtx { return manager } -func (manager *PluginsManagerCtx) loadDir(dir string) error { +func (manager *ManagerCtx) loadDir(dir string) error { return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -50,7 +50,7 @@ func (manager *PluginsManagerCtx) loadDir(dir string) error { }) } -func (manager *PluginsManagerCtx) load(path string) error { +func (manager *ManagerCtx) load(path string) error { pl, err := plugin.Open(path) if err != nil { return err @@ -66,11 +66,16 @@ func (manager *PluginsManagerCtx) load(path string) error { return fmt.Errorf("not a valid plugin") } - manager.plugins[path] = p + _, ok = manager.plugins[p.Name()] + if ok { + return fmt.Errorf("plugin '%s' already exists", p.Name()) + } + + manager.plugins[p.Name()] = p return nil } -func (manager *PluginsManagerCtx) InitConfigs(cmd *cobra.Command) { +func (manager *ManagerCtx) InitConfigs(cmd *cobra.Command) { for path, plug := range manager.plugins { if err := plug.Config().Init(cmd); err != nil { log.Err(err).Str("plugin", path).Msg("unable to initialize configuration") @@ -78,27 +83,28 @@ func (manager *PluginsManagerCtx) InitConfigs(cmd *cobra.Command) { } } -func (manager *PluginsManagerCtx) SetConfigs() { +func (manager *ManagerCtx) SetConfigs() { for _, plug := range manager.plugins { plug.Config().Set() } } -func (manager *PluginsManagerCtx) Start( +func (manager *ManagerCtx) Start( sessionManager types.SessionManager, webSocketManager types.WebSocketManager, apiManager types.ApiManager, ) { for _, plug := range manager.plugins { plug.Start(types.PluginManagers{ - SessionManager: sessionManager, - WebSocketManager: webSocketManager, - ApiManager: apiManager, + SessionManager: sessionManager, + WebSocketManager: webSocketManager, + ApiManager: apiManager, + LoadServiceFromPlugin: manager.LookupService, }) } } -func (manager *PluginsManagerCtx) Shutdown() error { +func (manager *ManagerCtx) Shutdown() error { for path, plug := range manager.plugins { err := plug.Shutdown() manager.logger.Err(err).Str("plugin", path).Msg("plugin shutdown") @@ -106,3 +112,17 @@ func (manager *PluginsManagerCtx) Shutdown() error { return nil } + +func (manager *ManagerCtx) LookupService(pluginName string) (any, error) { + plug, ok := manager.plugins[pluginName] + if !ok { + return nil, fmt.Errorf("plugin '%s' not found", pluginName) + } + + expPlug, ok := plug.(types.ExposablePlugin) + if !ok { + return nil, fmt.Errorf("plugin '%s' is not exposable", pluginName) + } + + return expPlug.ExposeService(), nil +} diff --git a/pkg/types/plugins.go b/pkg/types/plugins.go index 13c8324e..f6014e0b 100644 --- a/pkg/types/plugins.go +++ b/pkg/types/plugins.go @@ -1,22 +1,51 @@ package types import ( + "errors" + "github.com/spf13/cobra" ) type Plugin interface { + Name() string Config() PluginConfig Start(PluginManagers) Shutdown() error } +type ExposablePlugin interface { + Plugin + ExposeService() any +} + type PluginConfig interface { Init(cmd *cobra.Command) error Set() } type PluginManagers struct { - SessionManager SessionManager - WebSocketManager WebSocketManager - ApiManager ApiManager + SessionManager SessionManager + WebSocketManager WebSocketManager + ApiManager ApiManager + LoadServiceFromPlugin func(string) (any, error) +} + +func (p *PluginManagers) Validate() error { + if p.SessionManager == nil { + return errors.New("SessionManager is nil") + } + + if p.WebSocketManager == nil { + return errors.New("WebSocketManager is nil") + } + + if p.ApiManager == nil { + return errors.New("ApiManager is nil") + } + + if p.LoadServiceFromPlugin == nil { + return errors.New("LoadServiceFromPlugin is nil") + } + + return nil }