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

375 lines
9.0 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "fmt"
  7. "strings"
  8. api "code.gitea.io/sdk/gitea"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/auth"
  11. "code.gitea.io/gitea/modules/context"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/routers/api/v1/convert"
  15. )
  16. // Search repositories via options
  17. func Search(ctx *context.APIContext) {
  18. // swagger:route GET /repos/search repository repoSearch
  19. //
  20. // Produces:
  21. // - application/json
  22. //
  23. // Responses:
  24. // 200: SearchResults
  25. // 500: SearchError
  26. opts := &models.SearchRepoOptions{
  27. Keyword: strings.Trim(ctx.Query("q"), " "),
  28. OwnerID: ctx.QueryInt64("uid"),
  29. PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
  30. }
  31. if opts.OwnerID > 0 {
  32. var repoOwner *models.User
  33. if ctx.User != nil && ctx.User.ID == opts.OwnerID {
  34. repoOwner = ctx.User
  35. } else {
  36. var err error
  37. repoOwner, err = models.GetUserByID(opts.OwnerID)
  38. if err != nil {
  39. ctx.JSON(500, api.SearchError{
  40. OK: false,
  41. Error: err.Error(),
  42. })
  43. return
  44. }
  45. }
  46. if !repoOwner.IsOrganization() {
  47. opts.Collaborate = true
  48. }
  49. // Check visibility.
  50. if ctx.IsSigned && (ctx.User.ID == repoOwner.ID || (repoOwner.IsOrganization() && repoOwner.IsOwnedBy(ctx.User.ID))) {
  51. opts.Private = true
  52. }
  53. }
  54. repos, count, err := models.SearchRepositoryByName(opts)
  55. if err != nil {
  56. ctx.JSON(500, api.SearchError{
  57. OK: false,
  58. Error: err.Error(),
  59. })
  60. return
  61. }
  62. var userID int64
  63. if ctx.IsSigned {
  64. userID = ctx.User.ID
  65. }
  66. results := make([]*api.Repository, len(repos))
  67. for i, repo := range repos {
  68. if err = repo.GetOwner(); err != nil {
  69. ctx.JSON(500, api.SearchError{
  70. OK: false,
  71. Error: err.Error(),
  72. })
  73. return
  74. }
  75. accessMode, err := models.AccessLevel(userID, repo)
  76. if err != nil {
  77. ctx.JSON(500, api.SearchError{
  78. OK: false,
  79. Error: err.Error(),
  80. })
  81. }
  82. results[i] = repo.APIFormat(accessMode)
  83. }
  84. ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems)
  85. ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count))
  86. ctx.JSON(200, api.SearchResults{
  87. OK: true,
  88. Data: results,
  89. })
  90. }
  91. // CreateUserRepo create a repository for a user
  92. func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateRepoOption) {
  93. repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{
  94. Name: opt.Name,
  95. Description: opt.Description,
  96. Gitignores: opt.Gitignores,
  97. License: opt.License,
  98. Readme: opt.Readme,
  99. IsPrivate: opt.Private,
  100. AutoInit: opt.AutoInit,
  101. })
  102. if err != nil {
  103. if models.IsErrRepoAlreadyExist(err) ||
  104. models.IsErrNameReserved(err) ||
  105. models.IsErrNamePatternNotAllowed(err) {
  106. ctx.Error(422, "", err)
  107. } else {
  108. if repo != nil {
  109. if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil {
  110. log.Error(4, "DeleteRepository: %v", err)
  111. }
  112. }
  113. ctx.Error(500, "CreateRepository", err)
  114. }
  115. return
  116. }
  117. ctx.JSON(201, repo.APIFormat(models.AccessModeOwner))
  118. }
  119. // Create one repository of mine
  120. func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
  121. // swagger:route POST /user/repos repository user createCurrentUserRepo
  122. //
  123. // Consumes:
  124. // - application/json
  125. //
  126. // Produces:
  127. // - application/json
  128. //
  129. // Responses:
  130. // 201: Repository
  131. // 403: forbidden
  132. // 422: validationError
  133. // 500: error
  134. // Shouldn't reach this condition, but just in case.
  135. if ctx.User.IsOrganization() {
  136. ctx.Error(422, "", "not allowed creating repository for organization")
  137. return
  138. }
  139. CreateUserRepo(ctx, ctx.User, opt)
  140. }
  141. // CreateOrgRepo create one repository of the organization
  142. func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
  143. // swagger:route POST /org/{org}/repos organization createOrgRepo
  144. //
  145. // Consumes:
  146. // - application/json
  147. //
  148. // Produces:
  149. // - application/json
  150. //
  151. // Responses:
  152. // 201: Repository
  153. // 422: validationError
  154. // 403: forbidden
  155. // 500: error
  156. org, err := models.GetOrgByName(ctx.Params(":org"))
  157. if err != nil {
  158. if models.IsErrOrgNotExist(err) {
  159. ctx.Error(422, "", err)
  160. } else {
  161. ctx.Error(500, "GetOrgByName", err)
  162. }
  163. return
  164. }
  165. if !org.IsOwnedBy(ctx.User.ID) {
  166. ctx.Error(403, "", "Given user is not owner of organization.")
  167. return
  168. }
  169. CreateUserRepo(ctx, org, opt)
  170. }
  171. // Migrate migrate remote git repository to gitea
  172. func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
  173. // swagger:route POST /repos/migrate repository repoMigrate
  174. //
  175. // Consumes:
  176. // - application/json
  177. //
  178. // Produces:
  179. // - application/json
  180. //
  181. // Responses:
  182. // 201: Repository
  183. // 422: validationError
  184. // 500: error
  185. ctxUser := ctx.User
  186. // Not equal means context user is an organization,
  187. // or is another user/organization if current user is admin.
  188. if form.UID != ctxUser.ID {
  189. org, err := models.GetUserByID(form.UID)
  190. if err != nil {
  191. if models.IsErrUserNotExist(err) {
  192. ctx.Error(422, "", err)
  193. } else {
  194. ctx.Error(500, "GetUserByID", err)
  195. }
  196. return
  197. }
  198. ctxUser = org
  199. }
  200. if ctx.HasError() {
  201. ctx.Error(422, "", ctx.GetErrMsg())
  202. return
  203. }
  204. if ctxUser.IsOrganization() && !ctx.User.IsAdmin {
  205. // Check ownership of organization.
  206. if !ctxUser.IsOwnedBy(ctx.User.ID) {
  207. ctx.Error(403, "", "Given user is not owner of organization.")
  208. return
  209. }
  210. }
  211. remoteAddr, err := form.ParseRemoteAddr(ctx.User)
  212. if err != nil {
  213. if models.IsErrInvalidCloneAddr(err) {
  214. addrErr := err.(models.ErrInvalidCloneAddr)
  215. switch {
  216. case addrErr.IsURLError:
  217. ctx.Error(422, "", err)
  218. case addrErr.IsPermissionDenied:
  219. ctx.Error(422, "", "You are not allowed to import local repositories.")
  220. case addrErr.IsInvalidPath:
  221. ctx.Error(422, "", "Invalid local path, it does not exist or not a directory.")
  222. default:
  223. ctx.Error(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
  224. }
  225. } else {
  226. ctx.Error(500, "ParseRemoteAddr", err)
  227. }
  228. return
  229. }
  230. repo, err := models.MigrateRepository(ctx.User, ctxUser, models.MigrateRepoOptions{
  231. Name: form.RepoName,
  232. Description: form.Description,
  233. IsPrivate: form.Private || setting.Repository.ForcePrivate,
  234. IsMirror: form.Mirror,
  235. RemoteAddr: remoteAddr,
  236. })
  237. if err != nil {
  238. if repo != nil {
  239. if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
  240. log.Error(4, "DeleteRepository: %v", errDelete)
  241. }
  242. }
  243. ctx.Error(500, "MigrateRepository", models.HandleCloneUserCredentials(err.Error(), true))
  244. return
  245. }
  246. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  247. ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
  248. }
  249. // Get one repository
  250. func Get(ctx *context.APIContext) {
  251. // swagger:route GET /repos/{username}/{reponame} repository repoGet
  252. //
  253. // Produces:
  254. // - application/json
  255. //
  256. // Responses:
  257. // 200: Repository
  258. // 500: error
  259. ctx.JSON(200, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
  260. }
  261. // GetByID returns a single Repository
  262. func GetByID(ctx *context.APIContext) {
  263. // swagger:route GET /repositories/{id} repository repoGetByID
  264. //
  265. // Produces:
  266. // - application/json
  267. //
  268. // Responses:
  269. // 200: Repository
  270. // 500: error
  271. repo, err := models.GetRepositoryByID(ctx.ParamsInt64(":id"))
  272. if err != nil {
  273. if models.IsErrRepoNotExist(err) {
  274. ctx.Status(404)
  275. } else {
  276. ctx.Error(500, "GetRepositoryByID", err)
  277. }
  278. return
  279. }
  280. access, err := models.AccessLevel(ctx.User.ID, repo)
  281. if err != nil {
  282. ctx.Error(500, "AccessLevel", err)
  283. return
  284. } else if access < models.AccessModeRead {
  285. ctx.Status(404)
  286. return
  287. }
  288. ctx.JSON(200, repo.APIFormat(access))
  289. }
  290. // Delete one repository
  291. func Delete(ctx *context.APIContext) {
  292. // swagger:route DELETE /repos/{username}/{reponame} repository repoDelete
  293. //
  294. // Produces:
  295. // - application/json
  296. //
  297. // Responses:
  298. // 204: empty
  299. // 403: forbidden
  300. // 500: error
  301. if !ctx.Repo.IsAdmin() {
  302. ctx.Error(403, "", "Must have admin rights")
  303. return
  304. }
  305. owner := ctx.Repo.Owner
  306. repo := ctx.Repo.Repository
  307. if owner.IsOrganization() && !owner.IsOwnedBy(ctx.User.ID) {
  308. ctx.Error(403, "", "Given user is not owner of organization.")
  309. return
  310. }
  311. if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil {
  312. ctx.Error(500, "DeleteRepository", err)
  313. return
  314. }
  315. log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
  316. ctx.Status(204)
  317. }
  318. // MirrorSync adds a mirrored repository to the sync queue
  319. func MirrorSync(ctx *context.APIContext) {
  320. // swagger:route POST /repos/{username}/{reponame}/mirror-sync repository repoMirrorSync
  321. //
  322. // Produces:
  323. // - application/json
  324. //
  325. // Responses:
  326. // 200: empty
  327. // 403: forbidden
  328. repo := ctx.Repo.Repository
  329. if !ctx.Repo.IsWriter() {
  330. ctx.Error(403, "MirrorSync", "Must have write access")
  331. }
  332. go models.MirrorQueue.Add(repo.ID)
  333. ctx.Status(200)
  334. }