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.

272 lines
6.9 KiB

10 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
10 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 v1
  5. import (
  6. "net/url"
  7. "path"
  8. "strings"
  9. "github.com/Unknwon/com"
  10. api "github.com/gogits/go-gogs-client"
  11. "github.com/gogits/gogs/models"
  12. "github.com/gogits/gogs/modules/auth"
  13. "github.com/gogits/gogs/modules/base"
  14. "github.com/gogits/gogs/modules/log"
  15. "github.com/gogits/gogs/modules/middleware"
  16. "github.com/gogits/gogs/modules/setting"
  17. )
  18. // ToApiRepository converts repository to API format.
  19. func ToApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository {
  20. cl, err := repo.CloneLink()
  21. if err != nil {
  22. log.Error(4, "CloneLink: %v", err)
  23. }
  24. return &api.Repository{
  25. Id: repo.Id,
  26. Owner: *ToApiUser(owner),
  27. FullName: owner.Name + "/" + repo.Name,
  28. Private: repo.IsPrivate,
  29. Fork: repo.IsFork,
  30. HtmlUrl: setting.AppUrl + owner.Name + "/" + repo.Name,
  31. CloneUrl: cl.HTTPS,
  32. SshUrl: cl.SSH,
  33. Permissions: permission,
  34. }
  35. }
  36. func SearchRepos(ctx *middleware.Context) {
  37. opt := models.SearchOption{
  38. Keyword: path.Base(ctx.Query("q")),
  39. Uid: com.StrTo(ctx.Query("uid")).MustInt64(),
  40. Limit: com.StrTo(ctx.Query("limit")).MustInt(),
  41. }
  42. if opt.Limit == 0 {
  43. opt.Limit = 10
  44. }
  45. // Check visibility.
  46. if ctx.IsSigned && opt.Uid > 0 {
  47. if ctx.User.Id == opt.Uid {
  48. opt.Private = true
  49. } else {
  50. u, err := models.GetUserById(opt.Uid)
  51. if err != nil {
  52. ctx.JSON(500, map[string]interface{}{
  53. "ok": false,
  54. "error": err.Error(),
  55. })
  56. return
  57. }
  58. if u.IsOrganization() && u.IsOwnedBy(ctx.User.Id) {
  59. opt.Private = true
  60. }
  61. // FIXME: how about collaborators?
  62. }
  63. }
  64. repos, err := models.SearchRepositoryByName(opt)
  65. if err != nil {
  66. ctx.JSON(500, map[string]interface{}{
  67. "ok": false,
  68. "error": err.Error(),
  69. })
  70. return
  71. }
  72. results := make([]*api.Repository, len(repos))
  73. for i := range repos {
  74. if err = repos[i].GetOwner(); err != nil {
  75. ctx.JSON(500, map[string]interface{}{
  76. "ok": false,
  77. "error": err.Error(),
  78. })
  79. return
  80. }
  81. results[i] = &api.Repository{
  82. Id: repos[i].Id,
  83. FullName: path.Join(repos[i].Owner.Name, repos[i].Name),
  84. }
  85. }
  86. ctx.JSON(200, map[string]interface{}{
  87. "ok": true,
  88. "data": results,
  89. })
  90. }
  91. func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) {
  92. repo, err := models.CreateRepository(owner, opt.Name, opt.Description,
  93. opt.Gitignore, opt.License, opt.Private, false, opt.AutoInit)
  94. if err != nil {
  95. if err == models.ErrRepoAlreadyExist ||
  96. models.IsErrNameReserved(err) ||
  97. models.IsErrNamePatternNotAllowed(err) {
  98. ctx.JSON(422, &base.ApiJsonErr{err.Error(), base.DOC_URL})
  99. } else {
  100. log.Error(4, "CreateRepository: %v", err)
  101. if repo != nil {
  102. if err = models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); err != nil {
  103. log.Error(4, "DeleteRepository: %v", err)
  104. }
  105. }
  106. ctx.Error(500)
  107. }
  108. return
  109. }
  110. ctx.JSON(200, ToApiRepository(owner, repo, api.Permission{true, true, true}))
  111. }
  112. // POST /user/repos
  113. // https://developer.github.com/v3/repos/#create
  114. func CreateRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
  115. // Shouldn't reach this condition, but just in case.
  116. if ctx.User.IsOrganization() {
  117. ctx.JSON(422, "not allowed creating repository for organization")
  118. return
  119. }
  120. createRepo(ctx, ctx.User, opt)
  121. }
  122. // POST /orgs/:org/repos
  123. // https://developer.github.com/v3/repos/#create
  124. func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
  125. org, err := models.GetOrgByName(ctx.Params(":org"))
  126. if err != nil {
  127. if err == models.ErrUserNotExist {
  128. ctx.Error(404)
  129. } else {
  130. ctx.Error(500)
  131. }
  132. return
  133. }
  134. if !org.IsOwnedBy(ctx.User.Id) {
  135. ctx.Error(403)
  136. return
  137. }
  138. createRepo(ctx, org, opt)
  139. }
  140. func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
  141. u, err := models.GetUserByName(ctx.Query("username"))
  142. if err != nil {
  143. if err == models.ErrUserNotExist {
  144. ctx.HandleAPI(422, err)
  145. } else {
  146. ctx.HandleAPI(500, err)
  147. }
  148. return
  149. }
  150. if !u.ValidatePassword(ctx.Query("password")) {
  151. ctx.HandleAPI(422, "Username or password is not correct.")
  152. return
  153. }
  154. ctxUser := u
  155. // Not equal means current user is an organization.
  156. if form.Uid != u.Id {
  157. org, err := models.GetUserById(form.Uid)
  158. if err != nil {
  159. if err == models.ErrUserNotExist {
  160. ctx.HandleAPI(422, err)
  161. } else {
  162. ctx.HandleAPI(500, err)
  163. }
  164. return
  165. }
  166. ctxUser = org
  167. }
  168. if ctx.HasError() {
  169. ctx.HandleAPI(422, ctx.GetErrMsg())
  170. return
  171. }
  172. if ctxUser.IsOrganization() {
  173. // Check ownership of organization.
  174. if !ctxUser.IsOwnedBy(u.Id) {
  175. ctx.HandleAPI(403, "Given user is not owner of organization.")
  176. return
  177. }
  178. }
  179. // Remote address can be HTTP/HTTPS/Git URL or local path.
  180. remoteAddr := form.CloneAddr
  181. if strings.HasPrefix(form.CloneAddr, "http://") ||
  182. strings.HasPrefix(form.CloneAddr, "https://") ||
  183. strings.HasPrefix(form.CloneAddr, "git://") {
  184. u, err := url.Parse(form.CloneAddr)
  185. if err != nil {
  186. ctx.HandleAPI(422, err)
  187. return
  188. }
  189. if len(form.AuthUsername) > 0 || len(form.AuthPassword) > 0 {
  190. u.User = url.UserPassword(form.AuthUsername, form.AuthPassword)
  191. }
  192. remoteAddr = u.String()
  193. } else if !com.IsDir(remoteAddr) {
  194. ctx.HandleAPI(422, "Invalid local path, it does not exist or not a directory.")
  195. return
  196. }
  197. repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
  198. if err != nil {
  199. if repo != nil {
  200. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  201. log.Error(4, "DeleteRepository: %v", errDelete)
  202. }
  203. }
  204. ctx.HandleAPI(500, err)
  205. return
  206. }
  207. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  208. ctx.WriteHeader(200)
  209. }
  210. // GET /user/repos
  211. // https://developer.github.com/v3/repos/#list-your-repositories
  212. func ListMyRepos(ctx *middleware.Context) {
  213. ownRepos, err := models.GetRepositories(ctx.User.Id, true)
  214. if err != nil {
  215. ctx.JSON(500, &base.ApiJsonErr{"GetRepositories: " + err.Error(), base.DOC_URL})
  216. return
  217. }
  218. numOwnRepos := len(ownRepos)
  219. accessibleRepos, err := ctx.User.GetAccessibleRepositories()
  220. if err != nil {
  221. ctx.JSON(500, &base.ApiJsonErr{"GetAccessibleRepositories: " + err.Error(), base.DOC_URL})
  222. return
  223. }
  224. repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
  225. for i := range ownRepos {
  226. repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
  227. }
  228. i := numOwnRepos
  229. for repo, access := range accessibleRepos {
  230. if err = repo.GetOwner(); err != nil {
  231. ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
  232. return
  233. }
  234. repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.ACCESS_MODE_WRITE, true})
  235. // FIXME: cache result to reduce DB query?
  236. if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) {
  237. repos[i].Permissions.Admin = true
  238. }
  239. i++
  240. }
  241. ctx.JSON(200, &repos)
  242. }