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