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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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. "strings"
  11. "time"
  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. "code.gitea.io/gitea/modules/notification"
  19. packages_module "code.gitea.io/gitea/modules/packages"
  20. "code.gitea.io/gitea/modules/setting"
  21. "code.gitea.io/gitea/modules/util"
  22. container_service "code.gitea.io/gitea/services/packages/container"
  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 interface{}
  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(pvci *PackageCreationInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  61. return createPackageAndAddFile(pvci, pfci, false)
  62. }
  63. // CreatePackageOrAddFileToExisting creates a package with a file or adds the file if the package exists already
  64. func CreatePackageOrAddFileToExisting(pvci *PackageCreationInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  65. return createPackageAndAddFile(pvci, pfci, true)
  66. }
  67. func createPackageAndAddFile(pvci *PackageCreationInfo, pfci *PackageFileCreationInfo, allowDuplicate bool) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  68. ctx, committer, err := db.TxContext(db.DefaultContext)
  69. if err != nil {
  70. return nil, nil, err
  71. }
  72. defer committer.Close()
  73. pv, created, err := createPackageAndVersion(ctx, pvci, allowDuplicate)
  74. if err != nil {
  75. return nil, nil, err
  76. }
  77. pf, pb, blobCreated, err := addFileToPackageVersion(ctx, 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(db.DefaultContext, pv)
  97. if err != nil {
  98. return nil, nil, err
  99. }
  100. notification.NotifyPackageCreate(db.DefaultContext, 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(pvi *PackageInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  167. ctx, committer, err := db.TxContext(db.DefaultContext)
  168. if err != nil {
  169. return nil, nil, err
  170. }
  171. defer committer.Close()
  172. pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
  173. if err != nil {
  174. return nil, nil, err
  175. }
  176. pf, pb, blobCreated, err := addFileToPackageVersion(ctx, pv, pvi, pfci)
  177. removeBlob := false
  178. defer func() {
  179. if removeBlob {
  180. contentStore := packages_module.NewContentStore()
  181. if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
  182. log.Error("Error deleting package blob from content store: %v", err)
  183. }
  184. }
  185. }()
  186. if err != nil {
  187. removeBlob = blobCreated
  188. return nil, nil, err
  189. }
  190. if err := committer.Commit(); err != nil {
  191. removeBlob = blobCreated
  192. return nil, nil, err
  193. }
  194. return pv, pf, nil
  195. }
  196. // NewPackageBlob creates a package blob instance
  197. func NewPackageBlob(hsr packages_module.HashedSizeReader) *packages_model.PackageBlob {
  198. hashMD5, hashSHA1, hashSHA256, hashSHA512 := hsr.Sums()
  199. return &packages_model.PackageBlob{
  200. Size: hsr.Size(),
  201. HashMD5: hex.EncodeToString(hashMD5),
  202. HashSHA1: hex.EncodeToString(hashSHA1),
  203. HashSHA256: hex.EncodeToString(hashSHA256),
  204. HashSHA512: hex.EncodeToString(hashSHA512),
  205. }
  206. }
  207. func addFileToPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pvi *PackageInfo, pfci *PackageFileCreationInfo) (*packages_model.PackageFile, *packages_model.PackageBlob, bool, error) {
  208. log.Trace("Adding package file: %v, %s", pv.ID, pfci.Filename)
  209. if err := CheckSizeQuotaExceeded(ctx, pfci.Creator, pvi.Owner, pvi.PackageType, pfci.Data.Size()); err != nil {
  210. return nil, nil, false, err
  211. }
  212. pb, exists, err := packages_model.GetOrInsertBlob(ctx, NewPackageBlob(pfci.Data))
  213. if err != nil {
  214. log.Error("Error inserting package blob: %v", err)
  215. return nil, nil, false, err
  216. }
  217. if !exists {
  218. contentStore := packages_module.NewContentStore()
  219. if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), pfci.Data, pfci.Data.Size()); err != nil {
  220. log.Error("Error saving package blob in content store: %v", err)
  221. return nil, nil, false, err
  222. }
  223. }
  224. if pfci.OverwriteExisting {
  225. pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfci.Filename, pfci.CompositeKey)
  226. if err != nil && err != packages_model.ErrPackageFileNotExist {
  227. return nil, pb, !exists, err
  228. }
  229. if pf != nil {
  230. // Short circuit if blob is the same
  231. if pf.BlobID == pb.ID {
  232. return pf, pb, !exists, nil
  233. }
  234. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeFile, pf.ID); err != nil {
  235. return nil, pb, !exists, err
  236. }
  237. if err := packages_model.DeleteFileByID(ctx, pf.ID); err != nil {
  238. return nil, pb, !exists, err
  239. }
  240. }
  241. }
  242. pf := &packages_model.PackageFile{
  243. VersionID: pv.ID,
  244. BlobID: pb.ID,
  245. Name: pfci.Filename,
  246. LowerName: strings.ToLower(pfci.Filename),
  247. CompositeKey: pfci.CompositeKey,
  248. IsLead: pfci.IsLead,
  249. }
  250. if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
  251. if err != packages_model.ErrDuplicatePackageFile {
  252. log.Error("Error inserting package file: %v", err)
  253. }
  254. return nil, pb, !exists, err
  255. }
  256. for name, value := range pfci.Properties {
  257. if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeFile, pf.ID, name, value); err != nil {
  258. log.Error("Error setting package file property: %v", err)
  259. return pf, pb, !exists, err
  260. }
  261. }
  262. return pf, pb, !exists, nil
  263. }
  264. // CheckCountQuotaExceeded checks if the owner has more than the allowed packages
  265. // The check is skipped if the doer is an admin.
  266. func CheckCountQuotaExceeded(ctx context.Context, doer, owner *user_model.User) error {
  267. if doer.IsAdmin {
  268. return nil
  269. }
  270. if setting.Packages.LimitTotalOwnerCount > -1 {
  271. totalCount, err := packages_model.CountVersions(ctx, &packages_model.PackageSearchOptions{
  272. OwnerID: owner.ID,
  273. IsInternal: util.OptionalBoolFalse,
  274. })
  275. if err != nil {
  276. log.Error("CountVersions failed: %v", err)
  277. return err
  278. }
  279. if totalCount > setting.Packages.LimitTotalOwnerCount {
  280. return ErrQuotaTotalCount
  281. }
  282. }
  283. return nil
  284. }
  285. // CheckSizeQuotaExceeded checks if the upload size is bigger than the allowed size
  286. // The check is skipped if the doer is an admin.
  287. func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, packageType packages_model.Type, uploadSize int64) error {
  288. if doer.IsAdmin {
  289. return nil
  290. }
  291. var typeSpecificSize int64
  292. switch packageType {
  293. case packages_model.TypeComposer:
  294. typeSpecificSize = setting.Packages.LimitSizeComposer
  295. case packages_model.TypeConan:
  296. typeSpecificSize = setting.Packages.LimitSizeConan
  297. case packages_model.TypeConda:
  298. typeSpecificSize = setting.Packages.LimitSizeConda
  299. case packages_model.TypeContainer:
  300. typeSpecificSize = setting.Packages.LimitSizeContainer
  301. case packages_model.TypeGeneric:
  302. typeSpecificSize = setting.Packages.LimitSizeGeneric
  303. case packages_model.TypeHelm:
  304. typeSpecificSize = setting.Packages.LimitSizeHelm
  305. case packages_model.TypeMaven:
  306. typeSpecificSize = setting.Packages.LimitSizeMaven
  307. case packages_model.TypeNpm:
  308. typeSpecificSize = setting.Packages.LimitSizeNpm
  309. case packages_model.TypeNuGet:
  310. typeSpecificSize = setting.Packages.LimitSizeNuGet
  311. case packages_model.TypePub:
  312. typeSpecificSize = setting.Packages.LimitSizePub
  313. case packages_model.TypePyPI:
  314. typeSpecificSize = setting.Packages.LimitSizePyPI
  315. case packages_model.TypeRubyGems:
  316. typeSpecificSize = setting.Packages.LimitSizeRubyGems
  317. case packages_model.TypeVagrant:
  318. typeSpecificSize = setting.Packages.LimitSizeVagrant
  319. }
  320. if typeSpecificSize > -1 && typeSpecificSize < uploadSize {
  321. return ErrQuotaTypeSize
  322. }
  323. if setting.Packages.LimitTotalOwnerSize > -1 {
  324. totalSize, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{
  325. OwnerID: owner.ID,
  326. })
  327. if err != nil {
  328. log.Error("CalculateFileSize failed: %v", err)
  329. return err
  330. }
  331. if totalSize+uploadSize > setting.Packages.LimitTotalOwnerSize {
  332. return ErrQuotaTotalSize
  333. }
  334. }
  335. return nil
  336. }
  337. // RemovePackageVersionByNameAndVersion deletes a package version and all associated files
  338. func RemovePackageVersionByNameAndVersion(doer *user_model.User, pvi *PackageInfo) error {
  339. pv, err := packages_model.GetVersionByNameAndVersion(db.DefaultContext, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
  340. if err != nil {
  341. return err
  342. }
  343. return RemovePackageVersion(doer, pv)
  344. }
  345. // RemovePackageVersion deletes the package version and all associated files
  346. func RemovePackageVersion(doer *user_model.User, pv *packages_model.PackageVersion) error {
  347. ctx, committer, err := db.TxContext(db.DefaultContext)
  348. if err != nil {
  349. return err
  350. }
  351. defer committer.Close()
  352. pd, err := packages_model.GetPackageDescriptor(ctx, pv)
  353. if err != nil {
  354. return err
  355. }
  356. log.Trace("Deleting package: %v", pv.ID)
  357. if err := DeletePackageVersionAndReferences(ctx, pv); err != nil {
  358. return err
  359. }
  360. if err := committer.Commit(); err != nil {
  361. return err
  362. }
  363. notification.NotifyPackageDelete(db.DefaultContext, doer, pd)
  364. return nil
  365. }
  366. // DeletePackageVersionAndReferences deletes the package version and its properties and files
  367. func DeletePackageVersionAndReferences(ctx context.Context, pv *packages_model.PackageVersion) error {
  368. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeVersion, pv.ID); err != nil {
  369. return err
  370. }
  371. pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
  372. if err != nil {
  373. return err
  374. }
  375. for _, pf := range pfs {
  376. if err := DeletePackageFile(ctx, pf); err != nil {
  377. return err
  378. }
  379. }
  380. return packages_model.DeleteVersionByID(ctx, pv.ID)
  381. }
  382. // DeletePackageFile deletes the package file and its properties
  383. func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) error {
  384. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeFile, pf.ID); err != nil {
  385. return err
  386. }
  387. return packages_model.DeleteFileByID(ctx, pf.ID)
  388. }
  389. // Cleanup removes expired package data
  390. func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
  391. ctx, committer, err := db.TxContext(taskCtx)
  392. if err != nil {
  393. return err
  394. }
  395. defer committer.Close()
  396. err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
  397. select {
  398. case <-taskCtx.Done():
  399. return db.ErrCancelledf("While processing package cleanup rules")
  400. default:
  401. }
  402. if err := pcr.CompiledPattern(); err != nil {
  403. return fmt.Errorf("CleanupRule [%d]: CompilePattern failed: %w", pcr.ID, err)
  404. }
  405. olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
  406. packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
  407. if err != nil {
  408. return fmt.Errorf("CleanupRule [%d]: GetPackagesByType failed: %w", pcr.ID, err)
  409. }
  410. for _, p := range packages {
  411. pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  412. PackageID: p.ID,
  413. IsInternal: util.OptionalBoolFalse,
  414. Sort: packages_model.SortCreatedDesc,
  415. Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200),
  416. })
  417. if err != nil {
  418. return fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err)
  419. }
  420. for _, pv := range pvs {
  421. if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
  422. return fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err)
  423. } else if skip {
  424. log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
  425. continue
  426. }
  427. toMatch := pv.LowerVersion
  428. if pcr.MatchFullName {
  429. toMatch = p.LowerName + "/" + pv.LowerVersion
  430. }
  431. if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
  432. log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
  433. continue
  434. }
  435. if pv.CreatedUnix.AsLocalTime().After(olderThan) {
  436. log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version)
  437. continue
  438. }
  439. if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
  440. log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
  441. continue
  442. }
  443. log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
  444. if err := DeletePackageVersionAndReferences(ctx, pv); err != nil {
  445. return fmt.Errorf("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %w", pcr.ID, err)
  446. }
  447. }
  448. }
  449. return nil
  450. })
  451. if err != nil {
  452. return err
  453. }
  454. if err := container_service.Cleanup(ctx, olderThan); err != nil {
  455. return err
  456. }
  457. ps, err := packages_model.FindUnreferencedPackages(ctx)
  458. if err != nil {
  459. return err
  460. }
  461. for _, p := range ps {
  462. if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
  463. return err
  464. }
  465. if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
  466. return err
  467. }
  468. }
  469. pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
  470. if err != nil {
  471. return err
  472. }
  473. for _, pb := range pbs {
  474. if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
  475. return err
  476. }
  477. }
  478. if err := committer.Commit(); err != nil {
  479. return err
  480. }
  481. contentStore := packages_module.NewContentStore()
  482. for _, pb := range pbs {
  483. if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
  484. log.Error("Error deleting package blob [%v]: %v", pb.ID, err)
  485. }
  486. }
  487. return nil
  488. }
  489. // GetFileStreamByPackageNameAndVersion returns the content of the specific package file
  490. func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *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, err
  496. }
  497. log.Error("Error getting package: %v", err)
  498. return nil, nil, err
  499. }
  500. return GetFileStreamByPackageVersion(ctx, pv, pfi)
  501. }
  502. // GetFileStreamByPackageVersionAndFileID returns the content of the specific package file
  503. func GetFileStreamByPackageVersionAndFileID(ctx context.Context, owner *user_model.User, versionID, fileID int64) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
  504. log.Trace("Getting package file stream: %v, %v, %v", owner.ID, versionID, fileID)
  505. pv, err := packages_model.GetVersionByID(ctx, versionID)
  506. if err != nil {
  507. if err != packages_model.ErrPackageNotExist {
  508. log.Error("Error getting package version: %v", err)
  509. }
  510. return nil, nil, err
  511. }
  512. p, err := packages_model.GetPackageByID(ctx, pv.PackageID)
  513. if err != nil {
  514. log.Error("Error getting package: %v", err)
  515. return nil, nil, err
  516. }
  517. if p.OwnerID != owner.ID {
  518. return nil, nil, packages_model.ErrPackageNotExist
  519. }
  520. pf, err := packages_model.GetFileForVersionByID(ctx, versionID, fileID)
  521. if err != nil {
  522. log.Error("Error getting file: %v", err)
  523. return nil, nil, err
  524. }
  525. return GetPackageFileStream(ctx, pf)
  526. }
  527. // GetFileStreamByPackageVersion returns the content of the specific package file
  528. func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
  529. pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey)
  530. if err != nil {
  531. return nil, nil, err
  532. }
  533. return GetPackageFileStream(ctx, pf)
  534. }
  535. // GetPackageFileStream returns the content of the specific package file
  536. func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
  537. pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
  538. if err != nil {
  539. return nil, nil, err
  540. }
  541. s, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(pb.HashSHA256))
  542. if err == nil {
  543. if pf.IsLead {
  544. if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil {
  545. log.Error("Error incrementing download counter: %v", err)
  546. }
  547. }
  548. }
  549. return s, pf, err
  550. }
  551. // RemoveAllPackages for User
  552. func RemoveAllPackages(ctx context.Context, userID int64) (int, error) {
  553. count := 0
  554. for {
  555. pkgVersions, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  556. Paginator: &db.ListOptions{
  557. PageSize: repo_model.RepositoryListDefaultPageSize,
  558. Page: 1,
  559. },
  560. OwnerID: userID,
  561. IsInternal: util.OptionalBoolNone,
  562. })
  563. if err != nil {
  564. return count, fmt.Errorf("GetOwnedPackages[%d]: %w", userID, err)
  565. }
  566. if len(pkgVersions) == 0 {
  567. break
  568. }
  569. for _, pv := range pkgVersions {
  570. if err := DeletePackageVersionAndReferences(ctx, pv); err != nil {
  571. return count, fmt.Errorf("unable to delete package %d:%s[%d]. Error: %w", pv.PackageID, pv.Version, pv.ID, err)
  572. }
  573. count++
  574. }
  575. }
  576. return count, nil
  577. }