From 36e38f5a89d724207c5a152b28162c6cd2e317ed Mon Sep 17 00:00:00 2001 From: Lars Bahner Date: Sat, 23 Mar 2024 23:10:26 +0100 Subject: [PATCH] WIP: structuralise config --- .vscode/launch.json | 14 ++++ README.md | 4 +- cmd/actor/main.go | 3 +- cmd/node/config.go | 16 ++++ cmd/node/main.go | 2 +- cmd/node/template.go | 84 ++++++++----------- cmd/pong/config.go | 139 ++++++++++++------------------ cmd/pong/main.go | 10 ++- cmd/relay/config.go | 96 ++++++++++----------- cmd/robot/config.go | 123 ++++++++++++++------------- config/actor.go | 78 ++++++++++++----- config/api.go | 27 ++++++ config/config.go | 149 ++++++++++++--------------------- config/db.go | 32 ++++++- {cmd/actor => config}/debug.go | 13 ++- config/flags.go | 54 +++++++++++- config/generate.go | 57 ------------- config/http.go | 24 ++++++ config/logging.go | 48 ++++++++--- config/p2p.go | 50 ++++++++++- config/template.go | 11 --- config/ui.go | 16 ++++ config/xdg.go | 55 +++++++++++- db/db.go | 13 +-- entity/actor/config.go | 100 ---------------------- internal/path.go | 7 ++ ui/save.go | 2 +- ui/ui.go | 2 + 28 files changed, 657 insertions(+), 572 deletions(-) create mode 100644 config/api.go rename {cmd/actor => config}/debug.go (60%) delete mode 100644 config/template.go delete mode 100644 entity/actor/config.go create mode 100644 internal/path.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a5b0f3..bc708f3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -67,6 +67,20 @@ "GOLOG_OUTPUT": "file" } }, + { + "name": "Launch relay", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/relay/", + "console": "integratedTerminal", + "env": { + "GOLOG_FILE": "/tmp/debug.log", + "GOLOG_LOG_LEVEL": "debug", + "GOLOG_STDOUT": "false", + "GOLOG_OUTPUT": "file" + } + }, { "name": "go-ma-actor generate", "type": "go", diff --git a/README.md b/README.md index 996f756..da47669 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,7 @@ It provides The ability to browse IPFS properly, and to pin files and directorie ### Cros-compiling -Cross-cmpiling for Windows requires gcc-mingw-w64 and gcc-multilib tools to be installed. - -CGO is required for sqlite to work on Windows™. +Cross-compiling for Windows requires gcc-mingw-w64 and gcc-multilib tools to be installed. ## TL;DR diff --git a/cmd/actor/main.go b/cmd/actor/main.go index 7013485..adf3611 100644 --- a/cmd/actor/main.go +++ b/cmd/actor/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" - "github.com/bahner/go-ma-actor/config" "github.com/bahner/go-ma-actor/entity/actor" "github.com/bahner/go-ma-actor/p2p" "github.com/bahner/go-ma-actor/ui" @@ -19,7 +18,7 @@ func main() { ) fmt.Println("Initialising actor configuation...") - actor.InitConfig(config.Profile()) + // actor.InitConfig(config.Profile()) // P2P fmt.Println("Setting default p2p options...") diff --git a/cmd/node/config.go b/cmd/node/config.go index df5dcb4..b74d8f6 100644 --- a/cmd/node/config.go +++ b/cmd/node/config.go @@ -34,3 +34,19 @@ func init() { viper.SetDefault("node.debug_interval", defaultNodeDebugInterval) } + +func NodeSpace() string { + return viper.GetString("node.space") +} + +func NodeCookie() string { + return viper.GetString("node.cookie") +} + +func NodeName() string { + return viper.GetString("node.name") +} + +func NodeDebugInterval() time.Duration { + return viper.GetDuration("node.debug_interval") +} diff --git a/cmd/node/main.go b/cmd/node/main.go index 1638fef..f6042d4 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -18,7 +18,7 @@ func main() { // Init config and logger config.SetProfile(name) - actor.InitConfig(config.Profile()) + // actor.InitConfig(config.Profile()) p, err := p2p.Init(p2p.DefaultOptions()) if err != nil { diff --git a/cmd/node/template.go b/cmd/node/template.go index a46afae..2333631 100644 --- a/cmd/node/template.go +++ b/cmd/node/template.go @@ -1,58 +1,48 @@ package main import ( + "time" + "github.com/bahner/go-ma-actor/config" - "github.com/spf13/viper" ) -func configTemplate(identity string, node string) map[string]interface{} { +type NodeStruct struct { + Cookie string `yaml:"cookie"` + Name string `yaml:"name"` + Space string `yaml:"space"` + DebugInterval time.Duration `yaml:"debug-interval"` +} - // Get the default settings as a map - // Note: Viper does not have a built-in way to directly extract only the config - // so we manually recreate the structure based on the config we have set. - return map[string]interface{}{ - "actor": map[string]interface{}{ - "identity": identity, - "location": config.ActorLocation(), - "nick": config.ActorNick(), - }, - "db": map[string]interface{}{ - "dir": config.DefaultDbPath, - }, - // Use default log settings, so as not to pick up debug log settings - "log": map[string]interface{}{ - "level": viper.GetString("log.level"), - "file": viper.GetString("log.file"), - }, - // NB! This is a cross over from go-ma - "api": map[string]interface{}{ - // This must be set corretly for generation to work - "maddr": viper.GetString("api.maddr"), - }, - "http": map[string]interface{}{ - "socket": config.HttpSocket(), - }, - "p2p": map[string]interface{}{ - "identity": node, - "port": config.P2PPort(), - "connmgr": map[string]interface{}{ - "low-watermark": config.P2PConnmgrLowWatermark(), - "high-watermark": config.P2PConnmgrHighWatermark(), - "grace-period": config.P2PConnMgrGracePeriod(), - }, - "discovery": map[string]interface{}{ - "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), - "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), - "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), - "dht": config.P2PDiscoveryDHT(), - "mdns": config.P2PDiscoveryMDNS(), +type NodeConfigStruct struct { + Node NodeStruct `yaml:"node"` +} + +type NodeConfig struct { + Actor config.ActorConfigStruct `yaml:"actor"` + API config.APIConfigStruct `yaml:"api"` + DB config.DBConfigStruct `yaml:"db"` + HTTP config.HTTPConfigStruct `yaml:"http"` + Node NodeConfigStruct `yaml:"node"` + Log config.LogConfigStruct `yaml:"log"` + P2P config.P2PConfigStruct `yaml:"p2p"` +} + +func Config() NodeConfig { + + return NodeConfig{ + Actor: config.ActorConfig(), + API: config.APIConfig(), + DB: config.DBConfig(), + HTTP: config.HTTPConfig(), + Node: NodeConfigStruct{ + Node: NodeStruct{ + Cookie: NodeCookie(), + Name: NodeName(), + Space: NodeSpace(), + DebugInterval: NodeDebugInterval(), }, }, - "node": map[string]interface{}{ - "cookie": defaultNodeCookie, - "name": defaultNodeName, - "debug_interval": defaultNodeDebugInterval, - "space": defautSpaceNodeName, - }, + Log: config.LogConfig(), + P2P: config.P2PConfig(), } } diff --git a/cmd/pong/config.go b/cmd/pong/config.go index 5345a61..c8444cb 100644 --- a/cmd/pong/config.go +++ b/cmd/pong/config.go @@ -2,22 +2,20 @@ package main import ( "errors" - "os" "github.com/bahner/go-ma-actor/config" - "github.com/bahner/go-ma-actor/p2p" "github.com/bahner/go-ma/did/doc" - "github.com/libp2p/go-libp2p" - p2pDHT "github.com/libp2p/go-libp2p-kad-dht" log "github.com/sirupsen/logrus" "github.com/spf13/pflag" "github.com/spf13/viper" + "gopkg.in/yaml.v2" ) const ( defaultPongReply = "Pong!" defaultFortuneMode = false pong = "pong" + profile = pong ) var defaultFortuneArgs = []string{"-s"} @@ -34,47 +32,71 @@ func init() { viper.SetDefault("mode.pong.fortune.args", defaultFortuneArgs) } -func p2pOptions() p2p.Options { - return p2p.Options{ - DHT: []p2pDHT.Option{ - p2pDHT.Mode(p2pDHT.ModeServer), - }, - P2P: []libp2p.Option{ - libp2p.DefaultTransports, - libp2p.DefaultSecurity, - libp2p.EnableRelay(), - libp2p.EnableRelayService(), - libp2p.Ping(true), - }} +type FortuneStruct struct { + Enable bool `yaml:"enable"` + Args []string `yaml:"args"` } -func initConfig(profile string) { +type PongStruct struct { + Reply string `yaml:"reply"` + Fortune FortuneStruct `yaml:"fortune"` +} + +type PongConfigStruct struct { + Pong PongStruct `yaml:"pong"` +} + +type ModeConfigStruct struct { + Mode PongConfigStruct `yaml:"mode"` +} + +type PongConfig struct { + Mode ModeConfigStruct `yaml:"mode"` + API config.APIConfigStruct `yaml:"api"` + Actor config.ActorConfigStruct `yaml:"actor"` + DB config.DBConfigStruct `yaml:"db"` + Log config.LogConfigStruct `yaml:"log"` +} + +func Config() PongConfig { - // Always parse the flags first config.InitCommonFlags() config.InitActorFlags() pflag.Parse() + + // Always parse the flags first config.SetProfile(profile) config.Init() - if config.GenerateFlag() { - // Reinit logging to STDOUT - log.SetOutput(os.Stdout) - log.Info("Generating new actor and node identity") - actor, node := generateActorIdentitiesOrPanic(pong) - pongConfig := configTemplate(actor, node) - config.Generate(pongConfig) - os.Exit(0) + return PongConfig{ + Mode: ModeConfigStruct{ + Mode: PongConfigStruct{ + Pong: PongStruct{ + Reply: pongReply(), + Fortune: FortuneStruct{ + Enable: pongFortuneMode(), + Args: pongFortuneArgs(), + }, + }, + }, + }, + API: config.APIConfig(), + Actor: config.ActorConfig(), + DB: config.DBConfig(), + Log: config.LogConfig(), } +} - config.InitActor() +func (c *PongConfig) MarshalToYAML() ([]byte, error) { + return yaml.Marshal(c) +} - // This flag is dependent on the actor to be initialized to make sense. - if config.ShowConfigFlag() { - config.Print() - os.Exit(0) +func (c *PongConfig) Print() { + y, err := c.MarshalToYAML() + if err != nil { + log.Fatal(err) } - + log.Println(string(y)) } func pongFortuneMode() bool { @@ -100,56 +122,3 @@ func generateActorIdentitiesOrPanic(name string) (string, string) { } return actor, node } - -func configTemplate(identity string, node string) map[string]interface{} { - - // Get the default settings as a map - // Note: Viper does not have a built-in way to directly extract only the config - // so we manually recreate the structure based on the config we have set. - return map[string]interface{}{ - "actor": map[string]interface{}{ - "identity": identity, - "nick": pong, - }, - "db": map[string]interface{}{ - "dir": config.DefaultDbPath, - }, - "log": map[string]interface{}{ - "level": config.LogLevel(), - "file": config.LogFile(), - }, - // NB! This is a cross over from go-ma - "api": map[string]interface{}{ - // This must be set corretly for generation to work - "maddr": viper.GetString("api.maddr"), - }, - "http": map[string]interface{}{ - "socket": config.HttpSocket(), - }, - "p2p": map[string]interface{}{ - "identity": node, - "port": config.P2PPort(), - "connmgr": map[string]interface{}{ - "low-watermark": config.P2PConnmgrLowWatermark(), - "high-watermark": config.P2PConnmgrHighWatermark(), - "grace-period": config.P2PConnMgrGracePeriod(), - }, - "discovery": map[string]interface{}{ - "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), - "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), - "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), - "dht": config.P2PDiscoveryDHT(), - "mdns": config.P2PDiscoveryMDNS(), - }, - }, - "mode": map[string]interface{}{ - "pong": map[string]interface{}{ - "reply": pongReply(), - "fortune": map[string]interface{}{ - "enable": pongFortuneMode(), - "args": pongFortuneArgs(), - }, - }, - }, - } -} diff --git a/cmd/pong/main.go b/cmd/pong/main.go index 40d33f7..b72eb20 100644 --- a/cmd/pong/main.go +++ b/cmd/pong/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "os" "github.com/bahner/go-ma-actor/entity/actor" @@ -13,10 +14,15 @@ import ( func main() { ctx := context.Background() - initConfig(pong) + + c := Config() + c.Print() + if true { + os.Exit(0) + } // THese are the relay specific parts. - p, err := p2p.Init(p2pOptions()) + p, err := p2p.Init(p2p.DefaultOptions()) if err != nil { fmt.Printf("Failed to initialize p2p: %v\n", err) return diff --git a/cmd/relay/config.go b/cmd/relay/config.go index 0116702..9f9fa52 100644 --- a/cmd/relay/config.go +++ b/cmd/relay/config.go @@ -7,7 +7,6 @@ import ( "github.com/bahner/go-ma-actor/p2p" libp2p "github.com/libp2p/go-libp2p" p2pDHT "github.com/libp2p/go-libp2p-kad-dht" - log "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -32,60 +31,61 @@ func initConfig(name string) { config.SetProfile(name) config.Init() - if config.GenerateFlag() { - // Reinit logging to STDOUT - log.SetOutput(os.Stdout) - log.Info("Generating new node identity") - node, err := config.GenerateNodeIdentity() - if err != nil { - log.Fatal(err) - } - relayConfig := configTemplate(node) - config.Generate(relayConfig) - os.Exit(0) - } + // if config.GenerateFlag() { + // // Reinit logging to STDOUT + // log.SetOutput(os.Stdout) + // log.Info("Generating new node identity") + // node, err := config.GenerateNodeIdentity() + // if err != nil { + // log.Fatal(err) + // } + // relayConfig := configTemplate(node) + // config.Generate(relayConfig) + // os.Exit(0) + // } // This flag is dependent on the actor to be initialized to make sense. if config.ShowConfigFlag() { - config.Print() + config.PrintAll() os.Exit(0) } } -func configTemplate(node string) map[string]interface{} { +// func configTemplate(node string) map[string]interface{} { - // Get the default settings as a map - // Note: Viper does not have a built-in way to directly extract only the config - // so we manually recreate the structure based on the config we have set. - return map[string]interface{}{ - "db": map[string]interface{}{ - "dir": config.DefaultDbPath, - }, - "log": map[string]interface{}{ - "level": config.LogLevel(), - "file": config.LogFile(), - }, - "http": map[string]interface{}{ - "socket": config.HttpSocket(), - "refresh": config.HttpRefresh(), - }, - "p2p": map[string]interface{}{ - "identity": node, - "port": config.P2PPort(), - "connmgr": map[string]interface{}{ - "low-watermark": config.P2PConnmgrLowWatermark(), - "high-watermark": config.P2PConnmgrHighWatermark(), - "grace-period": config.P2PConnMgrGracePeriod(), - }, - "discovery": map[string]interface{}{ - "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), - "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), - "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), - "dht": config.P2PDiscoveryDHT(), - "mdns": config.P2PDiscoveryMDNS(), - }, - }, - } +// // Get the default settings as a map +// // Note: Viper does not have a built-in way to directly extract only the config +// // so we manually recreate the structure based on the config we have set. +// return map[string]interface{}{ +// "db": map[string]interface{}{ +// "dir": config.DefaultDbPath, +// }, +// "log": map[string]interface{}{ +// "level": config.LogLevel(), +// "file": config.LogFile(), +// }, +// "http": map[string]interface{}{ +// "socket": config.HttpSocket(), +// "refresh": config.HttpRefresh(), +// "debug-socket": config.HttpDebugSocket(), +// }, +// "p2p": map[string]interface{}{ +// "identity": node, +// "port": config.P2PPort(), +// "connmgr": map[string]interface{}{ +// "low-watermark": config.P2PConnmgrLowWatermark(), +// "high-watermark": config.P2PConnmgrHighWatermark(), +// "grace-period": config.P2PConnMgrGracePeriod(), +// }, +// "discovery": map[string]interface{}{ +// "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), +// "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), +// "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), +// "dht": config.P2PDiscoveryDHT(), +// "mdns": config.P2PDiscoveryMDNS(), +// }, +// }, +// } -} +// } diff --git a/cmd/robot/config.go b/cmd/robot/config.go index 1445c84..eb8a986 100644 --- a/cmd/robot/config.go +++ b/cmd/robot/config.go @@ -4,7 +4,6 @@ import ( "os" "github.com/bahner/go-ma-actor/config" - log "github.com/sirupsen/logrus" "github.com/spf13/pflag" "github.com/spf13/viper" ) @@ -28,24 +27,24 @@ func initConfig(profile string) { config.SetProfile(profile) config.Init() - if config.GenerateFlag() { - // Reinit logging to STDOUT - log.SetOutput(os.Stdout) - log.Info("Generating new actor and node identity") - actor, node, err := config.GenerateActorIdentities(name) - if err != nil { - log.Fatalf("Failed to generate identities: %v", err) - } - openaiConfig := configTemplate(actor, node) - config.Generate(openaiConfig) - os.Exit(0) - } + // if config.GenerateFlag() { + // // Reinit logging to STDOUT + // log.SetOutput(os.Stdout) + // log.Info("Generating new actor and node identity") + // actor, node, err := config.GenerateActorIdentities(name) + // if err != nil { + // log.Fatalf("Failed to generate identities: %v", err) + // } + // openaiConfig := configTemplate(actor, node) + // config.Generate(openaiConfig) + // os.Exit(0) + // } - config.InitActor() + // config.InitActor() // This flag is dependent on the actor to be initialized to make sense. if config.ShowConfigFlag() { - config.Print() + config.PrintAll() os.Exit(0) } @@ -54,51 +53,51 @@ func openAIKey() string { return viper.GetString("mode.openai.key") } -func configTemplate(identity string, node string) map[string]interface{} { +// func configTemplate(identity string, node string) map[string]interface{} { - // Get the default settings as a map - // Note: Viper does not have a built-in way to directly extract only the config - // so we manually recreate the structure based on the config we have set. - return map[string]interface{}{ - "actor": map[string]interface{}{ - "identity": identity, - "nick": name, - }, - "db": map[string]interface{}{ - "dir": config.DefaultDbPath, - }, - "log": map[string]interface{}{ - "level": config.LogLevel(), - "file": config.LogFile(), - }, - // NB! This is a cross over from go-ma - "api": map[string]interface{}{ - // This must be set corretly for generation to work - "maddr": viper.GetString("api.maddr"), - }, - "http": map[string]interface{}{ - "socket": config.HttpSocket(), - }, - "p2p": map[string]interface{}{ - "identity": node, - "port": config.P2PPort(), - "connmgr": map[string]interface{}{ - "low-watermark": config.P2PConnmgrLowWatermark(), - "high-watermark": config.P2PConnmgrHighWatermark(), - "grace-period": config.P2PConnMgrGracePeriod(), - }, - "discovery": map[string]interface{}{ - "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), - "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), - "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), - "dht": config.P2PDiscoveryDHT(), - "mdns": config.P2PDiscoveryMDNS(), - }, - }, - "mode": map[string]interface{}{ - "openai": map[string]interface{}{ - "key": openAIKey(), - }, - }, - } -} +// // Get the default settings as a map +// // Note: Viper does not have a built-in way to directly extract only the config +// // so we manually recreate the structure based on the config we have set. +// return map[string]interface{}{ +// "actor": map[string]interface{}{ +// "identity": identity, +// "nick": name, +// }, +// "db": map[string]interface{}{ +// "dir": config.DefaultDbPath, +// }, +// "log": map[string]interface{}{ +// "level": config.LogLevel(), +// "file": config.LogFile(), +// }, +// // NB! This is a cross over from go-ma +// "api": map[string]interface{}{ +// // This must be set corretly for generation to work +// "maddr": viper.GetString("api.maddr"), +// }, +// "http": map[string]interface{}{ +// "socket": config.HttpSocket(), +// }, +// "p2p": map[string]interface{}{ +// "identity": node, +// "port": config.P2PPort(), +// "connmgr": map[string]interface{}{ +// "low-watermark": config.P2PConnmgrLowWatermark(), +// "high-watermark": config.P2PConnmgrHighWatermark(), +// "grace-period": config.P2PConnMgrGracePeriod(), +// }, +// "discovery": map[string]interface{}{ +// "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), +// "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), +// "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), +// "dht": config.P2PDiscoveryDHT(), +// "mdns": config.P2PDiscoveryMDNS(), +// }, +// }, +// "mode": map[string]interface{}{ +// "openai": map[string]interface{}{ +// "key": openAIKey(), +// }, +// }, +// } +// } diff --git a/config/actor.go b/config/actor.go index 7d05805..2cbfa2e 100644 --- a/config/actor.go +++ b/config/actor.go @@ -36,24 +36,61 @@ func InitActorFlags() { } -// Set the default nick to the user's username, unless a profile is set. -func defaultNick() string { +type ActorStruct struct { + Identity string `yaml:"identity"` + Nick string `yaml:"nick"` + Location string `yaml:"location"` +} - if Profile() == defaultProfile { - return os.Getenv("USER") +type ActorConfigStruct struct { + Actor ActorStruct `yaml:"actor"` +} + +func ActorConfig() ActorConfigStruct { + + initActor() + + identity, err := actorIdentity() + if err != nil { + panic(err) } - return Profile() + return ActorConfigStruct{ + Actor: ActorStruct{ + Identity: identity, + Nick: ActorNick(), + Location: ActorLocation(), + }, + } +} + +// Fetches the actor nick from the config or the command line +// NB! This is a little more complex than the other config functions, as it +// needs to fetch the nick from the command line if it's not in the config. +// Due to being a required parameter when generating a new keyset. +func ActorNick() string { + return viper.GetString("actor.nick") +} + +func ActorLocation() string { + return viper.GetString("actor.location") +} + +func ActorKeyset() set.Keyset { + return keyset } // Load a keyset from string and initiate an Actor. // This is optional, but if you want to use the actor package, you need to call this. -func InitActor() { +func initActor() { - keyset_string := actorIdentity() + keyset_string, err := actorIdentity() if keyset_string == fakeActorIdentity { panic(ErrFakeIdentity) } + if err != nil { + panic(err) + } log.Debugf("config.initActor: %s", keyset_string) // Create the actor keyset @@ -74,28 +111,23 @@ func InitActor() { } -// Fetches the actor nick from the config or the command line -// NB! This is a little more complex than the other config functions, as it -// needs to fetch the nick from the command line if it's not in the config. -// Due to being a required parameter when generating a new keyset. -func ActorNick() string { - - return viper.GetString("actor.nick") - -} +func actorIdentity() (string, error) { -func ActorLocation() string { + if GenerateFlag() { + return GenerateActorIdentity(ActorNick()) + } - return viper.GetString("actor.location") + return viper.GetString("actor.identity"), nil } -func ActorKeyset() set.Keyset { - return keyset -} -func actorIdentity() string { +// Set the default nick to the user's username, unless a profile is set. +func defaultNick() string { - return viper.GetString("actor.identity") + if Profile() == defaultProfile { + return os.Getenv("USER") + } + return Profile() } func initActorKeyset(keyset_string string) { diff --git a/config/api.go b/config/api.go new file mode 100644 index 0000000..94e610a --- /dev/null +++ b/config/api.go @@ -0,0 +1,27 @@ +package config + +import ( + "github.com/bahner/go-ma" + "github.com/spf13/viper" +) + +type APIStruct struct { + Maddr string `yaml:"maddr"` +} + +type APIConfigStruct struct { + Api APIStruct `yaml:"api"` +} + +func APIConfig() APIConfigStruct { + viper.SetDefault("api.maddr", ma.DEFAULT_IPFS_API_MULTIADDR) + + return APIConfigStruct{ + Api: APIStruct{ + Maddr: APIAddr(), + }, + } +} +func APIAddr() string { + return viper.GetString("api.maddr") +} diff --git a/config/config.go b/config/config.go index c0744ac..a47e0e4 100644 --- a/config/config.go +++ b/config/config.go @@ -3,14 +3,9 @@ package config import ( "fmt" "os" - "path/filepath" "strings" - "github.com/adrg/xdg" - "github.com/bahner/go-ma" - "github.com/mitchellh/go-homedir" log "github.com/sirupsen/logrus" - "github.com/spf13/pflag" "github.com/spf13/viper" "gopkg.in/yaml.v2" ) @@ -25,11 +20,9 @@ const ( dataHomeMode os.FileMode = 0755 ) -var ( - configHome string = xdg.ConfigHome + "/" + ma.NAME + "/" - dataHome string = xdg.DataHome + "/" + ma.NAME + "/" - defaultConfigFile string = NormalisePath(configHome + Profile() + ".yaml") -) +type Config interface { + MarshalToYAML() ([]byte, error) +} // This should be called after pflag.Parse() in main. // If you want to use a specific config file, you need to call SetProfile() before Init(). @@ -64,15 +57,6 @@ func Init() error { } } - // API - viper.SetDefault("api.maddr", ma.DEFAULT_IPFS_API_MULTIADDR) - - // Logging - viper.SetDefault("log.file", genDefaultLogFileName(Profile())) - InitLogging() - - // FLAGS - // Handle the easy flags first. if versionFlag() { fmt.Println(VERSION) @@ -89,7 +73,7 @@ func Init() error { } -func Print() (int, error) { +func PrintAll() (int, error) { configMap := viper.AllSettings() @@ -103,100 +87,73 @@ func Print() (int, error) { return fmt.Println(string(configYAML)) } -func Save() error { - - return viper.WriteConfig() - -} - -func DataHome() string { - return dataHome +type CommonConfigStruct struct { + Actor ActorConfigStruct `yaml:"actor"` + API APIConfigStruct `yaml:"api"` + DB DBConfigStruct `yaml:"db"` + HTTP HTTPConfigStruct `yaml:"http"` + Log LogConfigStruct `yaml:"log"` + P2P P2PConfigStruct `yaml:"p2p"` } -func ConfigHome() string { - return configHome -} - -// Returns the configfile name to use. -// The preferred value is the explcitily requested config file on the command line. -// Else it uses the nick of the actor or the mode. -func File() string { - - var ( - filename string - err error - ) - - config, err := pflag.CommandLine.GetString("config") - if err != nil { - log.Fatal(err) +func Common() CommonConfigStruct { + return CommonConfigStruct{ + Actor: ActorConfig(), + API: APIConfig(), + DB: DBConfig(), + HTTP: HTTPConfig(), + Log: LogConfig(), + P2P: P2PConfig(), } - - // Prefer explicitly requested config. If not, use the name of the profile name. - if config != defaultConfigFile && config != "" { - filename, err = homedir.Expand(config) - if err != nil { - log.Fatal(err) - } - } else { - filename = configHome + Profile() + ".yaml" - } - - return filepath.Clean(filename) - } -func GenerateFlag() bool { - // This will exit when done. It will also publish if applicable. - generateFlag, err := pflag.CommandLine.GetBool("generate") +func Print(c Config) { + configYAML, err := c.MarshalToYAML() if err != nil { - log.Warnf("config.init: %v", err) - return false + log.Fatalf("Failed to marshal config to YAML: %v", err) } - return generateFlag + fmt.Println(string(configYAML)) } -func PublishFlag() bool { - publishFlag, err := pflag.CommandLine.GetBool("publish") - if err != nil { - log.Warnf("config.init: %v", err) - return false +// Write the generated config to the correct file +// NB! This fails fatally in case of an error. +func Write(c Config) error { + filePath := File() + var errMsg string + + // Determine the file open flags based on the forceFlag + var flags int + if ForceFlag() { + // Allow overwrite + log.Warnf("Force flag set, overwriting existing config file %s", filePath) + flags = os.O_WRONLY | os.O_CREATE | os.O_TRUNC + } else { + // Prevent overwrite + flags = os.O_WRONLY | os.O_CREATE | os.O_EXCL } - return publishFlag -} - -func ShowConfigFlag() bool { - showConfigFlag, err := pflag.CommandLine.GetBool("show-config") + file, err := os.OpenFile(filePath, flags, configFileMode) if err != nil { - log.Warnf("config.init: %v", err) - return false + if os.IsExist(err) { + errMsg = fmt.Sprintf("File %s already exists.", filePath) + } else { + errMsg = fmt.Sprintf("Failed to open file: %v", err) + } + return fmt.Errorf(errMsg) } + defer file.Close() - return showConfigFlag -} - -func versionFlag() bool { - versionFlag, err := pflag.CommandLine.GetBool("version") + content, err := c.MarshalToYAML() if err != nil { - log.Warnf("config.init: %v", err) - return false + return fmt.Errorf("failed to marshal to YAML: %w", err) } - return versionFlag -} - -func ForceFlag() bool { - forceFlag, err := pflag.CommandLine.GetBool("force") - if err != nil { - log.Warnf("config.init: %v", err) - return false + // Write content to file. + if _, err := file.Write(content); err != nil { + return fmt.Errorf("failed to write to file: %w", err) } - return forceFlag -} - -func NormalisePath(path string) string { - return filepath.ToSlash(filepath.Clean(path)) + log.Printf("Generated config file %s", filePath) + return nil } diff --git a/config/db.go b/config/db.go index 2c8b8e4..359c08f 100644 --- a/config/db.go +++ b/config/db.go @@ -1,10 +1,38 @@ package config -import "github.com/spf13/viper" +import ( + "github.com/bahner/go-ma-actor/internal" + "github.com/spf13/pflag" + "github.com/spf13/viper" +) const defaultDBDirname = ".madb" -var DefaultDbPath = NormalisePath(dataHome + defaultDBDirname) +var DefaultDbPath = internal.NormalisePath(dataHome + defaultDBDirname) + +func InitDBFlags() { + pflag.String("db-path", DefaultDbPath, "Directory to use for database.") + viper.BindPFlag("db.path", pflag.Lookup("db-path")) + viper.SetDefault("db.path", DefaultDbPath) +} + +type DBStruct struct { + Path string `yaml:"path"` +} + +type DBConfigStruct struct { + DB DBStruct `yaml:"db"` +} + +func DBConfig() DBConfigStruct { + viper.SetDefault("db.path", DefaultDbPath) + + return DBConfigStruct{ + DB: DBStruct{ + Path: DBPath(), + }, + } +} func DBPath() string { return viper.GetString("db.path") diff --git a/cmd/actor/debug.go b/config/debug.go similarity index 60% rename from cmd/actor/debug.go rename to config/debug.go index ba020d0..1c9b57f 100644 --- a/cmd/actor/debug.go +++ b/config/debug.go @@ -1,22 +1,31 @@ //go:build debug -package main +package config import ( "net/http" _ "net/http/pprof" "runtime" + + "github.com/spf13/pflag" + "github.com/spf13/viper" ) +const defaultDebugSocket = "0.0.0.0:6060" + func init() { // Assume you have a function setupDebugHandlers to register debug routes setupDebugHandlers() + + pflag.String("debug-socket", defaultDebugSocket, "Port to listen on for debug endpoints") + viper.BindPFlag("http.debug-socket", pflag.Lookup("debug-socket")) + viper.SetDefault("http.debug-socket", defaultDebugSocket) } func setupDebugHandlers() { // Register your pprof handlers or other debug routes here // Since "net/http/pprof" is imported above, its init function automatically registers its routes with the default mux - go http.ListenAndServe("localhost:6060", nil) + go http.ListenAndServe(HttpDebugSocket(), nil) http.HandleFunc("/force-gc", func(w http.ResponseWriter, r *http.Request) { // Force a garbage collection diff --git a/config/flags.go b/config/flags.go index 85d3e96..3e79ad6 100644 --- a/config/flags.go +++ b/config/flags.go @@ -1,6 +1,7 @@ package config import ( + log "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -11,9 +12,7 @@ func InitCommonFlags() { pflag.StringP("profile", "p", "", "Config profile (name) to use.") pflag.Bool("show-config", false, "Whether to print the config.") - pflag.BoolP("version", "v", false, "Print version and exit.") - pflag.Bool("generate", false, "Generates a new keyset") pflag.Bool("publish", false, "Publishes keyset to IPFS") pflag.Bool("force", false, "Forces regneration of config keyset and publishing") @@ -22,3 +21,54 @@ func InitCommonFlags() { InitHTTPFlags() InitP2PFlags() } + +func GenerateFlag() bool { + // This will exit when done. It will also publish if applicable. + generateFlag, err := pflag.CommandLine.GetBool("generate") + if err != nil { + log.Warnf("config.init: %v", err) + return false + } + + return generateFlag +} + +func PublishFlag() bool { + publishFlag, err := pflag.CommandLine.GetBool("publish") + if err != nil { + log.Warnf("config.init: %v", err) + return false + } + + return publishFlag +} + +func ShowConfigFlag() bool { + showConfigFlag, err := pflag.CommandLine.GetBool("show-config") + if err != nil { + log.Warnf("config.init: %v", err) + return false + } + + return showConfigFlag +} + +func versionFlag() bool { + versionFlag, err := pflag.CommandLine.GetBool("version") + if err != nil { + log.Warnf("config.init: %v", err) + return false + } + + return versionFlag +} + +func ForceFlag() bool { + forceFlag, err := pflag.CommandLine.GetBool("force") + if err != nil { + log.Warnf("config.init: %v", err) + return false + } + + return forceFlag +} diff --git a/config/generate.go b/config/generate.go index b84b42d..ef6173e 100644 --- a/config/generate.go +++ b/config/generate.go @@ -3,71 +3,14 @@ package config import ( "errors" "fmt" - "os" "github.com/bahner/go-ma/did/doc" "github.com/bahner/go-ma/key/set" "github.com/libp2p/go-libp2p/core/crypto" mb "github.com/multiformats/go-multibase" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v2" ) -func Generate(configMap map[string]interface{}) { - - if configMap == nil { - log.Fatalf("No template set.") - } - - // Convert the config map to YAML - configYAML, err := yaml.Marshal(configMap) - if err != nil { - log.Fatalf("Failed to marshal config to YAML: %v", err) - } - - if GenerateFlag() { - writeGeneratedConfigFile(configYAML) - } else { - fmt.Println(string(configYAML)) - } -} - -// Write the generated config to the correct file -// NB! This fails fatally in case of an error. -func writeGeneratedConfigFile(content []byte) { - filePath := File() - var errMsg string - - // Determine the file open flags based on the forceFlag - var flags int - if ForceFlag() { - // Allow overwrite - log.Warnf("Force flag set, overwriting existing config file %s", filePath) - flags = os.O_WRONLY | os.O_CREATE | os.O_TRUNC - } else { - // Prevent overwrite - flags = os.O_WRONLY | os.O_CREATE | os.O_EXCL - } - - file, err := os.OpenFile(filePath, flags, configFileMode) - if err != nil { - if os.IsExist(err) { - errMsg = fmt.Sprintf("File %s already exists.", filePath) - } else { - errMsg = fmt.Sprintf("Failed to open file: %v", err) - } - log.Fatalf(errMsg) - } - defer file.Close() - - // Write content to file. - if _, err := file.Write(content); err != nil { - log.Fatalf("Failed to write to file: %v", err) - } - - log.Printf("Generated config file %s", filePath) -} - // Genreates a libp2p and actor identity and returns the keyset and the actor identity // These are imperative, so failure to generate them is a fatal error. func GenerateActorIdentities(name string) (string, string, error) { diff --git a/config/http.go b/config/http.go index 3dcd07f..93736a5 100644 --- a/config/http.go +++ b/config/http.go @@ -10,6 +10,26 @@ const ( defaultHttpRefresh int = 10 ) +type HTTPStruct struct { + Socket string `yaml:"socket"` + Refresh int `yaml:"refresh"` + DebugSocket string `yaml:"debug_socket"` +} + +type HTTPConfigStruct struct { + HTTP HTTPStruct `yaml:"http"` +} + +func HTTPConfig() HTTPConfigStruct { + return HTTPConfigStruct{ + HTTP: HTTPStruct{ + Socket: HttpSocket(), + Refresh: HttpRefresh(), + DebugSocket: HttpDebugSocket(), + }, + } +} + func InitHTTPFlags() { pflag.String("http-socket", defaultHttpSocket, "Address for webserver to listen on") @@ -34,3 +54,7 @@ func HttpSocket() string { func HttpRefresh() int { return viper.GetInt("http.refresh") } + +func HttpDebugSocket() string { + return viper.GetString("http.debug-socket") +} diff --git a/config/logging.go b/config/logging.go index b908ff5..5a26a76 100644 --- a/config/logging.go +++ b/config/logging.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "github.com/bahner/go-ma-actor/internal" "github.com/mitchellh/go-homedir" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -17,7 +18,7 @@ const ( logFilePerm os.FileMode = 0640 ) -var defaultLogfile string = NormalisePath(dataHome + Profile() + ".log") +var defaultLogfile string = internal.NormalisePath(dataHome + Profile() + ".log") func InitLogFlags() { @@ -29,7 +30,38 @@ func InitLogFlags() { } -func InitLogging() { +type LogStruct struct { + Level string `yaml:"level"` + File string `yaml:"file"` +} + +type LogConfigStruct struct { + Log LogStruct `yaml:"log"` +} + +func LogConfig() LogConfigStruct { + + viper.SetDefault("log.file", genDefaultLogFileName(Profile())) + + initLogging() + + return LogConfigStruct{ + Log: LogStruct{ + Level: LogLevel(), + File: LogFile(), + }, + } +} + +func LogLevel() string { + return viper.GetString("log.level") +} + +func LogFile() string { + return viper.GetString("log.file") +} + +func initLogging() { viper.SetDefault("log.level", defaultLogLevel) @@ -85,17 +117,9 @@ func getLogFile() (string, error) { if err != nil { return "", err } - return NormalisePath(lf), nil -} - -func LogLevel() string { - return viper.GetString("log.level") -} - -func LogFile() string { - return viper.GetString("log.file") + return internal.NormalisePath(lf), nil } func genDefaultLogFileName(name string) string { - return NormalisePath(dataHome + name + ".log") + return internal.NormalisePath(dataHome + name + ".log") } diff --git a/config/p2p.go b/config/p2p.go index 0273f3f..ffb13a5 100644 --- a/config/p2p.go +++ b/config/p2p.go @@ -49,16 +49,58 @@ func InitP2PFlags() { viper.BindPFlag("p2p.port", pflag.Lookup("port")) } -func InitP2P() { +type ConnmgrStruct struct { + LowWatermark int `yaml:"low-watermark"` + HighWatermark int `yaml:"high-watermark"` + GracePeriod time.Duration `yaml:"grace-period"` +} + +type DiscoveryStruct struct { + AdvertiseInterval time.Duration `yaml:"advertise-interval"` + AdvertiseTTL time.Duration `yaml:"advertise-ttl"` + AdvertiseLimit int `yaml:"advertise-limit"` + DHT bool `yaml:"dht"` + MDNS bool `yaml:"mdns"` +} - // Identity +type P2PConfigStruct struct { + Identity string `yaml:"identity"` + Port int `yaml:"port"` + Connmgr ConnmgrStruct `yaml:"connmgr"` + Discovery DiscoveryStruct `yaml:"discovery"` +} + +func P2PConfig() P2PConfigStruct { viper.SetDefault("p2p.identity", fakeP2PIdentity) + p2pIdentity, err := P2PIdentity() + if err != nil { + panic(err) + } + + return P2PConfigStruct{ + Identity: p2pIdentity, + Port: P2PPort(), + Connmgr: ConnmgrStruct{ + LowWatermark: P2PConnmgrLowWatermark(), + HighWatermark: P2PConnmgrHighWatermark(), + GracePeriod: P2PConnMgrGracePeriod()}, + Discovery: DiscoveryStruct{ + AdvertiseInterval: P2PDiscoveryAdvertiseInterval(), + AdvertiseTTL: P2PDiscoveryAdvertiseTTL(), + AdvertiseLimit: P2PDiscoveryAdvertiseLimit(), + DHT: P2PDiscoveryDHT(), + MDNS: P2PDiscoveryMDNS()}, + } } -func P2PIdentity() string { +func P2PIdentity() (string, error) { + + if GenerateFlag() { + return GenerateNodeIdentity() + } - return viper.GetString("p2p.identity") + return viper.GetString("p2p.identity"), nil } func P2PDiscoveryAdvertiseInterval() time.Duration { diff --git a/config/template.go b/config/template.go deleted file mode 100644 index 934044d..0000000 --- a/config/template.go +++ /dev/null @@ -1,11 +0,0 @@ -package config - -var template map[string]interface{} - -func SetTemplate(t map[string]interface{}) { - template = t -} - -func GetTemplate() map[string]interface{} { - return template -} diff --git a/config/ui.go b/config/ui.go index a2b4f10..a4a3ff5 100644 --- a/config/ui.go +++ b/config/ui.go @@ -4,6 +4,22 @@ import ( "github.com/spf13/viper" ) +type UIStruct struct { + PeerslistWidth int `yaml:"peerslist-width"` +} + +type UIConfigStruct struct { + UI UIStruct `yaml:"ui"` +} + +func UIConfig() UIConfigStruct { + return UIConfigStruct{ + UI: UIStruct{ + PeerslistWidth: UIPeerslistWidth(), + }, + } +} + func UIPeerslistWidth() int { return viper.GetInt("ui.peerslist-width") } diff --git a/config/xdg.go b/config/xdg.go index 30ddff2..d75c990 100644 --- a/config/xdg.go +++ b/config/xdg.go @@ -1,6 +1,59 @@ package config -import "os" +import ( + "os" + "path/filepath" + + "github.com/adrg/xdg" + "github.com/bahner/go-ma" + "github.com/bahner/go-ma-actor/internal" + "github.com/mitchellh/go-homedir" + log "github.com/sirupsen/logrus" + "github.com/spf13/pflag" +) + +var ( + configHome string = xdg.ConfigHome + "/" + ma.NAME + "/" + dataHome string = xdg.DataHome + "/" + ma.NAME + "/" + defaultConfigFile string = internal.NormalisePath(configHome + Profile() + ".yaml") +) + +// Returns the configfile name to use. +// The preferred value is the explcitily requested config file on the command line. +// Else it uses the nick of the actor or the mode. +func File() string { + + var ( + filename string + err error + ) + + config, err := pflag.CommandLine.GetString("config") + if err != nil { + log.Fatal(err) + } + + // Prefer explicitly requested config. If not, use the name of the profile name. + if config != defaultConfigFile && config != "" { + filename, err = homedir.Expand(config) + if err != nil { + log.Fatal(err) + } + } else { + filename = configHome + Profile() + ".yaml" + } + + return filepath.Clean(filename) + +} + +func XDGConfigHome() string { + return configHome +} + +func XDGDataHome() string { + return dataHome +} func createXDGDirectories() error { diff --git a/db/db.go b/db/db.go index 9f55275..41b4c40 100644 --- a/db/db.go +++ b/db/db.go @@ -4,21 +4,12 @@ import ( "fmt" "sync" - "github.com/bahner/go-ma-actor/config" + "github.com/bahner/go-ma-actor/internal" badger "github.com/dgraph-io/badger/v3" "github.com/mitchellh/go-homedir" - "github.com/spf13/pflag" "github.com/spf13/viper" ) -func init() { - - pflag.String("db-path", config.DefaultDbPath, "Directory to use for database.") - viper.BindPFlag("db.path", pflag.Lookup("db-path")) - viper.SetDefault("db.path", config.DefaultDbPath) - -} - var ( db *badger.DB once sync.Once @@ -78,6 +69,6 @@ func dbDir() (string, error) { return "", err } - return config.NormalisePath(p), nil + return internal.NormalisePath(p), nil } diff --git a/entity/actor/config.go b/entity/actor/config.go deleted file mode 100644 index 53c33b2..0000000 --- a/entity/actor/config.go +++ /dev/null @@ -1,100 +0,0 @@ -package actor - -import ( - "errors" - "os" - - "github.com/bahner/go-ma-actor/config" - "github.com/bahner/go-ma/did/doc" - log "github.com/sirupsen/logrus" - "github.com/spf13/pflag" - "github.com/spf13/viper" -) - -func InitConfig(name string) { - - // Always parse the flags first - config.InitCommonFlags() - config.InitActorFlags() - pflag.Parse() - config.SetProfile(name) - config.Init() - - if config.GenerateFlag() { - // Reinit logging to STDOUT - log.SetOutput(os.Stdout) - log.Info("Generating new actor and node identity") - actor, node := generateActorIdentitiesOrPanic(name) - actorConfig := configTemplate(actor, node) - config.Generate(actorConfig) - os.Exit(0) - } - - // At this point an actor *must* be initialized - config.InitActor() - - // This flag is dependent on the actor to be initialized to make sense. - if config.ShowConfigFlag() { - config.Print() - os.Exit(0) - } - -} - -func generateActorIdentitiesOrPanic(name string) (string, string) { - actor, node, err := config.GenerateActorIdentities(name) - if err != nil { - if errors.Is(err, doc.ErrAlreadyPublished) { - log.Warnf("Actor document already published: %v", err) - } else { - log.Fatal(err) - } - } - return actor, node -} - -func configTemplate(identity string, node string) map[string]interface{} { - - // Get the default settings as a map - // Note: Viper does not have a built-in way to directly extract only the config - // so we manually recreate the structure based on the config we have set. - return map[string]interface{}{ - "actor": map[string]interface{}{ - "identity": identity, - "location": config.ActorLocation(), - "nick": config.ActorNick(), - }, - "db": map[string]interface{}{ - "dir": config.DefaultDbPath, - }, - // Use default log settings, so as not to pick up debug log settings - "log": map[string]interface{}{ - "level": viper.GetString("log.level"), - "file": viper.GetString("log.file"), - }, - // NB! This is a cross over from go-ma - "api": map[string]interface{}{ - // This must be set corretly for generation to work - "maddr": viper.GetString("api.maddr"), - }, - "http": map[string]interface{}{ - "socket": config.HttpSocket(), - }, - "p2p": map[string]interface{}{ - "identity": node, - "port": config.P2PPort(), - "connmgr": map[string]interface{}{ - "low-watermark": config.P2PConnmgrLowWatermark(), - "high-watermark": config.P2PConnmgrHighWatermark(), - "grace-period": config.P2PConnMgrGracePeriod(), - }, - "discovery": map[string]interface{}{ - "advertise-ttl": config.P2PDiscoveryAdvertiseTTL(), - "advertise-limit": config.P2PDiscoveryAdvertiseLimit(), - "advertise-interval": config.P2PDiscoveryAdvertiseInterval(), - "dht": config.P2PDiscoveryDHT(), - "mdns": config.P2PDiscoveryMDNS(), - }, - }, - } -} diff --git a/internal/path.go b/internal/path.go new file mode 100644 index 0000000..ee4d787 --- /dev/null +++ b/internal/path.go @@ -0,0 +1,7 @@ +package internal + +import "path/filepath" + +func NormalisePath(path string) string { + return filepath.ToSlash(filepath.Clean(path)) +} diff --git a/ui/save.go b/ui/save.go index 947a62d..9802a57 100644 --- a/ui/save.go +++ b/ui/save.go @@ -12,7 +12,7 @@ const ( func (ui *ChatUI) handleSaveCommand(args []string) { if len(args) == 1 { - err := config.Save() + err := config.Write(ui.c) if err != nil { ui.displaySystemMessage("Error: " + err.Error()) return diff --git a/ui/ui.go b/ui/ui.go index 8ff2d7a..52b54b8 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -5,6 +5,7 @@ import ( "fmt" "io" + "github.com/bahner/go-ma-actor/config" "github.com/bahner/go-ma-actor/entity" "github.com/bahner/go-ma-actor/entity/actor" "github.com/bahner/go-ma-actor/p2p" @@ -43,6 +44,7 @@ func init() { // chat prompt. type ChatUI struct { p *p2p.P2P + c config.Config // The actor is need to encrypt and sign messages in the event loop. a *actor.Actor