db.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package db
  2. import (
  3. "context"
  4. "errors"
  5. "net/http"
  6. "time"
  7. "gorm.io/gorm"
  8. )
  9. type ctxKey string
  10. const dbCtxKey ctxKey = "db"
  11. var db *gorm.DB
  12. var ErrDBNotFound = errors.New("no db instance in context")
  13. // InitializeDB initializes a connection to the
  14. // database (if not already done) and ensures it
  15. // has the latest schema.
  16. func InitializeDB(models ...interface{}) error {
  17. if db != nil {
  18. return nil
  19. }
  20. connector, err := newConnector()
  21. if err != nil {
  22. return err
  23. }
  24. // DB / LIFE ADVICE: try 5 times before giving up.
  25. for i := 0; i < 5; i++ {
  26. db, err = connector.connect()
  27. if err == nil {
  28. break
  29. }
  30. // wait 2s if you have the time.
  31. time.Sleep(2 * time.Second)
  32. }
  33. if err != nil {
  34. return err
  35. }
  36. return db.AutoMigrate(models...)
  37. }
  38. // WithContext returns a new context with the db
  39. // connection instance.
  40. //
  41. // Ensure InitializeDB has been called before using
  42. // this function.
  43. //
  44. // To extract the db connection use the FromContext
  45. // function.
  46. func WithContext(ctx context.Context) context.Context {
  47. return context.WithValue(ctx, dbCtxKey, db)
  48. }
  49. // Middleware to auto-inject the db connection instance
  50. // in a request's context.
  51. //
  52. // Ensure InitializeDB has been called before using this
  53. // middleware.
  54. func Middleware(next http.Handler) http.Handler {
  55. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  56. next.ServeHTTP(w, r.WithContext(WithContext(r.Context())))
  57. })
  58. }
  59. // FromContext extracts the db connection instance from
  60. // the given context.
  61. //
  62. // The function panics, if a connection does not exist.
  63. func FromContext(ctx context.Context) *gorm.DB {
  64. db, ok := ctx.Value(dbCtxKey).(*gorm.DB)
  65. if !ok {
  66. panic(ErrDBNotFound)
  67. }
  68. return db
  69. }
  70. func SetPagination(ctx context.Context, page, pageSize int) context.Context {
  71. if page < 1 {
  72. page = 1
  73. }
  74. if pageSize < 1 || pageSize > 100 {
  75. pageSize = 10
  76. }
  77. db := FromContext(ctx)
  78. offset := (page - 1) * pageSize
  79. return context.WithValue(ctx, dbCtxKey, db.Offset(offset).Limit(pageSize))
  80. }
  81. // BeginTx returns a context with a new transaction.
  82. // If the context already has a db connection instance,
  83. // it uses that instance. Otherwise, it uses the
  84. // connection initialized in the package.
  85. //
  86. // Ensure InitializeDB has been called before using
  87. // this function.
  88. func BeginTx(ctx context.Context) context.Context {
  89. dbInCtx, ok := ctx.Value(dbCtxKey).(*gorm.DB)
  90. if !ok {
  91. return context.WithValue(ctx, dbCtxKey, db.Begin())
  92. }
  93. return context.WithValue(ctx, dbCtxKey, dbInCtx.Begin())
  94. }
  95. // CloseDB close a connection to the database
  96. // (if one exists). It panics if any error
  97. // occurs.
  98. func CloseDB() {
  99. if db == nil {
  100. return
  101. }
  102. sqlDB, err := db.DB()
  103. if err != nil {
  104. panic(err)
  105. }
  106. err = sqlDB.Close()
  107. if err != nil {
  108. panic(err)
  109. }
  110. db = nil
  111. }