taler-mailbox

Service for asynchronous wallet-to-wallet payment messages
Log | Files | Refs | Submodules | README | LICENSE

commit 9af4c893d44d7d4d5ca87748cd06048f6ae46ee3
parent e00d4011db81cba7e9acd5ba0c462df1220efb2c
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Wed, 17 Dec 2025 23:26:37 +0900

add possibility of paid messages back (API only)

Diffstat:
Mcmd/mailbox-server/main.go | 2+-
Mcmd/mailbox-server/main_test.go | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mmailbox.conf.example | 2++
Mpkg/rest/mailbox.go | 133+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
4 files changed, 147 insertions(+), 61 deletions(-)

diff --git a/cmd/mailbox-server/main.go b/cmd/mailbox-server/main.go @@ -101,7 +101,7 @@ func main() { LibtoolVersion: ltversion, Version: version, Datahome: mailboxdatahome, - Db: db, + DB: db, Ini: iniCfg, Merchant: merch, Loglevel: loglevel, diff --git a/cmd/mailbox-server/main_test.go b/cmd/mailbox-server/main_test.go @@ -119,7 +119,7 @@ func TestMain(m *testing.M) { merch := merchant.NewMerchant(merchServer.URL, "supersecret") a.Initialize(mailbox.MailboxConfig{ Version: "testing", - Db: db, + DB: db, Merchant: merch, Ini: cfg}) testAliceSigningKey, testAliceSigningKeyPriv, _ = ed25519.GenerateKey(nil) @@ -131,12 +131,12 @@ func TestMain(m *testing.M) { code := m.Run() // Purge DB - a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{}) + a.DB.Where("1 = 1").Delete(&mailbox.InboxEntry{}) os.Exit(code) } func TestEmptyMailbox(t *testing.T) { - a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{}) + a.DB.Where("1 = 1").Delete(&mailbox.InboxEntry{}) req, _ := http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) response := executeRequest(req) @@ -148,9 +148,9 @@ func TestEmptyMailbox(t *testing.T) { } } -func TestPostMessage(t *testing.T) { +func TestSendMessage(t *testing.T) { testMessage := make([]byte, 256) - a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{}) + a.DB.Where("1 = 1").Delete(&mailbox.InboxEntry{}) req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) response := executeRequest(req) @@ -162,6 +162,50 @@ func TestPostMessage(t *testing.T) { } } +func setMailboxPaid(isPaid bool) { + var messageFee talerutil.Amount + if isPaid { + messageFee = talerutil.NewAmount("KUDOS", 1, 0) + } else { + messageFee = talerutil.NewAmount("KUDOS", 0, 0) + } + a.MessageFee = &messageFee + a.FreeMessageQuota = 1 +} + +func TestSendMessagePaid(t *testing.T) { + + // Make paid + setMailboxPaid(true) + + // Cleanup + a.DB.Where("1 = 1").Delete(&mailbox.InboxEntry{}) + + testMessage := make([]byte, 256) + rand.Read(testMessage) + req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) + response := executeRequest(req) + + checkResponseCode(t, http.StatusNoContent, response.Code) + + body := response.Body.String() + if body != "" { + t.Errorf("Expected empty response, Got %s", body) + } + testMessage2 := make([]byte, 256) + rand.Read(testMessage2) + req, _ = http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage2)) + response = executeRequest(req) + + checkResponseCode(t, http.StatusPaymentRequired, response.Code) + + body = response.Body.String() + if body != "" { + t.Errorf("Expected empty response, Got %s", body) + } + setMailboxPaid(false) +} + func TestGetKeysEmpty(t *testing.T) { req, _ := http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) response := executeRequest(req) @@ -175,11 +219,11 @@ func TestMailboxRegistration(t *testing.T) { aliceSigningKey := util.Base32CrockfordEncode(testAliceSigningKey) msg.MailboxMetadata.EncryptionKey = util.Base32CrockfordEncode(encKey) msg.MailboxMetadata.EncryptionKeyType = "X25519" - msg.MailboxMetadata.Expiration = mailbox.Timestamp{T_s: uint64(time.Now().Add(time.Hour*24*365).UnixMilli() / 1000)} + msg.MailboxMetadata.Expiration = mailbox.Timestamp{Seconds: uint64(time.Now().Add(time.Hour*24*365).UnixMilli() / 1000)} msg.MailboxMetadata.SigningKey = aliceSigningKey msg.MailboxMetadata.SigningKeyType = "EdDSA" expNbo := make([]byte, 8) - binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.T_s) + binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.Seconds) h := sha512.New() h.Write([]byte(msg.MailboxMetadata.EncryptionKeyType)) h.Write(encKey) @@ -218,8 +262,8 @@ func TestMailboxRegistration(t *testing.T) { if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) } - a.Db.Where("1 = 1").Delete(&mailbox.MailboxMetadata{}) - a.Db.Where("1 = 1").Delete(&mailbox.PendingMailboxRegistration{}) + a.DB.Where("1 = 1").Delete(&mailbox.MailboxMetadata{}) + a.DB.Where("1 = 1").Delete(&mailbox.PendingMailboxRegistration{}) } func TestMailboxRegistrationPaid(t *testing.T) { @@ -236,11 +280,11 @@ func TestMailboxRegistrationPaid(t *testing.T) { aliceSigningKey := util.Base32CrockfordEncode(testAliceSigningKey) msg.MailboxMetadata.EncryptionKey = util.Base32CrockfordEncode(encKey) msg.MailboxMetadata.EncryptionKeyType = "X25519" - msg.MailboxMetadata.Expiration = mailbox.Timestamp{T_s: uint64(time.Now().Add(time.Hour * 24 * 365).UnixMicro())} + msg.MailboxMetadata.Expiration = mailbox.Timestamp{Seconds: uint64(time.Now().Add(time.Hour * 24 * 365).UnixMicro())} msg.MailboxMetadata.SigningKey = aliceSigningKey msg.MailboxMetadata.SigningKeyType = "EdDSA" expNbo := make([]byte, 8) - binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.T_s) + binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.Seconds) h := sha512.New() h.Write([]byte(msg.MailboxMetadata.EncryptionKeyType)) h.Write(encKey) @@ -290,11 +334,12 @@ func TestMailboxRegistrationPaid(t *testing.T) { } func TestPostThenDeleteMessage(t *testing.T) { - // testMessage := make([]byte, 256) + + // make not paid numMessagesToPost := (a.MessageResponseLimit + 7) testMessages := make([]byte, 256*numMessagesToPost) _, _ = rand.Read(testMessages) - a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{}) + a.DB.Where("1 = 1").Delete(&mailbox.InboxEntry{}) for i := 0; i < int(numMessagesToPost); i++ { testMessage := testMessages[i*256 : (i+1)*256] diff --git a/mailbox.conf.example b/mailbox.conf.example @@ -5,6 +5,8 @@ production = false message_body_bytes = 256 message_response_limit = 50 monthly_fee = KUDOS:0 +message_fee = KUDOS:0 +free_message_quota = 0 [mailbox-pq] host = localhost diff --git a/pkg/rest/mailbox.go b/pkg/rest/mailbox.go @@ -16,6 +16,7 @@ // // SPDX-License-Identifier: AGPL3.0-or-later +// Package mailbox is a GNU Taler service. See https://docs.taler.net/core/api-mailbox.html package mailbox import ( @@ -76,7 +77,7 @@ type MailboxConfig struct { Ini *ini.File // DB connection - Db gorm.Dialector + DB gorm.Dialector // Merchant connection Merchant merchant.Merchant @@ -92,7 +93,7 @@ type Mailbox struct { Router *mux.Router // The main DB handle - Db *gorm.DB + DB *gorm.DB // Our configuration from the ini Cfg MailboxConfig @@ -104,7 +105,7 @@ type Mailbox struct { Merchant merchant.Merchant // Base URL - BaseUrl string + BaseURL string // Registration fee for each validity month mailbox MonthlyFee *talerutil.Amount @@ -112,6 +113,12 @@ type Mailbox struct { // Registration fee for registering or modifying mailbox RegistrationUpdateFee *talerutil.Amount + // Message fee for receiving a message + MessageFee *talerutil.Amount + + // The free message quota + FreeMessageQuota uint64 + // How many messages will a single response // contain at maximum. MessageResponseLimit uint64 @@ -124,11 +131,11 @@ type Mailbox struct { } type RelativeTime struct { - D_us uint64 `json:"d_us"` + Microseconds uint64 `json:"d_us"` } type Timestamp struct { - T_s uint64 `json:"t_s"` + Seconds uint64 `json:"t_s"` } // 1 Month as Go duration @@ -164,6 +171,18 @@ type VersionResponse struct { // registration period (30 days) of a mailbox // May be 0 for a free registration. MonthlyFee string `json:"monthly_fee"` + + // How much is the cost to send a single + // message to a mailbox. + // May be 0 for free message sending. + MessageFee string `json:"message_fee"` + + // How many messages can be send and + // are stored by the service for free. + // After the quota is reached, the + // regular message_fee applies. + // May be 0 for no free quota. + FreeMessageQuota string `json:"free_message_quota"` } type MailboxMetadata struct { @@ -263,7 +282,7 @@ func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) { MessageResponseLimit: m.MessageResponseLimit, MonthlyFee: m.MonthlyFee.String(), RegistrationUpdateFee: m.RegistrationUpdateFee.String(), - DeliveryPeriod: RelativeTime{D_us: uint64(dp.Microseconds())}, + DeliveryPeriod: RelativeTime{Microseconds: uint64(dp.Microseconds())}, } w.Header().Set("Content-Type", "application/json") response, _ := json.Marshal(cfg) @@ -278,7 +297,7 @@ func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) { // FIXME timeout // FIXME possibly limit results here m.checkPendingRegistrationUpdates(vars["h_mailbox"]) - err := m.Db.Where("hashed_signing_key = ?", vars["h_mailbox"]).Limit(int(m.MessageResponseLimit)).Find(&entries).Error + err := m.DB.Where("hashed_signing_key = ?", vars["h_mailbox"]).Limit(int(m.MessageResponseLimit)).Find(&entries).Error if err != nil { log.Printf("%v", err) w.WriteHeader(http.StatusNotFound) @@ -299,7 +318,6 @@ func (m *Mailbox) getMessagesResponse(w http.ResponseWriter, r *http.Request) { func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) var entry InboxEntry - //var body = make([]byte, m.MessageBodyBytes) if r.Body == nil { http.Error(w, "No request body", http.StatusBadRequest) return @@ -308,21 +326,35 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) { http.Error(w, "Wrong message size", http.StatusBadRequest) return } - body, err := io.ReadAll(r.Body) //.Read(body) + body, err := io.ReadAll(r.Body) if err != nil { log.Printf("%v", err) http.Error(w, "Cannot read body", http.StatusBadRequest) return } + if !m.MessageFee.IsZero() { + var count int64 + err = m.DB.Model(&InboxEntry{}).Where("hashed_signing_key = ?", vars["h_mailbox"]).Count(&count).Error + if nil != err { + m.Logf(LogError, "%v", err) + http.Error(w, "Cannot look for entries", http.StatusBadRequest) + return + } + if count >= int64(m.FreeMessageQuota) { + w.WriteHeader(http.StatusPaymentRequired) + //w.Header().Set("Taler", payto) FIXME generate payto + return + } + } m.checkPendingRegistrationUpdates(vars["h_mailbox"]) - err = m.Db.First(&entry, "hashed_signing_key = ? AND body = ?", vars["h_mailbox"], body, true).Error + err = m.DB.First(&entry, "hashed_signing_key = ? AND body = ?", vars["h_mailbox"], body, true).Error if err == nil { w.WriteHeader(http.StatusNotModified) return } entry.HashedSigningKey = vars["h_mailbox"] entry.Body = body - m.Db.Save(&entry) + m.DB.Save(&entry) w.WriteHeader(http.StatusNoContent) } @@ -330,13 +362,13 @@ func (m *Mailbox) getKeysResponse(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) var keyEntry MailboxMetadata m.checkPendingRegistrationUpdates(vars["h_mailbox"]) - err := m.Db.First(&keyEntry, "hashed_signing_key = ?", vars["h_mailbox"]).Error + err := m.DB.First(&keyEntry, "hashed_signing_key = ?", vars["h_mailbox"]).Error if err != nil { w.WriteHeader(http.StatusNotFound) return } - m.Logf(LogDebug, "entry expires at %d, have %d", keyEntry.Expiration.T_s, time.Now().Unix()) - if keyEntry.Expiration.T_s < uint64(time.Now().Unix()) { + m.Logf(LogDebug, "entry expires at %d, have %d", keyEntry.Expiration.Seconds, time.Now().Unix()) + if keyEntry.Expiration.Seconds < uint64(time.Now().Unix()) { w.WriteHeader(http.StatusNotFound) return } @@ -360,7 +392,7 @@ func (m *Mailbox) validateRegistrationSignature(msg MailboxRegistrationRequest) if nil != err { return fmt.Errorf("unable to decode signature") } - binary.BigEndian.PutUint64(expNbo[:], msg.MailboxMetadata.Expiration.T_s) + binary.BigEndian.PutUint64(expNbo[:], msg.MailboxMetadata.Expiration.Seconds) size := signedMsg[0:4] binary.BigEndian.PutUint32(size, 64+4+4) purp := signedMsg[4:8] @@ -392,7 +424,7 @@ func calculateCost(sliceCostAmount string, fixedCostAmount string, howLong time. Value: 0, Fraction: 0, } - for i := 0; i < sliceCount; i++ { + for range sliceCount { sum, err = sum.Add(*sliceCost) if nil != err { return nil, err @@ -435,10 +467,10 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request hMailbox := util.Base32CrockfordEncode(h.Sum(nil)) pendingRegistration.HashedSigningKey = hMailbox // Round to the nearest multiple of a month - reqExpiration := time.Unix(int64(msg.MailboxMetadata.Expiration.T_s), 0) + reqExpiration := time.Unix(int64(msg.MailboxMetadata.Expiration.Seconds), 0) now := time.Now() reqDuration := reqExpiration.Sub(now).Round(monthDuration) - err = m.Db.First(&registrationEntry, "hashed_signing_key = ?", hMailbox).Error + err = m.DB.First(&registrationEntry, "hashed_signing_key = ?", hMailbox).Error if err == nil { // This probably meansthe registration is modified or extended or both entryModified := (registrationEntry.EncryptionKey != msg.MailboxMetadata.EncryptionKey) @@ -451,17 +483,17 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request } else { // Entry does not yet exist, add but immediately expire it registrationEntry = msg.MailboxMetadata - registrationEntry.Expiration.T_s = uint64(time.Now().Unix() - 1) + registrationEntry.Expiration.Seconds = uint64(time.Now().Unix() - 1) hAddr := sha512.New() hAddr.Write(pk) registrationEntry.HashedSigningKey = util.Base32CrockfordEncode(hAddr.Sum(nil)) - err = m.Db.Create(&registrationEntry).Error + err = m.DB.Create(&registrationEntry).Error if nil != err { w.WriteHeader(http.StatusInternalServerError) return } } - err = m.Db.First(&pendingRegistration, "hashed_signing_key = ?", hMailbox).Error + err = m.DB.First(&pendingRegistration, "hashed_signing_key = ?", hMailbox).Error pendingRegistrationExists := (nil == err) if !pendingRegistrationExists { pendingRegistration.HashedSigningKey = hMailbox @@ -480,7 +512,7 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request if !cost.IsZero() { if len(pendingRegistration.OrderID) == 0 { // Add new order - orderID, newOrderErr := m.Merchant.AddNewOrder(*cost, "Mailbox registration", m.BaseUrl) + orderID, newOrderErr := m.Merchant.AddNewOrder(*cost, "Mailbox registration", m.BaseURL) if newOrderErr != nil { m.Logf(LogError, "%v", newOrderErr) w.WriteHeader(http.StatusInternalServerError) @@ -496,20 +528,20 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request if paytoErr != nil { fmt.Println(paytoErr) w.WriteHeader(http.StatusInternalServerError) - m.Logf(LogError, paytoErr.Error()+"\n") + m.Logf(LogError, "%s\n", paytoErr.Error()) return } if len(payto) != 0 { - m.Db.Save(&pendingRegistration) + m.DB.Save(&pendingRegistration) w.WriteHeader(http.StatusPaymentRequired) - w.Header().Set("Taler", payto) // FIXME no idea what to do with this. + w.Header().Set("Taler", payto) return } } // Update expiration time of registration. - registrationEntry.Expiration.T_s += uint64(reqDuration.Seconds()) - m.Db.Delete(pendingRegistration) - err = m.Db.Save(&registrationEntry).Error + registrationEntry.Expiration.Seconds += uint64(reqDuration.Seconds()) + m.DB.Delete(pendingRegistration) + err = m.DB.Save(&registrationEntry).Error if nil != err { w.WriteHeader(http.StatusInternalServerError) return @@ -520,7 +552,7 @@ func (m *Mailbox) registerMailboxResponse(w http.ResponseWriter, r *http.Request func (m *Mailbox) checkPendingRegistrationUpdates(hMailbox string) { var pendingEntry PendingMailboxRegistration var registrationEntry MailboxMetadata - err := m.Db.First(&pendingEntry, "hashed_signing_key = ?", hMailbox).Error + err := m.DB.First(&pendingEntry, "hashed_signing_key = ?", hMailbox).Error if err != nil { return } @@ -530,7 +562,7 @@ func (m *Mailbox) checkPendingRegistrationUpdates(hMailbox string) { if rc == http.StatusNotFound { m.Logf(LogInfo, "Registration order for `%s' not found, removing\n", hMailbox) } - err := m.Db.Delete(&pendingEntry) + err := m.DB.Delete(&pendingEntry) if nil != err { m.Logf(LogInfo, "%v\n", err) } @@ -539,12 +571,12 @@ func (m *Mailbox) checkPendingRegistrationUpdates(hMailbox string) { m.Logf(LogDebug, "Order status for %s is %s", pendingEntry.HashedSigningKey, orderStatus) if merchant.OrderPaid == orderStatus { m.Logf(LogDebug, "Order for %v appears to be paid", pendingEntry) - err = m.Db.First(&registrationEntry, "hashed_signing_key = ?", hMailbox).Error + err = m.DB.First(&registrationEntry, "hashed_signing_key = ?", hMailbox).Error if err == nil { m.Logf(LogDebug, "Adding %d seconds to entry expiration", uint64(pendingEntry.Duration.Seconds())) - registrationEntry.Expiration.T_s += uint64(pendingEntry.Duration.Seconds()) - m.Db.Save(&registrationEntry) - err := m.Db.Delete(&pendingEntry) + registrationEntry.Expiration.Seconds += uint64(pendingEntry.Duration.Seconds()) + m.DB.Save(&registrationEntry) + err := m.DB.Delete(&pendingEntry) if nil != err { m.Logf(LogInfo, "%v\n", err) } @@ -609,20 +641,20 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request) return } // Check that expectedETag actually exists - err = m.Db.Where("hashed_signing_key = ? AND id = ?", hHailbox, expectedETag).Find(&InboxEntry{}).Error + err = m.DB.Where("hashed_signing_key = ? AND id = ?", hHailbox, expectedETag).Find(&InboxEntry{}).Error if err != nil { m.Logf(LogDebug, "Message to delete not found with ID %d", expectedETag) w.WriteHeader(http.StatusNotFound) return } var entries []InboxEntry - err = m.Db.Where("hashed_signing_key = ? AND id >= ?", hHailbox, expectedETag).Limit(count).Find(&entries).Error + err = m.DB.Where("hashed_signing_key = ? AND id >= ?", hHailbox, expectedETag).Limit(count).Find(&entries).Error if err != nil { w.WriteHeader(http.StatusInternalServerError) return } m.Logf(LogDebug, "Found matching ID, deleting %d messages", len(entries)) - m.Db.Delete(entries) + m.DB.Delete(entries) w.WriteHeader(http.StatusNoContent) } @@ -692,42 +724,49 @@ func (m *Mailbox) Initialize(cfg MailboxConfig) { if cfg.Ini.Section("mailbox").Key("production").MustBool(false) { fmt.Println("Production mode enabled") } - m.BaseUrl = cfg.Ini.Section("mailbox").Key("base_url").MustString("https://example.com") + m.BaseURL = cfg.Ini.Section("mailbox").Key("base_url").MustString("https://example.com") m.MessageBodyBytes = cfg.Ini.Section("mailbox").Key("message_body_bytes").MustInt64(256) m.MessageResponseLimit = cfg.Ini.Section("mailbox").Key("message_response_limit").MustUint64(50) monthlyFee, err := talerutil.ParseAmount(cfg.Ini.Section("mailbox").Key("monthly_fee").MustString("KUDOS:0")) if err != nil { - fmt.Printf("Failed to parse cost: %v", err) + fmt.Printf("Failed to parse monthly fee: %v", err) os.Exit(1) } m.MonthlyFee = monthlyFee updateFee, err := talerutil.ParseAmount(cfg.Ini.Section("mailbox").Key("registration_update_fee").MustString("KUDOS:0")) if err != nil { - fmt.Printf("Failed to parse cost: %v", err) + fmt.Printf("Failed to parse update fee: %v", err) os.Exit(1) } m.RegistrationUpdateFee = updateFee - _db, err := gorm.Open(cfg.Db, &gorm.Config{ + messageFee, err := talerutil.ParseAmount(cfg.Ini.Section("mailbox").Key("message_fee").MustString("KUDOS:0")) + if err != nil { + fmt.Printf("Failed to parse message fee: %v", err) + os.Exit(1) + } + m.MessageFee = messageFee + m.FreeMessageQuota = cfg.Ini.Section("mailbox").Key("free_message_quota").MustUint64(0) + _db, err := gorm.Open(cfg.DB, &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { panic(err) } - m.Db = _db - if err := m.Db.AutoMigrate(&InboxEntry{}); err != nil { + m.DB = _db + if err := m.DB.AutoMigrate(&InboxEntry{}); err != nil { panic(err) } - if err := m.Db.AutoMigrate(&MailboxMetadata{}); err != nil { + if err := m.DB.AutoMigrate(&MailboxMetadata{}); err != nil { panic(err) } go func() { for { - tx := m.Db.Where("expiration < ?", time.Now()).Delete(&MailboxMetadata{}) + tx := m.DB.Where("expiration < ?", time.Now()).Delete(&MailboxMetadata{}) m.Logf(LogInfo, "Cleaned up %d stale registrations.\n", tx.RowsAffected) time.Sleep(time.Hour * 24) } }() - if err := m.Db.AutoMigrate(&PendingMailboxRegistration{}); err != nil { + if err := m.DB.AutoMigrate(&PendingMailboxRegistration{}); err != nil { panic(err) } // Clean up pending @@ -739,7 +778,7 @@ func (m *Mailbox) Initialize(cfg MailboxConfig) { } go func() { for { - tx := m.Db.Where("created_at < ?", time.Now().Add(-pendingExp)).Delete(&PendingMailboxRegistration{}) + tx := m.DB.Where("created_at < ?", time.Now().Add(-pendingExp)).Delete(&PendingMailboxRegistration{}) m.Logf(LogInfo, "Cleaned up %d stale pending registrations.\n", tx.RowsAffected) time.Sleep(pendingExp) }