gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package vagrant
  4. import (
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "sort"
  11. "strings"
  12. packages_model "code.gitea.io/gitea/models/packages"
  13. packages_module "code.gitea.io/gitea/modules/packages"
  14. vagrant_module "code.gitea.io/gitea/modules/packages/vagrant"
  15. "code.gitea.io/gitea/modules/setting"
  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. "github.com/hashicorp/go-version"
  20. )
  21. func apiError(ctx *context.Context, status int, obj any) {
  22. message := helper.ProcessErrorForUser(ctx, status, obj)
  23. ctx.JSON(status, struct {
  24. Errors []string `json:"errors"`
  25. }{
  26. Errors: []string{
  27. message,
  28. },
  29. })
  30. }
  31. func CheckAuthenticate(ctx *context.Context) {
  32. if ctx.Doer == nil {
  33. apiError(ctx, http.StatusUnauthorized, "Invalid access token")
  34. return
  35. }
  36. ctx.Status(http.StatusOK)
  37. }
  38. func CheckBoxAvailable(ctx *context.Context) {
  39. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeVagrant, ctx.PathParam("name"))
  40. if err != nil {
  41. apiError(ctx, http.StatusInternalServerError, err)
  42. return
  43. }
  44. if len(pvs) == 0 {
  45. apiError(ctx, http.StatusNotFound, err)
  46. return
  47. }
  48. ctx.JSON(http.StatusOK, nil) // needs to be Content-Type: application/json
  49. }
  50. type packageMetadata struct {
  51. Name string `json:"name"`
  52. Description string `json:"description,omitempty"`
  53. ShortDescription string `json:"short_description,omitempty"`
  54. Versions []*versionMetadata `json:"versions"`
  55. }
  56. type versionMetadata struct {
  57. Version string `json:"version"`
  58. Status string `json:"status"`
  59. DescriptionHTML string `json:"description_html,omitempty"`
  60. DescriptionMarkdown string `json:"description_markdown,omitempty"`
  61. Providers []*providerData `json:"providers"`
  62. }
  63. type providerData struct {
  64. Name string `json:"name"`
  65. URL string `json:"url"`
  66. Checksum string `json:"checksum"`
  67. ChecksumType string `json:"checksum_type"`
  68. }
  69. func packageDescriptorToMetadata(baseURL string, pd *packages_model.PackageDescriptor) *versionMetadata {
  70. versionURL := baseURL + "/" + url.PathEscape(pd.Version.Version)
  71. providers := make([]*providerData, 0, len(pd.Files))
  72. for _, f := range pd.Files {
  73. providers = append(providers, &providerData{
  74. Name: f.Properties.GetByName(vagrant_module.PropertyProvider),
  75. URL: versionURL + "/" + url.PathEscape(f.File.Name),
  76. Checksum: f.Blob.HashSHA512,
  77. ChecksumType: "sha512",
  78. })
  79. }
  80. return &versionMetadata{
  81. Status: "active",
  82. Version: pd.Version.Version,
  83. Providers: providers,
  84. }
  85. }
  86. func EnumeratePackageVersions(ctx *context.Context) {
  87. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeVagrant, ctx.PathParam("name"))
  88. if err != nil {
  89. apiError(ctx, http.StatusInternalServerError, err)
  90. return
  91. }
  92. if len(pvs) == 0 {
  93. apiError(ctx, http.StatusNotFound, err)
  94. return
  95. }
  96. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  97. if err != nil {
  98. apiError(ctx, http.StatusInternalServerError, err)
  99. return
  100. }
  101. sort.Slice(pds, func(i, j int) bool {
  102. return pds[i].SemVer.LessThan(pds[j].SemVer)
  103. })
  104. baseURL := fmt.Sprintf("%sapi/packages/%s/vagrant/%s", setting.AppURL, url.PathEscape(ctx.Package.Owner.Name), url.PathEscape(pds[0].Package.Name))
  105. versions := make([]*versionMetadata, 0, len(pds))
  106. for _, pd := range pds {
  107. versions = append(versions, packageDescriptorToMetadata(baseURL, pd))
  108. }
  109. ctx.JSON(http.StatusOK, &packageMetadata{
  110. Name: pds[0].Package.Name,
  111. Description: pds[len(pds)-1].Metadata.(*vagrant_module.Metadata).Description,
  112. Versions: versions,
  113. })
  114. }
  115. func UploadPackageFile(ctx *context.Context) {
  116. boxName := ctx.PathParam("name")
  117. boxVersion := ctx.PathParam("version")
  118. _, err := version.NewSemver(boxVersion)
  119. if err != nil {
  120. apiError(ctx, http.StatusBadRequest, err)
  121. return
  122. }
  123. boxProvider := ctx.PathParam("provider")
  124. if !strings.HasSuffix(boxProvider, ".box") {
  125. apiError(ctx, http.StatusBadRequest, err)
  126. return
  127. }
  128. upload, needsClose, err := ctx.UploadStream()
  129. if err != nil {
  130. apiError(ctx, http.StatusInternalServerError, err)
  131. return
  132. }
  133. if needsClose {
  134. defer upload.Close()
  135. }
  136. buf, err := packages_module.CreateHashedBufferFromReader(upload)
  137. if err != nil {
  138. apiError(ctx, http.StatusInternalServerError, err)
  139. return
  140. }
  141. defer buf.Close()
  142. metadata, err := vagrant_module.ParseMetadataFromBox(buf)
  143. if err != nil {
  144. apiError(ctx, http.StatusInternalServerError, err)
  145. return
  146. }
  147. if _, err := buf.Seek(0, io.SeekStart); err != nil {
  148. apiError(ctx, http.StatusInternalServerError, err)
  149. return
  150. }
  151. _, _, err = packages_service.CreatePackageOrAddFileToExisting(
  152. ctx,
  153. &packages_service.PackageCreationInfo{
  154. PackageInfo: packages_service.PackageInfo{
  155. Owner: ctx.Package.Owner,
  156. PackageType: packages_model.TypeVagrant,
  157. Name: boxName,
  158. Version: boxVersion,
  159. },
  160. SemverCompatible: true,
  161. Creator: ctx.Doer,
  162. Metadata: metadata,
  163. },
  164. &packages_service.PackageFileCreationInfo{
  165. PackageFileInfo: packages_service.PackageFileInfo{
  166. Filename: strings.ToLower(boxProvider),
  167. },
  168. Creator: ctx.Doer,
  169. Data: buf,
  170. IsLead: true,
  171. Properties: map[string]string{
  172. vagrant_module.PropertyProvider: strings.TrimSuffix(boxProvider, ".box"),
  173. },
  174. },
  175. )
  176. if err != nil {
  177. switch err {
  178. case packages_model.ErrDuplicatePackageFile:
  179. apiError(ctx, http.StatusConflict, err)
  180. case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
  181. apiError(ctx, http.StatusForbidden, err)
  182. default:
  183. apiError(ctx, http.StatusInternalServerError, err)
  184. }
  185. return
  186. }
  187. ctx.Status(http.StatusCreated)
  188. }
  189. func DownloadPackageFile(ctx *context.Context) {
  190. s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
  191. ctx,
  192. &packages_service.PackageInfo{
  193. Owner: ctx.Package.Owner,
  194. PackageType: packages_model.TypeVagrant,
  195. Name: ctx.PathParam("name"),
  196. Version: ctx.PathParam("version"),
  197. },
  198. &packages_service.PackageFileInfo{
  199. Filename: ctx.PathParam("provider"),
  200. },
  201. ctx.Req.Method,
  202. )
  203. if err != nil {
  204. if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
  205. apiError(ctx, http.StatusNotFound, err)
  206. return
  207. }
  208. apiError(ctx, http.StatusInternalServerError, err)
  209. return
  210. }
  211. helper.ServePackageFile(ctx, s, u, pf)
  212. }