fix: replace fatal logs with proper error handling (#707)

* fix: replace fatal logs with proper error handling

- fires nwc shutdown event synchronously
- requires at least one minute leeway for access token expiry

* chore: rename error

* fix: do not mask original reason for error

* fix: config error handling and error messages

* chore: rename
This commit is contained in:
Roland 2024-09-25 17:31:05 +07:00 committed by GitHub
parent ff417c4f1e
commit 8ebb4ad716
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 264 additions and 94 deletions

View file

@ -97,7 +97,10 @@ func (svc *albyOAuthService) CallbackHandler(ctx context.Context, code string, l
if err != nil { if err != nil {
logger.Logger.WithError(err).Error("Failed to fetch user me") logger.Logger.WithError(err).Error("Failed to fetch user me")
// remove token so user can retry // 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 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 // save the user's alby account ID on first time login
if existingUserIdentifier == "" { 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 { if svc.cfg.GetEnv().AutoLinkAlbyAccount {
// link account on first login // link account on first login
@ -121,7 +128,10 @@ func (svc *albyOAuthService) CallbackHandler(ctx context.Context, code string, l
} else if me.Identifier != existingUserIdentifier { } else if me.Identifier != existingUserIdentifier {
// remove token so user can retry with correct account // 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.") 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) { func (svc *albyOAuthService) saveToken(token *oauth2.Token) {
svc.cfg.SetUpdate(accessTokenExpiryKey, strconv.FormatInt(token.Expiry.Unix(), 10), "") err := svc.cfg.SetUpdate(accessTokenExpiryKey, strconv.FormatInt(token.Expiry.Unix(), 10), "")
svc.cfg.SetUpdate(accessTokenKey, token.AccessToken, "") if err != nil {
svc.cfg.SetUpdate(refreshTokenKey, token.RefreshToken, "") 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 var tokenMutex sync.Mutex
@ -202,8 +221,8 @@ func (svc *albyOAuthService) fetchUserToken(ctx context.Context) (*oauth2.Token,
RefreshToken: refreshToken, RefreshToken: refreshToken,
} }
// only use the current token if it has at least 20 seconds before expiry // only use the current token if it has at least 60 seconds before expiry
if currentToken.Expiry.After(time.Now().Add(time.Duration(20) * time.Second)) { if currentToken.Expiry.After(time.Now().Add(time.Duration(60) * time.Second)) {
logger.Logger.Debug("Using existing Alby OAuth token") logger.Logger.Debug("Using existing Alby OAuth token")
return currentToken, nil return currentToken, nil
} }
@ -290,7 +309,10 @@ func (svc *albyOAuthService) GetMe(ctx context.Context) (*AlbyMe, error) {
return nil, err 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") logger.Logger.WithFields(logrus.Fields{"me": me}).Info("Alby me response")
return me, nil return me, nil
@ -451,9 +473,6 @@ func (svc *albyOAuthService) SendPayment(ctx context.Context, invoice string) er
} }
func (svc *albyOAuthService) GetAuthUrl() string { 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") return svc.oauthConf.AuthCodeURL("unused")
} }
@ -464,11 +483,26 @@ func (svc *albyOAuthService) UnlinkAccount(ctx context.Context) error {
} }
svc.deleteAlbyAccountApps() svc.deleteAlbyAccountApps()
svc.cfg.SetUpdate(userIdentifierKey, "", "") err = svc.cfg.SetUpdate(userIdentifierKey, "", "")
svc.cfg.SetUpdate(accessTokenKey, "", "") if err != nil {
svc.cfg.SetUpdate(accessTokenExpiryKey, "", "") logger.Logger.WithError(err).Error("Failed to remove user identifier from config")
svc.cfg.SetUpdate(refreshTokenKey, "", "") }
svc.cfg.SetUpdate(lightningAddressKey, "", "") 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 return nil
} }

View file

@ -525,7 +525,10 @@ func (api *api) GetNewOnchainAddress(ctx context.Context) (string, error) {
return "", err 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 return address, nil
} }
@ -706,7 +709,10 @@ func (api *api) GetMnemonic(unlockPassword string) (*MnemonicResponse, error) {
} }
func (api *api) SetNextBackupReminder(backupReminderRequest *BackupReminderRequest) 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 return nil
} }
@ -751,44 +757,89 @@ func (api *api) Setup(ctx context.Context, setupRequest *SetupRequest) error {
return errors.New("no unlock password provided") return errors.New("no unlock password provided")
} }
api.cfg.Setup(setupRequest.UnlockPassword) err = api.cfg.SaveUnlockPasswordCheck(setupRequest.UnlockPassword)
if err != nil {
// TODO: move all below code to cfg.Setup() return err
}
// update next backup reminder // 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 // only update non-empty values
if setupRequest.LNBackendType != "" { 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 != "" { 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 != "" { 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 != "" { 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 != "" { 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 != "" { 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 != "" { 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 != "" { 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 != "" { 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 != "" { 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 return nil

View file

@ -21,8 +21,8 @@ func main() {
// Create a channel to receive OS signals. // Create a channel to receive OS signals.
osSignalChannel := make(chan os.Signal, 1) osSignalChannel := make(chan os.Signal, 1)
// Notify the channel on os.Interrupt, syscall.SIGTERM, and os.Kill. // Notify the channel on os.Interrupt, syscall.SIGTERM. os.Kill cannot be caught.
signal.Notify(osSignalChannel, os.Interrupt, syscall.SIGTERM, os.Kill) signal.Notify(osSignalChannel, os.Interrupt, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
svc, _ := service.NewService(ctx) svc, _ := service.NewService(ctx)
@ -35,7 +35,8 @@ func main() {
//start Echo server //start Echo server
go func() { go func() {
if err := e.Start(fmt.Sprintf(":%v", svc.GetConfig().GetEnv().Port)); err != nil && err != nethttp.ErrServerClosed { 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()
} }
}() }()

View file

@ -25,50 +25,77 @@ const (
unlockPasswordCheck = "THIS STRING SHOULD MATCH IF PASSWORD IS CORRECT" 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{ cfg := &config{
db: db, db: db,
} }
cfg.init(env) err := cfg.init(env)
return cfg 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 cfg.Env = env
if cfg.Env.Relay != "" { 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 != "" { 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 // LND specific to support env variables
if cfg.Env.LNDAddress != "" { 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 != "" { if cfg.Env.LNDCertFile != "" {
certBytes, err := os.ReadFile(cfg.Env.LNDCertFile) certBytes, err := os.ReadFile(cfg.Env.LNDCertFile)
if err != nil { 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) certHex := hex.EncodeToString(certBytes)
cfg.SetIgnore("LNDCertHex", certHex, "") err = cfg.SetIgnore("LNDCertHex", certHex, "")
if err != nil {
return err
}
} }
if cfg.Env.LNDMacaroonFile != "" { if cfg.Env.LNDMacaroonFile != "" {
macBytes, err := os.ReadFile(cfg.Env.LNDMacaroonFile) macBytes, err := os.ReadFile(cfg.Env.LNDMacaroonFile)
if err != nil { 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) macHex := hex.EncodeToString(macBytes)
cfg.SetIgnore("LNDMacaroonHex", macHex, "") err = cfg.SetIgnore("LNDMacaroonHex", macHex, "")
if err != nil {
return err
}
} }
// Phoenix specific to support env variables // Phoenix specific to support env variables
if cfg.Env.PhoenixdAddress != "" { 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 != "" { 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 // 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 cfg.JWTSecret = cfg.Env.JWTSecret
if cfg.JWTSecret == "" { if cfg.JWTSecret == "" {
hex, err := randomHex(32) hex, err := randomHex(32)
if err == nil { if err != nil {
cfg.SetIgnore("JWTSecret", hex, "") 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", "") cfg.JWTSecret, _ = cfg.Get("JWTSecret", "")
} }
return nil
} }
func (cfg *config) SetupCompleted() bool { func (cfg *config) SetupCompleted() bool {
@ -147,26 +180,30 @@ func (cfg *config) set(key string, value string, clauses clause.OnConflict, encr
return nil 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{ clauses := clause.OnConflict{
Columns: []clause.Column{{Name: "key"}}, Columns: []clause.Column{{Name: "key"}},
DoNothing: true, DoNothing: true,
} }
err := cfg.set(key, value, clauses, encryptionKey, cfg.db) err := cfg.set(key, value, clauses, encryptionKey, cfg.db)
if err != nil { 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{ clauses := clause.OnConflict{
Columns: []clause.Column{{Name: "key"}}, Columns: []clause.Column{{Name: "key"}},
DoUpdates: clause.AssignmentColumns([]string{"value"}), DoUpdates: clause.AssignmentColumns([]string{"value"}),
} }
err := cfg.set(key, value, clauses, encryptionKey, cfg.db) err := cfg.set(key, value, clauses, encryptionKey, cfg.db)
if err != nil { 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 { 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) return err == nil && (decryptedValue == "" || decryptedValue == unlockPasswordCheck)
} }
// TODO: rename func (cfg *config) SaveUnlockPasswordCheck(encryptionKey string) error {
func (cfg *config) Setup(encryptionKey string) { err := cfg.SetUpdate("UnlockPasswordCheck", unlockPasswordCheck, encryptionKey)
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 { func (cfg *config) GetEnv() *AppConfig {

View file

@ -50,13 +50,13 @@ func (c *AppConfig) IsDefaultClientId() bool {
type Config interface { type Config interface {
Get(key string, encryptionKey string) (string, error) Get(key string, encryptionKey string) (string, error)
SetIgnore(key string, value string, encryptionKey string) SetIgnore(key string, value string, encryptionKey string) error
SetUpdate(key string, value string, encryptionKey string) SetUpdate(key string, value string, encryptionKey string) error
GetJWTSecret() string GetJWTSecret() string
GetRelayUrl() string GetRelayUrl() string
GetEnv() *AppConfig GetEnv() *AppConfig
CheckUnlockPassword(password string) bool CheckUnlockPassword(password string) bool
ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error
Setup(encryptionKey string) SaveUnlockPasswordCheck(encryptionKey string) error
SetupCompleted() bool SetupCompleted() bool
} }

View file

@ -46,12 +46,23 @@ func (ep *eventPublisher) RemoveSubscriber(listenerToRemove EventSubscriber) {
} }
func (ep *eventPublisher) Publish(event *Event) { 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() ep.subscriberMtx.Lock()
defer ep.subscriberMtx.Unlock() defer ep.subscriberMtx.Unlock()
logger.Logger.WithFields(logrus.Fields{"event": event, "global": ep.globalProperties}).Debug("Publishing event") logger.Logger.WithFields(logrus.Fields{"event": event, "global": ep.globalProperties}).Debug("Publishing event")
for _, listener := range ep.listeners { for _, listener := range ep.listeners {
// consume event without blocking thread if sync {
go listener.ConsumeEvent(context.Background(), event, ep.globalProperties) listener.ConsumeEvent(context.Background(), event, ep.globalProperties)
} else {
// consume event without blocking thread
go listener.ConsumeEvent(context.Background(), event, ep.globalProperties)
}
} }
} }

View file

@ -12,6 +12,7 @@ type EventPublisher interface {
RegisterSubscriber(eventListener EventSubscriber) RegisterSubscriber(eventListener EventSubscriber)
RemoveSubscriber(eventListener EventSubscriber) RemoveSubscriber(eventListener EventSubscriber)
Publish(event *Event) Publish(event *Event)
PublishSync(event *Event)
SetGlobalProperty(key string, value interface{}) SetGlobalProperty(key string, value interface{})
} }

View file

@ -66,14 +66,19 @@ func NewGreenlightService(cfg config.Config, mnemonic, inviteCode, workDir, encr
credentials = &recoveredCredentials credentials = &recoveredCredentials
if err != nil { 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 == "" { if credentials == nil || credentials.GlCreds == "" {
return nil, errors.New("unexpected response from Recover") 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) client, err := glalby.NewBlockingGreenlightAlbyClient(mnemonic, *credentials)
@ -83,7 +88,7 @@ func NewGreenlightService(cfg config.Config, mnemonic, inviteCode, workDir, encr
return nil, err return nil, err
} }
if client == nil { if client == nil {
log.Fatalf("unexpected response from NewBlockingGreenlightAlbyClient") logger.Logger.Error("unexpected response from NewBlockingGreenlightAlbyClient")
} }
nodeInfo, err := client.GetInfo() nodeInfo, err := client.GetInfo()

View file

@ -415,10 +415,15 @@ func (ls *LDKService) resetRouterInternal() {
if err != nil { if err != nil {
logger.Logger.Error("Failed to retrieve ResetRouter key") logger.Logger.Error("Failed to retrieve ResetRouter key")
return
} }
if key != "" { 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") logger.Logger.WithField("key", key).Info("Resetting router")
ldkDbPath := filepath.Join(ls.workdir, "storage", "ldk_node_data.sqlite") 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 { 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 return nil
} }

View file

@ -28,7 +28,11 @@ func (keys *keys) Init(cfg config.Config, encryptionKey string) error {
if nostrSecretKey == "" { if nostrSecretKey == "" {
nostrSecretKey = nostr.GeneratePrivateKey() 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) nostrPublicKey, err := nostr.GetPublicKey(nostrSecretKey)
if err != nil { if err != nil {
@ -41,15 +45,9 @@ func (keys *keys) Init(cfg config.Config, encryptionKey string) error {
} }
func (keys *keys) GetNostrPublicKey() string { func (keys *keys) GetNostrPublicKey() string {
if keys.nostrPublicKey == "" {
logger.Logger.Fatal("keys not initialized")
}
return keys.nostrPublicKey return keys.nostrPublicKey
} }
func (keys *keys) GetNostrSecretKey() string { func (keys *keys) GetNostrSecretKey() string {
if keys.nostrSecretKey == "" {
logger.Logger.Fatal("keys not initialized")
}
return keys.nostrSecretKey return keys.nostrSecretKey
} }

View file

@ -2,7 +2,6 @@ package service
import ( import (
"context" "context"
"time"
"os" "os"
"path/filepath" "path/filepath"
@ -69,7 +68,11 @@ func NewService(ctx context.Context) (*service, error) {
return nil, err 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 DATABASE_URI is a URI or a path, leave it unchanged.
// If it only contains a filename, prepend the workdir. // If it only contains a filename, prepend the workdir.
@ -85,7 +88,10 @@ func NewService(ctx context.Context) (*service, error) {
return nil, err return nil, err
} }
cfg := config.NewConfig(appConfig, gormDB) cfg, err := config.NewConfig(appConfig, gormDB)
if err != nil {
return nil, err
}
eventPublisher := events.NewEventPublisher() eventPublisher := events.NewEventPublisher()
@ -167,21 +173,23 @@ func (svc *service) StartSubscription(ctx context.Context, sub *nostr.Subscripti
return nil return nil
} }
func finishRestoreNode(workDir string) { func finishRestoreNode(workDir string) error {
restoreDir := filepath.Join(workDir, "restore") restoreDir := filepath.Join(workDir, "restore")
if restoreDirStat, err := os.Stat(restoreDir); err == nil && restoreDirStat.IsDir() { if restoreDirStat, err := os.Stat(restoreDir); err == nil && restoreDirStat.IsDir() {
logger.Logger.WithField("restoreDir", restoreDir).Infof("Restore directory found. Finishing Node restore") logger.Logger.WithField("restoreDir", restoreDir).Infof("Restore directory found. Finishing Node restore")
existingFiles, err := os.ReadDir(restoreDir) existingFiles, err := os.ReadDir(restoreDir)
if err != nil { 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 { for _, file := range existingFiles {
if file.Name() != "restore" { if file.Name() != "restore" {
err = os.RemoveAll(filepath.Join(workDir, file.Name())) err = os.RemoveAll(filepath.Join(workDir, file.Name()))
if err != nil { 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") logger.Logger.WithField("filename", file.Name()).Info("removed file")
} }
@ -189,31 +197,33 @@ func finishRestoreNode(workDir string) {
files, err := os.ReadDir(restoreDir) files, err := os.ReadDir(restoreDir)
if err != nil { 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 { for _, file := range files {
err = os.Rename(filepath.Join(restoreDir, file.Name()), filepath.Join(workDir, file.Name())) err = os.Rename(filepath.Join(restoreDir, file.Name()), filepath.Join(workDir, file.Name()))
if err != nil { 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") logger.Logger.WithField("filename", file.Name()).Info("copied file from restore directory")
} }
err = os.RemoveAll(restoreDir) err = os.RemoveAll(restoreDir)
if err != nil { 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") logger.Logger.WithField("restoreDir", restoreDir).Info("removed restore directory")
} }
return nil
} }
func (svc *service) Shutdown() { func (svc *service) Shutdown() {
svc.StopApp() svc.StopApp()
svc.eventPublisher.Publish(&events.Event{ svc.eventPublisher.PublishSync(&events.Event{
Event: "nwc_stopped", Event: "nwc_stopped",
}) })
db.Stop(svc.db) db.Stop(svc.db)
// wait for any remaining events
time.Sleep(1 * time.Second)
} }
func (svc *service) GetDB() *gorm.DB { func (svc *service) GetDB() *gorm.DB {

View file

@ -3,6 +3,7 @@ package service
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"path" "path"
"strconv" "strconv"
"time" "time"
@ -29,12 +30,14 @@ func (svc *service) startNostr(ctx context.Context, encryptionKey string) error
err := svc.keys.Init(svc.cfg, encryptionKey) err := svc.keys.Init(svc.cfg, encryptionKey)
if err != nil { 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()) npub, err := nip19.EncodePublicKey(svc.keys.GetNostrPublicKey())
if err != nil { 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{ 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) lnClient, err = cashu.NewCashuService(cashuWorkdir, cashuMintUrl)
default: 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 { if err != nil {
logger.Logger.WithError(err).Error("Failed to launch LN backend") 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 // Mark that the node has successfully started
// This will ensure the user cannot go through the setup again // 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{ svc.eventPublisher.Publish(&events.Event{
Event: "nwc_node_started", Event: "nwc_node_started",

View file

@ -33,10 +33,13 @@ func CreateTestService() (svc *TestService, err error) {
Workdir: ".test", Workdir: ".test",
} }
cfg := config.NewConfig( cfg, err := config.NewConfig(
appConfig, appConfig,
gormDb, gormDb,
) )
if err != nil {
return nil, err
}
keys := keys.NewKeys() keys := keys.NewKeys()
keys.Init(cfg, "") keys.Init(cfg, "")

View file

@ -3,7 +3,6 @@ package wails
import ( import (
"context" "context"
"embed" "embed"
"log"
"github.com/getAlby/hub/api" "github.com/getAlby/hub/api"
"github.com/getAlby/hub/logger" "github.com/getAlby/hub/logger"
@ -65,7 +64,7 @@ func LaunchWailsApp(app *WailsApp, assets embed.FS, appIcon []byte) {
}) })
if err != nil { if err != nil {
log.Fatalf("Error %v", err) logger.Logger.WithError(err).Error("failed to run Wails app")
} }
} }