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  }