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 9.2 kB

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