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.

generic.go 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package generic
  4. import (
  5. "errors"
  6. "net/http"
  7. "regexp"
  8. "strings"
  9. packages_model "code.gitea.io/gitea/models/packages"
  10. "code.gitea.io/gitea/modules/context"
  11. "code.gitea.io/gitea/modules/log"
  12. packages_module "code.gitea.io/gitea/modules/packages"
  13. "code.gitea.io/gitea/routers/api/packages/helper"
  14. packages_service "code.gitea.io/gitea/services/packages"
  15. )
  16. var (
  17. packageNameRegex = regexp.MustCompile(`\A[A-Za-z0-9\.\_\-\+]+\z`)
  18. filenameRegex = packageNameRegex
  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. // DownloadPackageFile serves the specific generic package.
  26. func DownloadPackageFile(ctx *context.Context) {
  27. s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
  28. ctx,
  29. &packages_service.PackageInfo{
  30. Owner: ctx.Package.Owner,
  31. PackageType: packages_model.TypeGeneric,
  32. Name: ctx.Params("packagename"),
  33. Version: ctx.Params("packageversion"),
  34. },
  35. &packages_service.PackageFileInfo{
  36. Filename: ctx.Params("filename"),
  37. },
  38. )
  39. if err != nil {
  40. if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
  41. apiError(ctx, http.StatusNotFound, err)
  42. return
  43. }
  44. apiError(ctx, http.StatusInternalServerError, err)
  45. return
  46. }
  47. helper.ServePackageFile(ctx, s, u, pf)
  48. }
  49. // UploadPackage uploads the specific generic package.
  50. // Duplicated packages get rejected.
  51. func UploadPackage(ctx *context.Context) {
  52. packageName := ctx.Params("packagename")
  53. filename := ctx.Params("filename")
  54. if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) {
  55. apiError(ctx, http.StatusBadRequest, errors.New("Invalid package name or filename"))
  56. return
  57. }
  58. packageVersion := ctx.Params("packageversion")
  59. if packageVersion != strings.TrimSpace(packageVersion) {
  60. apiError(ctx, http.StatusBadRequest, errors.New("Invalid package version"))
  61. return
  62. }
  63. upload, close, err := ctx.UploadStream()
  64. if err != nil {
  65. apiError(ctx, http.StatusInternalServerError, err)
  66. return
  67. }
  68. if close {
  69. defer upload.Close()
  70. }
  71. buf, err := packages_module.CreateHashedBufferFromReader(upload)
  72. if err != nil {
  73. log.Error("Error creating hashed buffer: %v", err)
  74. apiError(ctx, http.StatusInternalServerError, err)
  75. return
  76. }
  77. defer buf.Close()
  78. _, _, err = packages_service.CreatePackageOrAddFileToExisting(
  79. ctx,
  80. &packages_service.PackageCreationInfo{
  81. PackageInfo: packages_service.PackageInfo{
  82. Owner: ctx.Package.Owner,
  83. PackageType: packages_model.TypeGeneric,
  84. Name: packageName,
  85. Version: packageVersion,
  86. },
  87. Creator: ctx.Doer,
  88. },
  89. &packages_service.PackageFileCreationInfo{
  90. PackageFileInfo: packages_service.PackageFileInfo{
  91. Filename: filename,
  92. },
  93. Creator: ctx.Doer,
  94. Data: buf,
  95. IsLead: true,
  96. },
  97. )
  98. if err != nil {
  99. switch err {
  100. case packages_model.ErrDuplicatePackageFile:
  101. apiError(ctx, http.StatusConflict, err)
  102. case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
  103. apiError(ctx, http.StatusForbidden, err)
  104. default:
  105. apiError(ctx, http.StatusInternalServerError, err)
  106. }
  107. return
  108. }
  109. ctx.Status(http.StatusCreated)
  110. }
  111. // DeletePackage deletes the specific generic package.
  112. func DeletePackage(ctx *context.Context) {
  113. err := packages_service.RemovePackageVersionByNameAndVersion(
  114. ctx,
  115. ctx.Doer,
  116. &packages_service.PackageInfo{
  117. Owner: ctx.Package.Owner,
  118. PackageType: packages_model.TypeGeneric,
  119. Name: ctx.Params("packagename"),
  120. Version: ctx.Params("packageversion"),
  121. },
  122. )
  123. if err != nil {
  124. if err == packages_model.ErrPackageNotExist {
  125. apiError(ctx, http.StatusNotFound, err)
  126. return
  127. }
  128. apiError(ctx, http.StatusInternalServerError, err)
  129. return
  130. }
  131. ctx.Status(http.StatusNoContent)
  132. }
  133. // DeletePackageFile deletes the specific file of a generic package.
  134. func DeletePackageFile(ctx *context.Context) {
  135. pv, pf, err := func() (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  136. pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeGeneric, ctx.Params("packagename"), ctx.Params("packageversion"))
  137. if err != nil {
  138. return nil, nil, err
  139. }
  140. pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey)
  141. if err != nil {
  142. return nil, nil, err
  143. }
  144. return pv, pf, nil
  145. }()
  146. if err != nil {
  147. if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
  148. apiError(ctx, http.StatusNotFound, err)
  149. return
  150. }
  151. apiError(ctx, http.StatusInternalServerError, err)
  152. return
  153. }
  154. pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
  155. if err != nil {
  156. apiError(ctx, http.StatusInternalServerError, err)
  157. return
  158. }
  159. if len(pfs) == 1 {
  160. if err := packages_service.RemovePackageVersion(ctx, ctx.Doer, pv); err != nil {
  161. apiError(ctx, http.StatusInternalServerError, err)
  162. return
  163. }
  164. } else {
  165. if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
  166. apiError(ctx, http.StatusInternalServerError, err)
  167. return
  168. }
  169. }
  170. ctx.Status(http.StatusNoContent)
  171. }