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

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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. err == models.ErrRepoNameIllegal {
  97. ctx.JSON(422, &base.ApiJsonErr{err.Error(), base.DOC_URL})
  98. } else {
  99. log.Error(4, "CreateRepository: %v", err)
  100. if repo != nil {
  101. if err = models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); err != nil {
  102. log.Error(4, "DeleteRepository: %v", err)
  103. }
  104. }
  105. ctx.Error(500)
  106. }
  107. return
  108. }
  109. ctx.JSON(200, ToApiRepository(owner, repo, api.Permission{true, true, true}))
  110. }
  111. // POST /user/repos
  112. // https://developer.github.com/v3/repos/#create
  113. func CreateRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
  114. // Shouldn't reach this condition, but just in case.
  115. if ctx.User.IsOrganization() {
  116. ctx.JSON(422, "not allowed creating repository for organization")
  117. return
  118. }
  119. createRepo(ctx, ctx.User, opt)
  120. }
  121. // POST /orgs/:org/repos
  122. // https://developer.github.com/v3/repos/#create
  123. func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
  124. org, err := models.GetOrgByName(ctx.Params(":org"))
  125. if err != nil {
  126. if err == models.ErrUserNotExist {
  127. ctx.Error(404)
  128. } else {
  129. ctx.Error(500)
  130. }
  131. return
  132. }
  133. if !org.IsOwnedBy(ctx.User.Id) {
  134. ctx.Error(403)
  135. return
  136. }
  137. createRepo(ctx, org, opt)
  138. }
  139. func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
  140. u, err := models.GetUserByName(ctx.Query("username"))
  141. if err != nil {
  142. if err == models.ErrUserNotExist {
  143. ctx.HandleAPI(422, err)
  144. } else {
  145. ctx.HandleAPI(500, err)
  146. }
  147. return
  148. }
  149. if !u.ValidatePassword(ctx.Query("password")) {
  150. ctx.HandleAPI(422, "Username or password is not correct.")
  151. return
  152. }
  153. ctxUser := u
  154. // Not equal means current user is an organization.
  155. if form.Uid != u.Id {
  156. org, err := models.GetUserById(form.Uid)
  157. if err != nil {
  158. if err == models.ErrUserNotExist {
  159. ctx.HandleAPI(422, err)
  160. } else {
  161. ctx.HandleAPI(500, err)
  162. }
  163. return
  164. }
  165. ctxUser = org
  166. }
  167. if ctx.HasError() {
  168. ctx.HandleAPI(422, ctx.GetErrMsg())
  169. return
  170. }
  171. if ctxUser.IsOrganization() {
  172. // Check ownership of organization.
  173. if !ctxUser.IsOwnedBy(u.Id) {
  174. ctx.HandleAPI(403, "Given user is not owner of organization.")
  175. return
  176. }
  177. }
  178. // Remote address can be HTTP/HTTPS/Git URL or local path.
  179. remoteAddr := form.CloneAddr
  180. if strings.HasPrefix(form.CloneAddr, "http://") ||
  181. strings.HasPrefix(form.CloneAddr, "https://") ||
  182. strings.HasPrefix(form.CloneAddr, "git://") {
  183. u, err := url.Parse(form.CloneAddr)
  184. if err != nil {
  185. ctx.HandleAPI(422, err)
  186. return
  187. }
  188. if len(form.AuthUsername) > 0 || len(form.AuthPassword) > 0 {
  189. u.User = url.UserPassword(form.AuthUsername, form.AuthPassword)
  190. }
  191. remoteAddr = u.String()
  192. } else if !com.IsDir(remoteAddr) {
  193. ctx.HandleAPI(422, "Invalid local path, it does not exist or not a directory.")
  194. return
  195. }
  196. repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
  197. if err != nil {
  198. if repo != nil {
  199. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  200. log.Error(4, "DeleteRepository: %v", errDelete)
  201. }
  202. }
  203. ctx.HandleAPI(500, err)
  204. return
  205. }
  206. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  207. ctx.WriteHeader(200)
  208. }
  209. // GET /user/repos
  210. // https://developer.github.com/v3/repos/#list-your-repositories
  211. func ListMyRepos(ctx *middleware.Context) {
  212. ownRepos, err := models.GetRepositories(ctx.User.Id, true)
  213. if err != nil {
  214. ctx.JSON(500, &base.ApiJsonErr{"GetRepositories: " + err.Error(), base.DOC_URL})
  215. return
  216. }
  217. numOwnRepos := len(ownRepos)
  218. accessibleRepos, err := ctx.User.GetAccessibleRepositories()
  219. if err != nil {
  220. ctx.JSON(500, &base.ApiJsonErr{"GetAccessibleRepositories: " + err.Error(), base.DOC_URL})
  221. return
  222. }
  223. repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
  224. for i := range ownRepos {
  225. repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
  226. }
  227. i := numOwnRepos
  228. for repo, access := range accessibleRepos {
  229. if err = repo.GetOwner(); err != nil {
  230. ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
  231. return
  232. }
  233. repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.ACCESS_MODE_WRITE, true})
  234. // FIXME: cache result to reduce DB query?
  235. if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) {
  236. repos[i].Permissions.Admin = true
  237. }
  238. i++
  239. }
  240. ctx.JSON(200, &repos)
  241. }