repo.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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.GetAccessibleRepositories()
  97. if err != nil {
  98. ctx.APIError(500, "GetAccessibleRepositories", 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 current user is an organization.
  171. if form.Uid != ctxUser.Id {
  172. org, err := models.GetUserByID(form.Uid)
  173. if err != nil {
  174. if models.IsErrUserNotExist(err) {
  175. ctx.APIError(422, "", err)
  176. } else {
  177. ctx.APIError(500, "GetUserByID", err)
  178. }
  179. return
  180. }
  181. ctxUser = org
  182. }
  183. if ctx.HasError() {
  184. ctx.APIError(422, "", ctx.GetErrMsg())
  185. return
  186. }
  187. if ctxUser.IsOrganization() {
  188. // Check ownership of organization.
  189. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  190. ctx.APIError(403, "", "Given user is not owner of organization.")
  191. return
  192. }
  193. }
  194. remoteAddr, err := form.ParseRemoteAddr(ctx.User)
  195. if err != nil {
  196. if models.IsErrInvalidCloneAddr(err) {
  197. addrErr := err.(models.ErrInvalidCloneAddr)
  198. switch {
  199. case addrErr.IsURLError:
  200. ctx.APIError(422, "", err)
  201. case addrErr.IsPermissionDenied:
  202. ctx.APIError(422, "", "You are not allowed to import local repositories.")
  203. case addrErr.IsInvalidPath:
  204. ctx.APIError(422, "", "Invalid local path, it does not exist or not a directory.")
  205. default:
  206. ctx.APIError(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
  207. }
  208. } else {
  209. ctx.APIError(500, "ParseRemoteAddr", err)
  210. }
  211. return
  212. }
  213. repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{
  214. Name: form.RepoName,
  215. Description: form.Description,
  216. IsPrivate: form.Private || setting.Repository.ForcePrivate,
  217. IsMirror: form.Mirror,
  218. RemoteAddr: remoteAddr,
  219. })
  220. if err != nil {
  221. if repo != nil {
  222. if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID); errDelete != nil {
  223. log.Error(4, "DeleteRepository: %v", errDelete)
  224. }
  225. }
  226. ctx.APIError(500, "MigrateRepository", err)
  227. return
  228. }
  229. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  230. ctx.JSON(201, ToApiRepository(ctxUser, repo, api.Permission{true, true, true}))
  231. }
  232. func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
  233. owner, err := models.GetUserByName(ctx.Params(":username"))
  234. if err != nil {
  235. if models.IsErrUserNotExist(err) {
  236. ctx.APIError(422, "", err)
  237. } else {
  238. ctx.APIError(500, "GetUserByName", err)
  239. }
  240. return nil, nil
  241. }
  242. repo, err := models.GetRepositoryByName(owner.Id, ctx.Params(":reponame"))
  243. if err != nil {
  244. if models.IsErrRepoNotExist(err) {
  245. ctx.Error(404)
  246. } else {
  247. ctx.APIError(500, "GetRepositoryByName", err)
  248. }
  249. return nil, nil
  250. }
  251. return owner, repo
  252. }
  253. func GetRepo(ctx *middleware.Context) {
  254. owner, repo := parseOwnerAndRepo(ctx)
  255. if ctx.Written() {
  256. return
  257. }
  258. ctx.JSON(200, ToApiRepository(owner, repo, api.Permission{true, true, true}))
  259. }
  260. func DeleteRepo(ctx *middleware.Context) {
  261. owner, repo := parseOwnerAndRepo(ctx)
  262. if ctx.Written() {
  263. return
  264. }
  265. if owner.IsOrganization() && !owner.IsOwnedBy(ctx.User.Id) {
  266. ctx.APIError(403, "", "Given user is not owner of organization.")
  267. return
  268. }
  269. if err := models.DeleteRepository(owner.Id, repo.ID); err != nil {
  270. ctx.APIError(500, "DeleteRepository", err)
  271. return
  272. }
  273. log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
  274. ctx.Status(204)
  275. }