diff --git a/internal/database/database.go b/internal/database/database.go index 3d75d9555..7edb9b30b 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -123,7 +123,6 @@ func NewConnection(w logger.Writer) (*gorm.DB, error) { } // Initialize stores, sorted in alphabetical order. - Notices = NewNoticesStore(db) Orgs = NewOrgsStore(db) Perms = NewPermsStore(db) Repos = NewReposStore(db) @@ -173,3 +172,7 @@ var loadedLoginSourceFilesStore loginSourceFilesStore func (db *DB) LoginSources() *LoginSourcesStore { return newLoginSourcesStore(db.db, loadedLoginSourceFilesStore) } + +func (db *DB) Notices() *NoticesStore { + return newNoticesStore(db.db) +} diff --git a/internal/database/mirror.go b/internal/database/mirror.go index 3d2043d7f..479570d64 100644 --- a/internal/database/mirror.go +++ b/internal/database/mirror.go @@ -215,7 +215,7 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { // good condition to prevent long blocking on URL resolution without syncing anything. if !git.IsURLAccessible(time.Minute, m.RawAddress()) { desc := fmt.Sprintf("Source URL of mirror repository '%s' is not accessible: %s", m.Repo.FullName(), m.MosaicsAddress()) - if err := Notices.Create(context.TODO(), NoticeTypeRepository, desc); err != nil { + if err := Handle.Notices().Create(context.TODO(), NoticeTypeRepository, desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } return nil, false @@ -231,7 +231,7 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { if err != nil { desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderr) log.Error(desc) - if err = Notices.Create(context.TODO(), NoticeTypeRepository, desc); err != nil { + if err = Handle.Notices().Create(context.TODO(), NoticeTypeRepository, desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } return nil, false @@ -249,7 +249,7 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) { "git", "remote", "update", "--prune"); err != nil { desc := fmt.Sprintf("Failed to update mirror wiki repository '%s': %s", wikiPath, stderr) log.Error(desc) - if err = Notices.Create(context.TODO(), NoticeTypeRepository, desc); err != nil { + if err = Handle.Notices().Create(context.TODO(), NoticeTypeRepository, desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } } diff --git a/internal/database/notices.go b/internal/database/notices.go index ebcb5dd91..d5feb7483 100644 --- a/internal/database/notices.go +++ b/internal/database/notices.go @@ -15,37 +15,18 @@ import ( log "unknwon.dev/clog/v2" ) -// NoticesStore is the persistent interface for system notices. -type NoticesStore interface { - // Create creates a system notice with the given type and description. - Create(ctx context.Context, typ NoticeType, desc string) error - // DeleteByIDs deletes system notices by given IDs. - DeleteByIDs(ctx context.Context, ids ...int64) error - // DeleteAll deletes all system notices. - DeleteAll(ctx context.Context) error - // List returns a list of system notices. Results are paginated by given page - // and page size, and sorted by primary key (id) in descending order. - List(ctx context.Context, page, pageSize int) ([]*Notice, error) - // Count returns the total number of system notices. - Count(ctx context.Context) int64 +// NoticesStore is the storage layer for system notices. +type NoticesStore struct { + db *gorm.DB } -var Notices NoticesStore - -var _ NoticesStore = (*noticesStore)(nil) - -type noticesStore struct { - *gorm.DB +func newNoticesStore(db *gorm.DB) *NoticesStore { + return &NoticesStore{db: db} } -// NewNoticesStore returns a persistent interface for system notices with given -// database connection. -func NewNoticesStore(db *gorm.DB) NoticesStore { - return ¬icesStore{DB: db} -} - -func (s *noticesStore) Create(ctx context.Context, typ NoticeType, desc string) error { - return s.WithContext(ctx).Create( +// Create creates a system notice with the given type and description. +func (s *NoticesStore) Create(ctx context.Context, typ NoticeType, desc string) error { + return s.db.WithContext(ctx).Create( &Notice{ Type: typ, Description: desc, @@ -53,26 +34,31 @@ func (s *noticesStore) Create(ctx context.Context, typ NoticeType, desc string) ).Error } -func (s *noticesStore) DeleteByIDs(ctx context.Context, ids ...int64) error { - return s.WithContext(ctx).Where("id IN (?)", ids).Delete(&Notice{}).Error +// DeleteByIDs deletes system notices by given IDs. +func (s *NoticesStore) DeleteByIDs(ctx context.Context, ids ...int64) error { + return s.db.WithContext(ctx).Where("id IN (?)", ids).Delete(&Notice{}).Error } -func (s *noticesStore) DeleteAll(ctx context.Context) error { - return s.WithContext(ctx).Where("TRUE").Delete(&Notice{}).Error +// DeleteAll deletes all system notices. +func (s *NoticesStore) DeleteAll(ctx context.Context) error { + return s.db.WithContext(ctx).Where("TRUE").Delete(&Notice{}).Error } -func (s *noticesStore) List(ctx context.Context, page, pageSize int) ([]*Notice, error) { +// List returns a list of system notices. Results are paginated by given page +// and page size, and sorted by primary key (id) in descending order. +func (s *NoticesStore) List(ctx context.Context, page, pageSize int) ([]*Notice, error) { notices := make([]*Notice, 0, pageSize) - return notices, s.WithContext(ctx). + return notices, s.db.WithContext(ctx). Limit(pageSize).Offset((page - 1) * pageSize). Order("id DESC"). Find(¬ices). Error } -func (s *noticesStore) Count(ctx context.Context) int64 { +// Count returns the total number of system notices. +func (s *NoticesStore) Count(ctx context.Context) int64 { var count int64 - s.WithContext(ctx).Model(&Notice{}).Count(&count) + s.db.WithContext(ctx).Model(&Notice{}).Count(&count) return count } @@ -89,7 +75,7 @@ func (t NoticeType) TrStr() string { // Notice represents a system notice for admin. type Notice struct { - ID int64 `gorm:"primarykey"` + ID int64 `gorm:"primaryKey"` Type NoticeType Description string `xorm:"TEXT" gorm:"type:TEXT"` Created time.Time `xorm:"-" gorm:"-" json:"-"` @@ -105,7 +91,7 @@ func (n *Notice) BeforeCreate(tx *gorm.DB) error { } // AfterFind implements the GORM query hook. -func (n *Notice) AfterFind(_ *gorm.DB) error { +func (n *Notice) AfterFind(*gorm.DB) error { n.Created = time.Unix(n.CreatedUnix, 0).Local() return nil } @@ -115,7 +101,7 @@ func (n *Notice) AfterFind(_ *gorm.DB) error { func RemoveAllWithNotice(title, path string) { if err := os.RemoveAll(path); err != nil { desc := fmt.Sprintf("%s [%s]: %v", title, path, err) - if err = Notices.Create(context.Background(), NoticeTypeRepository, desc); err != nil { + if err = Handle.Notices().Create(context.Background(), NoticeTypeRepository, desc); err != nil { log.Error("Failed to create repository notice: %v", err) } } diff --git a/internal/database/notices_test.go b/internal/database/notices_test.go index 56d631f3b..e45196e40 100644 --- a/internal/database/notices_test.go +++ b/internal/database/notices_test.go @@ -65,13 +65,13 @@ func TestNotices(t *testing.T) { t.Parallel() ctx := context.Background() - db := ¬icesStore{ - DB: newTestDB(t, "noticesStore"), + s := &NoticesStore{ + db: newTestDB(t, "NoticesStore"), } for _, tc := range []struct { name string - test func(t *testing.T, ctx context.Context, db *noticesStore) + test func(t *testing.T, ctx context.Context, s *NoticesStore) }{ {"Create", noticesCreate}, {"DeleteByIDs", noticesDeleteByIDs}, @@ -81,10 +81,10 @@ func TestNotices(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { t.Cleanup(func() { - err := clearTables(t, db.DB) + err := clearTables(t, s.db) require.NoError(t, err) }) - tc.test(t, ctx, db) + tc.test(t, ctx, s) }) if t.Failed() { break @@ -92,19 +92,19 @@ func TestNotices(t *testing.T) { } } -func noticesCreate(t *testing.T, ctx context.Context, db *noticesStore) { - err := db.Create(ctx, NoticeTypeRepository, "test") +func noticesCreate(t *testing.T, ctx context.Context, s *NoticesStore) { + err := s.Create(ctx, NoticeTypeRepository, "test") require.NoError(t, err) - count := db.Count(ctx) + count := s.Count(ctx) assert.Equal(t, int64(1), count) } -func noticesDeleteByIDs(t *testing.T, ctx context.Context, db *noticesStore) { - err := db.Create(ctx, NoticeTypeRepository, "test") +func noticesDeleteByIDs(t *testing.T, ctx context.Context, s *NoticesStore) { + err := s.Create(ctx, NoticeTypeRepository, "test") require.NoError(t, err) - notices, err := db.List(ctx, 1, 10) + notices, err := s.List(ctx, 1, 10) require.NoError(t, err) ids := make([]int64, 0, len(notices)) for _, notice := range notices { @@ -113,51 +113,51 @@ func noticesDeleteByIDs(t *testing.T, ctx context.Context, db *noticesStore) { // Non-existing IDs should be ignored ids = append(ids, 404) - err = db.DeleteByIDs(ctx, ids...) + err = s.DeleteByIDs(ctx, ids...) require.NoError(t, err) - count := db.Count(ctx) + count := s.Count(ctx) assert.Equal(t, int64(0), count) } -func noticesDeleteAll(t *testing.T, ctx context.Context, db *noticesStore) { - err := db.Create(ctx, NoticeTypeRepository, "test") +func noticesDeleteAll(t *testing.T, ctx context.Context, s *NoticesStore) { + err := s.Create(ctx, NoticeTypeRepository, "test") require.NoError(t, err) - err = db.DeleteAll(ctx) + err = s.DeleteAll(ctx) require.NoError(t, err) - count := db.Count(ctx) + count := s.Count(ctx) assert.Equal(t, int64(0), count) } -func noticesList(t *testing.T, ctx context.Context, db *noticesStore) { - err := db.Create(ctx, NoticeTypeRepository, "test 1") +func noticesList(t *testing.T, ctx context.Context, s *NoticesStore) { + err := s.Create(ctx, NoticeTypeRepository, "test 1") require.NoError(t, err) - err = db.Create(ctx, NoticeTypeRepository, "test 2") + err = s.Create(ctx, NoticeTypeRepository, "test 2") require.NoError(t, err) - got1, err := db.List(ctx, 1, 1) + got1, err := s.List(ctx, 1, 1) require.NoError(t, err) require.Len(t, got1, 1) - got2, err := db.List(ctx, 2, 1) + got2, err := s.List(ctx, 2, 1) require.NoError(t, err) require.Len(t, got2, 1) assert.True(t, got1[0].ID > got2[0].ID) - got, err := db.List(ctx, 1, 3) + got, err := s.List(ctx, 1, 3) require.NoError(t, err) require.Len(t, got, 2) } -func noticesCount(t *testing.T, ctx context.Context, db *noticesStore) { - count := db.Count(ctx) +func noticesCount(t *testing.T, ctx context.Context, s *NoticesStore) { + count := s.Count(ctx) assert.Equal(t, int64(0), count) - err := db.Create(ctx, NoticeTypeRepository, "test") + err := s.Create(ctx, NoticeTypeRepository, "test") require.NoError(t, err) - count = db.Count(ctx) + count = s.Count(ctx) assert.Equal(t, int64(1), count) } diff --git a/internal/database/repo.go b/internal/database/repo.go index 0be0fa00a..aaf9be26a 100644 --- a/internal/database/repo.go +++ b/internal/database/repo.go @@ -1947,7 +1947,7 @@ func DeleteOldRepositoryArchives() { if err = os.Remove(archivePath); err != nil { desc := fmt.Sprintf("Failed to health delete archive '%s': %v", archivePath, err) log.Warn(desc) - if err = Notices.Create(context.TODO(), NoticeTypeRepository, desc); err != nil { + if err = Handle.Notices().Create(context.TODO(), NoticeTypeRepository, desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } } @@ -1985,7 +1985,7 @@ func gatherMissingRepoRecords() ([]*Repository, error) { } return nil }); err != nil { - if err2 := Notices.Create(context.TODO(), NoticeTypeRepository, fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil { + if err2 := Handle.Notices().Create(context.TODO(), NoticeTypeRepository, fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil { return nil, fmt.Errorf("CreateRepositoryNotice: %v", err) } } @@ -2006,7 +2006,7 @@ func DeleteMissingRepositories() error { for _, repo := range repos { log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil { - if err2 := Notices.Create(context.TODO(), NoticeTypeRepository, fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil { + if err2 := Handle.Notices().Create(context.TODO(), NoticeTypeRepository, fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil { return fmt.Errorf("CreateRepositoryNotice: %v", err) } } @@ -2028,7 +2028,7 @@ func ReinitMissingRepositories() error { for _, repo := range repos { log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) if err := git.Init(repo.RepoPath(), git.InitOptions{Bare: true}); err != nil { - if err2 := Notices.Create(context.TODO(), NoticeTypeRepository, fmt.Sprintf("init repository [repo_id: %d]: %v", repo.ID, err)); err2 != nil { + if err2 := Handle.Notices().Create(context.TODO(), NoticeTypeRepository, fmt.Sprintf("init repository [repo_id: %d]: %v", repo.ID, err)); err2 != nil { return fmt.Errorf("create repository notice: %v", err) } } @@ -2086,7 +2086,7 @@ func GitFsck() { if err != nil { desc := fmt.Sprintf("Failed to perform health check on repository '%s': %v", repoPath, err) log.Warn(desc) - if err = Notices.Create(context.TODO(), NoticeTypeRepository, desc); err != nil { + if err = Handle.Notices().Create(context.TODO(), NoticeTypeRepository, desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } } diff --git a/internal/route/admin/notice.go b/internal/route/admin/notice.go index 77e89e497..90f6854fe 100644 --- a/internal/route/admin/notice.go +++ b/internal/route/admin/notice.go @@ -25,14 +25,14 @@ func Notices(c *context.Context) { c.Data["PageIsAdmin"] = true c.Data["PageIsAdminNotices"] = true - total := database.Notices.Count(c.Req.Context()) + total := database.Handle.Notices().Count(c.Req.Context()) page := c.QueryInt("page") if page <= 1 { page = 1 } c.Data["Page"] = paginater.New(int(total), conf.UI.Admin.NoticePagingNum, page, 5) - notices, err := database.Notices.List(c.Req.Context(), page, conf.UI.Admin.NoticePagingNum) + notices, err := database.Handle.Notices().List(c.Req.Context(), page, conf.UI.Admin.NoticePagingNum) if err != nil { c.Error(err, "list notices") return @@ -53,7 +53,7 @@ func DeleteNotices(c *context.Context) { } } - if err := database.Notices.DeleteByIDs(c.Req.Context(), ids...); err != nil { + if err := database.Handle.Notices().DeleteByIDs(c.Req.Context(), ids...); err != nil { c.Flash.Error("DeleteNoticesByIDs: " + err.Error()) c.Status(http.StatusInternalServerError) } else { @@ -63,7 +63,7 @@ func DeleteNotices(c *context.Context) { } func EmptyNotices(c *context.Context) { - if err := database.Notices.DeleteAll(c.Req.Context()); err != nil { + if err := database.Handle.Notices().DeleteAll(c.Req.Context()); err != nil { c.Error(err, "delete notices") return }