gitea源码

npm.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package npm
  4. import (
  5. "bytes"
  6. std_ctx "context"
  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. access_model "code.gitea.io/gitea/models/perm/access"
  15. repo_model "code.gitea.io/gitea/models/repo"
  16. "code.gitea.io/gitea/models/unit"
  17. "code.gitea.io/gitea/modules/optional"
  18. packages_module "code.gitea.io/gitea/modules/packages"
  19. npm_module "code.gitea.io/gitea/modules/packages/npm"
  20. "code.gitea.io/gitea/modules/setting"
  21. "code.gitea.io/gitea/modules/util"
  22. "code.gitea.io/gitea/routers/api/packages/helper"
  23. "code.gitea.io/gitea/services/context"
  24. packages_service "code.gitea.io/gitea/services/packages"
  25. "github.com/hashicorp/go-version"
  26. )
  27. // errInvalidTagName indicates an invalid tag name
  28. var errInvalidTagName = errors.New("The tag name is invalid")
  29. func apiError(ctx *context.Context, status int, obj any) {
  30. message := helper.ProcessErrorForUser(ctx, status, obj)
  31. ctx.JSON(status, map[string]string{
  32. "error": message,
  33. })
  34. }
  35. // packageNameFromParams gets the package name from the url parameters
  36. // Variations: /name/, /@scope/name/, /@scope%2Fname/
  37. func packageNameFromParams(ctx *context.Context) string {
  38. scope := ctx.PathParam("scope")
  39. id := ctx.PathParam("id")
  40. if scope != "" {
  41. return fmt.Sprintf("@%s/%s", scope, id)
  42. }
  43. return id
  44. }
  45. // PackageMetadata returns the metadata for a single package
  46. func PackageMetadata(ctx *context.Context) {
  47. packageName := packageNameFromParams(ctx)
  48. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  49. if err != nil {
  50. apiError(ctx, http.StatusInternalServerError, err)
  51. return
  52. }
  53. if len(pvs) == 0 {
  54. apiError(ctx, http.StatusNotFound, err)
  55. return
  56. }
  57. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  58. if err != nil {
  59. apiError(ctx, http.StatusInternalServerError, err)
  60. return
  61. }
  62. resp := createPackageMetadataResponse(
  63. setting.AppURL+"api/packages/"+ctx.Package.Owner.Name+"/npm",
  64. pds,
  65. )
  66. ctx.JSON(http.StatusOK, resp)
  67. }
  68. // DownloadPackageFile serves the content of a package
  69. func DownloadPackageFile(ctx *context.Context) {
  70. packageName := packageNameFromParams(ctx)
  71. packageVersion := ctx.PathParam("version")
  72. filename := ctx.PathParam("filename")
  73. s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
  74. ctx,
  75. &packages_service.PackageInfo{
  76. Owner: ctx.Package.Owner,
  77. PackageType: packages_model.TypeNpm,
  78. Name: packageName,
  79. Version: packageVersion,
  80. },
  81. &packages_service.PackageFileInfo{
  82. Filename: filename,
  83. },
  84. ctx.Req.Method,
  85. )
  86. if err != nil {
  87. if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
  88. apiError(ctx, http.StatusNotFound, err)
  89. return
  90. }
  91. apiError(ctx, http.StatusInternalServerError, err)
  92. return
  93. }
  94. helper.ServePackageFile(ctx, s, u, pf)
  95. }
  96. // DownloadPackageFileByName finds the version and serves the contents of a package
  97. func DownloadPackageFileByName(ctx *context.Context) {
  98. filename := ctx.PathParam("filename")
  99. pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  100. OwnerID: ctx.Package.Owner.ID,
  101. Type: packages_model.TypeNpm,
  102. Name: packages_model.SearchValue{
  103. ExactMatch: true,
  104. Value: packageNameFromParams(ctx),
  105. },
  106. HasFileWithName: filename,
  107. IsInternal: optional.Some(false),
  108. })
  109. if err != nil {
  110. apiError(ctx, http.StatusInternalServerError, err)
  111. return
  112. }
  113. if len(pvs) != 1 {
  114. apiError(ctx, http.StatusNotFound, nil)
  115. return
  116. }
  117. s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
  118. ctx,
  119. pvs[0],
  120. &packages_service.PackageFileInfo{
  121. Filename: filename,
  122. },
  123. ctx.Req.Method,
  124. )
  125. if err != nil {
  126. if errors.Is(err, packages_model.ErrPackageFileNotExist) {
  127. apiError(ctx, http.StatusNotFound, err)
  128. return
  129. }
  130. apiError(ctx, http.StatusInternalServerError, err)
  131. return
  132. }
  133. helper.ServePackageFile(ctx, s, u, pf)
  134. }
  135. // UploadPackage creates a new package
  136. func UploadPackage(ctx *context.Context) {
  137. npmPackage, err := npm_module.ParsePackage(ctx.Req.Body)
  138. if err != nil {
  139. if errors.Is(err, util.ErrInvalidArgument) {
  140. apiError(ctx, http.StatusBadRequest, err)
  141. } else {
  142. apiError(ctx, http.StatusInternalServerError, err)
  143. }
  144. return
  145. }
  146. repo, err := repo_model.GetRepositoryByURLRelax(ctx, npmPackage.Metadata.Repository.URL)
  147. if err == nil {
  148. canWrite := repo.OwnerID == ctx.Doer.ID
  149. if !canWrite {
  150. perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
  151. if err != nil {
  152. apiError(ctx, http.StatusInternalServerError, err)
  153. return
  154. }
  155. canWrite = perms.CanWrite(unit.TypePackages)
  156. }
  157. if !canWrite {
  158. apiError(ctx, http.StatusForbidden, "no permission to upload this package")
  159. return
  160. }
  161. }
  162. buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data))
  163. if err != nil {
  164. apiError(ctx, http.StatusInternalServerError, err)
  165. return
  166. }
  167. defer buf.Close()
  168. pv, _, err := packages_service.CreatePackageAndAddFile(
  169. ctx,
  170. &packages_service.PackageCreationInfo{
  171. PackageInfo: packages_service.PackageInfo{
  172. Owner: ctx.Package.Owner,
  173. PackageType: packages_model.TypeNpm,
  174. Name: npmPackage.Name,
  175. Version: npmPackage.Version,
  176. },
  177. SemverCompatible: true,
  178. Creator: ctx.Doer,
  179. Metadata: npmPackage.Metadata,
  180. },
  181. &packages_service.PackageFileCreationInfo{
  182. PackageFileInfo: packages_service.PackageFileInfo{
  183. Filename: npmPackage.Filename,
  184. },
  185. Creator: ctx.Doer,
  186. Data: buf,
  187. IsLead: true,
  188. },
  189. )
  190. if err != nil {
  191. switch err {
  192. case packages_model.ErrDuplicatePackageVersion:
  193. apiError(ctx, http.StatusConflict, err)
  194. case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
  195. apiError(ctx, http.StatusForbidden, err)
  196. default:
  197. apiError(ctx, http.StatusInternalServerError, err)
  198. }
  199. return
  200. }
  201. for _, tag := range npmPackage.DistTags {
  202. if err := setPackageTag(ctx, tag, pv, false); err != nil {
  203. if err == errInvalidTagName {
  204. apiError(ctx, http.StatusBadRequest, err)
  205. return
  206. }
  207. apiError(ctx, http.StatusInternalServerError, err)
  208. return
  209. }
  210. }
  211. if repo != nil {
  212. if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil {
  213. apiError(ctx, http.StatusInternalServerError, err)
  214. return
  215. }
  216. }
  217. ctx.Status(http.StatusCreated)
  218. }
  219. // DeletePreview does nothing
  220. // The client tells the server what package version it knows about after deleting a version.
  221. func DeletePreview(ctx *context.Context) {
  222. ctx.Status(http.StatusOK)
  223. }
  224. // DeletePackageVersion deletes the package version
  225. func DeletePackageVersion(ctx *context.Context) {
  226. packageName := packageNameFromParams(ctx)
  227. packageVersion := ctx.PathParam("version")
  228. err := packages_service.RemovePackageVersionByNameAndVersion(
  229. ctx,
  230. ctx.Doer,
  231. &packages_service.PackageInfo{
  232. Owner: ctx.Package.Owner,
  233. PackageType: packages_model.TypeNpm,
  234. Name: packageName,
  235. Version: packageVersion,
  236. },
  237. )
  238. if err != nil {
  239. if errors.Is(err, packages_model.ErrPackageNotExist) {
  240. apiError(ctx, http.StatusNotFound, err)
  241. return
  242. }
  243. apiError(ctx, http.StatusInternalServerError, err)
  244. return
  245. }
  246. ctx.Status(http.StatusOK)
  247. }
  248. // DeletePackage deletes the package and all versions
  249. func DeletePackage(ctx *context.Context) {
  250. packageName := packageNameFromParams(ctx)
  251. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  252. if err != nil {
  253. apiError(ctx, http.StatusInternalServerError, err)
  254. return
  255. }
  256. if len(pvs) == 0 {
  257. apiError(ctx, http.StatusNotFound, err)
  258. return
  259. }
  260. for _, pv := range pvs {
  261. if err := packages_service.RemovePackageVersion(ctx, ctx.Doer, pv); err != nil {
  262. apiError(ctx, http.StatusInternalServerError, err)
  263. return
  264. }
  265. }
  266. ctx.Status(http.StatusOK)
  267. }
  268. // ListPackageTags returns all tags for a package
  269. func ListPackageTags(ctx *context.Context) {
  270. packageName := packageNameFromParams(ctx)
  271. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  272. if err != nil {
  273. apiError(ctx, http.StatusInternalServerError, err)
  274. return
  275. }
  276. tags := make(map[string]string)
  277. for _, pv := range pvs {
  278. pvps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, npm_module.TagProperty)
  279. if err != nil {
  280. apiError(ctx, http.StatusInternalServerError, err)
  281. return
  282. }
  283. for _, pvp := range pvps {
  284. tags[pvp.Value] = pv.Version
  285. }
  286. }
  287. ctx.JSON(http.StatusOK, tags)
  288. }
  289. // AddPackageTag adds a tag to the package
  290. func AddPackageTag(ctx *context.Context) {
  291. packageName := packageNameFromParams(ctx)
  292. body, err := io.ReadAll(ctx.Req.Body)
  293. if err != nil {
  294. apiError(ctx, http.StatusInternalServerError, err)
  295. return
  296. }
  297. version := strings.Trim(string(body), "\"") // is as "version" in the body
  298. pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName, version)
  299. if err != nil {
  300. if errors.Is(err, packages_model.ErrPackageNotExist) {
  301. apiError(ctx, http.StatusNotFound, err)
  302. return
  303. }
  304. apiError(ctx, http.StatusInternalServerError, err)
  305. return
  306. }
  307. if err := setPackageTag(ctx, ctx.PathParam("tag"), pv, false); err != nil {
  308. if err == errInvalidTagName {
  309. apiError(ctx, http.StatusBadRequest, err)
  310. return
  311. }
  312. apiError(ctx, http.StatusInternalServerError, err)
  313. return
  314. }
  315. }
  316. // DeletePackageTag deletes a package tag
  317. func DeletePackageTag(ctx *context.Context) {
  318. packageName := packageNameFromParams(ctx)
  319. pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
  320. if err != nil {
  321. apiError(ctx, http.StatusInternalServerError, err)
  322. return
  323. }
  324. if len(pvs) != 0 {
  325. if err := setPackageTag(ctx, ctx.PathParam("tag"), pvs[0], true); err != nil {
  326. if err == errInvalidTagName {
  327. apiError(ctx, http.StatusBadRequest, err)
  328. return
  329. }
  330. apiError(ctx, http.StatusInternalServerError, err)
  331. return
  332. }
  333. }
  334. }
  335. func setPackageTag(ctx std_ctx.Context, tag string, pv *packages_model.PackageVersion, deleteOnly bool) error {
  336. if tag == "" {
  337. return errInvalidTagName
  338. }
  339. _, err := version.NewVersion(tag)
  340. if err == nil {
  341. return errInvalidTagName
  342. }
  343. return db.WithTx(ctx, func(ctx std_ctx.Context) error {
  344. pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  345. PackageID: pv.PackageID,
  346. Properties: map[string]string{
  347. npm_module.TagProperty: tag,
  348. },
  349. IsInternal: optional.Some(false),
  350. })
  351. if err != nil {
  352. return err
  353. }
  354. if len(pvs) == 1 {
  355. pvps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pvs[0].ID, npm_module.TagProperty)
  356. if err != nil {
  357. return err
  358. }
  359. for _, pvp := range pvps {
  360. if pvp.Value == tag {
  361. if err := packages_model.DeletePropertyByID(ctx, pvp.ID); err != nil {
  362. return err
  363. }
  364. break
  365. }
  366. }
  367. }
  368. if !deleteOnly {
  369. _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, npm_module.TagProperty, tag)
  370. if err != nil {
  371. return err
  372. }
  373. }
  374. return nil
  375. })
  376. }
  377. func PackageSearch(ctx *context.Context) {
  378. pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
  379. OwnerID: ctx.Package.Owner.ID,
  380. Type: packages_model.TypeNpm,
  381. IsInternal: optional.Some(false),
  382. Name: packages_model.SearchValue{
  383. ExactMatch: false,
  384. Value: ctx.FormTrim("text"),
  385. },
  386. Paginator: db.NewAbsoluteListOptions(
  387. ctx.FormInt("from"),
  388. ctx.FormInt("size"),
  389. ),
  390. })
  391. if err != nil {
  392. apiError(ctx, http.StatusInternalServerError, err)
  393. return
  394. }
  395. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  396. if err != nil {
  397. apiError(ctx, http.StatusInternalServerError, err)
  398. return
  399. }
  400. resp := createPackageSearchResponse(
  401. pds,
  402. total,
  403. )
  404. ctx.JSON(http.StatusOK, resp)
  405. }