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.

packages.go 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package packages
  4. import (
  5. "context"
  6. "encoding/hex"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "net/url"
  11. "strings"
  12. "code.gitea.io/gitea/models/db"
  13. packages_model "code.gitea.io/gitea/models/packages"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. user_model "code.gitea.io/gitea/models/user"
  16. "code.gitea.io/gitea/modules/json"
  17. "code.gitea.io/gitea/modules/log"
  18. packages_module "code.gitea.io/gitea/modules/packages"
  19. "code.gitea.io/gitea/modules/setting"
  20. "code.gitea.io/gitea/modules/storage"
  21. "code.gitea.io/gitea/modules/util"
  22. notify_service "code.gitea.io/gitea/services/notify"
  23. )
  24. var (
  25. ErrQuotaTypeSize = errors.New("maximum allowed package type size exceeded")
  26. ErrQuotaTotalSize = errors.New("maximum allowed package storage quota exceeded")
  27. ErrQuotaTotalCount = errors.New("maximum allowed package count exceeded")
  28. )
  29. // PackageInfo describes a package
  30. type PackageInfo struct {
  31. Owner *user_model.User
  32. PackageType packages_model.Type
  33. Name string
  34. Version string
  35. }
  36. // PackageCreationInfo describes a package to create
  37. type PackageCreationInfo struct {
  38. PackageInfo
  39. SemverCompatible bool
  40. Creator *user_model.User
  41. Metadata any
  42. PackageProperties map[string]string
  43. VersionProperties map[string]string
  44. }
  45. // PackageFileInfo describes a package file
  46. type PackageFileInfo struct {
  47. Filename string
  48. CompositeKey string
  49. }
  50. // PackageFileCreationInfo describes a package file to create
  51. type PackageFileCreationInfo struct {
  52. PackageFileInfo
  53. Creator *user_model.User
  54. Data packages_module.HashedSizeReader
  55. IsLead bool
  56. Properties map[string]string
  57. OverwriteExisting bool
  58. }
  59. // CreatePackageAndAddFile creates a package with a file. If the same package exists already, ErrDuplicatePackageVersion is returned
  60. func CreatePackageAndAddFile(ctx context.Context, pvci *PackageCreationInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  61. return createPackageAndAddFile(ctx, pvci, pfci, false)
  62. }
  63. // CreatePackageOrAddFileToExisting creates a package with a file or adds the file if the package exists already
  64. func CreatePackageOrAddFileToExisting(ctx context.Context, pvci *PackageCreationInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  65. return createPackageAndAddFile(ctx, pvci, pfci, true)
  66. }
  67. func createPackageAndAddFile(ctx context.Context, pvci *PackageCreationInfo, pfci *PackageFileCreationInfo, allowDuplicate bool) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  68. dbCtx, committer, err := db.TxContext(ctx)
  69. if err != nil {
  70. return nil, nil, err
  71. }
  72. defer committer.Close()
  73. pv, created, err := createPackageAndVersion(dbCtx, pvci, allowDuplicate)
  74. if err != nil {
  75. return nil, nil, err
  76. }
  77. pf, pb, blobCreated, err := addFileToPackageVersion(dbCtx, pv, &pvci.PackageInfo, pfci)
  78. removeBlob := false
  79. defer func() {
  80. if blobCreated && removeBlob {
  81. contentStore := packages_module.NewContentStore()
  82. if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
  83. log.Error("Error deleting package blob from content store: %v", err)
  84. }
  85. }
  86. }()
  87. if err != nil {
  88. removeBlob = true
  89. return nil, nil, err
  90. }
  91. if err := committer.Commit(); err != nil {
  92. removeBlob = true
  93. return nil, nil, err
  94. }
  95. if created {
  96. pd, err := packages_model.GetPackageDescriptor(ctx, pv)
  97. if err != nil {
  98. return nil, nil, err
  99. }
  100. notify_service.PackageCreate(ctx, pvci.Creator, pd)
  101. }
  102. return pv, pf, nil
  103. }
  104. func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, allowDuplicate bool) (*packages_model.PackageVersion, bool, error) {
  105. log.Trace("Creating package: %v, %v, %v, %s, %s, %+v, %+v, %v", pvci.Creator.ID, pvci.Owner.ID, pvci.PackageType, pvci.Name, pvci.Version, pvci.PackageProperties, pvci.VersionProperties, allowDuplicate)
  106. packageCreated := true
  107. p := &packages_model.Package{
  108. OwnerID: pvci.Owner.ID,
  109. Type: pvci.PackageType,
  110. Name: pvci.Name,
  111. LowerName: strings.ToLower(pvci.Name),
  112. SemverCompatible: pvci.SemverCompatible,
  113. }
  114. var err error
  115. if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
  116. if err == packages_model.ErrDuplicatePackage {
  117. packageCreated = false
  118. } else {
  119. log.Error("Error inserting package: %v", err)
  120. return nil, false, err
  121. }
  122. }
  123. if packageCreated {
  124. for name, value := range pvci.PackageProperties {
  125. if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, name, value); err != nil {
  126. log.Error("Error setting package property: %v", err)
  127. return nil, false, err
  128. }
  129. }
  130. }
  131. metadataJSON, err := json.Marshal(pvci.Metadata)
  132. if err != nil {
  133. return nil, false, err
  134. }
  135. versionCreated := true
  136. pv := &packages_model.PackageVersion{
  137. PackageID: p.ID,
  138. CreatorID: pvci.Creator.ID,
  139. Version: pvci.Version,
  140. LowerVersion: strings.ToLower(pvci.Version),
  141. MetadataJSON: string(metadataJSON),
  142. }
  143. if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
  144. if err == packages_model.ErrDuplicatePackageVersion {
  145. versionCreated = false
  146. }
  147. if err != packages_model.ErrDuplicatePackageVersion || !allowDuplicate {
  148. log.Error("Error inserting package: %v", err)
  149. return nil, false, err
  150. }
  151. }
  152. if versionCreated {
  153. if err := CheckCountQuotaExceeded(ctx, pvci.Creator, pvci.Owner); err != nil {
  154. return nil, false, err
  155. }
  156. for name, value := range pvci.VersionProperties {
  157. if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, name, value); err != nil {
  158. log.Error("Error setting package version property: %v", err)
  159. return nil, false, err
  160. }
  161. }
  162. }
  163. return pv, versionCreated, nil
  164. }
  165. // AddFileToExistingPackage adds a file to an existing package. If the package does not exist, ErrPackageNotExist is returned
  166. func AddFileToExistingPackage(ctx context.Context, pvi *PackageInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, error) {
  167. return addFileToPackageWrapper(ctx, func(ctx context.Context) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
  168. pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
  169. if err != nil {
  170. return nil, nil, false, err
  171. }
  172. return addFileToPackageVersion(ctx, pv, pvi, pfci)
  173. })
  174. }
  175. // AddFileToPackageVersionInternal adds a file to the package
  176. // This method skips quota checks and should only be used for system-managed packages.
  177. func AddFileToPackageVersionInternal(ctx context.Context, pv *packages_model.PackageVersion, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, error) {
  178. return addFileToPackageWrapper(ctx, func(ctx context.Context) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
  179. return addFileToPackageVersionUnchecked(ctx, pv, pfci)
  180. })
  181. }
  182. func addFileToPackageWrapper(ctx context.Context, fn func(ctx context.Context) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error)) (*packages_model.PackageFile, error) {
  183. ctx, committer, err := db.TxContext(ctx)
  184. if err != nil {
  185. return nil, err
  186. }
  187. defer committer.Close()
  188. pf, pb, blobCreated, err := fn(ctx)
  189. removeBlob := false
  190. defer func() {
  191. if removeBlob {
  192. contentStore := packages_module.NewContentStore()
  193. if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
  194. log.Error("Error deleting package blob from content store: %v", err)
  195. }
  196. }
  197. }()
  198. if err != nil {
  199. removeBlob = blobCreated
  200. return nil, err
  201. }
  202. if err := committer.Commit(); err != nil {
  203. removeBlob = blobCreated
  204. return nil, err
  205. }
  206. return pf, nil
  207. }
  208. // NewPackageBlob creates a package blob instance
  209. func NewPackageBlob(hsr packages_module.HashedSizeReader) *packages_model.PackageBlob {
  210. hashMD5, hashSHA1, hashSHA256, hashSHA512 := hsr.Sums()
  211. return &packages_model.PackageBlob{
  212. Size: hsr.Size(),
  213. HashMD5: hex.EncodeToString(hashMD5),
  214. HashSHA1: hex.EncodeToString(hashSHA1),
  215. HashSHA256: hex.EncodeToString(hashSHA256),
  216. HashSHA512: hex.EncodeToString(hashSHA512),
  217. }
  218. }
  219. func addFileToPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pvi *PackageInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
  220. if err := CheckSizeQuotaExceeded(ctx, pfci.Creator, pvi.Owner, pvi.PackageType, pfci.Data.Size()); err != nil {
  221. return nil, nil, false, err
  222. }
  223. return addFileToPackageVersionUnchecked(ctx, pv, pfci)
  224. }
  225. func addFileToPackageVersionUnchecked(ctx context.Context, pv *packages_model.PackageVersion, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
  226. log.Trace("Adding package file: %v, %s", pv.ID, pfci.Filename)
  227. pb, exists, err := packages_model.GetOrInsertBlob(ctx, NewPackageBlob(pfci.Data))
  228. if err != nil {
  229. log.Error("Error inserting package blob: %v", err)
  230. return nil, nil, false, err
  231. }
  232. if !exists {
  233. contentStore := packages_module.NewContentStore()
  234. if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), pfci.Data, pfci.Data.Size()); err != nil {
  235. log.Error("Error saving package blob in content store: %v", err)
  236. return nil, nil, false, err
  237. }
  238. }
  239. if pfci.OverwriteExisting {
  240. pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfci.Filename, pfci.CompositeKey)
  241. if err != nil && err != packages_model.ErrPackageFileNotExist {
  242. return nil, pb, !exists, err
  243. }
  244. if pf != nil {
  245. // Short circuit if blob is the same
  246. if pf.BlobID == pb.ID {
  247. return pf, pb, !exists, nil
  248. }
  249. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeFile, pf.ID); err != nil {
  250. return nil, pb, !exists, err
  251. }
  252. if err := packages_model.DeleteFileByID(ctx, pf.ID); err != nil {
  253. return nil, pb, !exists, err
  254. }
  255. }
  256. }
  257. pf := &packages_model.PackageFile{
  258. VersionID: pv.ID,
  259. BlobID: pb.ID,
  260. Name: pfci.Filename,
  261. LowerName: strings.ToLower(pfci.Filename),
  262. CompositeKey: pfci.CompositeKey,
  263. IsLead: pfci.IsLead,
  264. }
  265. if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
  266. if err != packages_model.ErrDuplicatePackageFile {
  267. log.Error("Error inserting package file: %v", err)
  268. }
  269. return nil, pb, !exists, err
  270. }
  271. for name, value := range pfci.Properties {
  272. if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeFile, pf.ID, name, value); err != nil {
  273. log.Error("Error setting package file property: %v", err)
  274. return pf, pb, !exists, err
  275. }
  276. }
  277. return pf, pb, !exists, nil
  278. }
  279. // CheckCountQuotaExceeded checks if the owner has more than the allowed packages
  280. // The check is skipped if the doer is an admin.
  281. func CheckCountQuotaExceeded(ctx context.Context, doer, owner *user_model.User) error {
  282. if doer.IsAdmin {
  283. return nil
  284. }
  285. if setting.Packages.LimitTotalOwnerCount > -1 {
  286. totalCount, err := packages_model.CountVersions(ctx, &packages_model.PackageSearchOptions{
  287. OwnerID: owner.ID,
  288. IsInternal: util.OptionalBoolFalse,
  289. })
  290. if err != nil {
  291. log.Error("CountVersions failed: %v", err)
  292. return err
  293. }
  294. if totalCount > setting.Packages.LimitTotalOwnerCount {
  295. return ErrQuotaTotalCount
  296. }
  297. }
  298. return nil
  299. }
  300. // CheckSizeQuotaExceeded checks if the upload size is bigger than the allowed size
  301. // The check is skipped if the doer is an admin.
  302. func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, packageType packages_model.Type, uploadSize int64) error {
  303. if doer.IsAdmin {
  304. return nil
  305. }
  306. var typeSpecificSize int64
  307. switch packageType {
  308. case packages_model.TypeAlpine:
  309. typeSpecificSize = setting.Packages.LimitSizeAlpine
  310. case packages_model.TypeCargo:
  311. typeSpecificSize = setting.Packages.LimitSizeCargo
  312. case packages_model.TypeChef:
  313. typeSpecificSize = setting.Packages.LimitSizeChef
  314. case packages_model.TypeComposer:
  315. typeSpecificSize = setting.Packages.LimitSizeComposer
  316. case packages_model.TypeConan:
  317. typeSpecificSize = setting.Packages.LimitSizeConan
  318. case packages_model.TypeConda:
  319. typeSpecificSize = setting.Packages.LimitSizeConda
  320. case packages_model.TypeContainer:
  321. typeSpecificSize = setting.Packages.LimitSizeContainer
  322. case packages_model.TypeCran:
  323. typeSpecificSize = setting.Packages.LimitSizeCran
  324. case packages_model.TypeDebian:
  325. typeSpecificSize = setting.Packages.LimitSizeDebian
  326. case packages_model.TypeGeneric:
  327. typeSpecificSize = setting.Packages.LimitSizeGeneric
  328. case packages_model.TypeGo:
  329. typeSpecificSize = setting.Packages.LimitSizeGo
  330. case packages_model.TypeHelm:
  331. typeSpecificSize = setting.Packages.LimitSizeHelm
  332. case packages_model.TypeMaven:
  333. typeSpecificSize = setting.Packages.LimitSizeMaven
  334. case packages_model.TypeNpm:
  335. typeSpecificSize = setting.Packages.LimitSizeNpm
  336. case packages_model.TypeNuGet:
  337. typeSpecificSize = setting.Packages.LimitSizeNuGet
  338. case packages_model.TypePub:
  339. typeSpecificSize = setting.Packages.LimitSizePub
  340. case packages_model.TypePyPI:
  341. typeSpecificSize = setting.Packages.LimitSizePyPI
  342. case packages_model.TypeRpm:
  343. typeSpecificSize = setting.Packages.LimitSizeRpm
  344. case packages_model.TypeRubyGems:
  345. typeSpecificSize = setting.Packages.LimitSizeRubyGems
  346. case packages_model.TypeSwift:
  347. typeSpecificSize = setting.Packages.LimitSizeSwift
  348. case packages_model.TypeVagrant:
  349. typeSpecificSize = setting.Packages.LimitSizeVagrant
  350. }
  351. if typeSpecificSize > -1 && typeSpecificSize < uploadSize {
  352. return ErrQuotaTypeSize
  353. }
  354. if setting.Packages.LimitTotalOwnerSize > -1 {
  355. totalSize, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{
  356. OwnerID: owner.ID,
  357. })
  358. if err != nil {
  359. log.Error("CalculateFileSize failed: %v", err)
  360. return err
  361. }
  362. if totalSize+uploadSize > setting.Packages.LimitTotalOwnerSize {
  363. return ErrQuotaTotalSize
  364. }
  365. }
  366. return nil
  367. }
  368. // GetOrCreateInternalPackageVersion gets or creates an internal package
  369. // Some package types need such internal packages for housekeeping.
  370. func GetOrCreateInternalPackageVersion(ctx context.Context, ownerID int64, packageType packages_model.Type, name, version string) (*packages_model.PackageVersion, error) {
  371. var pv *packages_model.PackageVersion
  372. return pv, db.WithTx(ctx, func(ctx context.Context) error {
  373. p := &packages_model.Package{
  374. OwnerID: ownerID,
  375. Type: packageType,
  376. Name: name,
  377. LowerName: name,
  378. IsInternal: true,
  379. }
  380. var err error
  381. if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
  382. if err != packages_model.ErrDuplicatePackage {
  383. log.Error("Error inserting package: %v", err)
  384. return err
  385. }
  386. }
  387. pv = &packages_model.PackageVersion{
  388. PackageID: p.ID,
  389. CreatorID: ownerID,
  390. Version: version,
  391. LowerVersion: version,
  392. IsInternal: true,
  393. MetadataJSON: "null",
  394. }
  395. if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
  396. if err != packages_model.ErrDuplicatePackageVersion {
  397. log.Error("Error inserting package version: %v", err)
  398. return err
  399. }
  400. }
  401. return nil
  402. })
  403. }
  404. // RemovePackageVersionByNameAndVersion deletes a package version and all associated files
  405. func RemovePackageVersionByNameAndVersion(ctx context.Context, doer *user_model.User, pvi *PackageInfo) error {
  406. pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
  407. if err != nil {
  408. return err
  409. }
  410. return RemovePackageVersion(ctx, doer, pv)
  411. }
  412. // RemovePackageVersion deletes the package version and all associated files
  413. func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
  414. dbCtx, committer, err := db.TxContext(ctx)
  415. if err != nil {
  416. return err
  417. }
  418. defer committer.Close()
  419. pd, err := packages_model.GetPackageDescriptor(dbCtx, pv)
  420. if err != nil {
  421. return err
  422. }
  423. log.Trace("Deleting package: %v", pv.ID)
  424. if err := DeletePackageVersionAndReferences(dbCtx, pv); err != nil {
  425. return err
  426. }
  427. if err := committer.Commit(); err != nil {
  428. return err
  429. }
  430. notify_service.PackageDelete(ctx, doer, pd)
  431. return nil
  432. }
  433. // RemovePackageFileAndVersionIfUnreferenced deletes the package file and the version if there are no referenced files afterwards
  434. func RemovePackageFileAndVersionIfUnreferenced(ctx context.Context, doer *user_model.User, pf *packages_model.PackageFile) error {
  435. var pd *packages_model.PackageDescriptor
  436. if err := db.WithTx(ctx, func(ctx context.Context) error {
  437. if err := DeletePackageFile(ctx, pf); err != nil {
  438. return err
  439. }
  440. has, err := packages_model.HasVersionFileReferences(ctx, pf.VersionID)
  441. if err != nil {
  442. return err
  443. }
  444. if !has {
  445. pv, err := packages_model.GetVersionByID(ctx, pf.VersionID)
  446. if err != nil {
  447. return err
  448. }
  449. pd, err = packages_model.GetPackageDescriptor(ctx, pv)
  450. if err != nil {
  451. return err
  452. }
  453. if err := DeletePackageVersionAndReferences(ctx, pv); err != nil {
  454. return err
  455. }
  456. }
  457. return nil
  458. }); err != nil {
  459. return err
  460. }
  461. if pd != nil {
  462. notify_service.PackageDelete(ctx, doer, pd)
  463. }
  464. return nil
  465. }
  466. // DeletePackageVersionAndReferences deletes the package version and its properties and files
  467. func DeletePackageVersionAndReferences(ctx context.Context, pv *packages_model.PackageVersion) error {
  468. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeVersion, pv.ID); err != nil {
  469. return err
  470. }
  471. pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
  472. if err != nil {
  473. return err
  474. }
  475. for _, pf := range pfs {
  476. if err := DeletePackageFile(ctx, pf); err != nil {
  477. return err
  478. }
  479. }
  480. return packages_model.DeleteVersionByID(ctx, pv.ID)
  481. }
  482. // DeletePackageFile deletes the package file and its properties
  483. func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) error {
  484. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeFile, pf.ID); err != nil {
  485. return err
  486. }
  487. return packages_model.DeleteFileByID(ctx, pf.ID)
  488. }
  489. // GetFileStreamByPackageNameAndVersion returns the content of the specific package file
  490. func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
  491. log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey)
  492. pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
  493. if err != nil {
  494. if err == packages_model.ErrPackageNotExist {
  495. return nil, nil, nil, err
  496. }
  497. log.Error("Error getting package: %v", err)
  498. return nil, nil, nil, err
  499. }
  500. return GetFileStreamByPackageVersion(ctx, pv, pfi)
  501. }
  502. // GetFileStreamByPackageVersion returns the content of the specific package file
  503. func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
  504. pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey)
  505. if err != nil {
  506. return nil, nil, nil, err
  507. }
  508. return GetPackageFileStream(ctx, pf)
  509. }
  510. // GetPackageFileStream returns the content of the specific package file
  511. func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
  512. pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
  513. if err != nil {
  514. return nil, nil, nil, err
  515. }
  516. return GetPackageBlobStream(ctx, pf, pb)
  517. }
  518. // GetPackageBlobStream returns the content of the specific package blob
  519. // If the storage supports direct serving and it's enabled, only the direct serving url is returned.
  520. func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
  521. key := packages_module.BlobHash256Key(pb.HashSHA256)
  522. cs := packages_module.NewContentStore()
  523. var s io.ReadSeekCloser
  524. var u *url.URL
  525. var err error
  526. if cs.ShouldServeDirect() {
  527. u, err = cs.GetServeDirectURL(key, pf.Name)
  528. if err != nil && !errors.Is(err, storage.ErrURLNotSupported) {
  529. log.Error("Error getting serve direct url: %v", err)
  530. }
  531. }
  532. if u == nil {
  533. s, err = cs.Get(key)
  534. }
  535. if err == nil {
  536. if pf.IsLead {
  537. if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil {
  538. log.Error("Error incrementing download counter: %v", err)
  539. }
  540. }
  541. }
  542. return s, u, pf, err
  543. }
  544. // RemoveAllPackages for User
  545. func RemoveAllPackages(ctx context.Context, userID int64) (int, error) {
  546. count := 0
  547. for {
  548. pkgVersions, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  549. Paginator: &db.ListOptions{
  550. PageSize: repo_model.RepositoryListDefaultPageSize,
  551. Page: 1,
  552. },
  553. OwnerID: userID,
  554. IsInternal: util.OptionalBoolNone,
  555. })
  556. if err != nil {
  557. return count, fmt.Errorf("GetOwnedPackages[%d]: %w", userID, err)
  558. }
  559. if len(pkgVersions) == 0 {
  560. break
  561. }
  562. for _, pv := range pkgVersions {
  563. if err := DeletePackageVersionAndReferences(ctx, pv); err != nil {
  564. return count, fmt.Errorf("unable to delete package %d:%s[%d]. Error: %w", pv.PackageID, pv.Version, pv.ID, err)
  565. }
  566. count++
  567. }
  568. }
  569. return count, nil
  570. }