Skip to content

Commit

Permalink
Add and initialize plugins in the database
Browse files Browse the repository at this point in the history
  • Loading branch information
vctt94 committed Jul 17, 2024
1 parent 28d6625 commit 862bf2f
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 37 deletions.
55 changes: 48 additions & 7 deletions brclient/appstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2662,6 +2662,37 @@ func (as *appState) handleCmd(rawText string, args []string) {
}
}

// initializePlugins initialize plugins on startup
func (as *appState) initializePlugins() error {
// Retrieve all enabled plugins from the database.
enabledPlugins, err := as.c.GetEnabledPlugins()
if err != nil {
return fmt.Errorf("failed to retrieve enabled plugins: %w", err)
}

// Initialize each enabled plugin.
for _, plugin := range enabledPlugins {
pluginClient, err := client.NewPluginClient(as.ctx, plugin.ID, plugin.Config)
if err != nil {
as.diagMsg("Unable to start plugin: %s", plugin.Name)
}

as.pluginsClient[plugin.ID] = pluginClient
req := &grpctypes.PluginStartStreamRequest{
ClientId: as.c.PublicID().Bytes(),
}
err = as.pluginsClient[plugin.ID].InitPlugin(as.ctx, req, func(stream grpctypes.PluginService_InitClient) {
as.listenForAppUpdates(stream)
})
if err != nil {
as.diagMsg("Unable to init plugin: %s", plugin.Name)
}
as.diagMsg("Plugin %s Initialized", plugin.Name)
}

return nil
}

