You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
3.4 KiB

  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package migrations
  5. import (
  6. "fmt"
  7. "code.gitea.io/gitea/modules/setting"
  8. "xorm.io/xorm"
  9. )
  10. func removeStaleWatches(x *xorm.Engine) error {
  11. type Watch struct {
  12. ID int64
  13. UserID int64
  14. RepoID int64
  15. }
  16. type IssueWatch struct {
  17. ID int64
  18. UserID int64
  19. RepoID int64
  20. IsWatching bool
  21. }
  22. type Repository struct {
  23. ID int64
  24. IsPrivate bool
  25. OwnerID int64
  26. }
  27. type Access struct {
  28. UserID int64
  29. RepoID int64
  30. Mode int
  31. }
  32. const (
  33. // AccessModeNone no access
  34. AccessModeNone int = iota // 0
  35. // AccessModeRead read access
  36. AccessModeRead // 1
  37. )
  38. accessLevel := func(e *xorm.Session, userID int64, repo *Repository) (int, error) {
  39. mode := AccessModeNone
  40. if !repo.IsPrivate {
  41. mode = AccessModeRead
  42. }
  43. if userID == 0 {
  44. return mode, nil
  45. }
  46. if userID == repo.OwnerID {
  47. return 4, nil
  48. }
  49. a := &Access{UserID: userID, RepoID: repo.ID}
  50. if has, err := e.Get(a); !has || err != nil {
  51. return mode, err
  52. }
  53. return a.Mode, nil
  54. }
  55. sess := x.NewSession()
  56. defer sess.Close()
  57. if err := sess.Begin(); err != nil {
  58. return err
  59. }
  60. var issueWatch IssueWatch
  61. if exist, err := sess.IsTableExist(&issueWatch); err != nil {
  62. return fmt.Errorf("IsExist IssueWatch: %v", err)
  63. } else if !exist {
  64. return nil
  65. }
  66. repoCache := make(map[int64]*Repository)
  67. err := sess.BufferSize(setting.Database.IterateBufferSize).Iterate(new(Watch),
  68. func(idx int, bean interface{}) error {
  69. watch := bean.(*Watch)
  70. repo := repoCache[watch.RepoID]
  71. if repo == nil {
  72. repo = &Repository{
  73. ID: watch.RepoID,
  74. }
  75. if _, err := sess.Get(repo); err != nil {
  76. return err
  77. }
  78. repoCache[watch.RepoID] = repo
  79. }
  80. // Remove watches from now unaccessible repositories
  81. mode, err := accessLevel(sess, watch.UserID, repo)
  82. if err != nil {
  83. return err
  84. }
  85. has := AccessModeRead <= mode
  86. if has {
  87. return nil
  88. }
  89. if _, err = sess.Delete(&Watch{0, watch.UserID, repo.ID}); err != nil {
  90. return err
  91. }
  92. _, err = sess.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repo.ID)
  93. return err
  94. })
  95. if err != nil {
  96. return err
  97. }
  98. repoCache = make(map[int64]*Repository)
  99. err = sess.BufferSize(setting.Database.IterateBufferSize).
  100. Distinct("issue_watch.user_id", "issue.repo_id").
  101. Join("INNER", "issue", "issue_watch.issue_id = issue.id").
  102. Where("issue_watch.is_watching = ?", true).
  103. Iterate(new(IssueWatch),
  104. func(idx int, bean interface{}) error {
  105. watch := bean.(*IssueWatch)
  106. repo := repoCache[watch.RepoID]
  107. if repo == nil {
  108. repo = &Repository{
  109. ID: watch.RepoID,
  110. }
  111. if _, err := sess.Get(repo); err != nil {
  112. return err
  113. }
  114. repoCache[watch.RepoID] = repo
  115. }
  116. // Remove issue watches from now unaccssible repositories
  117. mode, err := accessLevel(sess, watch.UserID, repo)
  118. if err != nil {
  119. return err
  120. }
  121. has := AccessModeRead <= mode
  122. if has {
  123. return nil
  124. }
  125. iw := &IssueWatch{
  126. IsWatching: false,
  127. }
  128. _, err = sess.
  129. Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", watch.RepoID).
  130. Cols("is_watching", "updated_unix").
  131. Where("`issue_watch`.user_id = ?", watch.UserID).
  132. Update(iw)
  133. return err
  134. })
  135. if err != nil {
  136. return err
  137. }
  138. return sess.Commit()
  139. }