diff --git a/api.go b/api.go index 1e7f7090..8e325f93 100644 --- a/api.go +++ b/api.go @@ -12,7 +12,6 @@ import ( "github.com/getAlby/nostr-wallet-connect/models/db" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" - "gorm.io/gorm/clause" ) // TODO: these methods should be moved to a separate object, not in Service @@ -221,40 +220,37 @@ func (svc *Service) GetInfo() *api.InfoResponse { } func (svc *Service) Setup(setupRequest *api.SetupRequest) error { - dbConfigEntries := []db.ConfigEntry{} - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "LN_BACKEND_TYPE", Value: setupRequest.LNBackendType}) - - // TODO: reduce duplication + dbConfig := db.Config{} + svc.db.First(&dbConfig) + // only update non-empty values + if setupRequest.LNBackendType != "" { + dbConfig.LNBackendType = setupRequest.LNBackendType + } if setupRequest.BreezMnemonic != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "BREEZ_MNEMONIC", Value: setupRequest.BreezMnemonic}) + dbConfig.BreezMnemonic = setupRequest.BreezMnemonic } if setupRequest.GreenlightInviteCode != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "GREENLIGHT_INVITE_CODE", Value: setupRequest.GreenlightInviteCode}) + dbConfig.GreenlightInviteCode = setupRequest.GreenlightInviteCode } - if setupRequest.LNDAddress != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "LND_ADDRESS", Value: setupRequest.LNDAddress}) + dbConfig.LNDAddress = setupRequest.LNDAddress } if setupRequest.LNDCertFile != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "LND_CERT_FILE", Value: setupRequest.LNDCertFile}) + dbConfig.LNDCertFile = setupRequest.LNDCertFile } if setupRequest.LNDCertHex != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "LND_CERT_HEX", Value: setupRequest.LNDCertHex}) + dbConfig.LNDCertHex = setupRequest.LNDCertHex } if setupRequest.LNDMacaroonFile != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "LND_MACAROON_FILE", Value: setupRequest.LNDMacaroonFile}) + dbConfig.LNDMacaroonFile = setupRequest.LNDMacaroonFile } if setupRequest.LNDMacaroonHex != "" { - dbConfigEntries = append(dbConfigEntries, db.ConfigEntry{Key: "LND_MACAROON_HEX", Value: setupRequest.LNDMacaroonHex}) + dbConfig.LNDMacaroonHex = setupRequest.LNDMacaroonHex } - // replace existing keys with latest values - res := svc.db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "key"}}, - DoUpdates: clause.AssignmentColumns([]string{"value"}), - }).Create(&dbConfigEntries) + res := svc.db.Save(&dbConfig) if res.Error != nil { svc.Logger.Errorf("Failed to update config: %v", res.Error) diff --git a/config.go b/config.go index 94ca8d5e..52739eee 100644 --- a/config.go +++ b/config.go @@ -1,5 +1,7 @@ package main +import "github.com/getAlby/nostr-wallet-connect/models/db" + const ( AlbyBackendType = "ALBY" LNDBackendType = "LND" @@ -11,22 +13,17 @@ const ( ) type Config struct { + // These config can be set either by .env or the database config table. + // database config always takes preference. + db.Config NostrSecretKey string `envconfig:"NOSTR_PRIVKEY"` CookieSecret string `envconfig:"COOKIE_SECRET" required:"true"` CookieDomain string `envconfig:"COOKIE_DOMAIN"` ClientPubkey string `envconfig:"CLIENT_NOSTR_PUBKEY"` Relay string `envconfig:"RELAY" default:"wss://relay.getalby.com/v1"` PublicRelay string `envconfig:"PUBLIC_RELAY"` - LNBackendType string `envconfig:"LN_BACKEND_TYPE"` AppType string `envconfig:"APP_TYPE" default:"HTTP"` - LNDAddress string `envconfig:"LND_ADDRESS"` - LNDCertFile string `envconfig:"LND_CERT_FILE"` - LNDCertHex string `envconfig:"LND_CERT_HEX"` - LNDMacaroonFile string `envconfig:"LND_MACAROON_FILE"` - LNDMacaroonHex string `envconfig:"LND_MACAROON_HEX"` - BreezMnemonic string `envconfig:"BREEZ_MNEMONIC"` BreezAPIKey string `envconfig:"BREEZ_API_KEY"` - GreenlightInviteCode string `envconfig:"GREENLIGHT_INVITE_CODE"` BreezWorkdir string `envconfig:"BREEZ_WORK_DIR" default:".breez"` BasicAuthUser string `envconfig:"BASIC_AUTH_USER"` BasicAuthPassword string `envconfig:"BASIC_AUTH_PASSWORD"` diff --git a/lnd.go b/lnd.go index 6b74e4b6..15061486 100644 --- a/lnd.go +++ b/lnd.go @@ -319,6 +319,7 @@ func NewLNDService(svc *Service, lndAddress, lndCertFile, lndCertHex, lndMacaroo MacaroonHex: lndMacaroonHex, }, svc.ctx) if err != nil { + svc.Logger.Errorf("Failed to create new LND client %v", err) return nil, err } info, err := lndClient.GetInfo(svc.ctx, &lnrpc.GetInfoRequest{}) diff --git a/main.go b/main.go index e5fbc941..551914f3 100644 --- a/main.go +++ b/main.go @@ -131,6 +131,11 @@ func main() { ctx: ctx, } + err = svc.setupDbConfig() + if err != nil { + log.Fatalf("Failed to setup DB config: %v", err) + } + // TODO: remove Datadog etc. if os.Getenv("DATADOG_AGENT_URL") != "" { tracer.Start(tracer.WithService("nostr-wallet-connect")) @@ -244,6 +249,42 @@ func main() { wg.Wait() } +func (svc *Service) setupDbConfig() error { + // setup database config from the env on first run + // after first run, changes to ENV will have no effect for these fields + // because the database values always take precedence! + + var existing db.Config + res := svc.db.Limit(1).Find(&existing) + + if res.Error != nil { + return res.Error + } + + if res.RowsAffected > 0 { + // do not overwrite the existing entry + return nil + } + + newDbConfig := db.Config{ + ID: 1, + LNBackendType: svc.cfg.LNBackendType, + LNDAddress: svc.cfg.LNDAddress, + LNDCertFile: svc.cfg.LNDCertFile, + LNDCertHex: svc.cfg.LNDCertHex, + LNDMacaroonFile: svc.cfg.LNDMacaroonFile, + LNDMacaroonHex: svc.cfg.LNDMacaroonHex, + BreezMnemonic: svc.cfg.BreezMnemonic, + GreenlightInviteCode: svc.cfg.GreenlightInviteCode, + } + err := svc.db.Save(&newDbConfig).Error + if err != nil { + return err + } + + return nil +} + func (svc *Service) launchLNBackend() error { if svc.lnClient != nil { err := svc.lnClient.Shutdown() @@ -253,48 +294,28 @@ func (svc *Service) launchLNBackend() error { svc.lnClient = nil } - dbCfgEntries := []db.ConfigEntry{} - svc.db.Find(&dbCfgEntries) + dbConfig := db.Config{} + svc.db.First(&dbConfig) - mergeConfigEntry := func(defaultValue string, key string) string { - for _, v := range dbCfgEntries { - if v.Key == key { - return v.Value - } - } - return "" + if dbConfig.LNBackendType == "" { + return errors.New("No LNBackendType specified") } - // TODO: is there a better way to do this? - lnBackendType := mergeConfigEntry(svc.cfg.LNBackendType, "LN_BACKEND_TYPE") - breezMnemonic := mergeConfigEntry(svc.cfg.BreezMnemonic, "BREEZ_MNEMONIC") - greenlightInviteCode := mergeConfigEntry(svc.cfg.GreenlightInviteCode, "GREENLIGHT_INVITE_CODE") - - lndAddress := mergeConfigEntry(svc.cfg.LNDAddress, "LND_ADDRESS") - lndCertFile := mergeConfigEntry(svc.cfg.LNDCertFile, "LND_CERT_FILE") - lndCertHex := mergeConfigEntry(svc.cfg.LNDCertHex, "LND_CERT_HEX") - lndMacaroonFile := mergeConfigEntry(svc.cfg.LNDMacaroonFile, "LND_MACAROON_FILE") - lndMacaroonHex := mergeConfigEntry(svc.cfg.LNDMacaroonHex, "LND_MACAROON_HEX") - - svc.Logger.Infof("Launching new LN Backend: %s", lnBackendType) - switch lnBackendType { + svc.Logger.Infof("Launching LN Backend: %s", dbConfig.LNBackendType) + var err error + var lnClient LNClient + switch dbConfig.LNBackendType { case LNDBackendType: - lndClient, err := NewLNDService(svc, lndAddress, lndCertFile, lndCertHex, lndMacaroonFile, lndMacaroonHex) - if err != nil { - return err - } - svc.lnClient = lndClient + lnClient, err = NewLNDService(svc, dbConfig.LNDAddress, dbConfig.LNDCertFile, dbConfig.LNDCertHex, dbConfig.LNDMacaroonFile, dbConfig.LNDMacaroonHex) case BreezBackendType: - breezSvc, err := NewBreezService(svc, breezMnemonic, svc.cfg.BreezAPIKey, greenlightInviteCode, svc.cfg.BreezWorkdir) - if err != nil { - return err - } - svc.lnClient = breezSvc - case "": - return errors.New("No LNBackendType specified") + lnClient, err = NewBreezService(svc, dbConfig.BreezMnemonic, svc.cfg.BreezAPIKey, dbConfig.GreenlightInviteCode, svc.cfg.BreezWorkdir) default: - svc.Logger.Fatalf("Unsupported LNBackendType: %v", lnBackendType) + svc.Logger.Fatalf("Unsupported LNBackendType: %v", dbConfig.LNBackendType) + } + if err != nil { + return err } + svc.lnClient = lnClient return nil } diff --git a/migrations/202401161432_add_config_table.go b/migrations/202401161432_add_config_table.go index 981fb71c..148ad575 100644 --- a/migrations/202401161432_add_config_table.go +++ b/migrations/202401161432_add_config_table.go @@ -10,9 +10,16 @@ var _202401161432_add_config_table = &gormigrate.Migration{ ID: "202401161432_add_config_table", Migrate: func(tx *gorm.DB) error { return tx.Exec(` -CREATE TABLE config_entries ( - key TEXT PRIMARY KEY, - value TEXT +CREATE TABLE configs ( + id int NOT NULL PRIMARY KEY, + ln_backend_type TEXT, + lnd_address TEXT, + lnd_cert_file TEXT, + lnd_cert_hex TEXT, + lnd_macaroon_file TEXT, + lnd_macaroon_hex TEXT, + breez_mnemonic TEXT, + greenlight_invite_code TEXT );`).Error }, Rollback: func(tx *gorm.DB) error { diff --git a/models/db/db.go b/models/db/db.go index dae5bded..3e47b93a 100644 --- a/models/db/db.go +++ b/models/db/db.go @@ -1,6 +1,13 @@ package db -type ConfigEntry struct { - Key string - Value string +type Config struct { + ID int // primary key, always 1 + LNBackendType string `envconfig:"LN_BACKEND_TYPE"` + LNDAddress string `envconfig:"LND_ADDRESS"` + LNDCertFile string `envconfig:"LND_CERT_FILE"` + LNDCertHex string `envconfig:"LND_CERT_HEX"` + LNDMacaroonFile string `envconfig:"LND_MACAROON_FILE"` + LNDMacaroonHex string `envconfig:"LND_MACAROON_HEX"` + BreezMnemonic string `envconfig:"BREEZ_MNEMONIC"` + GreenlightInviteCode string `envconfig:"GREENLIGHT_INVITE_CODE"` } diff --git a/service.go b/service.go index 04c4466b..b7472994 100644 --- a/service.go +++ b/service.go @@ -16,6 +16,7 @@ import ( ) type Service struct { + // config from .env only. Fetch dynamic config from db cfg *Config db *gorm.DB lnClient LNClient