gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package backend
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "strconv"
  12. "time"
  13. "code.gitea.io/gitea/modules/json"
  14. lfslock "code.gitea.io/gitea/modules/structs"
  15. "github.com/charmbracelet/git-lfs-transfer/transfer"
  16. )
  17. var _ transfer.LockBackend = &giteaLockBackend{}
  18. type giteaLockBackend struct {
  19. ctx context.Context
  20. g *GiteaBackend
  21. server *url.URL
  22. authToken string
  23. internalAuth string
  24. logger transfer.Logger
  25. }
  26. func newGiteaLockBackend(g *GiteaBackend) transfer.LockBackend {
  27. server := g.server.JoinPath("locks")
  28. return &giteaLockBackend{ctx: g.ctx, g: g, server: server, authToken: g.authToken, internalAuth: g.internalAuth, logger: g.logger}
  29. }
  30. // Create implements transfer.LockBackend
  31. func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
  32. reqBody := lfslock.LFSLockRequest{Path: path}
  33. bodyBytes, err := json.Marshal(reqBody)
  34. if err != nil {
  35. g.logger.Log("json marshal error", err)
  36. return nil, err
  37. }
  38. headers := map[string]string{
  39. headerAuthorization: g.authToken,
  40. headerGiteaInternalAuth: g.internalAuth,
  41. headerAccept: mimeGitLFS,
  42. headerContentType: mimeGitLFS,
  43. }
  44. req := newInternalRequestLFS(g.ctx, g.server.String(), http.MethodPost, headers, bodyBytes)
  45. resp, err := req.Response()
  46. if err != nil {
  47. g.logger.Log("http request error", err)
  48. return nil, err
  49. }
  50. defer resp.Body.Close()
  51. respBytes, err := io.ReadAll(resp.Body)
  52. if err != nil {
  53. g.logger.Log("http read error", err)
  54. return nil, err
  55. }
  56. if resp.StatusCode != http.StatusCreated {
  57. g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
  58. return nil, statusCodeToErr(resp.StatusCode)
  59. }
  60. var respBody lfslock.LFSLockResponse
  61. err = json.Unmarshal(respBytes, &respBody)
  62. if err != nil {
  63. g.logger.Log("json umarshal error", err)
  64. return nil, err
  65. }
  66. if respBody.Lock == nil {
  67. g.logger.Log("api returned nil lock")
  68. return nil, errors.New("api returned nil lock")
  69. }
  70. respLock := respBody.Lock
  71. owner := userUnknown
  72. if respLock.Owner != nil {
  73. owner = respLock.Owner.Name
  74. }
  75. lock := newGiteaLock(g, respLock.ID, respLock.Path, respLock.LockedAt, owner)
  76. return lock, nil
  77. }
  78. // Unlock implements transfer.LockBackend
  79. func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
  80. reqBody := lfslock.LFSLockDeleteRequest{}
  81. bodyBytes, err := json.Marshal(reqBody)
  82. if err != nil {
  83. g.logger.Log("json marshal error", err)
  84. return err
  85. }
  86. headers := map[string]string{
  87. headerAuthorization: g.authToken,
  88. headerGiteaInternalAuth: g.internalAuth,
  89. headerAccept: mimeGitLFS,
  90. headerContentType: mimeGitLFS,
  91. }
  92. req := newInternalRequestLFS(g.ctx, g.server.JoinPath(lock.ID(), "unlock").String(), http.MethodPost, headers, bodyBytes)
  93. resp, err := req.Response()
  94. if err != nil {
  95. g.logger.Log("http request error", err)
  96. return err
  97. }
  98. defer resp.Body.Close()
  99. if resp.StatusCode != http.StatusOK {
  100. g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
  101. return statusCodeToErr(resp.StatusCode)
  102. }
  103. // no need to read response
  104. return nil
  105. }
  106. // FromPath implements transfer.LockBackend
  107. func (g *giteaLockBackend) FromPath(path string) (transfer.Lock, error) {
  108. v := url.Values{
  109. argPath: []string{path},
  110. }
  111. respLocks, _, err := g.queryLocks(v)
  112. if err != nil {
  113. return nil, err
  114. }
  115. if len(respLocks) == 0 {
  116. return nil, transfer.ErrNotFound
  117. }
  118. return respLocks[0], nil
  119. }
  120. // FromID implements transfer.LockBackend
  121. func (g *giteaLockBackend) FromID(id string) (transfer.Lock, error) {
  122. v := url.Values{
  123. argID: []string{id},
  124. }
  125. respLocks, _, err := g.queryLocks(v)
  126. if err != nil {
  127. return nil, err
  128. }
  129. if len(respLocks) == 0 {
  130. return nil, transfer.ErrNotFound
  131. }
  132. return respLocks[0], nil
  133. }
  134. // Range implements transfer.LockBackend
  135. func (g *giteaLockBackend) Range(cursor string, limit int, iter func(transfer.Lock) error) (string, error) {
  136. v := url.Values{
  137. argLimit: []string{strconv.FormatInt(int64(limit), 10)},
  138. }
  139. if cursor != "" {
  140. v[argCursor] = []string{cursor}
  141. }
  142. respLocks, cursor, err := g.queryLocks(v)
  143. if err != nil {
  144. return "", err
  145. }
  146. for _, lock := range respLocks {
  147. err := iter(lock)
  148. if err != nil {
  149. return "", err
  150. }
  151. }
  152. return cursor, nil
  153. }
  154. func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, error) {
  155. serverURLWithQuery := g.server.JoinPath() // get a copy
  156. serverURLWithQuery.RawQuery = v.Encode()
  157. headers := map[string]string{
  158. headerAuthorization: g.authToken,
  159. headerGiteaInternalAuth: g.internalAuth,
  160. headerAccept: mimeGitLFS,
  161. headerContentType: mimeGitLFS,
  162. }
  163. req := newInternalRequestLFS(g.ctx, serverURLWithQuery.String(), http.MethodGet, headers, nil)
  164. resp, err := req.Response()
  165. if err != nil {
  166. g.logger.Log("http request error", err)
  167. return nil, "", err
  168. }
  169. defer resp.Body.Close()
  170. respBytes, err := io.ReadAll(resp.Body)
  171. if err != nil {
  172. g.logger.Log("http read error", err)
  173. return nil, "", err
  174. }
  175. if resp.StatusCode != http.StatusOK {
  176. g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
  177. return nil, "", statusCodeToErr(resp.StatusCode)
  178. }
  179. var respBody lfslock.LFSLockList
  180. err = json.Unmarshal(respBytes, &respBody)
  181. if err != nil {
  182. g.logger.Log("json umarshal error", err)
  183. return nil, "", err
  184. }
  185. respLocks := make([]transfer.Lock, 0, len(respBody.Locks))
  186. for _, respLock := range respBody.Locks {
  187. owner := userUnknown
  188. if respLock.Owner != nil {
  189. owner = respLock.Owner.Name
  190. }
  191. lock := newGiteaLock(g, respLock.ID, respLock.Path, respLock.LockedAt, owner)
  192. respLocks = append(respLocks, lock)
  193. }
  194. return respLocks, respBody.Next, nil
  195. }
  196. var _ transfer.Lock = &giteaLock{}
  197. type giteaLock struct {
  198. g *giteaLockBackend
  199. id string
  200. path string
  201. lockedAt time.Time
  202. owner string
  203. }
  204. func newGiteaLock(g *giteaLockBackend, id, path string, lockedAt time.Time, owner string) transfer.Lock {
  205. return &giteaLock{g: g, id: id, path: path, lockedAt: lockedAt, owner: owner}
  206. }
  207. // Unlock implements transfer.Lock
  208. func (g *giteaLock) Unlock() error {
  209. return g.g.Unlock(g)
  210. }
  211. // ID implements transfer.Lock
  212. func (g *giteaLock) ID() string {
  213. return g.id
  214. }
  215. // Path implements transfer.Lock
  216. func (g *giteaLock) Path() string {
  217. return g.path
  218. }
  219. // FormattedTimestamp implements transfer.Lock
  220. func (g *giteaLock) FormattedTimestamp() string {
  221. return g.lockedAt.UTC().Format(time.RFC3339)
  222. }
  223. // OwnerName implements transfer.Lock
  224. func (g *giteaLock) OwnerName() string {
  225. return g.owner
  226. }
  227. func (g *giteaLock) CurrentUser() (string, error) {
  228. return userSelf, nil
  229. }
  230. // AsLockSpec implements transfer.Lock
  231. func (g *giteaLock) AsLockSpec(ownerID bool) ([]string, error) {
  232. msgs := []string{
  233. "lock " + g.ID(),
  234. fmt.Sprintf("path %s %s", g.ID(), g.Path()),
  235. fmt.Sprintf("locked-at %s %s", g.ID(), g.FormattedTimestamp()),
  236. fmt.Sprintf("ownername %s %s", g.ID(), g.OwnerName()),
  237. }
  238. if ownerID {
  239. user, err := g.CurrentUser()
  240. if err != nil {
  241. return nil, fmt.Errorf("error getting current user: %w", err)
  242. }
  243. who := "theirs"
  244. if user == g.OwnerName() {
  245. who = "ours"
  246. }
  247. msgs = append(msgs, fmt.Sprintf("owner %s %s", g.ID(), who))
  248. }
  249. return msgs, nil
  250. }
  251. // AsArguments implements transfer.Lock
  252. func (g *giteaLock) AsArguments() []string {
  253. return []string{
  254. "id=" + g.ID(),
  255. "path=" + g.Path(),
  256. "locked-at=" + g.FormattedTimestamp(),
  257. "ownername=" + g.OwnerName(),
  258. }
  259. }