summaryrefslogtreecommitdiffstats
path: root/services/doctor/repository.go
blob: 6c33426636e7b0f7f7f871694990274f1e11b48d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package doctor

import (
	"context"

	"code.gitea.io/gitea/models/db"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/storage"
	repo_service "code.gitea.io/gitea/services/repository"

	"xorm.io/builder"
)

func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix bool) error {
	test := &consistencyCheck{
		Name:         "Repos with no existing owner",
		Counter:      countOrphanedRepos,
		Fixer:        deleteOrphanedRepos,
		FixedMessage: "Deleted all content related to orphaned repos",
	}
	return test.Run(ctx, logger, autofix)
}

// countOrphanedRepos count repository where user of owner_id do not exist
func countOrphanedRepos(ctx context.Context) (int64, error) {
	return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=`user`.id")
}

// deleteOrphanedRepos delete repository where user of owner_id do not exist
func deleteOrphanedRepos(ctx context.Context) (int64, error) {
	if err := storage.Init(); err != nil {
		return 0, err
	}

	batchSize := db.MaxBatchInsertSize("repository")
	e := db.GetEngine(ctx)
	var deleted int64
	adminUser := &user_model.User{IsAdmin: true}

	for {
		select {
		case <-ctx.Done():
			return deleted, ctx.Err()
		default:
			var ids []int64
			if err := e.Table("`repository`").
				Join("LEFT", "`user`", "repository.owner_id=`user`.id").
				Where(builder.IsNull{"`user`.id"}).
				Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
				return deleted, err
			}

			// if we don't get ids we have deleted them all
			if len(ids) == 0 {
				return deleted, nil
			}

			for _, id := range ids {
				if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, id, true); err != nil {
					return deleted, err
				}
				deleted++
			}
		}
	}
}

func init() {
	Register(&Check{
		Title:     "Deleted all content related to orphaned repos",
		Name:      "delete-orphaned-repos",
		IsDefault: false,
		Run:       handleDeleteOrphanedRepos,
		Priority:  4,
	})
}