/data/sqldb/sqldb.go

https://github.com/haydenwoodhead/burner.kiwi · Go · 187 lines · 152 code · 22 blank · 13 comment · 14 complexity · 9689a2e1189d8ce09cd8facc6a60aec7 MD5 · raw file

  1. package sqldb
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "time"
  6. "github.com/haydenwoodhead/burner.kiwi/burner"
  7. "github.com/jmoiron/sqlx"
  8. log "github.com/sirupsen/logrus"
  9. )
  10. // SQLDatabase implements the database interface for sqldb
  11. type SQLDatabase struct {
  12. *sqlx.DB
  13. dbType string
  14. }
  15. // New returns a new db or panics
  16. func New(dbType string, dbURL string) *SQLDatabase {
  17. s := &SQLDatabase{sqlx.MustOpen(dbType, dbURL), dbType}
  18. if dbType == "sqlite3" {
  19. s.SetMaxOpenConns(1)
  20. }
  21. return s
  22. }
  23. func (s *SQLDatabase) Start() error {
  24. log.Infof("Starting %s database connection", s.dbType)
  25. err := s.createTables()
  26. if err != nil {
  27. return fmt.Errorf("%s - failed to create tables: %w", s.dbType, err)
  28. }
  29. go func() {
  30. t := time.Now().Unix()
  31. var active int
  32. err := s.Get(&active, "select count(*) from inbox WHERE ttl > $1", t)
  33. if err != nil {
  34. log.WithError(err).Error("Failed to get number of active inboxes")
  35. }
  36. log.WithField("active", active).Info("Got count of active inboxes")
  37. for {
  38. count, err := s.RunTTLDelete()
  39. if err != nil {
  40. log.WithError(err).Error("Failed to delete old rows from db")
  41. break
  42. }
  43. log.WithField("deleted", count).Info("Deleted old inboxes from db")
  44. time.Sleep(1 * time.Hour)
  45. }
  46. }()
  47. return nil
  48. }
  49. // createTables creates the databse tables or panics
  50. func (s *SQLDatabase) createTables() error {
  51. _, err := s.Exec(`create table if not exists inbox (
  52. id uuid not null unique,
  53. address text not null unique,
  54. created_at numeric,
  55. created_by text,
  56. ep_routeid text,
  57. ttl numeric,
  58. failed_to_create bool,
  59. primary key (id)
  60. );
  61. create table if not exists message (
  62. inbox_id uuid references inbox(id) on delete cascade,
  63. message_id uuid not null unique,
  64. received_at numeric,
  65. ep_id text,
  66. sender text,
  67. from_name text,
  68. from_address text,
  69. subject text,
  70. body_html text,
  71. body_plain text,
  72. ttl numeric,
  73. primary key (message_id)
  74. );`)
  75. return err
  76. }
  77. // SaveNewInbox saves a new inbox
  78. func (s *SQLDatabase) SaveNewInbox(i burner.Inbox) error {
  79. _, err := s.NamedExec(
  80. "INSERT INTO inbox (id, address, created_at, created_by, ep_routeid, ttl, failed_to_create) VALUES (:id, lower(:address), :created_at, :created_by, :ep_routeid, :ttl, :failed_to_create)",
  81. map[string]interface{}{
  82. "id": i.ID,
  83. "address": i.Address,
  84. "created_at": i.CreatedAt,
  85. "created_by": i.CreatedBy,
  86. "ep_routeid": i.EmailProviderRouteID,
  87. "ttl": i.TTL,
  88. "failed_to_create": i.FailedToCreate,
  89. },
  90. )
  91. return err
  92. }
  93. // GetInboxByID gets an inbox by id
  94. func (s *SQLDatabase) GetInboxByID(id string) (burner.Inbox, error) {
  95. var i burner.Inbox
  96. err := s.Get(&i, "SELECT id, address, created_at, created_by, ep_routeid, ttl, failed_to_create FROM inbox WHERE id = $1", id)
  97. return i, err
  98. }
  99. // GetInboxByAddress gets an inbox by address
  100. func (s *SQLDatabase) GetInboxByAddress(address string) (burner.Inbox, error) {
  101. var i burner.Inbox
  102. err := s.Get(&i, "SELECT id, address, created_at, created_by, ep_routeid, ttl, failed_to_create FROM inbox WHERE lower(address) = lower($1)", address)
  103. return i, err
  104. }
  105. // EmailAddressExists checks if an address already exists
  106. func (s *SQLDatabase) EmailAddressExists(email string) (bool, error) {
  107. var count int
  108. err := s.Get(&count, "SELECT COUNT(*) FROM inbox WHERE lower(address) = lower($1)", email)
  109. return count > 0, err
  110. }
  111. // SetInboxCreated creates a new inbox
  112. func (s *SQLDatabase) SetInboxCreated(i burner.Inbox) error {
  113. _, err := s.Exec("UPDATE inbox SET failed_to_create = 'false', ep_routeid = $1 WHERE id = $2", i.EmailProviderRouteID, i.ID)
  114. return err
  115. }
  116. // SetInboxFailed sets a given inbox as having failed to register with the mail provider
  117. func (s *SQLDatabase) SetInboxFailed(i burner.Inbox) error {
  118. _, err := s.Exec("UPDATE inbox SET failed_to_create = 'true' WHERE id = $1", i.ID)
  119. return err
  120. }
  121. // SaveNewMessage saves a new message to the db
  122. func (s *SQLDatabase) SaveNewMessage(m burner.Message) error {
  123. _, err := s.NamedExec("INSERT INTO message (inbox_id, message_id, received_at, ep_id, sender, from_name, from_address, subject, body_html, body_plain, ttl) VALUES (:inbox_id, :message_id, :received_at, :ep_id, :sender, :from_name, :from_address, :subject, :body_html, :body_plain, :ttl)",
  124. map[string]interface{}{
  125. "inbox_id": m.InboxID,
  126. "message_id": m.ID,
  127. "received_at": m.ReceivedAt,
  128. "ep_id": m.EmailProviderID,
  129. "sender": m.Sender,
  130. "from_name": m.FromName,
  131. "from_address": m.FromAddress,
  132. "subject": m.Subject,
  133. "body_html": m.BodyHTML,
  134. "body_plain": m.BodyPlain,
  135. "ttl": m.TTL,
  136. },
  137. )
  138. return err
  139. }
  140. // GetMessagesByInboxID gets all messages for an inbox
  141. func (s *SQLDatabase) GetMessagesByInboxID(id string) ([]burner.Message, error) {
  142. var msgs []burner.Message
  143. err := s.Select(&msgs, "SELECT * FROM message WHERE inbox_id = $1", id)
  144. return msgs, err
  145. }
  146. // GetMessageByID gets a single message
  147. func (s *SQLDatabase) GetMessageByID(i, m string) (burner.Message, error) {
  148. var msg burner.Message
  149. err := s.Get(&msg, "SELECT * FROM message WHERE inbox_id = $1 and message_id = $2", i, m)
  150. if err == sql.ErrNoRows {
  151. return msg, burner.ErrMessageDoesntExist
  152. }
  153. return msg, err
  154. }
  155. // RunTTLDelete runs the TTL delete process
  156. func (s *SQLDatabase) RunTTLDelete() (int, error) {
  157. t := time.Now().Unix()
  158. res, err := s.Exec("DELETE from inbox WHERE ttl < $1", t)
  159. if err != nil {
  160. return -1, fmt.Errorf("%s - failed to delete expired inboxes: %w", s.dbType, err)
  161. }
  162. count, err := res.RowsAffected()
  163. return int(count), err
  164. }