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.

locks.go 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2017 The Gitea 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 lfs
  5. import (
  6. "encoding/json"
  7. "strconv"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/context"
  11. "code.gitea.io/gitea/modules/setting"
  12. api "code.gitea.io/sdk/gitea"
  13. "gopkg.in/macaron.v1"
  14. )
  15. func checkRequest(req macaron.Request, post bool) int {
  16. if !setting.LFS.StartServer {
  17. return 404
  18. }
  19. if !MetaMatcher(req) {
  20. return 400
  21. }
  22. if post {
  23. mediaParts := strings.Split(req.Header.Get("Content-Type"), ";")
  24. if mediaParts[0] != metaMediaType {
  25. return 400
  26. }
  27. }
  28. return 200
  29. }
  30. func handleLockListOut(ctx *context.Context, lock *models.LFSLock, err error) {
  31. if err != nil {
  32. if models.IsErrLFSLockNotExist(err) {
  33. ctx.JSON(200, api.LFSLockList{
  34. Locks: []*api.LFSLock{},
  35. })
  36. return
  37. }
  38. ctx.JSON(500, api.LFSLockError{
  39. Message: "unable to list locks : " + err.Error(),
  40. })
  41. return
  42. }
  43. if ctx.Repo.Repository.ID != lock.RepoID {
  44. ctx.JSON(200, api.LFSLockList{
  45. Locks: []*api.LFSLock{},
  46. })
  47. return
  48. }
  49. ctx.JSON(200, api.LFSLockList{
  50. Locks: []*api.LFSLock{lock.APIFormat()},
  51. })
  52. }
  53. // GetListLockHandler list locks
  54. func GetListLockHandler(ctx *context.Context) {
  55. status := checkRequest(ctx.Req, false)
  56. if status != 200 {
  57. writeStatus(ctx, status)
  58. return
  59. }
  60. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  61. err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository.ID, "list")
  62. if err != nil {
  63. if models.IsErrLFSLockUnauthorizedAction(err) {
  64. ctx.JSON(403, api.LFSLockError{
  65. Message: "You must have pull access to list locks : " + err.Error(),
  66. })
  67. return
  68. }
  69. ctx.JSON(500, api.LFSLockError{
  70. Message: "unable to list lock : " + err.Error(),
  71. })
  72. return
  73. }
  74. //TODO handle query cursor and limit
  75. id := ctx.Query("id")
  76. if id != "" { //Case where we request a specific id
  77. v, err := strconv.ParseInt(id, 10, 64)
  78. if err != nil {
  79. ctx.JSON(400, api.LFSLockError{
  80. Message: "bad request : " + err.Error(),
  81. })
  82. return
  83. }
  84. lock, err := models.GetLFSLockByID(int64(v))
  85. handleLockListOut(ctx, lock, err)
  86. return
  87. }
  88. path := ctx.Query("path")
  89. if path != "" { //Case where we request a specific id
  90. lock, err := models.GetLFSLock(ctx.Repo.Repository.ID, path)
  91. handleLockListOut(ctx, lock, err)
  92. return
  93. }
  94. //If no query params path or id
  95. lockList, err := models.GetLFSLockByRepoID(ctx.Repo.Repository.ID)
  96. if err != nil {
  97. ctx.JSON(500, api.LFSLockError{
  98. Message: "unable to list locks : " + err.Error(),
  99. })
  100. return
  101. }
  102. lockListAPI := make([]*api.LFSLock, len(lockList))
  103. for i, l := range lockList {
  104. lockListAPI[i] = l.APIFormat()
  105. }
  106. ctx.JSON(200, api.LFSLockList{
  107. Locks: lockListAPI,
  108. })
  109. }
  110. // PostLockHandler create lock
  111. func PostLockHandler(ctx *context.Context) {
  112. status := checkRequest(ctx.Req, true)
  113. if status != 200 {
  114. writeStatus(ctx, status)
  115. return
  116. }
  117. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  118. var req api.LFSLockRequest
  119. dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
  120. err := dec.Decode(&req)
  121. if err != nil {
  122. writeStatus(ctx, 400)
  123. return
  124. }
  125. lock, err := models.CreateLFSLock(&models.LFSLock{
  126. RepoID: ctx.Repo.Repository.ID,
  127. Path: req.Path,
  128. Owner: ctx.User,
  129. })
  130. if err != nil {
  131. if models.IsErrLFSLockAlreadyExist(err) {
  132. ctx.JSON(409, api.LFSLockError{
  133. Lock: lock.APIFormat(),
  134. Message: "already created lock",
  135. })
  136. return
  137. }
  138. if models.IsErrLFSLockUnauthorizedAction(err) {
  139. ctx.JSON(403, api.LFSLockError{
  140. Message: "You must have push access to create locks : " + err.Error(),
  141. })
  142. return
  143. }
  144. ctx.JSON(500, api.LFSLockError{
  145. Message: "internal server error : " + err.Error(),
  146. })
  147. return
  148. }
  149. ctx.JSON(201, api.LFSLockResponse{Lock: lock.APIFormat()})
  150. }
  151. // VerifyLockHandler list locks for verification
  152. func VerifyLockHandler(ctx *context.Context) {
  153. status := checkRequest(ctx.Req, true)
  154. if status != 200 {
  155. writeStatus(ctx, status)
  156. return
  157. }
  158. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  159. err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository.ID, "verify")
  160. if err != nil {
  161. if models.IsErrLFSLockUnauthorizedAction(err) {
  162. ctx.JSON(403, api.LFSLockError{
  163. Message: "You must have push access to verify locks : " + err.Error(),
  164. })
  165. return
  166. }
  167. ctx.JSON(500, api.LFSLockError{
  168. Message: "unable to verify lock : " + err.Error(),
  169. })
  170. return
  171. }
  172. //TODO handle body json cursor and limit
  173. lockList, err := models.GetLFSLockByRepoID(ctx.Repo.Repository.ID)
  174. if err != nil {
  175. ctx.JSON(500, api.LFSLockError{
  176. Message: "unable to list locks : " + err.Error(),
  177. })
  178. return
  179. }
  180. lockOursListAPI := make([]*api.LFSLock, 0, len(lockList))
  181. lockTheirsListAPI := make([]*api.LFSLock, 0, len(lockList))
  182. for _, l := range lockList {
  183. if l.Owner.ID == ctx.User.ID {
  184. lockOursListAPI = append(lockOursListAPI, l.APIFormat())
  185. } else {
  186. lockTheirsListAPI = append(lockTheirsListAPI, l.APIFormat())
  187. }
  188. }
  189. ctx.JSON(200, api.LFSLockListVerify{
  190. Ours: lockOursListAPI,
  191. Theirs: lockTheirsListAPI,
  192. })
  193. }
  194. // UnLockHandler delete locks
  195. func UnLockHandler(ctx *context.Context) {
  196. status := checkRequest(ctx.Req, true)
  197. if status != 200 {
  198. writeStatus(ctx, status)
  199. return
  200. }
  201. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  202. var req api.LFSLockDeleteRequest
  203. dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
  204. err := dec.Decode(&req)
  205. if err != nil {
  206. writeStatus(ctx, 400)
  207. return
  208. }
  209. lock, err := models.DeleteLFSLockByID(ctx.ParamsInt64("lid"), ctx.User, req.Force)
  210. if err != nil {
  211. if models.IsErrLFSLockUnauthorizedAction(err) {
  212. ctx.JSON(403, api.LFSLockError{
  213. Message: "You must have push access to delete locks : " + err.Error(),
  214. })
  215. return
  216. }
  217. ctx.JSON(500, api.LFSLockError{
  218. Message: "unable to delete lock : " + err.Error(),
  219. })
  220. return
  221. }
  222. ctx.JSON(200, api.LFSLockResponse{Lock: lock.APIFormat()})
  223. }