func (as *appState) initPlugin(cw *chatWindow, pid clientintf.PluginID, address string) {
req := &grpctypes.PluginStartStreamRequest{
ClientId: as.c.PublicID().Bytes(),
Expand All @@ -2672,31 +2703,37 @@ func (as *appState) initPlugin(cw *chatWindow, pid clientintf.PluginID, address
Log: as.logBknd.logger("Plugins"),
Address: address,
}
pluginClient, err := client.NewPluginClient(as.ctx, pluginClientCfg)
pluginClient, err := client.NewPluginClient(as.ctx, pid, pluginClientCfg)
if err != nil {
as.cwHelpMsg("Unable to start new plugin")
return
}
as.pluginsClient[pid] = pluginClient
}

err := as.pluginsClient[pid].InitPlugin(as.ctx, req, func(stream grpctypes.PluginService_InitClient) {
err := as.c.SavePluginInfo(as.pluginsClient[pid])
if err != nil {
// ignore if plugin already exists
if err != clientdb.ErrAlreadyExists {
as.cwHelpMsg("Unable to Save Plugin %q: %v",
cw.alias, err)
}
}

err = as.pluginsClient[pid].InitPlugin(as.ctx, req, func(stream grpctypes.PluginService_InitClient) {
as.listenForAppUpdates(stream)
})

if err != nil {
as.cwHelpMsg("Unable to init plugin %q: %v",
cw.alias, err)

// remove plugin in case try to start again
as.pluginsClient[pid] = nil
}

as.sendMsg(repaintActiveChat{})
}

func (as *appState) pluginVersion(cw *chatWindow, pid clientintf.PluginID) {
version, err := as.pluginsClient[pid].Version(as.ctx)
version, err := as.pluginsClient[pid].GetVersion(as.ctx)
if err != nil {
as.cwHelpMsg("Unable to get version: %v", err)
return
Expand All @@ -2708,7 +2745,7 @@ func (as *appState) pluginAction(cw *chatWindow, pid clientintf.PluginID, action
req := &grpctypes.PluginCallActionStreamRequest{
Action: action,
Data: data,
User: as.c.Public().Identity.String(),
User: as.c.Public().String(),
}

err := as.pluginsClient[pid].CallPluginAction(as.ctx, req, func(stream grpctypes.PluginService_CallActionClient) error {
Expand Down Expand Up @@ -3249,6 +3286,10 @@ func newAppState(sendMsg func(tea.Msg), lndLogLines *sloglinesbuffer.Buffer,
if showExpDays {
as.diagMsg("Days to Expire Data: %d", expDays)
}
err := as.initializePlugins()
if err != nil {
as.diagMsg("Initializing plugin errored: %v", err)
}
as.diagMsg("Client ready!")
} else {
as.diagMsg("Connection to server closed")
Expand Down
23 changes: 23 additions & 0 deletions brclient/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,29 @@ var listCommands = []tuicmd{
return nil
},
}, {
cmd: "plugins",
usableOffline: true,
descr: "List plugins inited",
handler: func(args []string, as *appState) error {
plugins, err := as.c.ListPlugins()
if err != nil {
return err
}

as.cwHelpMsgs(func(pf printf) {
pf("")
pf("Active KX attempts")
for _, plugin := range plugins {
pf("Plugin Name: %s Version: %v Enabled: %s",
plugin.Name, plugin.Version, plugin.Enabled)
pf("Plugin installed: %v Last updated: %v",
plugin.Installed.Format(ISO8601DateTime), plugin.Updated.Format(ISO8601DateTime))
pf("")
}
})
return nil
},
}, {
cmd: "svrrates",
aliases: []string{"serverrates"},
descr: "Show server fee rates",
Expand Down
9 changes: 0 additions & 9 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,6 @@ type Client struct {
gcmq *gcmcacher.Cacher
ntfns *NotificationManager

plugins map[string]*PluginClient

// abLoaded is closed when the address book has finished loading.
abLoaded chan struct{}

Expand Down Expand Up @@ -477,7 +475,6 @@ func New(cfg Config) (*Client, error) {
newUsersChan: make(chan *RemoteUser),
gcWarnedVersions: &singlesetmap.Map[zkidentity.ShortID]{},
unkxdWarnings: make(map[clientintf.UserID]time.Time),
plugins: make(map[string]*PluginClient),

onboardCancelChan: make(chan struct{}, 1),

Expand Down Expand Up @@ -1165,12 +1162,6 @@ func (c *Client) Run(ctx context.Context) error {

c.log.Infof("Starting client %s", c.localID.id)

if err := c.initializePlugins(); err != nil {
cancel()
return err
}
c.log.Info("Initialized plugin")

// From now on, all initialization data has been loaded. Init
// subsystems.
defer cancel()
Expand Down
122 changes: 102 additions & 20 deletions client/client_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package client

import (
"context"
"errors"
"fmt"
"time"

"github.com/companyzero/bisonrelay/client/clientdb"
grpctypes "github.com/companyzero/bisonrelay/clientplugin/grpctypes"
"github.com/decred/slog"
"google.golang.org/grpc"
Expand All @@ -12,11 +15,13 @@ import (
type PluginClient struct {
pluginrpc grpctypes.PluginServiceClient

ID string
Name string
Config map[string]interface{}
stream grpctypes.PluginService_InitClient
log slog.Logger
ID clientdb.PluginID
Name string
Version string
Config PluginClientCfg
Enabled bool
stream grpctypes.PluginService_InitClient
log slog.Logger
}

type PluginClientCfg struct {
Expand All @@ -25,7 +30,7 @@ type PluginClientCfg struct {
Log slog.Logger
}

func NewPluginClient(ctx context.Context, cfg PluginClientCfg) (*PluginClient, error) {
func NewPluginClient(ctx context.Context, id clientdb.PluginID, cfg PluginClientCfg) (*PluginClient, error) {
// First attempt to establish a connection to lnd's RPC sever.
// _, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
// if err != nil {
Expand All @@ -47,13 +52,28 @@ func NewPluginClient(ctx context.Context, cfg PluginClientCfg) (*PluginClient, e
log = cfg.Log
}

return &PluginClient{
p := &PluginClient{
ID: id,
pluginrpc: pc,
log: log,
}, nil
Config: PluginClientCfg{
Address: cfg.Address,
TLSCertPath: cfg.TLSCertPath,
},
Enabled: true,
}

version, err := p.GetVersion(ctx)
if err != nil {
return nil, err
}
p.Name = version.AppName
p.Version = version.AppVersion

return p, nil
}

func (p *PluginClient) Version(ctx context.Context) (*grpctypes.PluginVersionResponse, error) {
func (p *PluginClient) GetVersion(ctx context.Context) (*grpctypes.PluginVersionResponse, error) {
req := &grpctypes.PluginVersionRequest{}
return p.pluginrpc.GetVersion(ctx, req)
}
Expand Down Expand Up @@ -87,16 +107,78 @@ func (p *PluginClient) InitPlugin(ctx context.Context, req *grpctypes.PluginStar
return nil
}

// XXX From now on methods need to be implemented
// SavePluginInfo saves the plugin information to the database.
func (c *Client) SavePluginInfo(plugin *PluginClient) error {
// Ensure plugin does not already exist.
return c.dbUpdate(func(tx clientdb.ReadWriteTx) error {
_, err := c.db.GetPlugin(tx, plugin.ID)
if err == nil {
return fmt.Errorf("plugin %s already exists: %w", plugin.Name, clientdb.ErrAlreadyExists)
} else if !errors.Is(err, clientdb.ErrNotFound) {
return err
}

// Convert PluginClientCfg to map[string]interface{}
config := map[string]interface{}{
"address": plugin.Config.Address,
"tlsCertPath": plugin.Config.TLSCertPath,
}

pdb := clientdb.Plugin{
ID: plugin.ID.String(),
Name: plugin.Name,
Version: plugin.Version,
Config: config,
Enabled: plugin.Enabled,
Installed: time.Now(),
}
// Save the plugin data to the database.
return c.db.SavePlugin(tx, pdb)
})
}

// initializePlugins initializes all registered plugins.
func (c *Client) initializePlugins() error {
// for _, plugin := range c.plugins {
// if err := plugin.InitPlugin(c.ctx); err != nil {
// c.log.Errorf("Failed to initialize plugin %s: %v", plugin.ID(), err)
// return err
// }
// c.log.Infof("Initialized plugin %s", plugin.ID())
// }
return nil
// ListPlugins returns plugins saved on db.
func (c *Client) ListPlugins() ([]clientdb.Plugin, error) {
var res []clientdb.Plugin
err := c.dbView(func(tx clientdb.ReadTx) error {
var err error
res, err = c.db.ListPlugins(tx)
return err
})
return res, err
}

// GetEnabledPlugins returns the list of enabled plugins.
func (c *Client) GetEnabledPlugins() ([]PluginClient, error) {
var res []PluginClient
err := c.dbView(func(tx clientdb.ReadTx) error {
plugins, err := c.db.ListPlugins(tx)
if err != nil {
return err
}

// Filter enabled plugins and convert to PluginClient.
for _, plugin := range plugins {
if plugin.Enabled {
address, ok := plugin.Config["address"].(string)
if !ok {
return fmt.Errorf("address not found in plugin config for %s", plugin.ID)
}

pc := PluginClient{
ID: UserIDFromStr(plugin.ID),
Name: plugin.Name,
Version: plugin.Version,
Config: PluginClientCfg{
Address: address,
},
Enabled: plugin.Enabled,
}
res = append(res, pc)
}
}
return nil
})

return res, err
}
2 changes: 1 addition & 1 deletion client/clientdb/fscdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const (
unkxdUsersDir = "unkxd"
filtersDir = "contentfilters"
earlyPostStatusFile = "earlypoststatus.json"
pluginsDir = "plugin"
pluginsDir = "plugins"

pageSessionsDir = "pagesessions"
pageSessionOverviewFile = "overview.json"
Expand Down
11 changes: 11 additions & 0 deletions client/clientdb/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type RawRVID = clientintf.RawRVID
type SendQID = clientintf.ID
type FileID = clientintf.ID
type ChunkID = clientintf.ID
type PluginID = clientintf.PluginID

type ReadTx interface {
Context() context.Context
Expand Down Expand Up @@ -469,6 +470,16 @@ type EarlyPostStatus struct {
Status rpc.RMPostShare `json:"status"`
}

type Plugin struct {
ID string
Name string
Version string
Enabled bool
Config map[string]interface{}
Installed time.Time
Updated time.Time
}

var (
ErrLocalIDEmpty = errors.New("local ID is not initialized")
ErrServerIDEmpty = errors.New("server ID is not known")
Expand Down
Loading

0 comments on commit 862bf2f

Please sign in to comment.