You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo.go 17 kB

11 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
6 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
6 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "net/http"
  9. "strings"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/auth"
  12. "code.gitea.io/gitea/modules/context"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/migrations"
  15. "code.gitea.io/gitea/modules/setting"
  16. "code.gitea.io/gitea/modules/util"
  17. "code.gitea.io/gitea/routers/api/v1/convert"
  18. api "code.gitea.io/gitea/modules/structs"
  19. )
  20. var searchOrderByMap = map[string]map[string]models.SearchOrderBy{
  21. "asc": {
  22. "alpha": models.SearchOrderByAlphabetically,
  23. "created": models.SearchOrderByOldest,
  24. "updated": models.SearchOrderByLeastUpdated,
  25. "size": models.SearchOrderBySize,
  26. "id": models.SearchOrderByID,
  27. },
  28. "desc": {
  29. "alpha": models.SearchOrderByAlphabeticallyReverse,
  30. "created": models.SearchOrderByNewest,
  31. "updated": models.SearchOrderByRecentUpdated,
  32. "size": models.SearchOrderBySizeReverse,
  33. "id": models.SearchOrderByIDReverse,
  34. },
  35. }
  36. // Search repositories via options
  37. func Search(ctx *context.APIContext) {
  38. // swagger:operation GET /repos/search repository repoSearch
  39. // ---
  40. // summary: Search for repositories
  41. // produces:
  42. // - application/json
  43. // parameters:
  44. // - name: q
  45. // in: query
  46. // description: keyword
  47. // type: string
  48. // - name: uid
  49. // in: query
  50. // description: search only for repos that the user with the given id owns or contributes to
  51. // type: integer
  52. // format: int64
  53. // - name: starredBy
  54. // in: query
  55. // description: search only for repos that the user with the given id has starred
  56. // type: integer
  57. // format: int64
  58. // - name: private
  59. // in: query
  60. // description: include private repositories this user has access to (defaults to true)
  61. // type: boolean
  62. // - name: page
  63. // in: query
  64. // description: page number of results to return (1-based)
  65. // type: integer
  66. // - name: limit
  67. // in: query
  68. // description: page size of results, maximum page size is 50
  69. // type: integer
  70. // - name: mode
  71. // in: query
  72. // description: type of repository to search for. Supported values are
  73. // "fork", "source", "mirror" and "collaborative"
  74. // type: string
  75. // - name: exclusive
  76. // in: query
  77. // description: if `uid` is given, search only for repos that the user owns
  78. // type: boolean
  79. // - name: sort
  80. // in: query
  81. // description: sort repos by attribute. Supported values are
  82. // "alpha", "created", "updated", "size", and "id".
  83. // Default is "alpha"
  84. // type: string
  85. // - name: order
  86. // in: query
  87. // description: sort order, either "asc" (ascending) or "desc" (descending).
  88. // Default is "asc", ignored if "sort" is not specified.
  89. // type: string
  90. // responses:
  91. // "200":
  92. // "$ref": "#/responses/SearchResults"
  93. // "422":
  94. // "$ref": "#/responses/validationError"
  95. opts := &models.SearchRepoOptions{
  96. Keyword: strings.Trim(ctx.Query("q"), " "),
  97. OwnerID: ctx.QueryInt64("uid"),
  98. Page: ctx.QueryInt("page"),
  99. PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
  100. TopicOnly: ctx.QueryBool("topic"),
  101. Collaborate: util.OptionalBoolNone,
  102. Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
  103. UserIsAdmin: ctx.IsUserSiteAdmin(),
  104. UserID: ctx.Data["SignedUserID"].(int64),
  105. StarredByID: ctx.QueryInt64("starredBy"),
  106. }
  107. if ctx.QueryBool("exclusive") {
  108. opts.Collaborate = util.OptionalBoolFalse
  109. }
  110. var mode = ctx.Query("mode")
  111. switch mode {
  112. case "source":
  113. opts.Fork = util.OptionalBoolFalse
  114. opts.Mirror = util.OptionalBoolFalse
  115. case "fork":
  116. opts.Fork = util.OptionalBoolTrue
  117. case "mirror":
  118. opts.Mirror = util.OptionalBoolTrue
  119. case "collaborative":
  120. opts.Mirror = util.OptionalBoolFalse
  121. opts.Collaborate = util.OptionalBoolTrue
  122. case "":
  123. default:
  124. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode))
  125. return
  126. }
  127. var sortMode = ctx.Query("sort")
  128. if len(sortMode) > 0 {
  129. var sortOrder = ctx.Query("order")
  130. if len(sortOrder) == 0 {
  131. sortOrder = "asc"
  132. }
  133. if searchModeMap, ok := searchOrderByMap[sortOrder]; ok {
  134. if orderBy, ok := searchModeMap[sortMode]; ok {
  135. opts.OrderBy = orderBy
  136. } else {
  137. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid sort mode: \"%s\"", sortMode))
  138. return
  139. }
  140. } else {
  141. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid sort order: \"%s\"", sortOrder))
  142. return
  143. }
  144. }
  145. var err error
  146. repos, count, err := models.SearchRepositoryByName(opts)
  147. if err != nil {
  148. ctx.JSON(500, api.SearchError{
  149. OK: false,
  150. Error: err.Error(),
  151. })
  152. return
  153. }
  154. results := make([]*api.Repository, len(repos))
  155. for i, repo := range repos {
  156. if err = repo.GetOwner(); err != nil {
  157. ctx.JSON(500, api.SearchError{
  158. OK: false,
  159. Error: err.Error(),
  160. })
  161. return
  162. }
  163. accessMode, err := models.AccessLevel(ctx.User, repo)
  164. if err != nil {
  165. ctx.JSON(500, api.SearchError{
  166. OK: false,
  167. Error: err.Error(),
  168. })
  169. }
  170. results[i] = repo.APIFormat(accessMode)
  171. }
  172. ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems)
  173. ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count))
  174. ctx.JSON(200, api.SearchResults{
  175. OK: true,
  176. Data: results,
  177. })
  178. }
  179. // CreateUserRepo create a repository for a user
  180. func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateRepoOption) {
  181. if opt.AutoInit && opt.Readme == "" {
  182. opt.Readme = "Default"
  183. }
  184. repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{
  185. Name: opt.Name,
  186. Description: opt.Description,
  187. Gitignores: opt.Gitignores,
  188. License: opt.License,
  189. Readme: opt.Readme,
  190. IsPrivate: opt.Private,
  191. AutoInit: opt.AutoInit,
  192. })
  193. if err != nil {
  194. if models.IsErrRepoAlreadyExist(err) {
  195. ctx.Error(409, "", "The repository with the same name already exists.")
  196. } else if models.IsErrNameReserved(err) ||
  197. models.IsErrNamePatternNotAllowed(err) {
  198. ctx.Error(422, "", err)
  199. } else {
  200. if repo != nil {
  201. if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil {
  202. log.Error("DeleteRepository: %v", err)
  203. }
  204. }
  205. ctx.Error(500, "CreateRepository", err)
  206. }
  207. return
  208. }
  209. ctx.JSON(201, repo.APIFormat(models.AccessModeOwner))
  210. }
  211. // Create one repository of mine
  212. func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
  213. // swagger:operation POST /user/repos repository user createCurrentUserRepo
  214. // ---
  215. // summary: Create a repository
  216. // consumes:
  217. // - application/json
  218. // produces:
  219. // - application/json
  220. // parameters:
  221. // - name: body
  222. // in: body
  223. // schema:
  224. // "$ref": "#/definitions/CreateRepoOption"
  225. // responses:
  226. // "201":
  227. // "$ref": "#/responses/Repository"
  228. if ctx.User.IsOrganization() {
  229. // Shouldn't reach this condition, but just in case.
  230. ctx.Error(422, "", "not allowed creating repository for organization")
  231. return
  232. }
  233. CreateUserRepo(ctx, ctx.User, opt)
  234. }
  235. // CreateOrgRepo create one repository of the organization
  236. func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
  237. // swagger:operation POST /org/{org}/repos organization createOrgRepo
  238. // ---
  239. // summary: Create a repository in an organization
  240. // consumes:
  241. // - application/json
  242. // produces:
  243. // - application/json
  244. // parameters:
  245. // - name: org
  246. // in: path
  247. // description: name of organization
  248. // type: string
  249. // required: true
  250. // - name: body
  251. // in: body
  252. // schema:
  253. // "$ref": "#/definitions/CreateRepoOption"
  254. // responses:
  255. // "201":
  256. // "$ref": "#/responses/Repository"
  257. // "422":
  258. // "$ref": "#/responses/validationError"
  259. // "403":
  260. // "$ref": "#/responses/forbidden"
  261. org, err := models.GetOrgByName(ctx.Params(":org"))
  262. if err != nil {
  263. if models.IsErrOrgNotExist(err) {
  264. ctx.Error(422, "", err)
  265. } else {
  266. ctx.Error(500, "GetOrgByName", err)
  267. }
  268. return
  269. }
  270. if !models.HasOrgVisible(org, ctx.User) {
  271. ctx.NotFound("HasOrgVisible", nil)
  272. return
  273. }
  274. if !ctx.User.IsAdmin {
  275. isOwner, err := org.IsOwnedBy(ctx.User.ID)
  276. if err != nil {
  277. ctx.ServerError("IsOwnedBy", err)
  278. return
  279. } else if !isOwner {
  280. ctx.Error(403, "", "Given user is not owner of organization.")
  281. return
  282. }
  283. }
  284. CreateUserRepo(ctx, org, opt)
  285. }
  286. // Migrate migrate remote git repository to gitea
  287. func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
  288. // swagger:operation POST /repos/migrate repository repoMigrate
  289. // ---
  290. // summary: Migrate a remote git repository
  291. // consumes:
  292. // - application/json
  293. // produces:
  294. // - application/json
  295. // parameters:
  296. // - name: body
  297. // in: body
  298. // schema:
  299. // "$ref": "#/definitions/MigrateRepoForm"
  300. // responses:
  301. // "201":
  302. // "$ref": "#/responses/Repository"
  303. ctxUser := ctx.User
  304. // Not equal means context user is an organization,
  305. // or is another user/organization if current user is admin.
  306. if form.UID != ctxUser.ID {
  307. org, err := models.GetUserByID(form.UID)
  308. if err != nil {
  309. if models.IsErrUserNotExist(err) {
  310. ctx.Error(422, "", err)
  311. } else {
  312. ctx.Error(500, "GetUserByID", err)
  313. }
  314. return
  315. }
  316. ctxUser = org
  317. }
  318. if ctx.HasError() {
  319. ctx.Error(422, "", ctx.GetErrMsg())
  320. return
  321. }
  322. if !ctx.User.IsAdmin {
  323. if !ctxUser.IsOrganization() && ctx.User.ID != ctxUser.ID {
  324. ctx.Error(403, "", "Given user is not an organization.")
  325. return
  326. }
  327. if ctxUser.IsOrganization() {
  328. // Check ownership of organization.
  329. isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID)
  330. if err != nil {
  331. ctx.Error(500, "IsOwnedBy", err)
  332. return
  333. } else if !isOwner {
  334. ctx.Error(403, "", "Given user is not owner of organization.")
  335. return
  336. }
  337. }
  338. }
  339. remoteAddr, err := form.ParseRemoteAddr(ctx.User)
  340. if err != nil {
  341. if models.IsErrInvalidCloneAddr(err) {
  342. addrErr := err.(models.ErrInvalidCloneAddr)
  343. switch {
  344. case addrErr.IsURLError:
  345. ctx.Error(422, "", err)
  346. case addrErr.IsPermissionDenied:
  347. ctx.Error(422, "", "You are not allowed to import local repositories.")
  348. case addrErr.IsInvalidPath:
  349. ctx.Error(422, "", "Invalid local path, it does not exist or not a directory.")
  350. default:
  351. ctx.Error(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
  352. }
  353. } else {
  354. ctx.Error(500, "ParseRemoteAddr", err)
  355. }
  356. return
  357. }
  358. var opts = migrations.MigrateOptions{
  359. RemoteURL: remoteAddr,
  360. Name: form.RepoName,
  361. Description: form.Description,
  362. Private: form.Private || setting.Repository.ForcePrivate,
  363. Mirror: form.Mirror,
  364. AuthUsername: form.AuthUsername,
  365. AuthPassword: form.AuthPassword,
  366. Wiki: form.Wiki,
  367. Issues: form.Issues,
  368. Milestones: form.Milestones,
  369. Labels: form.Labels,
  370. Comments: true,
  371. PullRequests: form.PullRequests,
  372. Releases: form.Releases,
  373. }
  374. if opts.Mirror {
  375. opts.Issues = false
  376. opts.Milestones = false
  377. opts.Labels = false
  378. opts.Comments = false
  379. opts.PullRequests = false
  380. opts.Releases = false
  381. }
  382. repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
  383. if err == nil {
  384. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  385. ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
  386. return
  387. }
  388. switch {
  389. case models.IsErrRepoAlreadyExist(err):
  390. ctx.Error(409, "", "The repository with the same name already exists.")
  391. case migrations.IsRateLimitError(err):
  392. ctx.Error(422, "", "Remote visit addressed rate limitation.")
  393. case migrations.IsTwoFactorAuthError(err):
  394. ctx.Error(422, "", "Remote visit required two factors authentication.")
  395. case models.IsErrReachLimitOfRepo(err):
  396. ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit()))
  397. case models.IsErrNameReserved(err):
  398. ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
  399. case models.IsErrNamePatternNotAllowed(err):
  400. ctx.Error(422, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern))
  401. default:
  402. err = util.URLSanitizedError(err, remoteAddr)
  403. if strings.Contains(err.Error(), "Authentication failed") ||
  404. strings.Contains(err.Error(), "Bad credentials") ||
  405. strings.Contains(err.Error(), "could not read Username") {
  406. ctx.Error(422, "", fmt.Sprintf("Authentication failed: %v.", err))
  407. } else if strings.Contains(err.Error(), "fatal:") {
  408. ctx.Error(422, "", fmt.Sprintf("Migration failed: %v.", err))
  409. } else {
  410. ctx.Error(500, "MigrateRepository", err)
  411. }
  412. }
  413. }
  414. // Get one repository
  415. func Get(ctx *context.APIContext) {
  416. // swagger:operation GET /repos/{owner}/{repo} repository repoGet
  417. // ---
  418. // summary: Get a repository
  419. // produces:
  420. // - application/json
  421. // parameters:
  422. // - name: owner
  423. // in: path
  424. // description: owner of the repo
  425. // type: string
  426. // required: true
  427. // - name: repo
  428. // in: path
  429. // description: name of the repo
  430. // type: string
  431. // required: true
  432. // responses:
  433. // "200":
  434. // "$ref": "#/responses/Repository"
  435. ctx.JSON(200, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
  436. }
  437. // GetByID returns a single Repository
  438. func GetByID(ctx *context.APIContext) {
  439. // swagger:operation GET /repositories/{id} repository repoGetByID
  440. // ---
  441. // summary: Get a repository by id
  442. // produces:
  443. // - application/json
  444. // parameters:
  445. // - name: id
  446. // in: path
  447. // description: id of the repo to get
  448. // type: integer
  449. // format: int64
  450. // required: true
  451. // responses:
  452. // "200":
  453. // "$ref": "#/responses/Repository"
  454. repo, err := models.GetRepositoryByID(ctx.ParamsInt64(":id"))
  455. if err != nil {
  456. if models.IsErrRepoNotExist(err) {
  457. ctx.NotFound()
  458. } else {
  459. ctx.Error(500, "GetRepositoryByID", err)
  460. }
  461. return
  462. }
  463. perm, err := models.GetUserRepoPermission(repo, ctx.User)
  464. if err != nil {
  465. ctx.Error(500, "AccessLevel", err)
  466. return
  467. } else if !perm.HasAccess() {
  468. ctx.NotFound()
  469. return
  470. }
  471. ctx.JSON(200, repo.APIFormat(perm.AccessMode))
  472. }
  473. // Delete one repository
  474. func Delete(ctx *context.APIContext) {
  475. // swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
  476. // ---
  477. // summary: Delete a repository
  478. // produces:
  479. // - application/json
  480. // parameters:
  481. // - name: owner
  482. // in: path
  483. // description: owner of the repo to delete
  484. // type: string
  485. // required: true
  486. // - name: repo
  487. // in: path
  488. // description: name of the repo to delete
  489. // type: string
  490. // required: true
  491. // responses:
  492. // "204":
  493. // "$ref": "#/responses/empty"
  494. // "403":
  495. // "$ref": "#/responses/forbidden"
  496. owner := ctx.Repo.Owner
  497. repo := ctx.Repo.Repository
  498. if owner.IsOrganization() && !ctx.User.IsAdmin {
  499. isOwner, err := owner.IsOwnedBy(ctx.User.ID)
  500. if err != nil {
  501. ctx.Error(500, "IsOwnedBy", err)
  502. return
  503. } else if !isOwner {
  504. ctx.Error(403, "", "Given user is not owner of organization.")
  505. return
  506. }
  507. }
  508. if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil {
  509. ctx.Error(500, "DeleteRepository", err)
  510. return
  511. }
  512. log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
  513. ctx.Status(204)
  514. }
  515. // MirrorSync adds a mirrored repository to the sync queue
  516. func MirrorSync(ctx *context.APIContext) {
  517. // swagger:operation POST /repos/{owner}/{repo}/mirror-sync repository repoMirrorSync
  518. // ---
  519. // summary: Sync a mirrored repository
  520. // produces:
  521. // - application/json
  522. // parameters:
  523. // - name: owner
  524. // in: path
  525. // description: owner of the repo to sync
  526. // type: string
  527. // required: true
  528. // - name: repo
  529. // in: path
  530. // description: name of the repo to sync
  531. // type: string
  532. // required: true
  533. // responses:
  534. // "200":
  535. // "$ref": "#/responses/empty"
  536. repo := ctx.Repo.Repository
  537. if !ctx.Repo.CanWrite(models.UnitTypeCode) {
  538. ctx.Error(403, "MirrorSync", "Must have write access")
  539. }
  540. go models.MirrorQueue.Add(repo.ID)
  541. ctx.Status(200)
  542. }
  543. // TopicSearch search for creating topic
  544. func TopicSearch(ctx *context.Context) {
  545. // swagger:operation GET /topics/search repository topicSearch
  546. // ---
  547. // summary: search topics via keyword
  548. // produces:
  549. // - application/json
  550. // parameters:
  551. // - name: q
  552. // in: query
  553. // description: keywords to search
  554. // required: true
  555. // type: string
  556. // responses:
  557. // "200":
  558. // "$ref": "#/responses/Repository"
  559. if ctx.User == nil {
  560. ctx.JSON(403, map[string]interface{}{
  561. "message": "Only owners could change the topics.",
  562. })
  563. return
  564. }
  565. kw := ctx.Query("q")
  566. topics, err := models.FindTopics(&models.FindTopicOptions{
  567. Keyword: kw,
  568. Limit: 10,
  569. })
  570. if err != nil {
  571. log.Error("SearchTopics failed: %v", err)
  572. ctx.JSON(500, map[string]interface{}{
  573. "message": "Search topics failed.",
  574. })
  575. return
  576. }
  577. ctx.JSON(200, map[string]interface{}{
  578. "topics": topics,
  579. })
  580. }