mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	
		
			
				
	
	
		
			168 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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()
 | 
						|
}
 |