store.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package store
  2. import (
  3. "context"
  4. "database/sql"
  5. "sync"
  6. "modernc.org/sqlite"
  7. "github.com/pkg/errors"
  8. "github.com/usememos/memos/server/profile"
  9. )
  10. // Store provides database access to all raw objects.
  11. type Store struct {
  12. Profile *profile.Profile
  13. db *sql.DB
  14. systemSettingCache sync.Map // map[string]*SystemSetting
  15. userCache sync.Map // map[int]*User
  16. userSettingCache sync.Map // map[string]*UserSetting
  17. idpCache sync.Map // map[int]*IdentityProvider
  18. }
  19. // New creates a new instance of Store.
  20. func New(db *sql.DB, profile *profile.Profile) *Store {
  21. return &Store{
  22. Profile: profile,
  23. db: db,
  24. }
  25. }
  26. func (s *Store) GetDB() *sql.DB {
  27. return s.db
  28. }
  29. func (s *Store) BackupTo(ctx context.Context, filename string) error {
  30. conn, err := s.db.Conn(ctx)
  31. if err != nil {
  32. return errors.Errorf("fail to get conn %s", err)
  33. }
  34. defer conn.Close()
  35. err = conn.Raw(func(driverConn any) error {
  36. type backuper interface {
  37. NewBackup(string) (*sqlite.Backup, error)
  38. }
  39. backupConn, ok := driverConn.(backuper)
  40. if !ok {
  41. return errors.Errorf("db connection is not a sqlite backuper")
  42. }
  43. bck, err := backupConn.NewBackup(filename)
  44. if err != nil {
  45. return errors.Errorf("fail to create sqlite backup %s", err)
  46. }
  47. for more := true; more; {
  48. more, err = bck.Step(-1)
  49. if err != nil {
  50. return errors.Errorf("fail to execute sqlite backup %s", err)
  51. }
  52. }
  53. return bck.Finish()
  54. })
  55. if err != nil {
  56. return errors.Errorf("fail to backup %s", err)
  57. }
  58. return nil
  59. }
  60. func (s *Store) Vacuum(ctx context.Context) error {
  61. tx, err := s.db.BeginTx(ctx, nil)
  62. if err != nil {
  63. return err
  64. }
  65. defer tx.Rollback()
  66. if err := s.vacuumImpl(ctx, tx); err != nil {
  67. return err
  68. }
  69. if err := tx.Commit(); err != nil {
  70. return err
  71. }
  72. // Vacuum sqlite database file size after deleting resource.
  73. if _, err := s.db.Exec("VACUUM"); err != nil {
  74. return err
  75. }
  76. return nil
  77. }
  78. func (*Store) vacuumImpl(ctx context.Context, tx *sql.Tx) error {
  79. if err := vacuumMemo(ctx, tx); err != nil {
  80. return err
  81. }
  82. if err := vacuumResource(ctx, tx); err != nil {
  83. return err
  84. }
  85. if err := vacuumUserSetting(ctx, tx); err != nil {
  86. return err
  87. }
  88. if err := vacuumMemoOrganizer(ctx, tx); err != nil {
  89. return err
  90. }
  91. if err := vacuumMemoResource(ctx, tx); err != nil {
  92. return err
  93. }
  94. if err := vacuumMemoRelations(ctx, tx); err != nil {
  95. return err
  96. }
  97. if err := vacuumTag(ctx, tx); err != nil {
  98. // Prevent revive warning.
  99. return err
  100. }
  101. return nil
  102. }