repo.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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. "path"
  7. "github.com/Unknwon/com"
  8. api "github.com/gogits/go-gogs-client"
  9. "github.com/gogits/gogs/models"
  10. "github.com/gogits/gogs/modules/auth"
  11. "github.com/gogits/gogs/modules/log"
  12. "github.com/gogits/gogs/modules/middleware"
  13. "github.com/gogits/gogs/modules/setting"
  14. )
  15. // ToApiRepository converts repository to API format.
  16. func ToApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository {
  17. cl, err := repo.CloneLink()
  18. if err != nil {
  19. log.Error(4, "CloneLink: %v", err)
  20. }
  21. return &api.Repository{
  22. Id: repo.ID,
  23. Owner: *ToApiUser(owner),
  24. FullName: owner.Name + "/" + repo.Name,
  25. Private: repo.IsPrivate,
  26. Fork: repo.IsFork,
  27. HtmlUrl: setting.AppUrl + owner.Name + "/" + repo.Name,
  28. CloneUrl: cl.HTTPS,
  29. SshUrl: cl.SSH,
  30. Permissions: permission,
  31. }
  32. }
  33. func SearchRepos(ctx *middleware.Context) {
  34. opt := models.SearchOption{
  35. Keyword: path.Base(ctx.Query("q")),
  36. Uid: com.StrTo(ctx.Query("uid")).MustInt64(),
  37. Limit: com.StrTo(ctx.Query("limit")).MustInt(),
  38. }
  39. if opt.Limit == 0 {
  40. opt.Limit = 10
  41. }
  42. // Check visibility.
  43. if ctx.IsSigned && opt.Uid > 0 {
  44. if ctx.User.Id == opt.Uid {
  45. opt.Private = true
  46. } else {
  47. u, err := models.GetUserByID(opt.Uid)
  48. if err != nil {
  49. ctx.JSON(500, map[string]interface{}{
  50. "ok": false,
  51. "error": err.Error(),
  52. })
  53. return
  54. }
  55. if u.IsOrganization() && u.IsOwnedBy(ctx.User.Id) {
  56. opt.Private = true
  57. }
  58. // FIXME: how about collaborators?
  59. }
  60. }
  61. repos, err := models.SearchRepositoryByName(opt)
  62. if err != nil {
  63. ctx.JSON(500, map[string]interface{}{
  64. "ok": false,
  65. "error": err.Error(),
  66. })
  67. return
  68. }
  69. results := make([]*api.Repository, len(repos))
  70. for i := range repos {
  71. if err = repos[i].GetOwner(); err != nil {
  72. ctx.JSON(500, map[string]interface{}{
  73. "ok": false,
  74. "error": err.Error(),
  75. })
  76. return
  77. }
  78. results[i] = &api.Repository{
  79. Id: repos[i].ID,
  80. FullName: path.Join(repos[i].Owner.Name, repos[i].Name),
  81. }
  82. }
  83. ctx.JSON(200, map[string]interface{}{
  84. "ok": true,
  85. "data": results,
  86. })
  87. }
  88. // https://github.com/gogits/go-gogs-client/wiki/Repositories#list-your-repositories
  89. func ListMyRepos(ctx *middleware.Context) {
  90. ownRepos, err := models.GetRepositories(ctx.User.Id, true)
  91. if err != nil {
  92. ctx.APIError(500, "GetRepositories", err)
  93. return
  94. }
  95. numOwnRepos := len(ownRepos)
  96. accessibleRepos, err := ctx.User.GetRepositoryAccesses()
  97. if err != nil {
  98. ctx.APIError(500, "GetRepositoryAccesses", err)
  99. return
  100. }
  101. repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
  102. for i := range ownRepos {
  103. repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
  104. }
  105. i := numOwnRepos
  106. for repo, access := range accessibleRepos {
  107. repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{
  108. Admin: access >= models.ACCESS_MODE_ADMIN,
  109. Push: access >= models.ACCESS_MODE_WRITE,
  110. Pull: true,
  111. })
  112. i++
  113. }
  114. ctx.JSON(200, &repos)
  115. }
  116. func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) {
  117. repo, err := models.CreateRepository(owner, models.CreateRepoOptions{
  118. Name: opt.Name,
  119. Description: opt.Description,
  120. Gitignores: opt.Gitignores,
  121. License: opt.License,
  122. Readme: opt.Readme,
  123. IsPrivate: opt.Private,
  124. AutoInit: opt.AutoInit,
  125. })
  126. if err != nil {
  127. if models.IsErrRepoAlreadyExist(err) ||
  128. models.IsErrNameReserved(err) ||
  129. models.IsErrNamePatternNotAllowed(err) {
  130. ctx.APIError(422, "", err)
  131. } else {
  132. if repo != nil {
  133. if err = models.DeleteRepository(ctx.User.Id, repo.ID); err != nil {
  134. log.Error(4, "DeleteRepository: %v", err)
  135. }
  136. }
  137. ctx.APIError(500, "CreateRepository", err)
  138. }
  139. return
  140. }
  141. ctx.JSON(201, ToApiRepository(owner, repo, api.Permission{true, true, true}))
  142. }
  143. // https://github.com/gogits/go-gogs-client/wiki/Repositories#create
  144. func CreateRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
  145. // Shouldn't reach this condition, but just in case.
  146. if ctx.User.IsOrganization() {
  147. ctx.APIError(422, "", "not allowed creating repository for organization")
  148. return
  149. }
  150. createRepo(ctx, ctx.User, opt)
  151. }
  152. func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
  153. org, err := models.GetOrgByName(ctx.Params(":org"))
  154. if err != nil {
  155. if models.IsErrUserNotExist(err) {
  156. ctx.APIError(422, "", err)
  157. } else {
  158. ctx.APIError(500, "GetOrgByName", err)
  159. }
  160. return
  161. }
  162. if !org.IsOwnedBy(ctx.User.Id) {
  163. ctx.APIError(403, "", "Given user is not owner of organization.")
  164. return
  165. }
  166. createRepo(ctx, org, opt)
  167. }
  168. func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
  169. ctxUser := ctx.User
  170. // Not equal means context user is an organization,
  171. // or is another user/organization if current user is admin.
  172. if form.Uid != ctxUser.Id {
  173. org, err := models.GetUserByID(form.Uid)
  174. if err != nil {
  175. if models.IsErrUserNotExist(err) {
  176. ctx.APIError(422, "", err)
  177. } else {
  178. ctx.APIError(500, "GetUserByID", err)
  179. }
  180. return
  181. }
  182. ctxUser = org
  183. }
  184. if ctx.HasError() {
  185. ctx.APIError(422, "", ctx.GetErrMsg())
  186. return
  187. }
  188. if ctxUser.IsOrganization() && !ctx.User.IsAdmin {
  189. // Check ownership of organization.
  190. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  191. ctx.APIError(403, "", "Given user is not owner of organization.")
  192. return
  193. }
  194. }
  195. remoteAddr, err := form.ParseRemoteAddr(ctx.User)
  196. if err != nil {
  197. if models.IsErrInvalidCloneAddr(err) {
  198. addrErr := err.(models.ErrInvalidCloneAddr)
  199. switch {
  200. case addrErr.IsURLError:
  201. ctx.APIError(422, "", err)
  202. case addrErr.IsPermissionDenied:
  203. ctx.APIError(422, "", "You are not allowed to import local repositories.")
  204. case addrErr.IsInvalidPath:
  205. ctx.APIError(422, "", "Invalid local path, it does not exist or not a directory.")
  206. default:
  207. ctx.APIError(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
  208. }
  209. } else {
  210. ctx.APIError(500, "ParseRemoteAddr", err)
  211. }
  212. return
  213. }
  214. repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{
  215. Name: form.RepoName,
  216. Description: form.Description,
  217. IsPrivate: form.Private || setting.Repository.ForcePrivate,
  218. IsMirror: form.Mirror,
  219. RemoteAddr: remoteAddr,
  220. })
  221. if err != nil {
  222. if repo != nil {
  223. if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID); errDelete != nil {
  224. log.Error(4, "DeleteRepository: %v", errDelete)
  225. }
  226. }
  227. ctx.APIError(500, "MigrateRepository", err)
  228. return
  229. }
  230. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  231. ctx.JSON(201, ToApiRepository(ctxUser, repo, api.Permission{true, true, true}))
  232. }
  233. func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
  234. owner, err := models.GetUserByName(ctx.Params(":username"))
  235. if err != nil {
  236. if models.IsErrUserNotExist(err) {
  237. ctx.APIError(422, "", err)
  238. } else {
  239. ctx.APIError(500, "GetUserByName", err)
  240. }
  241. return nil, nil
  242. }
  243. repo, err := models.GetRepositoryByName(owner.Id, ctx.Params(":reponame"))
  244. if err != nil {
  245. if models.IsErrRepoNotExist(err) {
  246. ctx.Error(404)
  247. } else {
  248. ctx.APIError(500, "GetRepositoryByName", err)
  249. }
  250. return nil, nil
  251. }
  252. return owner, repo
  253. }
  254. func GetRepo(ctx *middleware.Context) {
  255. owner, repo := parseOwnerAndRepo(ctx)
  256. if ctx.Written() {
  257. return
  258. }
  259. ctx.JSON(200, ToApiRepository(owner, repo, api.Permission{true, true, true}))
  260. }
  261. func DeleteRepo(ctx *middleware.Context) {
  262. owner, repo := parseOwnerAndRepo(ctx)
  263. if ctx.Written() {
  264. return
  265. }
  266. if owner.IsOrganization() && !owner.IsOwnedBy(ctx.User.Id) {
  267. ctx.APIError(403, "", "Given user is not owner of organization.")
  268. return
  269. }
  270. if err := models.DeleteRepository(owner.Id, repo.ID); err != nil {
  271. ctx.APIError(500, "DeleteRepository", err)
  272. return
  273. }
  274. log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
  275. ctx.Status(204)
  276. }