neko/server/internal/plugins/manager.go

184 lines
4.2 KiB
Go
Raw Permalink Normal View History

2022-04-16 07:28:00 +12:00
package plugins
import (
"fmt"
"os"
"path/filepath"
"plugin"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/demodesk/neko/internal/config"
2024-06-10 05:05:21 +12:00
"github.com/demodesk/neko/internal/plugins/chat"
2024-04-20 06:22:43 +12:00
"github.com/demodesk/neko/internal/plugins/filetransfer"
"github.com/demodesk/neko/pkg/types"
2022-04-16 07:28:00 +12:00
)
2022-04-19 22:14:59 +12:00
type ManagerCtx struct {
2022-04-16 07:28:00 +12:00
logger zerolog.Logger
config *config.Plugins
2022-05-03 23:17:04 +12:00
plugins dependiencies
2022-04-16 07:28:00 +12:00
}
2022-04-19 22:14:59 +12:00
func New(config *config.Plugins) *ManagerCtx {
manager := &ManagerCtx{
2022-05-03 23:17:04 +12:00
logger: log.With().Str("module", "plugins").Logger(),
config: config,
2022-05-03 23:17:04 +12:00
plugins: dependiencies{
deps: make(map[string]*dependency),
},
2022-04-16 07:28:00 +12:00
}
2022-05-03 23:17:04 +12:00
manager.plugins.logger = manager.logger
2022-04-16 07:28:00 +12:00
if config.Enabled {
err := manager.loadDir(config.Dir)
// only log error if plugin is not required
if err != nil && config.Required {
manager.logger.Fatal().Err(err).Msg("error loading plugins")
}
manager.logger.Info().Msgf("loading finished, total %d plugins", manager.plugins.len())
2022-04-16 07:28:00 +12:00
}
2024-04-20 06:22:43 +12:00
// add built-in plugins
manager.plugins.addPlugin(filetransfer.NewPlugin())
2024-06-10 05:05:21 +12:00
manager.plugins.addPlugin(chat.NewPlugin())
2024-04-20 06:22:43 +12:00
2022-04-16 07:28:00 +12:00
return manager
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) loadDir(dir string) error {
2022-04-16 07:28:00 +12:00
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
err = manager.load(path)
// return error if plugin is required
if err != nil && manager.config.Required {
return err
}
// otherwise only log error if plugin is not required
2022-04-16 07:28:00 +12:00
manager.logger.Err(err).Str("plugin", path).Msg("loading a plugin")
return nil
})
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) load(path string) error {
2022-04-16 07:28:00 +12:00
pl, err := plugin.Open(path)
if err != nil {
return err
}
sym, err := pl.Lookup("Plugin")
if err != nil {
return err
}
p, ok := sym.(types.Plugin)
if !ok {
return fmt.Errorf("not a valid plugin")
}
2022-05-03 23:17:04 +12:00
if err = manager.plugins.addPlugin(p); err != nil {
return fmt.Errorf("failed to add plugin: %w", err)
2022-04-19 22:14:59 +12:00
}
2022-04-16 07:28:00 +12:00
return nil
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) InitConfigs(cmd *cobra.Command) {
_ = manager.plugins.forEach(func(d *dependency) error {
if err := d.plugin.Config().Init(cmd); err != nil {
log.Err(err).Str("plugin", d.plugin.Name()).Msg("unable to initialize configuration")
2022-04-16 07:28:00 +12:00
}
2022-05-03 23:17:04 +12:00
return nil
})
2022-04-16 07:28:00 +12:00
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) SetConfigs() {
_ = manager.plugins.forEach(func(d *dependency) error {
d.plugin.Config().Set()
2022-05-03 23:17:04 +12:00
return nil
})
2022-04-16 07:28:00 +12:00
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) Start(
2022-04-16 07:28:00 +12:00
sessionManager types.SessionManager,
webSocketManager types.WebSocketManager,
apiManager types.ApiManager,
) {
err := manager.plugins.start(types.PluginManagers{
2022-05-03 23:17:04 +12:00
SessionManager: sessionManager,
WebSocketManager: webSocketManager,
ApiManager: apiManager,
LoadServiceFromPlugin: manager.LookupService,
})
if err != nil {
if manager.config.Required {
manager.logger.Fatal().Err(err).Msg("failed to start plugins, exiting...")
} else {
manager.logger.Err(err).Msg("failed to start plugins, skipping...")
}
}
2022-04-16 07:28:00 +12:00
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) Shutdown() error {
_ = manager.plugins.forEach(func(d *dependency) error {
err := d.plugin.Shutdown()
manager.logger.Err(err).Str("plugin", d.plugin.Name()).Msg("plugin shutdown")
2022-05-03 23:17:04 +12:00
return nil
})
2022-04-16 07:28:00 +12:00
return nil
}
2022-04-19 22:14:59 +12:00
func (manager *ManagerCtx) LookupService(pluginName string) (any, error) {
2022-05-03 23:17:04 +12:00
plug, ok := manager.plugins.findPlugin(pluginName)
2022-04-19 22:14:59 +12:00
if !ok {
return nil, fmt.Errorf("plugin '%s' not found", pluginName)
}
2022-05-03 23:17:04 +12:00
expPlug, ok := plug.plugin.(types.ExposablePlugin)
2022-04-19 22:14:59 +12:00
if !ok {
return nil, fmt.Errorf("plugin '%s' is not exposable", pluginName)
}
return expPlug.ExposeService(), nil
}
func (manager *ManagerCtx) Metadata() []types.PluginMetadata {
var plugins []types.PluginMetadata
_ = manager.plugins.forEach(func(d *dependency) error {
dependsOn := make([]string, 0)
deps, isDependalbe := d.plugin.(types.DependablePlugin)
if isDependalbe {
dependsOn = deps.DependsOn()
}
_, isExposable := d.plugin.(types.ExposablePlugin)
plugins = append(plugins, types.PluginMetadata{
Name: d.plugin.Name(),
IsDependable: isDependalbe,
IsExposable: isExposable,
DependsOn: dependsOn,
})
return nil
})
return plugins
}