diff --git a/internal/nostr/models.go b/internal/nostr/models.go index f72d5dc..756f3b6 100644 --- a/internal/nostr/models.go +++ b/internal/nostr/models.go @@ -45,49 +45,36 @@ type Subscription struct { EventChan chan *nostr.Event `gorm:"-"` RequestEvent *RequestEvent `gorm:"-"` - // TODO: fix an elegant solution to store datatypes - IdsString string - KindsString string - AuthorsString string - TagsString string + IdsJson json.RawMessage `gorm:"type:jsonb"` + KindsJson json.RawMessage `gorm:"type:jsonb"` + AuthorsJson json.RawMessage `gorm:"type:jsonb"` + TagsJson json.RawMessage `gorm:"type:jsonb"` } func (s *Subscription) BeforeSave(tx *gorm.DB) error { var err error if s.Ids != nil { - var idsJson []byte - idsJson, err = json.Marshal(s.Ids) - if err != nil { - return err - } - s.IdsString = string(idsJson) + if s.IdsJson, err = json.Marshal(s.Ids); err != nil { + return err + } } if s.Kinds != nil { - var kindsJson []byte - kindsJson, err = json.Marshal(s.Kinds) - if err != nil { - return err - } - s.KindsString = string(kindsJson) + if s.KindsJson, err = json.Marshal(s.Kinds); err != nil { + return err + } } if s.Authors != nil { - var authorsJson []byte - authorsJson, err = json.Marshal(s.Authors) - if err != nil { - return err - } - s.AuthorsString = string(authorsJson) + if s.AuthorsJson, err = json.Marshal(s.Authors); err != nil { + return err + } } if s.Tags != nil { - var tagsJson []byte - tagsJson, err = json.Marshal(s.Tags) - if err != nil { - return err - } - s.TagsString = string(tagsJson) + if s.TagsJson, err = json.Marshal(s.Tags); err != nil { + return err + } } return nil @@ -95,32 +82,28 @@ func (s *Subscription) BeforeSave(tx *gorm.DB) error { func (s *Subscription) AfterFind(tx *gorm.DB) error { var err error - if s.IdsString != "" { - err = json.Unmarshal([]byte(s.IdsString), &s.Ids) - if err != nil { - return err - } + if len(s.IdsJson) > 0 { + if err = json.Unmarshal(s.IdsJson, &s.Ids); err != nil { + return err + } } - if s.KindsString != "" { - err = json.Unmarshal([]byte(s.KindsString), &s.Kinds) - if err != nil { - return err - } + if len(s.KindsJson) > 0 { + if err = json.Unmarshal(s.KindsJson, &s.Kinds); err != nil { + return err + } } - if s.AuthorsString != "" { - err = json.Unmarshal([]byte(s.AuthorsString), &s.Authors) - if err != nil { - return err - } + if len(s.AuthorsJson) > 0 { + if err = json.Unmarshal(s.AuthorsJson, &s.Authors); err != nil { + return err + } } - if s.TagsString != "" { - err = json.Unmarshal([]byte(s.TagsString), &s.Tags) - if err != nil { - return err - } + if len(s.TagsJson) > 0 { + if err = json.Unmarshal(s.TagsJson, &s.Tags); err != nil { + return err + } } return nil diff --git a/internal/nostr/nostr.go b/internal/nostr/nostr.go index 5251745..499ab03 100644 --- a/internal/nostr/nostr.go +++ b/internal/nostr/nostr.go @@ -118,7 +118,6 @@ func NewService(ctx context.Context) (*Service, error) { subscriptions := make(map[string]*nostr.Subscription) - // TODO: Check limits client := expo.NewPushClient(&expo.ClientConfig{ Host: "https://api.expo.dev", APIURL: "/v2", diff --git a/internal/nostr/push.go b/internal/nostr/push.go index 812c9fc..5407aa4 100644 --- a/internal/nostr/push.go +++ b/internal/nostr/push.go @@ -49,7 +49,7 @@ func (svc *Service) NIP47PushNotificationHandler(c echo.Context) error { } var existingSubscriptions []Subscription - if err := svc.db.Where("push_token = ? AND open = ?", requestData.PushToken, true).Find(&existingSubscriptions).Error; err != nil { + if err := svc.db.Where("push_token = ? AND open = ? AND authors_json->>0 = ? AND tags_json->'p'->>0 = ?", requestData.PushToken, true, requestData.WalletPubkey, requestData.ConnPubkey).Find(&existingSubscriptions).Error; err != nil { svc.Logger.WithError(err).WithFields(logrus.Fields{ "push_token": requestData.PushToken, }).Error("Failed to check existing subscriptions") @@ -59,23 +59,19 @@ func (svc *Service) NIP47PushNotificationHandler(c echo.Context) error { }) } - for _, existingSubscription := range existingSubscriptions { - existingWalletPubkey := (*existingSubscription.Authors)[0] - existingConnPubkey := (*existingSubscription.Tags)["p"][0] - - if existingWalletPubkey == requestData.WalletPubkey && existingConnPubkey == requestData.ConnPubkey { - svc.Logger.WithFields(logrus.Fields{ - "wallet_pubkey": requestData.WalletPubkey, - "relay_url": requestData.RelayUrl, - "push_token": requestData.PushToken, - }).Debug("Subscription already started") - return c.JSON(http.StatusOK, PushSubscriptionResponse{ - SubscriptionId: existingSubscription.Uuid, - PushToken: requestData.PushToken, - WalletPubkey: requestData.WalletPubkey, - AppPubkey: requestData.ConnPubkey, - }) - } + if len(existingSubscriptions) > 0 { + existingSubscription := existingSubscriptions[0] + svc.Logger.WithFields(logrus.Fields{ + "wallet_pubkey": requestData.WalletPubkey, + "relay_url": requestData.RelayUrl, + "push_token": requestData.PushToken, + }).Debug("Subscription already started") + return c.JSON(http.StatusOK, PushSubscriptionResponse{ + SubscriptionId: existingSubscription.Uuid, + PushToken: requestData.PushToken, + WalletPubkey: requestData.WalletPubkey, + AppPubkey: requestData.ConnPubkey, + }) } svc.Logger.WithFields(logrus.Fields{ diff --git a/migrations/202411131742_update_subscriptions_jsonb.go b/migrations/202411131742_update_subscriptions_jsonb.go new file mode 100644 index 0000000..0eeffc2 --- /dev/null +++ b/migrations/202411131742_update_subscriptions_jsonb.go @@ -0,0 +1,143 @@ +package migrations + +import ( + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" +) + +// Update subscriptions table to use JSONB columns for ids, kinds, authors, and tags +var _202411131742_update_subscriptions_jsonb = &gormigrate.Migration{ + ID: "202411131742_update_subscriptions_jsonb", + Migrate: func(tx *gorm.DB) error { + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN ids_json jsonb").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN kinds_json jsonb").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN authors_json jsonb").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN tags_json jsonb").Error; err != nil { + return err + } + + if err := tx.Exec(` + UPDATE subscriptions + SET ids_json = CASE + WHEN ids_string = '' THEN '[]'::jsonb + ELSE ids_string::jsonb + END + WHERE ids_string IS NOT NULL + `).Error; err != nil { + return err + } + + if err := tx.Exec(` + UPDATE subscriptions + SET kinds_json = CASE + WHEN kinds_string = '' THEN '[]'::jsonb + ELSE kinds_string::jsonb + END + WHERE kinds_string IS NOT NULL + `).Error; err != nil { + return err + } + + if err := tx.Exec(` + UPDATE subscriptions + SET authors_json = CASE + WHEN authors_string = '' THEN '[]'::jsonb + ELSE authors_string::jsonb + END + WHERE authors_string IS NOT NULL + `).Error; err != nil { + return err + } + + if err := tx.Exec(` + UPDATE subscriptions + SET tags_json = CASE + WHEN tags_string = '' THEN '{}'::jsonb + ELSE tags_string::jsonb + END + WHERE tags_string IS NOT NULL + `).Error; err != nil { + return err + } + + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN ids_string").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN kinds_string").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN authors_string").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN tags_string").Error; err != nil { + return err + } + + if err := tx.Exec("CREATE INDEX IF NOT EXISTS subscriptions_open ON subscriptions (open)").Error; err != nil { + return err + } + if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_subscriptions_authors_json ON subscriptions USING gin (authors_json)").Error; err != nil { + return err + } + if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_subscriptions_tags_json ON subscriptions USING gin (tags_json)").Error; err != nil { + return err + } + + return nil + }, + Rollback: func(tx *gorm.DB) error { + if err := tx.Exec("DROP INDEX IF EXISTS idx_subscriptions_authors_json").Error; err != nil { + return err + } + if err := tx.Exec("DROP INDEX IF EXISTS idx_subscriptions_tags_json").Error; err != nil { + return err + } + + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN ids_string TEXT").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN kinds_string TEXT").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN authors_string TEXT").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN tags_string TEXT").Error; err != nil { + return err + } + + if err := tx.Exec("UPDATE subscriptions SET ids_string = ids_json::text WHERE ids IS NOT NULL").Error; err != nil { + return err + } + if err := tx.Exec("UPDATE subscriptions SET kinds_string = kinds_json::text WHERE kinds IS NOT NULL").Error; err != nil { + return err + } + if err := tx.Exec("UPDATE subscriptions SET authors_string = authors_json::text WHERE authors IS NOT NULL").Error; err != nil { + return err + } + if err := tx.Exec("UPDATE subscriptions SET tags_string = tags_json::text WHERE tags IS NOT NULL").Error; err != nil { + return err + } + + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN ids_json").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN kinds_json").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN authors_json").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN tags_json").Error; err != nil { + return err + } + + return nil + }, +} diff --git a/migrations/migrate.go b/migrations/migrate.go index f64863a..ea1f3f8 100644 --- a/migrations/migrate.go +++ b/migrations/migrate.go @@ -13,6 +13,7 @@ func Migrate(db *gorm.DB) error { _202404031539_add_indexes, _202407171220_add_response_received_at_to_request_events, _202411071013_add_push_token_and_is_ios_to_subscriptions, + _202411131742_update_subscriptions_jsonb, }) return m.Migrate()