code.gitea.io/gitea@v1.22.3/services/doctor/repository.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package doctor
     5  
     6  import (
     7  	"context"
     8  
     9  	"code.gitea.io/gitea/models/db"
    10  	user_model "code.gitea.io/gitea/models/user"
    11  	"code.gitea.io/gitea/modules/log"
    12  	"code.gitea.io/gitea/modules/storage"
    13  	repo_service "code.gitea.io/gitea/services/repository"
    14  
    15  	"xorm.io/builder"
    16  )
    17  
    18  func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix bool) error {
    19  	test := &consistencyCheck{
    20  		Name:         "Repos with no existing owner",
    21  		Counter:      countOrphanedRepos,
    22  		Fixer:        deleteOrphanedRepos,
    23  		FixedMessage: "Deleted all content related to orphaned repos",
    24  	}
    25  	return test.Run(ctx, logger, autofix)
    26  }
    27  
    28  // countOrphanedRepos count repository where user of owner_id do not exist
    29  func countOrphanedRepos(ctx context.Context) (int64, error) {
    30  	return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=`user`.id")
    31  }
    32  
    33  // deleteOrphanedRepos delete repository where user of owner_id do not exist
    34  func deleteOrphanedRepos(ctx context.Context) (int64, error) {
    35  	if err := storage.Init(); err != nil {
    36  		return 0, err
    37  	}
    38  
    39  	batchSize := db.MaxBatchInsertSize("repository")
    40  	e := db.GetEngine(ctx)
    41  	var deleted int64
    42  	adminUser := &user_model.User{IsAdmin: true}
    43  
    44  	for {
    45  		select {
    46  		case <-ctx.Done():
    47  			return deleted, ctx.Err()
    48  		default:
    49  			var ids []int64
    50  			if err := e.Table("`repository`").
    51  				Join("LEFT", "`user`", "repository.owner_id=`user`.id").
    52  				Where(builder.IsNull{"`user`.id"}).
    53  				Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
    54  				return deleted, err
    55  			}
    56  
    57  			// if we don't get ids we have deleted them all
    58  			if len(ids) == 0 {
    59  				return deleted, nil
    60  			}
    61  
    62  			for _, id := range ids {
    63  				if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, id, true); err != nil {
    64  					return deleted, err
    65  				}
    66  				deleted++
    67  			}
    68  		}
    69  	}
    70  }
    71  
    72  func init() {
    73  	Register(&Check{
    74  		Title:     "Deleted all content related to orphaned repos",
    75  		Name:      "delete-orphaned-repos",
    76  		IsDefault: false,
    77  		Run:       handleDeleteOrphanedRepos,
    78  		Priority:  4,
    79  	})
    80  }