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

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