From 15018595fe9badd890de3190f015691223c1e50c Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 28 Dec 2022 15:35:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E9=A1=B9=E7=9B=AE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routers/hat/hat.go | 10 ++++ routers/hat/repo/transfer.go | 92 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 routers/hat/repo/transfer.go diff --git a/routers/hat/hat.go b/routers/hat/hat.go index 57958e1..21722ca 100644 --- a/routers/hat/hat.go +++ b/routers/hat/hat.go @@ -65,6 +65,7 @@ func Routers(ctx gocontext.Context) *web.Route { m.Get("/version", misc.Version) m.Group("/repos", func() { m.Group("/{username}/{reponame}", func() { + m.Post("/transfer", reqOwner(), bind(gitea_api.TransferRepoOption{}), repo.Transfer) m.Get("/branch_name_set", context.ReferencesGitRepo(), repo.BranchNameSet) m.Get("/tag_name_set", context.ReferencesGitRepo(), repo.TagNameSet) m.Group("/branches", func() { @@ -317,6 +318,15 @@ func bind(obj interface{}) http.HandlerFunc { }) } +func reqOwner() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if !ctx.IsUserRepoOwner() && !ctx.IsUserSiteAdmin() { + ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo") + return + } + } +} + func reqAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { diff --git a/routers/hat/repo/transfer.go b/routers/hat/repo/transfer.go new file mode 100644 index 0000000..ee5f064 --- /dev/null +++ b/routers/hat/repo/transfer.go @@ -0,0 +1,92 @@ +package repo + +import ( + "fmt" + "net/http" + + "code.gitea.io/gitea/models" + organization "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" + "code.gitea.io/gitea/modules/log" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + hat_repo_service "code.gitea.io/gitea/services/repository" +) + +func Transfer(ctx *context.APIContext) { + opts := web.GetForm(ctx).(*api.TransferRepoOption) + + newOwner, err := user_model.GetUserByName(ctx, opts.NewOwner) + if err != nil { + if user_model.IsErrUserNotExist(err) { + ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found") + return + } + ctx.InternalServerError(err) + return + } + + if newOwner.Type == user_model.UserTypeOrganization { + if !ctx.Doer.IsAdmin && newOwner.Visibility == api.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx.Doer.ID) { + ctx.Error(http.StatusNotFound, "", "The new owner does ot exist or cannot be found") + return + } + } + + var teams []*organization.Team + if opts.TeamIDs != nil { + if !newOwner.IsOrganization() { + ctx.Error(http.StatusUnprocessableEntity, "repoTrasfer", "Teams can only be added to organization-owned repositories") + return + } + + org := convert.ToOrganization(organization.OrgFromUser(newOwner)) + for _, tID := range *opts.TeamIDs { + team, err := organization.GetTeamByID(ctx, tID) + if err != nil { + ctx.Error(http.StatusUnprocessableEntity, "team", fmt.Errorf("team %d not found", tID)) + return + } + + if team.OrgID != org.ID { + ctx.Error(http.StatusForbidden, "team", fmt.Errorf("team %d belongs not to org %d", tID, org.ID)) + return + } + + teams = append(teams, team) + } + } + + if ctx.Repo.GitRepo != nil { + ctx.Repo.GitRepo.Close() + ctx.Repo.GitRepo = nil + } + + oldFullName := ctx.Repo.Repository.FullName() + if err := hat_repo_service.TransferOwnership(ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil { + if models.IsErrRepoTransferInProgress(err) { + ctx.Error(http.StatusConflict, "TransferOwnership", err) + return + } + + if repo_model.IsErrRepoAlreadyExist(err) { + ctx.Error(http.StatusUnprocessableEntity, "TransferOwnership", err) + return + } + + ctx.InternalServerError(err) + return + } + + if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { + log.Trace("Repository transfer initiated: %s -> %s", oldFullName, ctx.Repo.Repository.FullName()) + ctx.JSON(http.StatusCreated, convert.ToRepo(ctx.Repo.Repository, perm.AccessModeAdmin)) + } + + log.Trace("Repository transferred: %s -> %s", oldFullName, ctx.Repo.Repository.FullName()) + ctx.JSON(http.StatusAccepted, convert.ToRepo(ctx.Repo.Repository, perm.AccessModeAdmin)) +}