2014-08-26 18:11:15 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2018-11-28 19:26:14 +08:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-28 02:20:29 +08:00
// SPDX-License-Identifier: MIT
2014-08-26 18:11:15 +08:00
2015-12-05 06:16:42 +08:00
package repo
2014-08-26 18:11:15 +08:00
import (
2017-08-17 09:31:34 +08:00
"fmt"
2017-10-27 05:16:13 +08:00
"net/http"
2023-09-07 17:37:47 +08:00
"slices"
2024-02-14 09:32:31 +08:00
"strconv"
2017-02-11 12:00:01 +08:00
"strings"
2021-01-03 07:47:47 +08:00
"time"
2014-08-26 18:11:15 +08:00
2023-04-04 21:35:31 +08:00
activities_model "code.gitea.io/gitea/models/activities"
2021-11-24 17:49:20 +08:00
"code.gitea.io/gitea/models/db"
2022-03-29 14:29:02 +08:00
"code.gitea.io/gitea/models/organization"
2021-11-28 19:58:28 +08:00
"code.gitea.io/gitea/models/perm"
2022-05-11 18:09:36 +08:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 09:27:50 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 03:57:58 +08:00
unit_model "code.gitea.io/gitea/models/unit"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2016-11-11 00:24:48 +08:00
"code.gitea.io/gitea/modules/context"
2019-11-17 14:30:01 +08:00
"code.gitea.io/gitea/modules/git"
2023-03-02 07:44:23 +08:00
"code.gitea.io/gitea/modules/label"
2016-11-11 00:24:48 +08:00
"code.gitea.io/gitea/modules/log"
2022-04-23 01:19:55 +08:00
repo_module "code.gitea.io/gitea/modules/repository"
2016-11-11 00:24:48 +08:00
"code.gitea.io/gitea/modules/setting"
2019-08-24 00:40:30 +08:00
api "code.gitea.io/gitea/modules/structs"
2017-10-27 05:16:13 +08:00
"code.gitea.io/gitea/modules/util"
2019-10-02 17:30:41 +08:00
"code.gitea.io/gitea/modules/validation"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2020-01-25 03:00:29 +08:00
"code.gitea.io/gitea/routers/api/v1/utils"
2022-12-29 10:57:15 +08:00
"code.gitea.io/gitea/services/convert"
2023-05-09 07:30:14 +08:00
"code.gitea.io/gitea/services/issue"
2019-10-26 14:54:11 +08:00
repo_service "code.gitea.io/gitea/services/repository"
2014-08-26 18:11:15 +08:00
)
2016-11-24 15:04:31 +08:00
// Search repositories via options
2016-03-14 06:49:16 +08:00
func Search ( ctx * context . APIContext ) {
2017-11-13 15:02:25 +08:00
// swagger:operation GET /repos/search repository repoSearch
// ---
// summary: Search for repositories
// produces:
// - application/json
// parameters:
// - name: q
// in: query
// description: keyword
// type: string
2019-08-24 11:17:10 +08:00
// - name: topic
// in: query
// description: Limit search to repositories with keyword as topic
// type: boolean
2019-08-26 01:06:36 +08:00
// - name: includeDesc
// in: query
// description: include search of keyword within repository description
// type: boolean
2017-11-13 15:02:25 +08:00
// - name: uid
// in: query
2017-11-15 16:10:26 +08:00
// description: search only for repos that the user with the given id owns or contributes to
// type: integer
2018-10-21 11:40:42 +08:00
// format: int64
2019-11-11 23:15:29 +08:00
// - name: priority_owner_id
// in: query
// description: repo owner to prioritize in the results
// type: integer
// format: int64
2020-12-28 03:58:03 +08:00
// - name: team_id
// in: query
// description: search only for repos that belong to the given team id
// type: integer
// format: int64
2019-05-15 23:24:39 +08:00
// - name: starredBy
// in: query
// description: search only for repos that the user with the given id has starred
// type: integer
// format: int64
// - name: private
// in: query
// description: include private repositories this user has access to (defaults to true)
// type: boolean
2020-05-21 09:15:30 +08:00
// - name: is_private
2020-05-17 04:07:01 +08:00
// in: query
2020-05-21 09:15:30 +08:00
// description: show only pubic, private or all repositories (defaults to all)
2020-05-17 04:07:01 +08:00
// type: boolean
2019-11-11 23:15:29 +08:00
// - name: template
// in: query
// description: include template repositories this user has access to (defaults to true)
// type: boolean
2020-05-17 04:07:01 +08:00
// - name: archived
// in: query
// description: show only archived, non-archived or all repositories (defaults to all)
// type: boolean
2017-11-13 15:02:25 +08:00
// - name: mode
// in: query
// description: type of repository to search for. Supported values are
// "fork", "source", "mirror" and "collaborative"
// type: string
// - name: exclusive
// in: query
2017-11-15 16:10:26 +08:00
// description: if `uid` is given, search only for repos that the user owns
2017-11-13 15:02:25 +08:00
// type: boolean
2018-08-02 16:10:02 +08:00
// - name: sort
// in: query
// description: sort repos by attribute. Supported values are
// "alpha", "created", "updated", "size", and "id".
// Default is "alpha"
// type: string
// - name: order
// in: query
// description: sort order, either "asc" (ascending) or "desc" (descending).
// Default is "asc", ignored if "sort" is not specified.
// type: string
2020-01-25 03:00:29 +08:00
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 12:57:38 +08:00
// description: page size of results
2020-01-25 03:00:29 +08:00
// type: integer
2017-11-13 15:02:25 +08:00
// responses:
// "200":
// "$ref": "#/responses/SearchResults"
// "422":
// "$ref": "#/responses/validationError"
2019-12-21 01:07:12 +08:00
2022-06-06 16:01:49 +08:00
opts := & repo_model . SearchRepoOptions {
2020-01-25 03:00:29 +08:00
ListOptions : utils . GetListOptions ( ctx ) ,
2022-03-22 15:03:22 +08:00
Actor : ctx . Doer ,
2021-08-11 23:08:52 +08:00
Keyword : ctx . FormTrim ( "q" ) ,
2021-07-29 09:42:15 +08:00
OwnerID : ctx . FormInt64 ( "uid" ) ,
PriorityOwnerID : ctx . FormInt64 ( "priority_owner_id" ) ,
TeamID : ctx . FormInt64 ( "team_id" ) ,
TopicOnly : ctx . FormBool ( "topic" ) ,
2019-08-26 01:06:36 +08:00
Collaborate : util . OptionalBoolNone ,
2021-08-11 08:31:13 +08:00
Private : ctx . IsSigned && ( ctx . FormString ( "private" ) == "" || ctx . FormBool ( "private" ) ) ,
2019-11-11 23:15:29 +08:00
Template : util . OptionalBoolNone ,
2021-07-29 09:42:15 +08:00
StarredByID : ctx . FormInt64 ( "starredBy" ) ,
IncludeDescription : ctx . FormBool ( "includeDesc" ) ,
2017-10-27 05:16:13 +08:00
}
2021-08-11 08:31:13 +08:00
if ctx . FormString ( "template" ) != "" {
2021-07-29 09:42:15 +08:00
opts . Template = util . OptionalBoolOf ( ctx . FormBool ( "template" ) )
2019-11-11 23:15:29 +08:00
}
2021-07-29 09:42:15 +08:00
if ctx . FormBool ( "exclusive" ) {
2017-10-27 05:16:13 +08:00
opts . Collaborate = util . OptionalBoolFalse
}
2022-01-21 01:46:10 +08:00
mode := ctx . FormString ( "mode" )
2017-10-27 05:16:13 +08:00
switch mode {
case "source" :
opts . Fork = util . OptionalBoolFalse
opts . Mirror = util . OptionalBoolFalse
case "fork" :
opts . Fork = util . OptionalBoolTrue
case "mirror" :
opts . Mirror = util . OptionalBoolTrue
case "collaborative" :
opts . Mirror = util . OptionalBoolFalse
opts . Collaborate = util . OptionalBoolTrue
case "" :
default :
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "Invalid search mode: \"%s\"" , mode ) )
return
2014-08-26 18:11:15 +08:00
}
2021-08-11 08:31:13 +08:00
if ctx . FormString ( "archived" ) != "" {
2021-07-29 09:42:15 +08:00
opts . Archived = util . OptionalBoolOf ( ctx . FormBool ( "archived" ) )
2020-05-17 04:07:01 +08:00
}
2021-08-11 08:31:13 +08:00
if ctx . FormString ( "is_private" ) != "" {
2021-07-29 09:42:15 +08:00
opts . IsPrivate = util . OptionalBoolOf ( ctx . FormBool ( "is_private" ) )
2020-05-21 09:15:30 +08:00
}
2022-01-21 01:46:10 +08:00
sortMode := ctx . FormString ( "sort" )
2018-08-02 16:10:02 +08:00
if len ( sortMode ) > 0 {
2022-01-21 01:46:10 +08:00
sortOrder := ctx . FormString ( "order" )
2018-08-02 16:10:02 +08:00
if len ( sortOrder ) == 0 {
sortOrder = "asc"
}
2023-05-08 17:36:54 +08:00
if searchModeMap , ok := repo_model . SearchOrderByMap [ sortOrder ] ; ok {
2018-08-02 16:10:02 +08:00
if orderBy , ok := searchModeMap [ sortMode ] ; ok {
opts . OrderBy = orderBy
} else {
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "Invalid sort mode: \"%s\"" , sortMode ) )
return
}
} else {
ctx . Error ( http . StatusUnprocessableEntity , "" , fmt . Errorf ( "Invalid sort order: \"%s\"" , sortOrder ) )
return
}
}
2017-10-27 05:16:13 +08:00
var err error
2022-11-19 16:12:33 +08:00
repos , count , err := repo_model . SearchRepository ( ctx , opts )
2014-08-26 18:11:15 +08:00
if err != nil {
2019-12-21 01:07:12 +08:00
ctx . JSON ( http . StatusInternalServerError , api . SearchError {
2017-05-02 21:35:59 +08:00
OK : false ,
Error : err . Error ( ) ,
2014-08-26 18:11:15 +08:00
} )
return
}
2014-11-15 06:11:30 +08:00
results := make ( [ ] * api . Repository , len ( repos ) )
2017-02-10 09:30:26 +08:00
for i , repo := range repos {
2023-02-18 20:11:03 +08:00
if err = repo . LoadOwner ( ctx ) ; err != nil {
2019-12-21 01:07:12 +08:00
ctx . JSON ( http . StatusInternalServerError , api . SearchError {
2017-05-02 21:35:59 +08:00
OK : false ,
Error : err . Error ( ) ,
2014-08-26 18:11:15 +08:00
} )
return
}
2023-06-22 21:08:08 +08:00
permission , err := access_model . GetUserRepoPermission ( ctx , repo , ctx . Doer )
2017-02-10 09:30:26 +08:00
if err != nil {
2019-12-21 01:07:12 +08:00
ctx . JSON ( http . StatusInternalServerError , api . SearchError {
2017-05-02 21:35:59 +08:00
OK : false ,
Error : err . Error ( ) ,
2017-02-10 09:30:26 +08:00
} )
2014-08-26 18:11:15 +08:00
}
2023-06-22 21:08:08 +08:00
results [ i ] = convert . ToRepo ( ctx , repo , permission )
2014-08-26 18:11:15 +08:00
}
2020-01-25 03:00:29 +08:00
ctx . SetLinkHeader ( int ( count ) , opts . PageSize )
2021-08-12 20:43:08 +08:00
ctx . SetTotalCountHeader ( count )
2019-12-21 01:07:12 +08:00
ctx . JSON ( http . StatusOK , api . SearchResults {
2017-05-02 21:35:59 +08:00
OK : true ,
Data : results ,
2014-08-26 18:11:15 +08:00
} )
}
2014-08-29 11:24:37 +08:00
2016-11-24 15:04:31 +08:00
// CreateUserRepo create a repository for a user
2021-11-24 17:49:20 +08:00
func CreateUserRepo ( ctx * context . APIContext , owner * user_model . User , opt api . CreateRepoOption ) {
2019-02-22 06:07:58 +08:00
if opt . AutoInit && opt . Readme == "" {
opt . Readme = "Default"
}
2023-03-14 05:55:30 +08:00
// If the readme template does not exist, a 400 will be returned.
2023-09-07 17:37:47 +08:00
if opt . AutoInit && len ( opt . Readme ) > 0 && ! slices . Contains ( repo_module . Readmes , opt . Readme ) {
2023-03-14 05:55:30 +08:00
ctx . Error ( http . StatusBadRequest , "" , fmt . Errorf ( "readme template does not exist, available templates: %v" , repo_module . Readmes ) )
return
}
2023-09-06 20:08:51 +08:00
repo , err := repo_service . CreateRepository ( ctx , ctx . Doer , owner , repo_service . CreateRepoOptions {
2020-03-27 03:14:51 +08:00
Name : opt . Name ,
Description : opt . Description ,
IssueLabels : opt . IssueLabels ,
Gitignores : opt . Gitignores ,
License : opt . License ,
Readme : opt . Readme ,
IsPrivate : opt . Private ,
AutoInit : opt . AutoInit ,
DefaultBranch : opt . DefaultBranch ,
2021-12-10 09:27:50 +08:00
TrustModel : repo_model . ToTrustModel ( opt . TrustModel ) ,
2020-09-25 13:18:37 +08:00
IsTemplate : opt . Template ,
2015-08-28 18:33:09 +08:00
} )
2014-12-13 09:30:32 +08:00
if err != nil {
2021-12-12 23:48:20 +08:00
if repo_model . IsErrRepoAlreadyExist ( err ) {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusConflict , "" , "The repository with the same name already exists." )
2021-11-24 17:49:20 +08:00
} else if db . IsErrNameReserved ( err ) ||
2022-04-23 01:19:55 +08:00
db . IsErrNamePatternNotAllowed ( err ) ||
2023-03-02 07:44:23 +08:00
label . IsErrTemplateLoad ( err ) {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
2014-12-13 09:30:32 +08:00
} else {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusInternalServerError , "CreateRepository" , err )
2014-12-13 09:30:32 +08:00
}
return
}
2020-09-16 06:42:19 +08:00
// reload repo from db to get a real state after creation
2022-12-03 10:48:26 +08:00
repo , err = repo_model . GetRepositoryByID ( ctx , repo . ID )
2020-09-16 06:42:19 +08:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "GetRepositoryByID" , err )
}
2023-06-22 21:08:08 +08:00
ctx . JSON ( http . StatusCreated , convert . ToRepo ( ctx , repo , access_model . Permission { AccessMode : perm . AccessModeOwner } ) )
2014-12-13 09:30:32 +08:00
}
2016-10-08 01:17:27 +08:00
// Create one repository of mine
2021-01-26 23:36:53 +08:00
func Create ( ctx * context . APIContext ) {
2017-11-13 15:02:25 +08:00
// swagger:operation POST /user/repos repository user createCurrentUserRepo
// ---
// summary: Create a repository
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateRepoOption"
// responses:
// "201":
// "$ref": "#/responses/Repository"
2023-03-14 05:55:30 +08:00
// "400":
// "$ref": "#/responses/error"
2019-05-30 23:09:05 +08:00
// "409":
// description: The repository with the same name already exists.
// "422":
// "$ref": "#/responses/validationError"
2021-01-26 23:36:53 +08:00
opt := web . GetForm ( ctx ) . ( * api . CreateRepoOption )
2022-03-22 15:03:22 +08:00
if ctx . Doer . IsOrganization ( ) {
2017-11-13 15:02:25 +08:00
// Shouldn't reach this condition, but just in case.
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusUnprocessableEntity , "" , "not allowed creating repository for organization" )
2014-12-13 09:30:32 +08:00
return
}
2022-03-22 15:03:22 +08:00
CreateUserRepo ( ctx , ctx . Doer , * opt )
2014-12-13 09:30:32 +08:00
}
2021-07-05 23:29:08 +08:00
// Generate Create a repository using a template
func Generate ( ctx * context . APIContext ) {
// swagger:operation POST /repos/{template_owner}/{template_repo}/generate repository generateRepo
// ---
// summary: Create a repository using a template
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: template_owner
// in: path
// description: name of the template repository owner
// type: string
// required: true
// - name: template_repo
// in: path
// description: name of the template repository
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/GenerateRepoOption"
// responses:
// "201":
// "$ref": "#/responses/Repository"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// description: The repository with the same name already exists.
// "422":
// "$ref": "#/responses/validationError"
form := web . GetForm ( ctx ) . ( * api . GenerateRepoOption )
if ! ctx . Repo . Repository . IsTemplate {
ctx . Error ( http . StatusUnprocessableEntity , "" , "this is not a template repo" )
return
}
2022-03-22 15:03:22 +08:00
if ctx . Doer . IsOrganization ( ) {
2021-07-05 23:29:08 +08:00
ctx . Error ( http . StatusUnprocessableEntity , "" , "not allowed creating repository for organization" )
return
}
2022-06-06 16:01:49 +08:00
opts := repo_module . GenerateRepoOptions {
2023-07-21 12:32:47 +08:00
Name : form . Name ,
DefaultBranch : form . DefaultBranch ,
Description : form . Description ,
Private : form . Private ,
GitContent : form . GitContent ,
Topics : form . Topics ,
GitHooks : form . GitHooks ,
Webhooks : form . Webhooks ,
Avatar : form . Avatar ,
IssueLabels : form . Labels ,
ProtectedBranch : form . ProtectedBranch ,
2021-07-05 23:29:08 +08:00
}
if ! opts . IsValid ( ) {
ctx . Error ( http . StatusUnprocessableEntity , "" , "must select at least one template item" )
return
}
2022-03-22 15:03:22 +08:00
ctxUser := ctx . Doer
2021-07-05 23:29:08 +08:00
var err error
if form . Owner != ctxUser . Name {
2022-05-20 22:08:52 +08:00
ctxUser , err = user_model . GetUserByName ( ctx , form . Owner )
2021-07-05 23:29:08 +08:00
if err != nil {
2021-11-24 17:49:20 +08:00
if user_model . IsErrUserNotExist ( err ) {
2023-07-05 02:36:08 +08:00
ctx . JSON ( http . StatusNotFound , map [ string ] any {
2021-07-16 02:19:39 +08:00
"error" : "request owner `" + form . Owner + "` does not exist" ,
2021-07-05 23:29:08 +08:00
} )
return
}
2021-07-16 02:19:39 +08:00
ctx . Error ( http . StatusInternalServerError , "GetUserByName" , err )
return
}
2022-03-22 15:03:22 +08:00
if ! ctx . Doer . IsAdmin && ! ctxUser . IsOrganization ( ) {
2021-07-16 02:19:39 +08:00
ctx . Error ( http . StatusForbidden , "" , "Only admin can generate repository for other user." )
2021-07-05 23:29:08 +08:00
return
}
2022-03-22 15:03:22 +08:00
if ! ctx . Doer . IsAdmin {
2022-03-29 14:29:02 +08:00
canCreate , err := organization . OrgFromUser ( ctxUser ) . CanCreateOrgRepo ( ctx . Doer . ID )
2021-07-05 23:29:08 +08:00
if err != nil {
ctx . ServerError ( "CanCreateOrgRepo" , err )
return
} else if ! canCreate {
ctx . Error ( http . StatusForbidden , "" , "Given user is not allowed to create repository in organization." )
return
}
}
}
2023-03-01 06:17:51 +08:00
repo , err := repo_service . GenerateRepository ( ctx , ctx . Doer , ctxUser , ctx . Repo . Repository , opts )
2021-07-05 23:29:08 +08:00
if err != nil {
2021-12-12 23:48:20 +08:00
if repo_model . IsErrRepoAlreadyExist ( err ) {
2021-07-05 23:29:08 +08:00
ctx . Error ( http . StatusConflict , "" , "The repository with the same name already exists." )
2021-11-24 17:49:20 +08:00
} else if db . IsErrNameReserved ( err ) ||
db . IsErrNamePatternNotAllowed ( err ) {
2021-07-05 23:29:08 +08:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
} else {
ctx . Error ( http . StatusInternalServerError , "CreateRepository" , err )
}
return
}
log . Trace ( "Repository generated [%d]: %s/%s" , repo . ID , ctxUser . Name , repo . Name )
2023-06-22 21:08:08 +08:00
ctx . JSON ( http . StatusCreated , convert . ToRepo ( ctx , repo , access_model . Permission { AccessMode : perm . AccessModeOwner } ) )
2021-07-05 23:29:08 +08:00
}
2020-01-10 00:40:01 +08:00
// CreateOrgRepoDeprecated create one repository of the organization
2021-01-26 23:36:53 +08:00
func CreateOrgRepoDeprecated ( ctx * context . APIContext ) {
2020-01-10 00:40:01 +08:00
// swagger:operation POST /org/{org}/repos organization createOrgRepoDeprecated
2017-11-13 15:02:25 +08:00
// ---
// summary: Create a repository in an organization
2020-01-10 00:40:01 +08:00
// deprecated: true
2017-11-13 15:02:25 +08:00
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of organization
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateRepoOption"
// responses:
// "201":
// "$ref": "#/responses/Repository"
// "422":
// "$ref": "#/responses/validationError"
// "403":
// "$ref": "#/responses/forbidden"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-21 01:07:12 +08:00
2021-01-26 23:36:53 +08:00
CreateOrgRepo ( ctx )
2020-01-10 00:40:01 +08:00
}
// CreateOrgRepo create one repository of the organization
2021-01-26 23:36:53 +08:00
func CreateOrgRepo ( ctx * context . APIContext ) {
2020-01-10 00:40:01 +08:00
// swagger:operation POST /orgs/{org}/repos organization createOrgRepo
// ---
// summary: Create a repository in an organization
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of organization
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateRepoOption"
// responses:
// "201":
// "$ref": "#/responses/Repository"
2023-03-14 05:55:30 +08:00
// "400":
// "$ref": "#/responses/error"
2020-01-10 00:40:01 +08:00
// "404":
// "$ref": "#/responses/notFound"
// "403":
// "$ref": "#/responses/forbidden"
2021-01-26 23:36:53 +08:00
opt := web . GetForm ( ctx ) . ( * api . CreateRepoOption )
2023-02-08 14:44:42 +08:00
org , err := organization . GetOrgByName ( ctx , ctx . Params ( ":org" ) )
2014-12-13 09:30:32 +08:00
if err != nil {
2022-03-29 14:29:02 +08:00
if organization . IsErrOrgNotExist ( err ) {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusUnprocessableEntity , "" , err )
2014-12-13 09:30:32 +08:00
} else {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusInternalServerError , "GetOrgByName" , err )
2014-12-13 09:30:32 +08:00
}
return
}
2022-03-29 14:29:02 +08:00
if ! organization . HasOrgOrUserVisible ( ctx , org . AsUser ( ) , ctx . Doer ) {
2021-06-27 03:53:14 +08:00
ctx . NotFound ( "HasOrgOrUserVisible" , nil )
2019-02-19 00:00:27 +08:00
return
}
2022-03-22 15:03:22 +08:00
if ! ctx . Doer . IsAdmin {
canCreate , err := org . CanCreateOrgRepo ( ctx . Doer . ID )
2018-07-05 07:51:02 +08:00
if err != nil {
2020-09-21 04:20:14 +08:00
ctx . Error ( http . StatusInternalServerError , "CanCreateOrgRepo" , err )
2018-07-05 07:51:02 +08:00
return
2019-11-20 19:27:49 +08:00
} else if ! canCreate {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusForbidden , "" , "Given user is not allowed to create repository in organization." )
2018-07-05 07:51:02 +08:00
return
}
2014-12-13 09:30:32 +08:00
}
2021-11-19 19:41:40 +08:00
CreateUserRepo ( ctx , org . AsUser ( ) , * opt )
2014-12-13 09:30:32 +08:00
}
2016-10-08 01:17:27 +08:00
// Get one repository
2016-03-14 06:49:16 +08:00
func Get ( ctx * context . APIContext ) {
2017-11-13 15:02:25 +08:00
// swagger:operation GET /repos/{owner}/{repo} repository repoGet
// ---
// summary: Get a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/Repository"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-21 01:07:12 +08:00
2022-01-25 14:33:40 +08:00
if err := ctx . Repo . Repository . LoadAttributes ( ctx ) ; err != nil {
ctx . Error ( http . StatusInternalServerError , "Repository.LoadAttributes" , err )
return
}
2023-06-22 21:08:08 +08:00
ctx . JSON ( http . StatusOK , convert . ToRepo ( ctx , ctx . Repo . Repository , ctx . Repo . Permission ) )
2015-10-23 05:46:07 +08:00
}
2016-10-03 18:35:42 +08:00
// GetByID returns a single Repository
func GetByID ( ctx * context . APIContext ) {
2017-11-13 15:02:25 +08:00
// swagger:operation GET /repositories/{id} repository repoGetByID
// ---
// summary: Get a repository by id
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the repo to get
// type: integer
2018-10-21 11:40:42 +08:00
// format: int64
2017-11-13 15:02:25 +08:00
// required: true
// responses:
// "200":
// "$ref": "#/responses/Repository"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-21 01:07:12 +08:00
2022-12-03 10:48:26 +08:00
repo , err := repo_model . GetRepositoryByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2016-10-03 18:35:42 +08:00
if err != nil {
2021-12-10 09:27:50 +08:00
if repo_model . IsErrRepoNotExist ( err ) {
2019-03-19 10:29:43 +08:00
ctx . NotFound ( )
2016-10-03 18:35:42 +08:00
} else {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusInternalServerError , "GetRepositoryByID" , err )
2016-10-03 18:35:42 +08:00
}
return
}
2023-06-22 21:08:08 +08:00
permission , err := access_model . GetUserRepoPermission ( ctx , repo , ctx . Doer )
2016-12-06 07:48:51 +08:00
if err != nil {
2023-06-22 21:08:08 +08:00
ctx . Error ( http . StatusInternalServerError , "GetUserRepoPermission" , err )
2017-07-30 09:13:33 +08:00
return
2023-06-22 21:08:08 +08:00
} else if ! permission . HasAccess ( ) {
2019-03-19 10:29:43 +08:00
ctx . NotFound ( )
2016-12-06 07:48:51 +08:00
return
}
2023-06-22 21:08:08 +08:00
ctx . JSON ( http . StatusOK , convert . ToRepo ( ctx , repo , permission ) )
2016-10-03 18:35:42 +08:00
}
2019-05-30 23:09:05 +08:00
// Edit edit repository properties
2021-01-26 23:36:53 +08:00
func Edit ( ctx * context . APIContext ) {
2019-05-30 23:09:05 +08:00
// swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit
// ---
// summary: Edit a repository's properties. Only fields that are set will be changed.
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo to edit
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo to edit
// type: string
// required: true
// - name: body
// in: body
// description: "Properties of a repo that you can edit"
// schema:
// "$ref": "#/definitions/EditRepoOption"
// responses:
// "200":
// "$ref": "#/responses/Repository"
// "403":
// "$ref": "#/responses/forbidden"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2019-05-30 23:09:05 +08:00
// "422":
// "$ref": "#/responses/validationError"
2019-12-21 01:07:12 +08:00
2021-01-26 23:36:53 +08:00
opts := * web . GetForm ( ctx ) . ( * api . EditRepoOption )
2019-05-30 23:09:05 +08:00
if err := updateBasicProperties ( ctx , opts ) ; err != nil {
return
}
if err := updateRepoUnits ( ctx , opts ) ; err != nil {
return
}
if opts . Archived != nil {
if err := updateRepoArchivedState ( ctx , opts ) ; err != nil {
return
}
}
2024-01-26 07:14:38 +08:00
if opts . MirrorInterval != nil || opts . EnablePrune != nil {
2022-04-20 16:20:53 +08:00
if err := updateMirror ( ctx , opts ) ; err != nil {
2021-01-03 07:47:47 +08:00
return
}
}
2022-12-03 10:48:26 +08:00
repo , err := repo_model . GetRepositoryByID ( ctx , ctx . Repo . Repository . ID )
2021-07-14 03:31:59 +08:00
if err != nil {
ctx . InternalServerError ( err )
return
}
2023-06-22 21:08:08 +08:00
ctx . JSON ( http . StatusOK , convert . ToRepo ( ctx , repo , ctx . Repo . Permission ) )
2019-05-30 23:09:05 +08:00
}
// updateBasicProperties updates the basic properties of a repo: Name, Description, Website and Visibility
func updateBasicProperties ( ctx * context . APIContext , opts api . EditRepoOption ) error {
owner := ctx . Repo . Owner
repo := ctx . Repo . Repository
newRepoName := repo . Name
if opts . Name != nil {
newRepoName = * opts . Name
}
// Check if repository name has been changed and not just a case change
if repo . LowerName != strings . ToLower ( newRepoName ) {
2023-03-01 06:17:51 +08:00
if err := repo_service . ChangeRepositoryName ( ctx , ctx . Doer , repo , newRepoName ) ; err != nil {
2019-05-30 23:09:05 +08:00
switch {
2021-12-12 23:48:20 +08:00
case repo_model . IsErrRepoAlreadyExist ( err ) :
2019-05-30 23:09:05 +08:00
ctx . Error ( http . StatusUnprocessableEntity , fmt . Sprintf ( "repo name is already taken [name: %s]" , newRepoName ) , err )
2021-11-24 17:49:20 +08:00
case db . IsErrNameReserved ( err ) :
2019-05-30 23:09:05 +08:00
ctx . Error ( http . StatusUnprocessableEntity , fmt . Sprintf ( "repo name is reserved [name: %s]" , newRepoName ) , err )
2021-11-24 17:49:20 +08:00
case db . IsErrNamePatternNotAllowed ( err ) :
ctx . Error ( http . StatusUnprocessableEntity , fmt . Sprintf ( "repo name's pattern is not allowed [name: %s, pattern: %s]" , newRepoName , err . ( db . ErrNamePatternNotAllowed ) . Pattern ) , err )
2019-05-30 23:09:05 +08:00
default :
ctx . Error ( http . StatusUnprocessableEntity , "ChangeRepositoryName" , err )
}
return err
}
log . Trace ( "Repository name changed: %s/%s -> %s" , ctx . Repo . Owner . Name , repo . Name , newRepoName )
}
// Update the name in the repo object for the response
repo . Name = newRepoName
repo . LowerName = strings . ToLower ( newRepoName )
if opts . Description != nil {
repo . Description = * opts . Description
}
if opts . Website != nil {
repo . Website = * opts . Website
}
visibilityChanged := false
if opts . Private != nil {
// Visibility of forked repository is forced sync with base repository.
if repo . IsFork {
2022-12-03 10:48:26 +08:00
if err := repo . GetBaseRepo ( ctx ) ; err != nil {
2021-03-12 02:09:52 +08:00
ctx . Error ( http . StatusInternalServerError , "Unable to load base repository" , err )
return err
}
2019-05-30 23:09:05 +08:00
* opts . Private = repo . BaseRepo . IsPrivate
}
visibilityChanged = repo . IsPrivate != * opts . Private
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
2022-03-22 15:03:22 +08:00
if visibilityChanged && setting . Repository . ForcePrivate && ! * opts . Private && ! ctx . Doer . IsAdmin {
2019-05-30 23:09:05 +08:00
err := fmt . Errorf ( "cannot change private repository to public" )
ctx . Error ( http . StatusUnprocessableEntity , "Force Private enabled" , err )
return err
}
repo . IsPrivate = * opts . Private
}
2019-11-11 23:15:29 +08:00
if opts . Template != nil {
repo . IsTemplate = * opts . Template
}
2021-05-08 20:11:36 +08:00
if ctx . Repo . GitRepo == nil && ! repo . IsEmpty {
2021-02-12 03:53:41 +08:00
var err error
2022-03-30 03:13:41 +08:00
ctx . Repo . GitRepo , err = git . OpenRepository ( ctx , ctx . Repo . Repository . RepoPath ( ) )
2021-02-12 03:53:41 +08:00
if err != nil {
ctx . Error ( http . StatusInternalServerError , "Unable to OpenRepository" , err )
return err
}
defer ctx . Repo . GitRepo . Close ( )
}
// Default branch only updated if changed and exist or the repository is empty
2021-05-08 20:11:36 +08:00
if opts . DefaultBranch != nil && repo . DefaultBranch != * opts . DefaultBranch && ( repo . IsEmpty || ctx . Repo . GitRepo . IsBranchExist ( * opts . DefaultBranch ) ) {
if ! repo . IsEmpty {
if err := ctx . Repo . GitRepo . SetDefaultBranch ( * opts . DefaultBranch ) ; err != nil {
if ! git . IsErrUnsupportedVersion ( err ) {
ctx . Error ( http . StatusInternalServerError , "SetDefaultBranch" , err )
return err
}
2019-11-17 14:30:01 +08:00
}
}
repo . DefaultBranch = * opts . DefaultBranch
}
2023-03-01 06:17:51 +08:00
if err := repo_service . UpdateRepository ( ctx , repo , visibilityChanged ) ; err != nil {
2019-05-30 23:09:05 +08:00
ctx . Error ( http . StatusInternalServerError , "UpdateRepository" , err )
return err
}
log . Trace ( "Repository basic settings updated: %s/%s" , owner . Name , repo . Name )
return nil
}
// updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
func updateRepoUnits ( ctx * context . APIContext , opts api . EditRepoOption ) error {
owner := ctx . Repo . Owner
repo := ctx . Repo . Repository
2021-12-10 09:27:50 +08:00
var units [ ] repo_model . RepoUnit
2021-11-10 03:57:58 +08:00
var deleteUnitTypes [ ] unit_model . Type
2019-05-30 23:09:05 +08:00
2022-12-10 10:46:31 +08:00
currHasIssues := repo . UnitEnabled ( ctx , unit_model . TypeIssues )
2022-09-28 06:23:58 +08:00
newHasIssues := currHasIssues
2020-01-17 15:34:37 +08:00
if opts . HasIssues != nil {
2022-09-28 06:23:58 +08:00
newHasIssues = * opts . HasIssues
}
if currHasIssues || newHasIssues {
if newHasIssues && opts . ExternalTracker != nil && ! unit_model . TypeExternalTracker . UnitGlobalDisabled ( ) {
2019-10-02 17:30:41 +08:00
// Check that values are valid
if ! validation . IsValidExternalURL ( opts . ExternalTracker . ExternalTrackerURL ) {
err := fmt . Errorf ( "External tracker URL not valid" )
ctx . Error ( http . StatusUnprocessableEntity , "Invalid external tracker URL" , err )
return err
}
if len ( opts . ExternalTracker . ExternalTrackerFormat ) != 0 && ! validation . IsValidExternalTrackerURLFormat ( opts . ExternalTracker . ExternalTrackerFormat ) {
err := fmt . Errorf ( "External tracker URL format not valid" )
ctx . Error ( http . StatusUnprocessableEntity , "Invalid external tracker URL format" , err )
return err
2019-05-30 23:09:05 +08:00
}
2019-10-02 17:30:41 +08:00
2021-12-10 09:27:50 +08:00
units = append ( units , repo_model . RepoUnit {
2019-10-02 17:30:41 +08:00
RepoID : repo . ID ,
2021-11-10 03:57:58 +08:00
Type : unit_model . TypeExternalTracker ,
2021-12-10 09:27:50 +08:00
Config : & repo_model . ExternalTrackerConfig {
2022-10-07 20:49:30 +08:00
ExternalTrackerURL : opts . ExternalTracker . ExternalTrackerURL ,
ExternalTrackerFormat : opts . ExternalTracker . ExternalTrackerFormat ,
ExternalTrackerStyle : opts . ExternalTracker . ExternalTrackerStyle ,
ExternalTrackerRegexpPattern : opts . ExternalTracker . ExternalTrackerRegexpPattern ,
2019-10-02 17:30:41 +08:00
} ,
} )
2021-11-10 03:57:58 +08:00
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeIssues )
2022-09-28 06:23:58 +08:00
} else if newHasIssues && opts . ExternalTracker == nil && ! unit_model . TypeIssues . UnitGlobalDisabled ( ) {
2019-10-02 17:30:41 +08:00
// Default to built-in tracker
2021-12-10 09:27:50 +08:00
var config * repo_model . IssuesConfig
2019-10-02 17:30:41 +08:00
if opts . InternalTracker != nil {
2021-12-10 09:27:50 +08:00
config = & repo_model . IssuesConfig {
2019-10-02 17:30:41 +08:00
EnableTimetracker : opts . InternalTracker . EnableTimeTracker ,
AllowOnlyContributorsToTrackTime : opts . InternalTracker . AllowOnlyContributorsToTrackTime ,
EnableDependencies : opts . InternalTracker . EnableIssueDependencies ,
}
2022-12-10 10:46:31 +08:00
} else if unit , err := repo . GetUnit ( ctx , unit_model . TypeIssues ) ; err != nil {
2019-10-02 17:30:41 +08:00
// Unit type doesn't exist so we make a new config file with default values
2021-12-10 09:27:50 +08:00
config = & repo_model . IssuesConfig {
2019-10-02 17:30:41 +08:00
EnableTimetracker : true ,
AllowOnlyContributorsToTrackTime : true ,
EnableDependencies : true ,
}
} else {
config = unit . IssuesConfig ( )
}
2021-12-10 09:27:50 +08:00
units = append ( units , repo_model . RepoUnit {
2019-10-02 17:30:41 +08:00
RepoID : repo . ID ,
2021-11-10 03:57:58 +08:00
Type : unit_model . TypeIssues ,
2019-10-02 17:30:41 +08:00
Config : config ,
} )
2021-11-10 03:57:58 +08:00
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeExternalTracker )
2022-09-28 06:23:58 +08:00
} else if ! newHasIssues {
2021-11-10 03:57:58 +08:00
if ! unit_model . TypeExternalTracker . UnitGlobalDisabled ( ) {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeExternalTracker )
2020-01-17 15:34:37 +08:00
}
2021-11-10 03:57:58 +08:00
if ! unit_model . TypeIssues . UnitGlobalDisabled ( ) {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeIssues )
2020-01-17 15:34:37 +08:00
}
2019-05-30 23:09:05 +08:00
}
}
2022-12-10 10:46:31 +08:00
currHasWiki := repo . UnitEnabled ( ctx , unit_model . TypeWiki )
2022-09-28 06:23:58 +08:00
newHasWiki := currHasWiki
2020-01-17 15:34:37 +08:00
if opts . HasWiki != nil {
2022-09-28 06:23:58 +08:00
newHasWiki = * opts . HasWiki
}
if currHasWiki || newHasWiki {
if newHasWiki && opts . ExternalWiki != nil && ! unit_model . TypeExternalWiki . UnitGlobalDisabled ( ) {
2019-10-02 17:30:41 +08:00
// Check that values are valid
if ! validation . IsValidExternalURL ( opts . ExternalWiki . ExternalWikiURL ) {
err := fmt . Errorf ( "External wiki URL not valid" )
ctx . Error ( http . StatusUnprocessableEntity , "" , "Invalid external wiki URL" )
return err
}
2021-12-10 09:27:50 +08:00
units = append ( units , repo_model . RepoUnit {
2019-10-02 17:30:41 +08:00
RepoID : repo . ID ,
2021-11-10 03:57:58 +08:00
Type : unit_model . TypeExternalWiki ,
2021-12-10 09:27:50 +08:00
Config : & repo_model . ExternalWikiConfig {
2019-10-02 17:30:41 +08:00
ExternalWikiURL : opts . ExternalWiki . ExternalWikiURL ,
} ,
} )
2021-11-10 03:57:58 +08:00
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeWiki )
2022-09-28 06:23:58 +08:00
} else if newHasWiki && opts . ExternalWiki == nil && ! unit_model . TypeWiki . UnitGlobalDisabled ( ) {
2021-12-10 09:27:50 +08:00
config := & repo_model . UnitConfig { }
units = append ( units , repo_model . RepoUnit {
2019-10-02 17:30:41 +08:00
RepoID : repo . ID ,
2021-11-10 03:57:58 +08:00
Type : unit_model . TypeWiki ,
2019-10-02 17:30:41 +08:00
Config : config ,
} )
2021-11-10 03:57:58 +08:00
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeExternalWiki )
2022-09-28 06:23:58 +08:00
} else if ! newHasWiki {
2021-11-10 03:57:58 +08:00
if ! unit_model . TypeExternalWiki . UnitGlobalDisabled ( ) {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeExternalWiki )
2020-01-17 15:34:37 +08:00
}
2021-11-10 03:57:58 +08:00
if ! unit_model . TypeWiki . UnitGlobalDisabled ( ) {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeWiki )
2020-01-17 15:34:37 +08:00
}
2019-10-02 17:30:41 +08:00
}
2019-05-30 23:09:05 +08:00
}
2022-12-10 10:46:31 +08:00
currHasPullRequests := repo . UnitEnabled ( ctx , unit_model . TypePullRequests )
2022-09-28 06:23:58 +08:00
newHasPullRequests := currHasPullRequests
2020-01-17 15:34:37 +08:00
if opts . HasPullRequests != nil {
2022-09-28 06:23:58 +08:00
newHasPullRequests = * opts . HasPullRequests
}
if currHasPullRequests || newHasPullRequests {
if newHasPullRequests && ! unit_model . TypePullRequests . UnitGlobalDisabled ( ) {
2020-01-17 15:34:37 +08:00
// We do allow setting individual PR settings through the API, so
// we get the config settings and then set them
// if those settings were provided in the opts.
2022-12-10 10:46:31 +08:00
unit , err := repo . GetUnit ( ctx , unit_model . TypePullRequests )
2021-12-10 09:27:50 +08:00
var config * repo_model . PullRequestsConfig
2020-01-17 15:34:37 +08:00
if err != nil {
// Unit type doesn't exist so we make a new config file with default values
2021-12-10 09:27:50 +08:00
config = & repo_model . PullRequestsConfig {
2021-07-13 07:26:25 +08:00
IgnoreWhitespaceConflicts : false ,
AllowMerge : true ,
AllowRebase : true ,
AllowRebaseMerge : true ,
AllowSquash : true ,
AllowManualMerge : true ,
AutodetectManualMerge : false ,
2022-03-04 16:30:49 +08:00
AllowRebaseUpdate : true ,
2021-07-13 07:26:25 +08:00
DefaultDeleteBranchAfterMerge : false ,
2021-12-10 09:27:50 +08:00
DefaultMergeStyle : repo_model . MergeStyleMerge ,
2023-02-13 14:09:52 +08:00
DefaultAllowMaintainerEdit : false ,
2020-01-17 15:34:37 +08:00
}
} else {
config = unit . PullRequestsConfig ( )
2019-05-30 23:09:05 +08:00
}
2020-01-17 15:34:37 +08:00
if opts . IgnoreWhitespaceConflicts != nil {
config . IgnoreWhitespaceConflicts = * opts . IgnoreWhitespaceConflicts
}
if opts . AllowMerge != nil {
config . AllowMerge = * opts . AllowMerge
}
if opts . AllowRebase != nil {
config . AllowRebase = * opts . AllowRebase
}
if opts . AllowRebaseMerge != nil {
config . AllowRebaseMerge = * opts . AllowRebaseMerge
}
if opts . AllowSquash != nil {
config . AllowSquash = * opts . AllowSquash
}
2021-03-04 11:41:23 +08:00
if opts . AllowManualMerge != nil {
config . AllowManualMerge = * opts . AllowManualMerge
}
if opts . AutodetectManualMerge != nil {
config . AutodetectManualMerge = * opts . AutodetectManualMerge
}
2022-03-04 16:30:49 +08:00
if opts . AllowRebaseUpdate != nil {
config . AllowRebaseUpdate = * opts . AllowRebaseUpdate
}
2021-07-13 07:26:25 +08:00
if opts . DefaultDeleteBranchAfterMerge != nil {
config . DefaultDeleteBranchAfterMerge = * opts . DefaultDeleteBranchAfterMerge
}
2021-03-27 22:55:40 +08:00
if opts . DefaultMergeStyle != nil {
2021-12-10 09:27:50 +08:00
config . DefaultMergeStyle = repo_model . MergeStyle ( * opts . DefaultMergeStyle )
2021-03-27 22:55:40 +08:00
}
2023-02-13 14:09:52 +08:00
if opts . DefaultAllowMaintainerEdit != nil {
config . DefaultAllowMaintainerEdit = * opts . DefaultAllowMaintainerEdit
}
2019-08-10 17:32:46 +08:00
2021-12-10 09:27:50 +08:00
units = append ( units , repo_model . RepoUnit {
2020-01-17 15:34:37 +08:00
RepoID : repo . ID ,
2021-11-10 03:57:58 +08:00
Type : unit_model . TypePullRequests ,
2020-01-17 15:34:37 +08:00
Config : config ,
} )
2022-09-28 06:23:58 +08:00
} else if ! newHasPullRequests && ! unit_model . TypePullRequests . UnitGlobalDisabled ( ) {
2021-11-10 03:57:58 +08:00
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypePullRequests )
2020-01-17 15:34:37 +08:00
}
2019-05-30 23:09:05 +08:00
}
2021-11-10 03:57:58 +08:00
if opts . HasProjects != nil && ! unit_model . TypeProjects . UnitGlobalDisabled ( ) {
2020-08-17 11:07:38 +08:00
if * opts . HasProjects {
2021-12-10 09:27:50 +08:00
units = append ( units , repo_model . RepoUnit {
2020-08-17 11:07:38 +08:00
RepoID : repo . ID ,
2021-11-10 03:57:58 +08:00
Type : unit_model . TypeProjects ,
2020-08-17 11:07:38 +08:00
} )
} else {
2021-11-10 03:57:58 +08:00
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeProjects )
2020-08-17 11:07:38 +08:00
}
}
2023-03-17 01:30:42 +08:00
if opts . HasReleases != nil && ! unit_model . TypeReleases . UnitGlobalDisabled ( ) {
if * opts . HasReleases {
units = append ( units , repo_model . RepoUnit {
RepoID : repo . ID ,
Type : unit_model . TypeReleases ,
} )
} else {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeReleases )
}
}
if opts . HasPackages != nil && ! unit_model . TypePackages . UnitGlobalDisabled ( ) {
if * opts . HasPackages {
units = append ( units , repo_model . RepoUnit {
RepoID : repo . ID ,
Type : unit_model . TypePackages ,
} )
} else {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypePackages )
}
}
if opts . HasActions != nil && ! unit_model . TypeActions . UnitGlobalDisabled ( ) {
if * opts . HasActions {
units = append ( units , repo_model . RepoUnit {
RepoID : repo . ID ,
Type : unit_model . TypeActions ,
} )
} else {
deleteUnitTypes = append ( deleteUnitTypes , unit_model . TypeActions )
}
}
2023-05-06 17:39:06 +08:00
if len ( units ) + len ( deleteUnitTypes ) > 0 {
2024-01-15 06:54:22 +08:00
if err := repo_service . UpdateRepositoryUnits ( ctx , repo , units , deleteUnitTypes ) ; err != nil {
2023-05-06 17:39:06 +08:00
ctx . Error ( http . StatusInternalServerError , "UpdateRepositoryUnits" , err )
return err
}
2019-05-30 23:09:05 +08:00
}
log . Trace ( "Repository advanced settings updated: %s/%s" , owner . Name , repo . Name )
return nil
}
// updateRepoArchivedState updates repo's archive state
func updateRepoArchivedState ( ctx * context . APIContext , opts api . EditRepoOption ) error {
repo := ctx . Repo . Repository
// archive / un-archive
if opts . Archived != nil {
if repo . IsMirror {
err := fmt . Errorf ( "repo is a mirror, cannot archive/un-archive" )
ctx . Error ( http . StatusUnprocessableEntity , err . Error ( ) , err )
return err
}
if * opts . Archived {
2023-09-16 22:39:12 +08:00
if err := repo_model . SetArchiveRepoState ( ctx , repo , * opts . Archived ) ; err != nil {
2019-05-30 23:09:05 +08:00
log . Error ( "Tried to archive a repo: %s" , err )
ctx . Error ( http . StatusInternalServerError , "ArchiveRepoState" , err )
return err
}
log . Trace ( "Repository was archived: %s/%s" , ctx . Repo . Owner . Name , repo . Name )
} else {
2023-09-16 22:39:12 +08:00
if err := repo_model . SetArchiveRepoState ( ctx , repo , * opts . Archived ) ; err != nil {
2019-05-30 23:09:05 +08:00
log . Error ( "Tried to un-archive a repo: %s" , err )
ctx . Error ( http . StatusInternalServerError , "ArchiveRepoState" , err )
return err
}
log . Trace ( "Repository was un-archived: %s/%s" , ctx . Repo . Owner . Name , repo . Name )
}
}
return nil
}
2022-04-20 16:20:53 +08:00
// updateMirror updates a repo's mirror Interval and EnablePrune
func updateMirror ( ctx * context . APIContext , opts api . EditRepoOption ) error {
2021-01-03 07:47:47 +08:00
repo := ctx . Repo . Repository
2022-04-20 16:20:53 +08:00
// only update mirror if interval or enable prune are provided
if opts . MirrorInterval == nil && opts . EnablePrune == nil {
return nil
}
// these values only make sense if the repo is a mirror
if ! repo . IsMirror {
err := fmt . Errorf ( "repo is not a mirror, can not change mirror interval" )
ctx . Error ( http . StatusUnprocessableEntity , err . Error ( ) , err )
return err
}
// get the mirror from the repo
2022-05-20 22:08:52 +08:00
mirror , err := repo_model . GetMirrorByRepoID ( ctx , repo . ID )
2022-04-20 16:20:53 +08:00
if err != nil {
log . Error ( "Failed to get mirror: %s" , err )
ctx . Error ( http . StatusInternalServerError , "MirrorInterval" , err )
return err
}
// update MirrorInterval
2021-01-03 07:47:47 +08:00
if opts . MirrorInterval != nil {
2022-04-20 16:20:53 +08:00
// MirrorInterval should be a duration
interval , err := time . ParseDuration ( * opts . MirrorInterval )
2021-12-10 09:27:50 +08:00
if err != nil {
2022-04-20 16:20:53 +08:00
log . Error ( "Wrong format for MirrorInternal Sent: %s" , err )
ctx . Error ( http . StatusUnprocessableEntity , "MirrorInterval" , err )
2021-01-03 07:47:47 +08:00
return err
}
2022-04-20 16:20:53 +08:00
// Ensure the provided duration is not too short
if interval != 0 && interval < setting . Mirror . MinInterval {
err := fmt . Errorf ( "invalid mirror interval: %s is below minimum interval: %s" , interval , setting . Mirror . MinInterval )
2021-01-03 07:47:47 +08:00
ctx . Error ( http . StatusUnprocessableEntity , "MirrorInterval" , err )
return err
}
2022-04-20 16:20:53 +08:00
mirror . Interval = interval
mirror . Repo = repo
mirror . ScheduleNextUpdate ( )
log . Trace ( "Repository %s Mirror[%d] Set Interval: %s NextUpdateUnix: %s" , repo . FullName ( ) , mirror . ID , interval , mirror . NextUpdateUnix )
2021-01-03 07:47:47 +08:00
}
2022-04-20 16:20:53 +08:00
// update EnablePrune
if opts . EnablePrune != nil {
mirror . EnablePrune = * opts . EnablePrune
log . Trace ( "Repository %s Mirror[%d] Set EnablePrune: %t" , repo . FullName ( ) , mirror . ID , mirror . EnablePrune )
}
// finally update the mirror in the DB
2022-05-20 22:08:52 +08:00
if err := repo_model . UpdateMirror ( ctx , mirror ) ; err != nil {
2022-04-20 16:20:53 +08:00
log . Error ( "Failed to Set Mirror Interval: %s" , err )
ctx . Error ( http . StatusUnprocessableEntity , "MirrorInterval" , err )
return err
}
2021-01-03 07:47:47 +08:00
return nil
}
2016-10-08 01:17:27 +08:00
// Delete one repository
2016-03-14 06:49:16 +08:00
func Delete ( ctx * context . APIContext ) {
2017-11-13 15:02:25 +08:00
// swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
// ---
// summary: Delete a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo to delete
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo to delete
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-21 01:07:12 +08:00
2016-11-15 06:33:58 +08:00
owner := ctx . Repo . Owner
repo := ctx . Repo . Repository
2015-10-04 23:09:16 +08:00
2023-09-26 01:24:35 +08:00
canDelete , err := repo_module . CanUserDelete ( ctx , repo , ctx . Doer )
2019-10-26 14:54:11 +08:00
if err != nil {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusInternalServerError , "CanUserDelete" , err )
2019-10-26 14:54:11 +08:00
return
} else if ! canDelete {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusForbidden , "" , "Given user is not owner of organization." )
2019-10-26 14:54:11 +08:00
return
2015-10-04 23:09:16 +08:00
}
2021-05-15 04:19:38 +08:00
if ctx . Repo . GitRepo != nil {
ctx . Repo . GitRepo . Close ( )
}
2022-03-22 15:03:22 +08:00
if err := repo_service . DeleteRepository ( ctx , ctx . Doer , repo , true ) ; err != nil {
2019-12-21 01:07:12 +08:00
ctx . Error ( http . StatusInternalServerError , "DeleteRepository" , err )
2015-10-04 23:09:16 +08:00
return
}
2015-10-23 05:46:07 +08:00
log . Trace ( "Repository deleted: %s/%s" , owner . Name , repo . Name )
2019-12-21 01:07:12 +08:00
ctx . Status ( http . StatusNoContent )
2015-10-04 23:09:16 +08:00
}
2020-09-11 22:48:39 +08:00
// GetIssueTemplates returns the issue templates for a repository
func GetIssueTemplates ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/issue_templates repository repoGetIssueTemplates
// ---
// summary: Get available issue templates for a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/IssueTemplates"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2024-02-14 09:32:31 +08:00
ret := issue . ParseTemplatesFromDefaultBranch ( ctx . Repo . Repository , ctx . Repo . GitRepo )
if cnt := len ( ret . TemplateErrors ) ; cnt != 0 {
ctx . Resp . Header ( ) . Add ( "X-Gitea-Warning" , "error occurs when parsing issue template: count=" + strconv . Itoa ( cnt ) )
2023-05-09 07:30:14 +08:00
}
2024-02-14 09:32:31 +08:00
ctx . JSON ( http . StatusOK , ret . IssueTemplates )
2020-09-11 22:48:39 +08:00
}
2023-03-29 02:22:07 +08:00
// GetIssueConfig returns the issue config for a repo
func GetIssueConfig ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/issue_config repository repoGetIssueConfig
// ---
// summary: Returns the issue config for a repo
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/RepoIssueConfig"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2023-05-09 07:30:14 +08:00
issueConfig , _ := issue . GetTemplateConfigFromDefaultBranch ( ctx . Repo . Repository , ctx . Repo . GitRepo )
2023-03-29 02:22:07 +08:00
ctx . JSON ( http . StatusOK , issueConfig )
}
// ValidateIssueConfig returns validation errors for the issue config
func ValidateIssueConfig ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/issue_config/validate repository repoValidateIssueConfig
// ---
// summary: Returns the validation information for a issue config
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/RepoIssueConfigValidation"
2023-09-13 10:37:54 +08:00
// "404":
// "$ref": "#/responses/notFound"
2023-05-09 07:30:14 +08:00
_ , err := issue . GetTemplateConfigFromDefaultBranch ( ctx . Repo . Repository , ctx . Repo . GitRepo )
2023-03-29 02:22:07 +08:00
if err == nil {
ctx . JSON ( http . StatusOK , api . IssueConfigValidation { Valid : true , Message : "" } )
} else {
ctx . JSON ( http . StatusOK , api . IssueConfigValidation { Valid : false , Message : err . Error ( ) } )
}
}
2023-04-04 21:35:31 +08:00
func ListRepoActivityFeeds ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/activities/feeds repository repoListActivityFeeds
// ---
// summary: List a repository's activity feeds
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: date
// in: query
// description: the date of the activities to be found
// type: string
// format: date
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/ActivityFeedsList"
// "404":
// "$ref": "#/responses/notFound"
listOptions := utils . GetListOptions ( ctx )
opts := activities_model . GetFeedsOptions {
RequestedRepo : ctx . Repo . Repository ,
Actor : ctx . Doer ,
IncludePrivate : true ,
Date : ctx . FormString ( "date" ) ,
ListOptions : listOptions ,
}
feeds , count , err := activities_model . GetFeeds ( ctx , opts )
if err != nil {
ctx . Error ( http . StatusInternalServerError , "GetFeeds" , err )
return
}
ctx . SetTotalCountHeader ( count )
ctx . JSON ( http . StatusOK , convert . ToActivities ( ctx , feeds , ctx . Doer ) )
}