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.

goproxy.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package goproxy
  4. import (
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "sort"
  10. "time"
  11. packages_model "code.gitea.io/gitea/models/packages"
  12. "code.gitea.io/gitea/modules/optional"
  13. packages_module "code.gitea.io/gitea/modules/packages"
  14. goproxy_module "code.gitea.io/gitea/modules/packages/goproxy"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/routers/api/packages/helper"
  17. "code.gitea.io/gitea/services/context"
  18. packages_service "code.gitea.io/gitea/services/packages"
  19. )
  20. func apiError(ctx *context.Context, status int, obj any) {
  21. helper.LogAndProcessError(ctx, status, obj, func(message string) {
  22. ctx.PlainText(status, message)
  23. })
  24. }
  25. func EnumeratePackageVersions(ctx *context.Context) {
  26. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeGo, ctx.Params("name"))
  27. if err != nil {
  28. apiError(ctx, http.StatusInternalServerError, err)
  29. return
  30. }
  31. if len(pvs) == 0 {
  32. apiError(ctx, http.StatusNotFound, err)
  33. return
  34. }
  35. sort.Slice(pvs, func(i, j int) bool {
  36. return pvs[i].CreatedUnix < pvs[j].CreatedUnix
  37. })
  38. ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
  39. for _, pv := range pvs {
  40. fmt.Fprintln(ctx.Resp, pv.Version)
  41. }
  42. }
  43. func PackageVersionMetadata(ctx *context.Context) {
  44. pv, err := resolvePackage(ctx, ctx.Package.Owner.ID, ctx.Params("name"), ctx.Params("version"))
  45. if err != nil {
  46. if errors.Is(err, util.ErrNotExist) {
  47. apiError(ctx, http.StatusNotFound, err)
  48. } else {
  49. apiError(ctx, http.StatusInternalServerError, err)
  50. }
  51. return
  52. }
  53. ctx.JSON(http.StatusOK, struct {
  54. Version string `json:"Version"`
  55. Time time.Time `json:"Time"`
  56. }{
  57. Version: pv.Version,
  58. Time: pv.CreatedUnix.AsLocalTime(),
  59. })
  60. }
  61. func PackageVersionGoModContent(ctx *context.Context) {
  62. pv, err := resolvePackage(ctx, ctx.Package.Owner.ID, ctx.Params("name"), ctx.Params("version"))
  63. if err != nil {
  64. if errors.Is(err, util.ErrNotExist) {
  65. apiError(ctx, http.StatusNotFound, err)
  66. } else {
  67. apiError(ctx, http.StatusInternalServerError, err)
  68. }
  69. return
  70. }
  71. pps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, goproxy_module.PropertyGoMod)
  72. if err != nil || len(pps) != 1 {
  73. apiError(ctx, http.StatusInternalServerError, err)
  74. return
  75. }
  76. ctx.PlainText(http.StatusOK, pps[0].Value)
  77. }
  78. func DownloadPackageFile(ctx *context.Context) {
  79. pv, err := resolvePackage(ctx, ctx.Package.Owner.ID, ctx.Params("name"), ctx.Params("version"))
  80. if err != nil {
  81. if errors.Is(err, util.ErrNotExist) {
  82. apiError(ctx, http.StatusNotFound, err)
  83. } else {
  84. apiError(ctx, http.StatusInternalServerError, err)
  85. }
  86. return
  87. }
  88. pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
  89. if err != nil || len(pfs) != 1 {
  90. apiError(ctx, http.StatusInternalServerError, err)
  91. return
  92. }
  93. s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
  94. if err != nil {
  95. if errors.Is(err, util.ErrNotExist) {
  96. apiError(ctx, http.StatusNotFound, err)
  97. } else {
  98. apiError(ctx, http.StatusInternalServerError, err)
  99. }
  100. return
  101. }
  102. helper.ServePackageFile(ctx, s, u, pfs[0])
  103. }
  104. func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (*packages_model.PackageVersion, error) {
  105. var pv *packages_model.PackageVersion
  106. if version == "latest" {
  107. pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
  108. OwnerID: ownerID,
  109. Type: packages_model.TypeGo,
  110. Name: packages_model.SearchValue{
  111. Value: name,
  112. ExactMatch: true,
  113. },
  114. IsInternal: optional.Some(false),
  115. Sort: packages_model.SortCreatedDesc,
  116. })
  117. if err != nil {
  118. return nil, err
  119. }
  120. if len(pvs) != 1 {
  121. return nil, packages_model.ErrPackageNotExist
  122. }
  123. pv = pvs[0]
  124. } else {
  125. var err error
  126. pv, err = packages_model.GetVersionByNameAndVersion(ctx, ownerID, packages_model.TypeGo, name, version)
  127. if err != nil {
  128. return nil, err
  129. }
  130. }
  131. return pv, nil
  132. }
  133. func UploadPackage(ctx *context.Context) {
  134. upload, needToClose, err := ctx.UploadStream()
  135. if err != nil {
  136. apiError(ctx, http.StatusInternalServerError, err)
  137. return
  138. }
  139. if needToClose {
  140. defer upload.Close()
  141. }
  142. buf, err := packages_module.CreateHashedBufferFromReader(upload)
  143. if err != nil {
  144. apiError(ctx, http.StatusInternalServerError, err)
  145. return
  146. }
  147. defer buf.Close()
  148. pck, err := goproxy_module.ParsePackage(buf, buf.Size())
  149. if err != nil {
  150. if errors.Is(err, util.ErrInvalidArgument) {
  151. apiError(ctx, http.StatusBadRequest, err)
  152. } else {
  153. apiError(ctx, http.StatusInternalServerError, err)
  154. }
  155. return
  156. }
  157. if _, err := buf.Seek(0, io.SeekStart); err != nil {
  158. apiError(ctx, http.StatusInternalServerError, err)
  159. return
  160. }
  161. _, _, err = packages_service.CreatePackageAndAddFile(
  162. ctx,
  163. &packages_service.PackageCreationInfo{
  164. PackageInfo: packages_service.PackageInfo{
  165. Owner: ctx.Package.Owner,
  166. PackageType: packages_model.TypeGo,
  167. Name: pck.Name,
  168. Version: pck.Version,
  169. },
  170. Creator: ctx.Doer,
  171. VersionProperties: map[string]string{
  172. goproxy_module.PropertyGoMod: pck.GoMod,
  173. },
  174. },
  175. &packages_service.PackageFileCreationInfo{
  176. PackageFileInfo: packages_service.PackageFileInfo{
  177. Filename: fmt.Sprintf("%v.zip", pck.Version),
  178. },
  179. Creator: ctx.Doer,
  180. Data: buf,
  181. IsLead: true,
  182. },
  183. )
  184. if err != nil {
  185. switch err {
  186. case packages_model.ErrDuplicatePackageVersion:
  187. apiError(ctx, http.StatusConflict, err)
  188. case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
  189. apiError(ctx, http.StatusForbidden, err)
  190. default:
  191. apiError(ctx, http.StatusInternalServerError, err)
  192. }
  193. return
  194. }
  195. ctx.Status(http.StatusCreated)
  196. }