code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/fork.go (about) 1 // Copyright 2016 The Gogs Authors. All rights reserved. 2 // Copyright 2020 The Gitea Authors. 3 // SPDX-License-Identifier: MIT 4 5 package repo 6 7 import ( 8 "errors" 9 "fmt" 10 "net/http" 11 12 "code.gitea.io/gitea/models/organization" 13 "code.gitea.io/gitea/models/perm" 14 access_model "code.gitea.io/gitea/models/perm/access" 15 repo_model "code.gitea.io/gitea/models/repo" 16 user_model "code.gitea.io/gitea/models/user" 17 "code.gitea.io/gitea/modules/context" 18 api "code.gitea.io/gitea/modules/structs" 19 "code.gitea.io/gitea/modules/util" 20 "code.gitea.io/gitea/modules/web" 21 "code.gitea.io/gitea/routers/api/v1/utils" 22 "code.gitea.io/gitea/services/convert" 23 repo_service "code.gitea.io/gitea/services/repository" 24 ) 25 26 // ListForks list a repository's forks 27 func ListForks(ctx *context.APIContext) { 28 // swagger:operation GET /repos/{owner}/{repo}/forks repository listForks 29 // --- 30 // summary: List a repository's forks 31 // produces: 32 // - application/json 33 // parameters: 34 // - name: owner 35 // in: path 36 // description: owner of the repo 37 // type: string 38 // required: true 39 // - name: repo 40 // in: path 41 // description: name of the repo 42 // type: string 43 // required: true 44 // - name: page 45 // in: query 46 // description: page number of results to return (1-based) 47 // type: integer 48 // - name: limit 49 // in: query 50 // description: page size of results 51 // type: integer 52 // responses: 53 // "200": 54 // "$ref": "#/responses/RepositoryList" 55 // "404": 56 // "$ref": "#/responses/notFound" 57 58 forks, err := repo_model.GetForks(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx)) 59 if err != nil { 60 ctx.Error(http.StatusInternalServerError, "GetForks", err) 61 return 62 } 63 apiForks := make([]*api.Repository, len(forks)) 64 for i, fork := range forks { 65 permission, err := access_model.GetUserRepoPermission(ctx, fork, ctx.Doer) 66 if err != nil { 67 ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) 68 return 69 } 70 apiForks[i] = convert.ToRepo(ctx, fork, permission) 71 } 72 73 ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumForks)) 74 ctx.JSON(http.StatusOK, apiForks) 75 } 76 77 // CreateFork create a fork of a repo 78 func CreateFork(ctx *context.APIContext) { 79 // swagger:operation POST /repos/{owner}/{repo}/forks repository createFork 80 // --- 81 // summary: Fork a repository 82 // produces: 83 // - application/json 84 // parameters: 85 // - name: owner 86 // in: path 87 // description: owner of the repo to fork 88 // type: string 89 // required: true 90 // - name: repo 91 // in: path 92 // description: name of the repo to fork 93 // type: string 94 // required: true 95 // - name: body 96 // in: body 97 // schema: 98 // "$ref": "#/definitions/CreateForkOption" 99 // responses: 100 // "202": 101 // "$ref": "#/responses/Repository" 102 // "403": 103 // "$ref": "#/responses/forbidden" 104 // "404": 105 // "$ref": "#/responses/notFound" 106 // "409": 107 // description: The repository with the same name already exists. 108 // "422": 109 // "$ref": "#/responses/validationError" 110 111 form := web.GetForm(ctx).(*api.CreateForkOption) 112 repo := ctx.Repo.Repository 113 var forker *user_model.User // user/org that will own the fork 114 if form.Organization == nil { 115 forker = ctx.Doer 116 } else { 117 org, err := organization.GetOrgByName(ctx, *form.Organization) 118 if err != nil { 119 if organization.IsErrOrgNotExist(err) { 120 ctx.Error(http.StatusUnprocessableEntity, "", err) 121 } else { 122 ctx.Error(http.StatusInternalServerError, "GetOrgByName", err) 123 } 124 return 125 } 126 isMember, err := org.IsOrgMember(ctx.Doer.ID) 127 if err != nil { 128 ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) 129 return 130 } else if !isMember { 131 ctx.Error(http.StatusForbidden, "isMemberNot", fmt.Sprintf("User is no Member of Organisation '%s'", org.Name)) 132 return 133 } 134 forker = org.AsUser() 135 } 136 137 var name string 138 if form.Name == nil { 139 name = repo.Name 140 } else { 141 name = *form.Name 142 } 143 144 fork, err := repo_service.ForkRepository(ctx, ctx.Doer, forker, repo_service.ForkRepoOptions{ 145 BaseRepo: repo, 146 Name: name, 147 Description: repo.Description, 148 }) 149 if err != nil { 150 if errors.Is(err, util.ErrAlreadyExist) || repo_model.IsErrReachLimitOfRepo(err) { 151 ctx.Error(http.StatusConflict, "ForkRepository", err) 152 } else { 153 ctx.Error(http.StatusInternalServerError, "ForkRepository", err) 154 } 155 return 156 } 157 158 // TODO change back to 201 159 ctx.JSON(http.StatusAccepted, convert.ToRepo(ctx, fork, access_model.Permission{AccessMode: perm.AccessModeOwner})) 160 }