db.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package db
  2. import (
  3. "context"
  4. "errors"
  5. "gorm.io/gorm"
  6. "net/http"
  7. "time"
  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. // BeginTx returns a context with a new transaction.
  71. // If the context already has a db connection instance,
  72. // it uses that instance. Otherwise, it uses the
  73. // connection initialized in the package.
  74. //
  75. // Ensure InitializeDB has been called before using
  76. // this function.
  77. func BeginTx(ctx context.Context) context.Context {
  78. dbInCtx, ok := ctx.Value(dbCtxKey).(*gorm.DB)
  79. if !ok {
  80. return context.WithValue(ctx, dbCtxKey, db.Begin())
  81. }
  82. return context.WithValue(ctx, dbCtxKey, dbInCtx.Begin())
  83. }