store.go 2.5 KB

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