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.

npm.go 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 2021 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 npm
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "strings"
  12. "code.gitea.io/gitea/models/db"
  13. packages_model "code.gitea.io/gitea/models/packages"
  14. "code.gitea.io/gitea/modules/context"
  15. packages_module "code.gitea.io/gitea/modules/packages"
  16. npm_module "code.gitea.io/gitea/modules/packages/npm"
  17. "code.gitea.io/gitea/modules/setting"
  18. "code.gitea.io/gitea/modules/util"
  19. "code.gitea.io/gitea/routers/api/packages/helper"
  20. packages_service "code.gitea.io/gitea/services/packages"
  21. "github.com/hashicorp/go-version"
  22. )
  23. // errInvalidTagName indicates an invalid tag name
  24. var errInvalidTagName = errors.New("The tag name is invalid")
  25. func apiError(ctx *context.Context, status int, obj interface{}) {
  26. helper.LogAndProcessError(ctx, status, obj, func(message string) {
  27. ctx.JSON(status, map[string]string{
  28. "error": message,
  29. })
  30. })
  31. }
  32. // packageNameFromParams gets the package name from the url parameters
  33. // Variations: /name/, /@scope/name/, /@scope%2Fname/
  34. func packageNameFromParams(ctx *context.Context) string {
  35. scope := ctx.Params("scope")
  36. id := ctx.Params("id")
  37. if scope != "" {
  38. return fmt.Sprintf("@%s/%s", scope, id)
  39. }
  40. return id
  41. }
  42. // PackageMetadata returns the metadata for a single package
  43. func PackageMetadata(ctx *context.Context) {
  44. packageName := packageNameFromParams(ctx)
  45. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  46. if err != nil {
  47. apiError(ctx, http.StatusInternalServerError, err)
  48. return
  49. }
  50. if len(pvs) == 0 {
  51. apiError(ctx, http.StatusNotFound, err)
  52. return
  53. }
  54. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  55. if err != nil {
  56. apiError(ctx, http.StatusInternalServerError, err)
  57. return
  58. }
  59. resp := createPackageMetadataResponse(
  60. setting.AppURL+"api/packages/"+ctx.Package.Owner.Name+"/npm",
  61. pds,
  62. )
  63. ctx.JSON(http.StatusOK, resp)
  64. }
  65. // DownloadPackageFile serves the content of a package
  66. func DownloadPackageFile(ctx *context.Context) {
  67. packageName := packageNameFromParams(ctx)
  68. packageVersion := ctx.Params("version")
  69. filename := ctx.Params("filename")
  70. s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
  71. ctx,
  72. &packages_service.PackageInfo{
  73. Owner: ctx.Package.Owner,
  74. PackageType: packages_model.TypeNpm,
  75. Name: packageName,
  76. Version: packageVersion,
  77. },
  78. &packages_service.PackageFileInfo{
  79. Filename: filename,
  80. },
  81. )
  82. if err != nil {
  83. if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
  84. apiError(ctx, http.StatusNotFound, err)
  85. return
  86. }
  87. apiError(ctx, http.StatusInternalServerError, err)
  88. return
  89. }
  90. defer s.Close()
  91. ctx.ServeStream(s, pf.Name)
  92. }
  93. // UploadPackage creates a new package
  94. func UploadPackage(ctx *context.Context) {
  95. npmPackage, err := npm_module.ParsePackage(ctx.Req.Body)
  96. if err != nil {
  97. apiError(ctx, http.StatusBadRequest, err)
  98. return
  99. }
  100. buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data), 32*1024*1024)
  101. if err != nil {
  102. apiError(ctx, http.StatusInternalServerError, err)
  103. return
  104. }
  105. defer buf.Close()
  106. pv, _, err := packages_service.CreatePackageAndAddFile(
  107. &packages_service.PackageCreationInfo{
  108. PackageInfo: packages_service.PackageInfo{
  109. Owner: ctx.Package.Owner,
  110. PackageType: packages_model.TypeNpm,
  111. Name: npmPackage.Name,
  112. Version: npmPackage.Version,
  113. },
  114. SemverCompatible: true,
  115. Creator: ctx.Doer,
  116. Metadata: npmPackage.Metadata,
  117. },
  118. &packages_service.PackageFileCreationInfo{
  119. PackageFileInfo: packages_service.PackageFileInfo{
  120. Filename: npmPackage.Filename,
  121. },
  122. Data: buf,
  123. IsLead: true,
  124. },
  125. )
  126. if err != nil {
  127. if err == packages_model.ErrDuplicatePackageVersion {
  128. apiError(ctx, http.StatusBadRequest, err)
  129. return
  130. }
  131. apiError(ctx, http.StatusInternalServerError, err)
  132. return
  133. }
  134. for _, tag := range npmPackage.DistTags {
  135. if err := setPackageTag(tag, pv, false); err != nil {
  136. if err == errInvalidTagName {
  137. apiError(ctx, http.StatusBadRequest, err)
  138. return
  139. }
  140. apiError(ctx, http.StatusInternalServerError, err)
  141. return
  142. }
  143. }
  144. ctx.Status(http.StatusCreated)
  145. }
  146. // ListPackageTags returns all tags for a package
  147. func ListPackageTags(ctx *context.Context) {
  148. packageName := packageNameFromParams(ctx)
  149. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  150. if err != nil {
  151. apiError(ctx, http.StatusInternalServerError, err)
  152. return
  153. }
  154. tags := make(map[string]string)
  155. for _, pv := range pvs {
  156. pvps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, npm_module.TagProperty)
  157. if err != nil {
  158. apiError(ctx, http.StatusInternalServerError, err)
  159. return
  160. }
  161. for _, pvp := range pvps {
  162. tags[pvp.Value] = pv.Version
  163. }
  164. }
  165. ctx.JSON(http.StatusOK, tags)
  166. }
  167. // AddPackageTag adds a tag to the package
  168. func AddPackageTag(ctx *context.Context) {
  169. packageName := packageNameFromParams(ctx)
  170. body, err := io.ReadAll(ctx.Req.Body)
  171. if err != nil {
  172. apiError(ctx, http.StatusInternalServerError, err)
  173. return
  174. }
  175. version := strings.Trim(string(body), "\"") // is as "version" in the body
  176. pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName, version)
  177. if err != nil {
  178. if err == packages_model.ErrPackageNotExist {
  179. apiError(ctx, http.StatusNotFound, err)
  180. return
  181. }
  182. apiError(ctx, http.StatusInternalServerError, err)
  183. return
  184. }
  185. if err := setPackageTag(ctx.Params("tag"), pv, false); err != nil {
  186. if err == errInvalidTagName {
  187. apiError(ctx, http.StatusBadRequest, err)
  188. return
  189. }
  190. apiError(ctx, http.StatusInternalServerError, err)
  191. return
  192. }
  193. }
  194. // DeletePackageTag deletes a package tag
  195. func DeletePackageTag(ctx *context.Context) {
  196. packageName := packageNameFromParams(ctx)
  197. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  198. if err != nil {
  199. apiError(ctx, http.StatusInternalServerError, err)
  200. return
  201. }
  202. if len(pvs) != 0 {
  203. if err := setPackageTag(ctx.Params("tag"), pvs[0], true); err != nil {
  204. if err == errInvalidTagName {
  205. apiError(ctx, http.StatusBadRequest, err)
  206. return
  207. }
  208. apiError(ctx, http.StatusInternalServerError, err)
  209. return
  210. }
  211. }
  212. }
  213. func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly bool) error {
  214. if tag == "" {
  215. return errInvalidTagName
  216. }
  217. _, err := version.NewVersion(tag)
  218. if err == nil {
  219. return errInvalidTagName
  220. }
  221. ctx, committer, err := db.TxContext()
  222. if err != nil {
  223. return err
  224. }
  225. defer committer.Close()
  226. pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  227. PackageID: pv.PackageID,
  228. Properties: map[string]string{
  229. npm_module.TagProperty: tag,
  230. },
  231. IsInternal: util.OptionalBoolFalse,
  232. })
  233. if err != nil {
  234. return err
  235. }
  236. if len(pvs) == 1 {
  237. pvps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pvs[0].ID, npm_module.TagProperty)
  238. if err != nil {
  239. return err
  240. }
  241. for _, pvp := range pvps {
  242. if pvp.Value == tag {
  243. if err := packages_model.DeletePropertyByID(ctx, pvp.ID); err != nil {
  244. return err
  245. }
  246. break
  247. }
  248. }
  249. }
  250. if !deleteOnly {
  251. _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, npm_module.TagProperty, tag)
  252. if err != nil {
  253. return err
  254. }
  255. }
  256. return committer.Commit()
  257. }