diff --git a/alby/alby_oauth_service.go b/alby/alby_oauth_service.go index dd9915b9..13bb554e 100644 --- a/alby/alby_oauth_service.go +++ b/alby/alby_oauth_service.go @@ -97,7 +97,10 @@ func (svc *albyOAuthService) CallbackHandler(ctx context.Context, code string, l if err != nil { logger.Logger.WithError(err).Error("Failed to fetch user me") // remove token so user can retry - svc.cfg.SetUpdate(accessTokenKey, "", "") + cfgErr := svc.cfg.SetUpdate(accessTokenKey, "", "") + if cfgErr != nil { + logger.Logger.WithError(cfgErr).Error("failed to remove existing access token") + } return err } @@ -109,7 +112,11 @@ func (svc *albyOAuthService) CallbackHandler(ctx context.Context, code string, l // save the user's alby account ID on first time login if existingUserIdentifier == "" { - svc.cfg.SetUpdate(userIdentifierKey, me.Identifier, "") + err := svc.cfg.SetUpdate(userIdentifierKey, me.Identifier, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to set user identifier") + return err + } if svc.cfg.GetEnv().AutoLinkAlbyAccount { // link account on first login @@ -121,7 +128,10 @@ func (svc *albyOAuthService) CallbackHandler(ctx context.Context, code string, l } else if me.Identifier != existingUserIdentifier { // remove token so user can retry with correct account - svc.cfg.SetUpdate(accessTokenKey, "", "") + err := svc.cfg.SetUpdate(accessTokenKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to set user access token") + } return errors.New("Alby Hub is connected to a different alby account. Please log out of your Alby Account at getalby.com and try again.") } @@ -155,9 +165,18 @@ func (svc *albyOAuthService) IsConnected(ctx context.Context) bool { } func (svc *albyOAuthService) saveToken(token *oauth2.Token) { - svc.cfg.SetUpdate(accessTokenExpiryKey, strconv.FormatInt(token.Expiry.Unix(), 10), "") - svc.cfg.SetUpdate(accessTokenKey, token.AccessToken, "") - svc.cfg.SetUpdate(refreshTokenKey, token.RefreshToken, "") + err := svc.cfg.SetUpdate(accessTokenExpiryKey, strconv.FormatInt(token.Expiry.Unix(), 10), "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save access token expiry") + } + err = svc.cfg.SetUpdate(accessTokenKey, token.AccessToken, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save access token") + } + err = svc.cfg.SetUpdate(refreshTokenKey, token.RefreshToken, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save refresh token") + } } var tokenMutex sync.Mutex @@ -202,8 +221,8 @@ func (svc *albyOAuthService) fetchUserToken(ctx context.Context) (*oauth2.Token, RefreshToken: refreshToken, } - // only use the current token if it has at least 20 seconds before expiry - if currentToken.Expiry.After(time.Now().Add(time.Duration(20) * time.Second)) { + // only use the current token if it has at least 60 seconds before expiry + if currentToken.Expiry.After(time.Now().Add(time.Duration(60) * time.Second)) { logger.Logger.Debug("Using existing Alby OAuth token") return currentToken, nil } @@ -290,7 +309,10 @@ func (svc *albyOAuthService) GetMe(ctx context.Context) (*AlbyMe, error) { return nil, err } - svc.cfg.SetUpdate(lightningAddressKey, me.LightningAddress, "") + err = svc.cfg.SetUpdate(lightningAddressKey, me.LightningAddress, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save lightning address") + } logger.Logger.WithFields(logrus.Fields{"me": me}).Info("Alby me response") return me, nil @@ -451,9 +473,6 @@ func (svc *albyOAuthService) SendPayment(ctx context.Context, invoice string) er } func (svc *albyOAuthService) GetAuthUrl() string { - if svc.cfg.GetEnv().AlbyClientId == "" || svc.cfg.GetEnv().AlbyClientSecret == "" { - logger.Logger.Fatalf("No ALBY_OAUTH_CLIENT_ID or ALBY_OAUTH_CLIENT_SECRET set") - } return svc.oauthConf.AuthCodeURL("unused") } @@ -464,11 +483,26 @@ func (svc *albyOAuthService) UnlinkAccount(ctx context.Context) error { } svc.deleteAlbyAccountApps() - svc.cfg.SetUpdate(userIdentifierKey, "", "") - svc.cfg.SetUpdate(accessTokenKey, "", "") - svc.cfg.SetUpdate(accessTokenExpiryKey, "", "") - svc.cfg.SetUpdate(refreshTokenKey, "", "") - svc.cfg.SetUpdate(lightningAddressKey, "", "") + err = svc.cfg.SetUpdate(userIdentifierKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to remove user identifier from config") + } + err = svc.cfg.SetUpdate(accessTokenKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to remove access token from config") + } + err = svc.cfg.SetUpdate(accessTokenExpiryKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to remove access token expiry from config") + } + err = svc.cfg.SetUpdate(refreshTokenKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to remove refresh token from config") + } + err = svc.cfg.SetUpdate(lightningAddressKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to remove lightning address from config") + } return nil } diff --git a/api/api.go b/api/api.go index 51ea0e2f..ce4719e2 100644 --- a/api/api.go +++ b/api/api.go @@ -525,7 +525,10 @@ func (api *api) GetNewOnchainAddress(ctx context.Context) (string, error) { return "", err } - api.cfg.SetUpdate(config.OnchainAddressKey, address, "") + err = api.cfg.SetUpdate(config.OnchainAddressKey, address, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save new onchain address to config") + } return address, nil } @@ -706,7 +709,10 @@ func (api *api) GetMnemonic(unlockPassword string) (*MnemonicResponse, error) { } func (api *api) SetNextBackupReminder(backupReminderRequest *BackupReminderRequest) error { - api.cfg.SetUpdate("NextBackupReminder", backupReminderRequest.NextBackupReminder, "") + err := api.cfg.SetUpdate("NextBackupReminder", backupReminderRequest.NextBackupReminder, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save next backup reminder to config") + } return nil } @@ -751,44 +757,89 @@ func (api *api) Setup(ctx context.Context, setupRequest *SetupRequest) error { return errors.New("no unlock password provided") } - api.cfg.Setup(setupRequest.UnlockPassword) - - // TODO: move all below code to cfg.Setup() + err = api.cfg.SaveUnlockPasswordCheck(setupRequest.UnlockPassword) + if err != nil { + return err + } // update next backup reminder - api.cfg.SetUpdate("NextBackupReminder", setupRequest.NextBackupReminder, "") + err = api.cfg.SetUpdate("NextBackupReminder", setupRequest.NextBackupReminder, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save next backup reminder") + } + // only update non-empty values if setupRequest.LNBackendType != "" { - api.cfg.SetUpdate("LNBackendType", setupRequest.LNBackendType, "") + err = api.cfg.SetUpdate("LNBackendType", setupRequest.LNBackendType, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to save backend type") + return err + } } if setupRequest.BreezAPIKey != "" { - api.cfg.SetUpdate("BreezAPIKey", setupRequest.BreezAPIKey, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("BreezAPIKey", setupRequest.BreezAPIKey, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save breez api key") + return err + } } if setupRequest.Mnemonic != "" { - api.cfg.SetUpdate("Mnemonic", setupRequest.Mnemonic, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("Mnemonic", setupRequest.Mnemonic, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save encrypted mnemonic") + return err + } } if setupRequest.GreenlightInviteCode != "" { - api.cfg.SetUpdate("GreenlightInviteCode", setupRequest.GreenlightInviteCode, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("GreenlightInviteCode", setupRequest.GreenlightInviteCode, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save greenlight invite code") + return err + } } if setupRequest.LNDAddress != "" { - api.cfg.SetUpdate("LNDAddress", setupRequest.LNDAddress, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("LNDAddress", setupRequest.LNDAddress, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save lnd address") + return err + } } if setupRequest.LNDCertHex != "" { - api.cfg.SetUpdate("LNDCertHex", setupRequest.LNDCertHex, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("LNDCertHex", setupRequest.LNDCertHex, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save lnd cert hex") + return err + } } if setupRequest.LNDMacaroonHex != "" { - api.cfg.SetUpdate("LNDMacaroonHex", setupRequest.LNDMacaroonHex, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("LNDMacaroonHex", setupRequest.LNDMacaroonHex, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save lnd macaroon hex") + return err + } } if setupRequest.PhoenixdAddress != "" { - api.cfg.SetUpdate("PhoenixdAddress", setupRequest.PhoenixdAddress, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("PhoenixdAddress", setupRequest.PhoenixdAddress, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save phoenix address") + return err + } } if setupRequest.PhoenixdAuthorization != "" { - api.cfg.SetUpdate("PhoenixdAuthorization", setupRequest.PhoenixdAuthorization, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("PhoenixdAuthorization", setupRequest.PhoenixdAuthorization, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save phoenix auth") + return err + } } if setupRequest.CashuMintUrl != "" { - api.cfg.SetUpdate("CashuMintUrl", setupRequest.CashuMintUrl, setupRequest.UnlockPassword) + err = api.cfg.SetUpdate("CashuMintUrl", setupRequest.CashuMintUrl, setupRequest.UnlockPassword) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save cashu mint url") + return err + } } return nil diff --git a/cmd/http/main.go b/cmd/http/main.go index 134c529e..6be3eb61 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -21,8 +21,8 @@ func main() { // Create a channel to receive OS signals. osSignalChannel := make(chan os.Signal, 1) - // Notify the channel on os.Interrupt, syscall.SIGTERM, and os.Kill. - signal.Notify(osSignalChannel, os.Interrupt, syscall.SIGTERM, os.Kill) + // Notify the channel on os.Interrupt, syscall.SIGTERM. os.Kill cannot be caught. + signal.Notify(osSignalChannel, os.Interrupt, syscall.SIGTERM) ctx, cancel := context.WithCancel(context.Background()) svc, _ := service.NewService(ctx) @@ -35,7 +35,8 @@ func main() { //start Echo server go func() { if err := e.Start(fmt.Sprintf(":%v", svc.GetConfig().GetEnv().Port)); err != nil && err != nethttp.ErrServerClosed { - logger.Logger.Fatalf("shutting down the server: %v", err) + logger.Logger.WithError(err).Error("echo server failed to start") + ctx.Done() } }() diff --git a/config/config.go b/config/config.go index f456a2ac..84eeeec5 100644 --- a/config/config.go +++ b/config/config.go @@ -25,50 +25,77 @@ const ( unlockPasswordCheck = "THIS STRING SHOULD MATCH IF PASSWORD IS CORRECT" ) -func NewConfig(env *AppConfig, db *gorm.DB) *config { +func NewConfig(env *AppConfig, db *gorm.DB) (*config, error) { cfg := &config{ db: db, } - cfg.init(env) - return cfg + err := cfg.init(env) + if err != nil { + return nil, err + } + + return cfg, nil } -func (cfg *config) init(env *AppConfig) { +func (cfg *config) init(env *AppConfig) error { cfg.Env = env if cfg.Env.Relay != "" { - cfg.SetIgnore("Relay", cfg.Env.Relay, "") + err := cfg.SetIgnore("Relay", cfg.Env.Relay, "") + if err != nil { + return err + } } if cfg.Env.LNBackendType != "" { - cfg.SetIgnore("LNBackendType", cfg.Env.LNBackendType, "") + err := cfg.SetIgnore("LNBackendType", cfg.Env.LNBackendType, "") + if err != nil { + return err + } } // LND specific to support env variables if cfg.Env.LNDAddress != "" { - cfg.SetIgnore("LNDAddress", cfg.Env.LNDAddress, "") + err := cfg.SetIgnore("LNDAddress", cfg.Env.LNDAddress, "") + if err != nil { + return err + } } if cfg.Env.LNDCertFile != "" { certBytes, err := os.ReadFile(cfg.Env.LNDCertFile) if err != nil { - logger.Logger.Fatalf("Failed to read LND cert file: %v", err) + logger.Logger.WithError(err).Error("Failed to read LND cert file") + return err } certHex := hex.EncodeToString(certBytes) - cfg.SetIgnore("LNDCertHex", certHex, "") + err = cfg.SetIgnore("LNDCertHex", certHex, "") + if err != nil { + return err + } } if cfg.Env.LNDMacaroonFile != "" { macBytes, err := os.ReadFile(cfg.Env.LNDMacaroonFile) if err != nil { - logger.Logger.Fatalf("Failed to read LND macaroon file: %v", err) + logger.Logger.WithError(err).Error("Failed to read LND macaroon file") + return err } macHex := hex.EncodeToString(macBytes) - cfg.SetIgnore("LNDMacaroonHex", macHex, "") + err = cfg.SetIgnore("LNDMacaroonHex", macHex, "") + if err != nil { + return err + } } // Phoenix specific to support env variables if cfg.Env.PhoenixdAddress != "" { - cfg.SetIgnore("PhoenixdAddress", cfg.Env.PhoenixdAddress, "") + err := cfg.SetIgnore("PhoenixdAddress", cfg.Env.PhoenixdAddress, "") + if err != nil { + return err + } } if cfg.Env.PhoenixdAuthorization != "" { - cfg.SetIgnore("PhoenixdAuthorization", cfg.Env.PhoenixdAuthorization, "") + err := cfg.SetIgnore("PhoenixdAuthorization", cfg.Env.PhoenixdAuthorization, "") + if err != nil { + return err + } } // set the JWT secret to the one from the env @@ -76,11 +103,17 @@ func (cfg *config) init(env *AppConfig) { cfg.JWTSecret = cfg.Env.JWTSecret if cfg.JWTSecret == "" { hex, err := randomHex(32) - if err == nil { - cfg.SetIgnore("JWTSecret", hex, "") + if err != nil { + logger.Logger.WithError(err).Error("failed to generate JWT secret") + return err + } + err = cfg.SetIgnore("JWTSecret", hex, "") + if err != nil { + return err } cfg.JWTSecret, _ = cfg.Get("JWTSecret", "") } + return nil } func (cfg *config) SetupCompleted() bool { @@ -147,26 +180,30 @@ func (cfg *config) set(key string, value string, clauses clause.OnConflict, encr return nil } -func (cfg *config) SetIgnore(key string, value string, encryptionKey string) { +func (cfg *config) SetIgnore(key string, value string, encryptionKey string) error { clauses := clause.OnConflict{ Columns: []clause.Column{{Name: "key"}}, DoNothing: true, } err := cfg.set(key, value, clauses, encryptionKey, cfg.db) if err != nil { - logger.Logger.Fatalf("Failed to save config: %v", err) + logger.Logger.WithField("key", key).WithError(err).Error("Failed to set config key with ignore", err) + return err } + return nil } -func (cfg *config) SetUpdate(key string, value string, encryptionKey string) { +func (cfg *config) SetUpdate(key string, value string, encryptionKey string) error { clauses := clause.OnConflict{ Columns: []clause.Column{{Name: "key"}}, DoUpdates: clause.AssignmentColumns([]string{"value"}), } err := cfg.set(key, value, clauses, encryptionKey, cfg.db) if err != nil { - logger.Logger.Fatalf("Failed to save config: %v", err) + logger.Logger.WithField("key", key).WithError(err).Error("Failed to set config key with update", err) + return err } + return nil } func (cfg *config) ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error { @@ -219,9 +256,13 @@ func (cfg *config) CheckUnlockPassword(encryptionKey string) bool { return err == nil && (decryptedValue == "" || decryptedValue == unlockPasswordCheck) } -// TODO: rename -func (cfg *config) Setup(encryptionKey string) { - cfg.SetUpdate("UnlockPasswordCheck", unlockPasswordCheck, encryptionKey) +func (cfg *config) SaveUnlockPasswordCheck(encryptionKey string) error { + err := cfg.SetUpdate("UnlockPasswordCheck", unlockPasswordCheck, encryptionKey) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save unlock password check to config") + return err + } + return nil } func (cfg *config) GetEnv() *AppConfig { diff --git a/config/models.go b/config/models.go index 9b2dea66..0491494b 100644 --- a/config/models.go +++ b/config/models.go @@ -50,13 +50,13 @@ func (c *AppConfig) IsDefaultClientId() bool { type Config interface { Get(key string, encryptionKey string) (string, error) - SetIgnore(key string, value string, encryptionKey string) - SetUpdate(key string, value string, encryptionKey string) + SetIgnore(key string, value string, encryptionKey string) error + SetUpdate(key string, value string, encryptionKey string) error GetJWTSecret() string GetRelayUrl() string GetEnv() *AppConfig CheckUnlockPassword(password string) bool ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error - Setup(encryptionKey string) + SaveUnlockPasswordCheck(encryptionKey string) error SetupCompleted() bool } diff --git a/events/events.go b/events/events.go index cb609c5f..8030243b 100644 --- a/events/events.go +++ b/events/events.go @@ -46,12 +46,23 @@ func (ep *eventPublisher) RemoveSubscriber(listenerToRemove EventSubscriber) { } func (ep *eventPublisher) Publish(event *Event) { + ep.publish(event, false) +} +func (ep *eventPublisher) PublishSync(event *Event) { + ep.publish(event, true) +} + +func (ep *eventPublisher) publish(event *Event, sync bool) { ep.subscriberMtx.Lock() defer ep.subscriberMtx.Unlock() logger.Logger.WithFields(logrus.Fields{"event": event, "global": ep.globalProperties}).Debug("Publishing event") for _, listener := range ep.listeners { - // consume event without blocking thread - go listener.ConsumeEvent(context.Background(), event, ep.globalProperties) + if sync { + listener.ConsumeEvent(context.Background(), event, ep.globalProperties) + } else { + // consume event without blocking thread + go listener.ConsumeEvent(context.Background(), event, ep.globalProperties) + } } } diff --git a/events/models.go b/events/models.go index 5887cff6..0bdcfaa6 100644 --- a/events/models.go +++ b/events/models.go @@ -12,6 +12,7 @@ type EventPublisher interface { RegisterSubscriber(eventListener EventSubscriber) RemoveSubscriber(eventListener EventSubscriber) Publish(event *Event) + PublishSync(event *Event) SetGlobalProperty(key string, value interface{}) } diff --git a/lnclient/greenlight/greenlight.go b/lnclient/greenlight/greenlight.go index 3dab6067..432313a7 100644 --- a/lnclient/greenlight/greenlight.go +++ b/lnclient/greenlight/greenlight.go @@ -66,14 +66,19 @@ func NewGreenlightService(cfg config.Config, mnemonic, inviteCode, workDir, encr credentials = &recoveredCredentials if err != nil { - logger.Logger.Fatalf("Failed to register new node") + logger.Logger.WithError(err).Error("Failed to register new node") + return nil, err } } if credentials == nil || credentials.GlCreds == "" { return nil, errors.New("unexpected response from Recover") } - cfg.SetUpdate(DEVICE_CREDENTIALS_KEY, credentials.GlCreds, encryptionKey) + err = cfg.SetUpdate(DEVICE_CREDENTIALS_KEY, credentials.GlCreds, encryptionKey) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save greenlight credentials") + return nil, err + } } client, err := glalby.NewBlockingGreenlightAlbyClient(mnemonic, *credentials) @@ -83,7 +88,7 @@ func NewGreenlightService(cfg config.Config, mnemonic, inviteCode, workDir, encr return nil, err } if client == nil { - log.Fatalf("unexpected response from NewBlockingGreenlightAlbyClient") + logger.Logger.Error("unexpected response from NewBlockingGreenlightAlbyClient") } nodeInfo, err := client.GetInfo() diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index 86a4fc77..3886dbcf 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -415,10 +415,15 @@ func (ls *LDKService) resetRouterInternal() { if err != nil { logger.Logger.Error("Failed to retrieve ResetRouter key") + return } if key != "" { - ls.cfg.SetUpdate(resetRouterKey, "", "") + err = ls.cfg.SetUpdate(resetRouterKey, "", "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to remove reset router key") + return + } logger.Logger.WithField("key", key).Info("Resetting router") ldkDbPath := filepath.Join(ls.workdir, "storage", "ldk_node_data.sqlite") @@ -1086,7 +1091,11 @@ func (ls *LDKService) RedeemOnchainFunds(ctx context.Context, toAddress string, } func (ls *LDKService) ResetRouter(key string) error { - ls.cfg.SetUpdate(resetRouterKey, key, "") + err := ls.cfg.SetUpdate(resetRouterKey, key, "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to set reset router key") + return err + } return nil } diff --git a/service/keys/keys.go b/service/keys/keys.go index cde80dd9..803f3846 100644 --- a/service/keys/keys.go +++ b/service/keys/keys.go @@ -28,7 +28,11 @@ func (keys *keys) Init(cfg config.Config, encryptionKey string) error { if nostrSecretKey == "" { nostrSecretKey = nostr.GeneratePrivateKey() - cfg.SetUpdate("NostrSecretKey", nostrSecretKey, encryptionKey) + err := cfg.SetUpdate("NostrSecretKey", nostrSecretKey, encryptionKey) + if err != nil { + logger.Logger.WithError(err).Error("Failed to save generated nostr secret key") + return err + } } nostrPublicKey, err := nostr.GetPublicKey(nostrSecretKey) if err != nil { @@ -41,15 +45,9 @@ func (keys *keys) Init(cfg config.Config, encryptionKey string) error { } func (keys *keys) GetNostrPublicKey() string { - if keys.nostrPublicKey == "" { - logger.Logger.Fatal("keys not initialized") - } return keys.nostrPublicKey } func (keys *keys) GetNostrSecretKey() string { - if keys.nostrSecretKey == "" { - logger.Logger.Fatal("keys not initialized") - } return keys.nostrSecretKey } diff --git a/service/service.go b/service/service.go index 04eb4cd9..34cd0b10 100644 --- a/service/service.go +++ b/service/service.go @@ -2,7 +2,6 @@ package service import ( "context" - "time" "os" "path/filepath" @@ -69,7 +68,11 @@ func NewService(ctx context.Context) (*service, error) { return nil, err } - finishRestoreNode(appConfig.Workdir) + err = finishRestoreNode(appConfig.Workdir) + if err != nil { + logger.Logger.WithError(err).Error("failed to restore backup") + return nil, err + } // If DATABASE_URI is a URI or a path, leave it unchanged. // If it only contains a filename, prepend the workdir. @@ -85,7 +88,10 @@ func NewService(ctx context.Context) (*service, error) { return nil, err } - cfg := config.NewConfig(appConfig, gormDB) + cfg, err := config.NewConfig(appConfig, gormDB) + if err != nil { + return nil, err + } eventPublisher := events.NewEventPublisher() @@ -167,21 +173,23 @@ func (svc *service) StartSubscription(ctx context.Context, sub *nostr.Subscripti return nil } -func finishRestoreNode(workDir string) { +func finishRestoreNode(workDir string) error { restoreDir := filepath.Join(workDir, "restore") if restoreDirStat, err := os.Stat(restoreDir); err == nil && restoreDirStat.IsDir() { logger.Logger.WithField("restoreDir", restoreDir).Infof("Restore directory found. Finishing Node restore") existingFiles, err := os.ReadDir(restoreDir) if err != nil { - logger.Logger.WithError(err).Fatal("Failed to read WORK_DIR") + logger.Logger.WithError(err).Error("Failed to read WORK_DIR") + return err } for _, file := range existingFiles { if file.Name() != "restore" { err = os.RemoveAll(filepath.Join(workDir, file.Name())) if err != nil { - logger.Logger.WithField("filename", file.Name()).WithError(err).Fatal("Failed to remove file") + logger.Logger.WithField("filename", file.Name()).WithError(err).Error("Failed to remove file") + return err } logger.Logger.WithField("filename", file.Name()).Info("removed file") } @@ -189,31 +197,33 @@ func finishRestoreNode(workDir string) { files, err := os.ReadDir(restoreDir) if err != nil { - logger.Logger.WithError(err).Fatal("Failed to read restore directory") + logger.Logger.WithError(err).Error("Failed to read restore directory") + return err } for _, file := range files { err = os.Rename(filepath.Join(restoreDir, file.Name()), filepath.Join(workDir, file.Name())) if err != nil { - logger.Logger.WithField("filename", file.Name()).WithError(err).Fatal("Failed to move file") + logger.Logger.WithField("filename", file.Name()).WithError(err).Error("Failed to move file") + return err } logger.Logger.WithField("filename", file.Name()).Info("copied file from restore directory") } err = os.RemoveAll(restoreDir) if err != nil { - logger.Logger.WithError(err).Fatal("Failed to remove restore directory") + logger.Logger.WithError(err).Error("Failed to remove restore directory") + return err } logger.Logger.WithField("restoreDir", restoreDir).Info("removed restore directory") } + return nil } func (svc *service) Shutdown() { svc.StopApp() - svc.eventPublisher.Publish(&events.Event{ + svc.eventPublisher.PublishSync(&events.Event{ Event: "nwc_stopped", }) db.Stop(svc.db) - // wait for any remaining events - time.Sleep(1 * time.Second) } func (svc *service) GetDB() *gorm.DB { diff --git a/service/start.go b/service/start.go index c5adb54c..b3bd21a6 100644 --- a/service/start.go +++ b/service/start.go @@ -3,6 +3,7 @@ package service import ( "context" "errors" + "fmt" "path" "strconv" "time" @@ -29,12 +30,14 @@ func (svc *service) startNostr(ctx context.Context, encryptionKey string) error err := svc.keys.Init(svc.cfg, encryptionKey) if err != nil { - logger.Logger.WithError(err).Fatal("Failed to init nostr keys") + logger.Logger.WithError(err).Error("Failed to init nostr keys") + return err } npub, err := nip19.EncodePublicKey(svc.keys.GetNostrPublicKey()) if err != nil { - logger.Logger.WithError(err).Fatal("Error converting nostr privkey to pubkey") + logger.Logger.WithError(err).Error("Error converting nostr privkey to pubkey") + return err } logger.Logger.WithFields(logrus.Fields{ @@ -204,7 +207,8 @@ func (svc *service) launchLNBackend(ctx context.Context, encryptionKey string) e lnClient, err = cashu.NewCashuService(cashuWorkdir, cashuMintUrl) default: - logger.Logger.Fatalf("Unsupported LNBackendType: %v", lnBackend) + logger.Logger.WithField("backend_type", lnBackend).Error("Unsupported LNBackendType") + return fmt.Errorf("unsupported backend type: %s", lnBackend) } if err != nil { logger.Logger.WithError(err).Error("Failed to launch LN backend") @@ -227,7 +231,10 @@ func (svc *service) launchLNBackend(ctx context.Context, encryptionKey string) e // Mark that the node has successfully started // This will ensure the user cannot go through the setup again - svc.cfg.SetUpdate("NodeLastStartTime", strconv.FormatInt(time.Now().Unix(), 10), "") + err = svc.cfg.SetUpdate("NodeLastStartTime", strconv.FormatInt(time.Now().Unix(), 10), "") + if err != nil { + logger.Logger.WithError(err).Error("Failed to set last node start time") + } svc.eventPublisher.Publish(&events.Event{ Event: "nwc_node_started", diff --git a/tests/test_service.go b/tests/test_service.go index faaab670..baed73d6 100644 --- a/tests/test_service.go +++ b/tests/test_service.go @@ -33,10 +33,13 @@ func CreateTestService() (svc *TestService, err error) { Workdir: ".test", } - cfg := config.NewConfig( + cfg, err := config.NewConfig( appConfig, gormDb, ) + if err != nil { + return nil, err + } keys := keys.NewKeys() keys.Init(cfg, "") diff --git a/wails/wails_app.go b/wails/wails_app.go index 4425476e..4b776e3c 100644 --- a/wails/wails_app.go +++ b/wails/wails_app.go @@ -3,7 +3,6 @@ package wails import ( "context" "embed" - "log" "github.com/getAlby/hub/api" "github.com/getAlby/hub/logger" @@ -65,7 +64,7 @@ func LaunchWailsApp(app *WailsApp, assets embed.FS, appIcon []byte) { }) if err != nil { - log.Fatalf("Error %v", err) + logger.Logger.WithError(err).Error("failed to run Wails app") } }