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.
2014-08-26 18:11:15 +08:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2015-12-04 17:16:42 -05:00
package repo
2014-08-26 18:11:15 +08:00
import (
2017-08-17 03:31:34 +02:00
"fmt"
2017-10-26 23:16:13 +02:00
"net/http"
2017-02-11 12:00:01 +08:00
"strings"
2014-08-26 18:11:15 +08:00
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2017-10-26 23:16:13 +02:00
"code.gitea.io/gitea/modules/util"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/routers/api/v1/convert"
2018-03-29 21:32:40 +08:00
2017-12-03 17:48:03 -08:00
api "code.gitea.io/sdk/gitea"
2014-08-26 18:11:15 +08:00
)
2018-08-02 04:10:02 -04:00
var searchOrderByMap = map [ string ] map [ string ] models . SearchOrderBy {
"asc" : {
"alpha" : models . SearchOrderByAlphabetically ,
"created" : models . SearchOrderByOldest ,
"updated" : models . SearchOrderByLeastUpdated ,
"size" : models . SearchOrderBySize ,
"id" : models . SearchOrderByID ,
} ,
"desc" : {
"alpha" : models . SearchOrderByAlphabeticallyReverse ,
"created" : models . SearchOrderByNewest ,
"updated" : models . SearchOrderByRecentUpdated ,
"size" : models . SearchOrderBySizeReverse ,
"id" : models . SearchOrderByIDReverse ,
} ,
}
2016-11-24 15:04:31 +08:00
// Search repositories via options
2016-03-13 18:49:16 -04:00
func Search ( ctx * context . APIContext ) {
2017-11-12 23: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
// - name: uid
// in: query
2017-11-15 00: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 04:40:42 +01:00
// format: int64
2017-11-15 00:10:26 -08:00
// - name: page
// in: query
// description: page number of results to return (1-based)
2017-11-12 23:02:25 -08:00
// type: integer
// - name: limit
// in: query
2017-11-15 00:10:26 -08:00
// description: page size of results, maximum page size is 50
2017-11-12 23:02:25 -08:00
// type: integer
// - 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 00:10:26 -08:00
// description: if `uid` is given, search only for repos that the user owns
2017-11-12 23:02:25 -08:00
// type: boolean
2018-08-02 04:10:02 -04: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
2017-11-12 23:02:25 -08:00
// responses:
// "200":
// "$ref": "#/responses/SearchResults"
// "422":
// "$ref": "#/responses/validationError"
2016-03-11 15:33:12 -05:00
opts := & models . SearchRepoOptions {
2017-10-26 23:16:13 +02:00
Keyword : strings . Trim ( ctx . Query ( "q" ) , " " ) ,
OwnerID : ctx . QueryInt64 ( "uid" ) ,
2017-11-15 00:10:26 -08:00
Page : ctx . QueryInt ( "page" ) ,
2017-10-26 23:16:13 +02:00
PageSize : convert . ToCorrectPageSize ( ctx . QueryInt ( "limit" ) ) ,
2018-09-13 10:33:48 +08:00
TopicOnly : ctx . QueryBool ( "topic" ) ,
2017-10-26 23:16:13 +02:00
Collaborate : util . OptionalBoolNone ,
}
if ctx . QueryBool ( "exclusive" ) {
opts . Collaborate = util . OptionalBoolFalse
}
var mode = ctx . Query ( "mode" )
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
}
2018-08-02 04:10:02 -04:00
var sortMode = ctx . Query ( "sort" )
if len ( sortMode ) > 0 {
var sortOrder = ctx . Query ( "order" )
if len ( sortOrder ) == 0 {
sortOrder = "asc"
}
if searchModeMap , ok := searchOrderByMap [ sortOrder ] ; ok {
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-26 23:16:13 +02:00
var err error
2017-10-17 17:20:22 +02:00
if opts . OwnerID > 0 {
var repoOwner * models . User
if ctx . User != nil && ctx . User . ID == opts . OwnerID {
repoOwner = ctx . User
2014-10-25 07:50:19 -04:00
} else {
2017-10-17 17:20:22 +02:00
repoOwner , err = models . GetUserByID ( opts . OwnerID )
2014-10-25 07:50:19 -04:00
if err != nil {
2017-05-02 15:35:59 +02:00
ctx . JSON ( 500 , api . SearchError {
OK : false ,
Error : err . Error ( ) ,
2014-10-25 07:50:19 -04:00
} )
return
}
2017-10-17 17:20:22 +02:00
}
2017-08-23 03:30:54 +02:00
2017-10-26 23:16:13 +02:00
if repoOwner . IsOrganization ( ) {
opts . Collaborate = util . OptionalBoolFalse
2017-10-17 17:20:22 +02:00
}
// Check visibility.
2017-12-20 23:43:26 -08:00
if ctx . IsSigned {
if ctx . User . ID == repoOwner . ID {
opts . Private = true
} else if repoOwner . IsOrganization ( ) {
opts . Private , err = repoOwner . IsOwnedBy ( ctx . User . ID )
if err != nil {
ctx . JSON ( 500 , api . SearchError {
OK : false ,
Error : err . Error ( ) ,
} )
return
}
}
2014-10-25 07:50:19 -04:00
}
}
2016-07-04 17:27:06 +08:00
repos , count , err := models . SearchRepositoryByName ( opts )
2014-08-26 18:11:15 +08:00
if err != nil {
2017-05-02 15:35:59 +02:00
ctx . JSON ( 500 , api . SearchError {
OK : false ,
Error : err . Error ( ) ,
2014-08-26 18:11:15 +08:00
} )
return
}
2014-11-14 17:11:30 -05:00
results := make ( [ ] * api . Repository , len ( repos ) )
2017-02-09 20:30:26 -05:00
for i , repo := range repos {
if err = repo . GetOwner ( ) ; err != nil {
2017-05-02 15:35:59 +02:00
ctx . JSON ( 500 , api . SearchError {
OK : false ,
Error : err . Error ( ) ,
2014-08-26 18:11:15 +08:00
} )
return
}
2018-11-28 19:26:14 +08:00
accessMode , err := models . AccessLevel ( ctx . User , repo )
2017-02-09 20:30:26 -05:00
if err != nil {
2017-05-02 15:35:59 +02:00
ctx . JSON ( 500 , api . SearchError {
OK : false ,
Error : err . Error ( ) ,
2017-02-09 20:30:26 -05:00
} )
2014-08-26 18:11:15 +08:00
}
2017-02-09 20:30:26 -05:00
results [ i ] = repo . APIFormat ( accessMode )
2014-08-26 18:11:15 +08:00
}
2016-07-04 17:27:06 +08:00
ctx . SetLinkHeader ( int ( count ) , setting . API . MaxResponseItems )
2017-08-17 03:31:34 +02:00
ctx . Header ( ) . Set ( "X-Total-Count" , fmt . Sprintf ( "%d" , count ) )
2017-05-02 15:35:59 +02:00
ctx . JSON ( 200 , api . SearchResults {
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
2016-03-13 18:49:16 -04:00
func CreateUserRepo ( ctx * context . APIContext , owner * models . User , opt api . CreateRepoOption ) {
2017-09-03 01:20:24 -07:00
repo , err := models . CreateRepository ( ctx . User , owner , models . CreateRepoOptions {
2015-08-28 18:33:09 +08:00
Name : opt . Name ,
Description : opt . Description ,
2015-08-28 19:06:18 +08:00
Gitignores : opt . Gitignores ,
2015-08-28 18:33:09 +08:00
License : opt . License ,
2015-08-28 19:06:18 +08:00
Readme : opt . Readme ,
IsPrivate : opt . Private ,
AutoInit : opt . AutoInit ,
2015-08-28 18:33:09 +08:00
} )
2014-12-12 20:30:32 -05:00
if err != nil {
2015-08-08 17:10:34 +08:00
if models . IsErrRepoAlreadyExist ( err ) ||
2015-03-26 17:11:47 -04:00
models . IsErrNameReserved ( err ) ||
models . IsErrNamePatternNotAllowed ( err ) {
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , err )
2014-12-12 20:30:32 -05:00
} else {
if repo != nil {
2017-09-03 01:20:24 -07:00
if err = models . DeleteRepository ( ctx . User , ctx . User . ID , repo . ID ) ; err != nil {
2014-12-12 20:30:32 -05:00
log . Error ( 4 , "DeleteRepository: %v" , err )
}
}
2016-03-13 18:49:16 -04:00
ctx . Error ( 500 , "CreateRepository" , err )
2014-12-12 20:30:32 -05:00
}
return
}
2016-12-05 18:48:51 -05:00
ctx . JSON ( 201 , repo . APIFormat ( models . AccessModeOwner ) )
2014-12-12 20:30:32 -05:00
}
2016-10-07 19:17:27 +02:00
// Create one repository of mine
2016-03-13 18:49:16 -04:00
func Create ( ctx * context . APIContext , opt api . CreateRepoOption ) {
2017-11-12 23: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"
2014-12-12 20:30:32 -05:00
if ctx . User . IsOrganization ( ) {
2017-11-12 23:02:25 -08:00
// Shouldn't reach this condition, but just in case.
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , "not allowed creating repository for organization" )
2014-12-12 20:30:32 -05:00
return
}
2015-12-17 22:57:41 -05:00
CreateUserRepo ( ctx , ctx . User , opt )
2014-12-12 20:30:32 -05:00
}
2016-11-24 15:04:31 +08:00
// CreateOrgRepo create one repository of the organization
2016-03-13 18:49:16 -04:00
func CreateOrgRepo ( ctx * context . APIContext , opt api . CreateRepoOption ) {
2017-11-12 23:02:25 -08:00
// swagger:operation POST /org/{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"
// "422":
// "$ref": "#/responses/validationError"
// "403":
// "$ref": "#/responses/forbidden"
2014-12-12 20:30:32 -05:00
org , err := models . GetOrgByName ( ctx . Params ( ":org" ) )
if err != nil {
2017-07-06 15:30:19 +02:00
if models . IsErrOrgNotExist ( err ) {
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , err )
2014-12-12 20:30:32 -05:00
} else {
2016-03-13 18:49:16 -04:00
ctx . Error ( 500 , "GetOrgByName" , err )
2014-12-12 20:30:32 -05:00
}
return
}
2018-07-05 02:51:02 +03:00
if ! ctx . User . IsAdmin {
isOwner , err := org . IsOwnedBy ( ctx . User . ID )
if err != nil {
ctx . ServerError ( "IsOwnedBy" , err )
return
} else if ! isOwner {
ctx . Error ( 403 , "" , "Given user is not owner of organization." )
return
}
2014-12-12 20:30:32 -05:00
}
2015-12-17 22:57:41 -05:00
CreateUserRepo ( ctx , org , opt )
2014-12-12 20:30:32 -05:00
}
2016-11-24 15:04:31 +08:00
// Migrate migrate remote git repository to gitea
2016-03-13 18:49:16 -04:00
func Migrate ( ctx * context . APIContext , form auth . MigrateRepoForm ) {
2017-11-12 23:02:25 -08:00
// swagger:operation POST /repos/migrate repository repoMigrate
// ---
// summary: Migrate a remote git repository
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/MigrateRepoForm"
// responses:
// "201":
// "$ref": "#/responses/Repository"
2015-09-03 06:17:33 -04:00
ctxUser := ctx . User
2015-11-25 00:55:37 -05:00
// Not equal means context user is an organization,
// or is another user/organization if current user is admin.
2016-11-27 14:03:59 +08:00
if form . UID != ctxUser . ID {
org , err := models . GetUserByID ( form . UID )
2014-08-29 17:31:53 +08:00
if err != nil {
2015-08-05 11:14:17 +08:00
if models . IsErrUserNotExist ( err ) {
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , err )
2015-02-22 09:49:25 -05:00
} else {
2016-03-13 18:49:16 -04:00
ctx . Error ( 500 , "GetUserByID" , err )
2015-02-22 09:49:25 -05:00
}
2014-08-29 11:24:37 +08:00
return
}
ctxUser = org
}
if ctx . HasError ( ) {
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , ctx . GetErrMsg ( ) )
2014-08-29 11:24:37 +08:00
return
}
2018-07-05 01:45:15 +03:00
if ! ctx . User . IsAdmin {
if ! ctxUser . IsOrganization ( ) && ctx . User . ID != ctxUser . ID {
ctx . Error ( 403 , "" , "Given user is not an organization." )
2014-08-29 11:24:37 +08:00
return
}
2018-07-05 01:45:15 +03:00
if ctxUser . IsOrganization ( ) {
// Check ownership of organization.
isOwner , err := ctxUser . IsOwnedBy ( ctx . User . ID )
if err != nil {
ctx . Error ( 500 , "IsOwnedBy" , err )
return
} else if ! isOwner {
ctx . Error ( 403 , "" , "Given user is not owner of organization." )
return
}
}
2014-08-29 11:24:37 +08:00
}
2015-11-03 18:40:52 -05:00
remoteAddr , err := form . ParseRemoteAddr ( ctx . User )
if err != nil {
if models . IsErrInvalidCloneAddr ( err ) {
addrErr := err . ( models . ErrInvalidCloneAddr )
switch {
case addrErr . IsURLError :
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , err )
2015-11-03 18:40:52 -05:00
case addrErr . IsPermissionDenied :
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , "You are not allowed to import local repositories." )
2015-11-03 18:40:52 -05:00
case addrErr . IsInvalidPath :
2016-03-13 18:49:16 -04:00
ctx . Error ( 422 , "" , "Invalid local path, it does not exist or not a directory." )
2015-11-03 18:40:52 -05:00
default :
2016-03-13 18:49:16 -04:00
ctx . Error ( 500 , "ParseRemoteAddr" , "Unknown error type (ErrInvalidCloneAddr): " + err . Error ( ) )
2015-11-03 18:40:52 -05:00
}
} else {
2016-03-13 18:49:16 -04:00
ctx . Error ( 500 , "ParseRemoteAddr" , err )
2015-02-22 09:49:25 -05:00
}
2014-08-29 11:24:37 +08:00
return
}
2017-09-03 01:20:24 -07:00
repo , err := models . MigrateRepository ( ctx . User , ctxUser , models . MigrateRepoOptions {
2015-10-25 04:26:26 -04:00
Name : form . RepoName ,
Description : form . Description ,
IsPrivate : form . Private || setting . Repository . ForcePrivate ,
IsMirror : form . Mirror ,
RemoteAddr : remoteAddr ,
} )
2015-02-22 09:49:25 -05:00
if err != nil {
2017-12-03 17:48:03 -08:00
err = util . URLSanitizedError ( err , remoteAddr )
2015-02-22 09:49:25 -05:00
if repo != nil {
2017-09-03 01:20:24 -07:00
if errDelete := models . DeleteRepository ( ctx . User , ctxUser . ID , repo . ID ) ; errDelete != nil {
2015-02-22 09:49:25 -05:00
log . Error ( 4 , "DeleteRepository: %v" , errDelete )
}
2014-08-29 11:24:37 +08:00
}
2017-12-03 17:48:03 -08:00
ctx . Error ( 500 , "MigrateRepository" , err )
2015-02-22 09:49:25 -05:00
return
2014-08-29 11:24:37 +08:00
}
2015-02-22 09:49:25 -05:00
log . Trace ( "Repository migrated: %s/%s" , ctxUser . Name , form . RepoName )
2016-12-05 18:48:51 -05:00
ctx . JSON ( 201 , repo . APIFormat ( models . AccessModeAdmin ) )
2014-08-29 11:24:37 +08:00
}
2015-10-04 17:09:16 +02:00
2016-10-07 19:17:27 +02:00
// Get one repository
2016-03-13 18:49:16 -04:00
func Get ( ctx * context . APIContext ) {
2017-11-12 23: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"
2017-07-11 21:23:41 -04:00
ctx . JSON ( 200 , ctx . Repo . Repository . APIFormat ( ctx . Repo . AccessMode ) )
2015-10-22 17:46:07 -04:00
}
2016-10-03 12:35:42 +02:00
// GetByID returns a single Repository
func GetByID ( ctx * context . APIContext ) {
2017-11-12 23: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 04:40:42 +01:00
// format: int64
2017-11-12 23:02:25 -08:00
// required: true
// responses:
// "200":
// "$ref": "#/responses/Repository"
2016-10-03 12:35:42 +02:00
repo , err := models . GetRepositoryByID ( ctx . ParamsInt64 ( ":id" ) )
if err != nil {
if models . IsErrRepoNotExist ( err ) {
ctx . Status ( 404 )
} else {
ctx . Error ( 500 , "GetRepositoryByID" , err )
}
return
}
2018-11-28 19:26:14 +08:00
perm , err := models . GetUserRepoPermission ( repo , ctx . User )
2016-12-05 18:48:51 -05:00
if err != nil {
2017-07-29 18:13:33 -07:00
ctx . Error ( 500 , "AccessLevel" , err )
return
2018-11-28 19:26:14 +08:00
} else if ! perm . HasAccess ( ) {
2017-07-29 18:13:33 -07:00
ctx . Status ( 404 )
2016-12-05 18:48:51 -05:00
return
}
2018-11-28 19:26:14 +08:00
ctx . JSON ( 200 , repo . APIFormat ( perm . AccessMode ) )
2016-10-03 12:35:42 +02:00
}
2016-10-07 19:17:27 +02:00
// Delete one repository
2016-03-13 18:49:16 -04:00
func Delete ( ctx * context . APIContext ) {
2017-11-12 23: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"
2016-11-14 17:33:58 -05:00
owner := ctx . Repo . Owner
repo := ctx . Repo . Repository
2015-10-04 17:09:16 +02:00
2017-12-20 23:43:26 -08:00
if owner . IsOrganization ( ) {
isOwner , err := owner . IsOwnedBy ( ctx . User . ID )
if err != nil {
ctx . Error ( 500 , "IsOwnedBy" , err )
return
} else if ! isOwner {
ctx . Error ( 403 , "" , "Given user is not owner of organization." )
return
}
2015-10-04 17:09:16 +02:00
}
2017-09-03 01:20:24 -07:00
if err := models . DeleteRepository ( ctx . User , owner . ID , repo . ID ) ; err != nil {
2016-03-13 18:49:16 -04:00
ctx . Error ( 500 , "DeleteRepository" , err )
2015-10-04 17:09:16 +02:00
return
}
2015-10-22 17:46:07 -04:00
log . Trace ( "Repository deleted: %s/%s" , owner . Name , repo . Name )
2015-10-04 17:09:16 +02:00
ctx . Status ( 204 )
}
2017-04-19 13:09:49 +02:00
// MirrorSync adds a mirrored repository to the sync queue
func MirrorSync ( ctx * context . APIContext ) {
2017-11-12 23:02:25 -08:00
// swagger:operation POST /repos/{owner}/{repo}/mirror-sync repository repoMirrorSync
// ---
// summary: Sync a mirrored repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo to sync
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo to sync
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/empty"
2017-04-19 13:09:49 +02:00
repo := ctx . Repo . Repository
2018-11-28 19:26:14 +08:00
if ! ctx . Repo . CanWrite ( models . UnitTypeCode ) {
2017-04-19 13:09:49 +02:00
ctx . Error ( 403 , "MirrorSync" , "Must have write access" )
}
go models . MirrorQueue . Add ( repo . ID )
ctx . Status ( 200 )
}
2018-04-11 10:51:44 +08:00
// TopicSearch search for creating topic
func TopicSearch ( ctx * context . Context ) {
// swagger:operation GET /topics/search repository topicSearch
// ---
// summary: search topics via keyword
// produces:
2018-06-12 16:59:22 +02:00
// - application/json
2018-04-11 10:51:44 +08:00
// parameters:
2018-06-12 16:59:22 +02:00
// - name: q
// in: query
// description: keywords to search
// required: true
// type: string
2018-04-11 10:51:44 +08:00
// responses:
// "200":
// "$ref": "#/responses/Repository"
if ctx . User == nil {
ctx . JSON ( 403 , map [ string ] interface { } {
"message" : "Only owners could change the topics." ,
} )
return
}
kw := ctx . Query ( "q" )
topics , err := models . FindTopics ( & models . FindTopicOptions {
Keyword : kw ,
Limit : 10 ,
} )
if err != nil {
log . Error ( 2 , "SearchTopics failed: %v" , err )
ctx . JSON ( 500 , map [ string ] interface { } {
"message" : "Search topics failed." ,
} )
return
}
ctx . JSON ( 200 , map [ string ] interface { } {
"topics" : topics ,
} )
}