|
- // Copyright 2018 The Gitea Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package migrations
-
- import (
- "fmt"
-
- "code.gitea.io/gitea/modules/setting"
-
- "xorm.io/xorm"
- )
-
- func removeStaleWatches(x *xorm.Engine) error {
- type Watch struct {
- ID int64
- UserID int64
- RepoID int64
- }
-
- type IssueWatch struct {
- ID int64
- UserID int64
- RepoID int64
- IsWatching bool
- }
-
- type Repository struct {
- ID int64
- IsPrivate bool
- OwnerID int64
- }
-
- type Access struct {
- UserID int64
- RepoID int64
- Mode int
- }
-
- const (
- // AccessModeNone no access
- AccessModeNone int = iota // 0
- // AccessModeRead read access
- AccessModeRead // 1
- )
-
- accessLevel := func(e *xorm.Session, userID int64, repo *Repository) (int, error) {
- mode := AccessModeNone
- if !repo.IsPrivate {
- mode = AccessModeRead
- }
-
- if userID == 0 {
- return mode, nil
- }
-
- if userID == repo.OwnerID {
- return 4, nil
- }
-
- a := &Access{UserID: userID, RepoID: repo.ID}
- if has, err := e.Get(a); !has || err != nil {
- return mode, err
- }
- return a.Mode, nil
- }
-
- sess := x.NewSession()
- defer sess.Close()
- if err := sess.Begin(); err != nil {
- return err
- }
-
- var issueWatch IssueWatch
- if exist, err := sess.IsTableExist(&issueWatch); err != nil {
- return fmt.Errorf("IsExist IssueWatch: %v", err)
- } else if !exist {
- return nil
- }
-
- repoCache := make(map[int64]*Repository)
- err := sess.BufferSize(setting.Database.IterateBufferSize).Iterate(new(Watch),
- func(idx int, bean interface{}) error {
- watch := bean.(*Watch)
-
- repo := repoCache[watch.RepoID]
- if repo == nil {
- repo = &Repository{
- ID: watch.RepoID,
- }
- if _, err := sess.Get(repo); err != nil {
- return err
- }
- repoCache[watch.RepoID] = repo
- }
-
- // Remove watches from now unaccessible repositories
- mode, err := accessLevel(sess, watch.UserID, repo)
- if err != nil {
- return err
- }
- has := AccessModeRead <= mode
- if has {
- return nil
- }
-
- if _, err = sess.Delete(&Watch{0, watch.UserID, repo.ID}); err != nil {
- return err
- }
- _, err = sess.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repo.ID)
-
- return err
- })
- if err != nil {
- return err
- }
-
- repoCache = make(map[int64]*Repository)
- err = sess.BufferSize(setting.Database.IterateBufferSize).
- Distinct("issue_watch.user_id", "issue.repo_id").
- Join("INNER", "issue", "issue_watch.issue_id = issue.id").
- Where("issue_watch.is_watching = ?", true).
- Iterate(new(IssueWatch),
- func(idx int, bean interface{}) error {
- watch := bean.(*IssueWatch)
-
- repo := repoCache[watch.RepoID]
- if repo == nil {
- repo = &Repository{
- ID: watch.RepoID,
- }
- if _, err := sess.Get(repo); err != nil {
- return err
- }
- repoCache[watch.RepoID] = repo
- }
-
- // Remove issue watches from now unaccssible repositories
- mode, err := accessLevel(sess, watch.UserID, repo)
- if err != nil {
- return err
- }
- has := AccessModeRead <= mode
- if has {
- return nil
- }
-
- iw := &IssueWatch{
- IsWatching: false,
- }
-
- _, err = sess.
- Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", watch.RepoID).
- Cols("is_watching", "updated_unix").
- Where("`issue_watch`.user_id = ?", watch.UserID).
- Update(iw)
-
- return err
-
- })
- if err != nil {
- return err
- }
-
- return sess.Commit()
- }
|