2022-03-30 10:42:47 +02:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2022-03-30 10:42:47 +02:00
package packages
import (
"context"
2022-11-28 19:19:18 +08:00
"encoding/hex"
2022-11-09 07:34:27 +01:00
"errors"
2022-03-30 10:42:47 +02:00
"fmt"
"io"
2023-07-03 15:33:28 +02:00
"net/url"
2022-03-30 10:42:47 +02:00
"strings"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
2022-07-14 08:22:09 +01:00
repo_model "code.gitea.io/gitea/models/repo"
2022-03-30 10:42:47 +02:00
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
2024-03-02 16:42:31 +01:00
"code.gitea.io/gitea/modules/optional"
2022-03-30 10:42:47 +02:00
packages_module "code.gitea.io/gitea/modules/packages"
2022-11-09 07:34:27 +01:00
"code.gitea.io/gitea/modules/setting"
2023-07-03 15:33:28 +02:00
"code.gitea.io/gitea/modules/storage"
2023-09-06 02:37:47 +08:00
notify_service "code.gitea.io/gitea/services/notify"
2022-03-30 10:42:47 +02:00
)
2022-11-09 07:34:27 +01:00
var (
ErrQuotaTypeSize = errors . New ( "maximum allowed package type size exceeded" )
ErrQuotaTotalSize = errors . New ( "maximum allowed package storage quota exceeded" )
ErrQuotaTotalCount = errors . New ( "maximum allowed package count exceeded" )
)
2022-03-30 10:42:47 +02:00
// PackageInfo describes a package
type PackageInfo struct {
Owner * user_model . User
PackageType packages_model . Type
Name string
Version string
}
// PackageCreationInfo describes a package to create
type PackageCreationInfo struct {
PackageInfo
2022-07-28 05:59:39 +02:00
SemverCompatible bool
Creator * user_model . User
2023-07-04 20:36:08 +02:00
Metadata any
2022-07-28 05:59:39 +02:00
PackageProperties map [ string ] string
VersionProperties map [ string ] string
2022-03-30 10:42:47 +02:00
}
// PackageFileInfo describes a package file
type PackageFileInfo struct {
Filename string
CompositeKey string
}
// PackageFileCreationInfo describes a package file to create
type PackageFileCreationInfo struct {
PackageFileInfo
2022-11-09 07:34:27 +01:00
Creator * user_model . User
2022-03-30 10:42:47 +02:00
Data packages_module . HashedSizeReader
IsLead bool
Properties map [ string ] string
OverwriteExisting bool
}
// CreatePackageAndAddFile creates a package with a file. If the same package exists already, ErrDuplicatePackageVersion is returned
2023-09-25 15:17:37 +02:00
func CreatePackageAndAddFile ( ctx context . Context , pvci * PackageCreationInfo , pfci * PackageFileCreationInfo ) ( * packages_model . PackageVersion , * packages_model . PackageFile , error ) {
return createPackageAndAddFile ( ctx , pvci , pfci , false )
2022-03-30 10:42:47 +02:00
}
// CreatePackageOrAddFileToExisting creates a package with a file or adds the file if the package exists already
2023-09-25 15:17:37 +02:00
func CreatePackageOrAddFileToExisting ( ctx context . Context , pvci * PackageCreationInfo , pfci * PackageFileCreationInfo ) ( * packages_model . PackageVersion , * packages_model . PackageFile , error ) {
return createPackageAndAddFile ( ctx , pvci , pfci , true )
2022-03-30 10:42:47 +02:00
}
2023-09-25 15:17:37 +02:00
func createPackageAndAddFile ( ctx context . Context , pvci * PackageCreationInfo , pfci * PackageFileCreationInfo , allowDuplicate bool ) ( * packages_model . PackageVersion , * packages_model . PackageFile , error ) {
dbCtx , committer , err := db . TxContext ( ctx )
2022-03-30 10:42:47 +02:00
if err != nil {
return nil , nil , err
}
defer committer . Close ( )
2023-09-25 15:17:37 +02:00
pv , created , err := createPackageAndVersion ( dbCtx , pvci , allowDuplicate )
2022-03-30 10:42:47 +02:00
if err != nil {
return nil , nil , err
}
2023-09-25 15:17:37 +02:00
pf , pb , blobCreated , err := addFileToPackageVersion ( dbCtx , pv , & pvci . PackageInfo , pfci )
2022-03-30 10:42:47 +02:00
removeBlob := false
defer func ( ) {
if blobCreated && removeBlob {
contentStore := packages_module . NewContentStore ( )
if err := contentStore . Delete ( packages_module . BlobHash256Key ( pb . HashSHA256 ) ) ; err != nil {
log . Error ( "Error deleting package blob from content store: %v" , err )
}
}
} ( )
if err != nil {
removeBlob = true
return nil , nil , err
}
if err := committer . Commit ( ) ; err != nil {
removeBlob = true
return nil , nil , err
}
if created {
2023-09-25 15:17:37 +02:00
pd , err := packages_model . GetPackageDescriptor ( ctx , pv )
2022-03-30 10:42:47 +02:00
if err != nil {
return nil , nil , err
}
2023-09-25 15:17:37 +02:00
notify_service . PackageCreate ( ctx , pvci . Creator , pd )
2022-03-30 10:42:47 +02:00
}
return pv , pf , nil
}
func createPackageAndVersion ( ctx context . Context , pvci * PackageCreationInfo , allowDuplicate bool ) ( * packages_model . PackageVersion , bool , error ) {
2022-07-28 05:59:39 +02:00
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 )
2022-03-30 10:42:47 +02:00
2022-07-28 05:59:39 +02:00
packageCreated := true
2022-03-30 10:42:47 +02:00
p := & packages_model . Package {
OwnerID : pvci . Owner . ID ,
Type : pvci . PackageType ,
Name : pvci . Name ,
LowerName : strings . ToLower ( pvci . Name ) ,
SemverCompatible : pvci . SemverCompatible ,
}
var err error
if p , err = packages_model . TryInsertPackage ( ctx , p ) ; err != nil {
2022-07-28 05:59:39 +02:00
if err == packages_model . ErrDuplicatePackage {
packageCreated = false
} else {
2022-03-30 10:42:47 +02:00
log . Error ( "Error inserting package: %v" , err )
return nil , false , err
}
}
2022-07-28 05:59:39 +02:00
if packageCreated {
for name , value := range pvci . PackageProperties {
if _ , err := packages_model . InsertProperty ( ctx , packages_model . PropertyTypePackage , p . ID , name , value ) ; err != nil {
log . Error ( "Error setting package property: %v" , err )
return nil , false , err
}
}
}
2022-03-30 10:42:47 +02:00
metadataJSON , err := json . Marshal ( pvci . Metadata )
if err != nil {
return nil , false , err
}
2022-07-28 05:59:39 +02:00
versionCreated := true
2022-03-30 10:42:47 +02:00
pv := & packages_model . PackageVersion {
PackageID : p . ID ,
CreatorID : pvci . Creator . ID ,
Version : pvci . Version ,
LowerVersion : strings . ToLower ( pvci . Version ) ,
MetadataJSON : string ( metadataJSON ) ,
}
if pv , err = packages_model . GetOrInsertVersion ( ctx , pv ) ; err != nil {
if err == packages_model . ErrDuplicatePackageVersion {
2022-07-28 05:59:39 +02:00
versionCreated = false
2024-03-19 16:17:24 +01:00
} else {
2022-03-30 10:42:47 +02:00
log . Error ( "Error inserting package: %v" , err )
return nil , false , err
}
2024-03-19 16:17:24 +01:00
if ! allowDuplicate {
// no need to log an error
return nil , false , err
}
2022-03-30 10:42:47 +02:00
}
2022-07-28 05:59:39 +02:00
if versionCreated {
2023-01-29 18:34:29 +01:00
if err := CheckCountQuotaExceeded ( ctx , pvci . Creator , pvci . Owner ) ; err != nil {
2022-11-09 07:34:27 +01:00
return nil , false , err
}
2022-07-28 05:59:39 +02:00
for name , value := range pvci . VersionProperties {
2022-03-30 10:42:47 +02:00
if _ , err := packages_model . InsertProperty ( ctx , packages_model . PropertyTypeVersion , pv . ID , name , value ) ; err != nil {
log . Error ( "Error setting package version property: %v" , err )
return nil , false , err
}
}
}
2022-07-28 05:59:39 +02:00
return pv , versionCreated , nil
2022-03-30 10:42:47 +02:00
}
// AddFileToExistingPackage adds a file to an existing package. If the package does not exist, ErrPackageNotExist is returned
2023-09-25 15:17:37 +02:00
func AddFileToExistingPackage ( ctx context . Context , pvi * PackageInfo , pfci * PackageFileCreationInfo ) ( * packages_model . PackageFile , error ) {
return addFileToPackageWrapper ( ctx , func ( ctx context . Context ) ( * packages_model . PackageFile , * packages_model . PackageBlob , bool , error ) {
2023-05-02 18:31:35 +02:00
pv , err := packages_model . GetVersionByNameAndVersion ( ctx , pvi . Owner . ID , pvi . PackageType , pvi . Name , pvi . Version )
if err != nil {
return nil , nil , false , err
}
return addFileToPackageVersion ( ctx , pv , pvi , pfci )
} )
}
// AddFileToPackageVersionInternal adds a file to the package
// This method skips quota checks and should only be used for system-managed packages.
2023-09-25 15:17:37 +02:00
func AddFileToPackageVersionInternal ( ctx context . Context , pv * packages_model . PackageVersion , pfci * PackageFileCreationInfo ) ( * packages_model . PackageFile , error ) {
return addFileToPackageWrapper ( ctx , func ( ctx context . Context ) ( * packages_model . PackageFile , * packages_model . PackageBlob , bool , error ) {
2023-05-02 18:31:35 +02:00
return addFileToPackageVersionUnchecked ( ctx , pv , pfci )
} )
}
2023-09-25 15:17:37 +02:00
func addFileToPackageWrapper ( ctx context . Context , fn func ( ctx context . Context ) ( * packages_model . PackageFile , * packages_model . PackageBlob , bool , error ) ) ( * packages_model . PackageFile , error ) {
ctx , committer , err := db . TxContext ( ctx )
2022-03-30 10:42:47 +02:00
if err != nil {
2023-05-02 18:31:35 +02:00
return nil , err
2022-03-30 10:42:47 +02:00
}
defer committer . Close ( )
2023-05-02 18:31:35 +02:00
pf , pb , blobCreated , err := fn ( ctx )
2022-03-30 10:42:47 +02:00
removeBlob := false
defer func ( ) {
if removeBlob {
contentStore := packages_module . NewContentStore ( )
if err := contentStore . Delete ( packages_module . BlobHash256Key ( pb . HashSHA256 ) ) ; err != nil {
log . Error ( "Error deleting package blob from content store: %v" , err )
}
}
} ( )
if err != nil {
removeBlob = blobCreated
2023-05-02 18:31:35 +02:00
return nil , err
2022-03-30 10:42:47 +02:00
}
if err := committer . Commit ( ) ; err != nil {
removeBlob = blobCreated
2023-05-02 18:31:35 +02:00
return nil , err
2022-03-30 10:42:47 +02:00
}
2023-05-02 18:31:35 +02:00
return pf , nil
2022-03-30 10:42:47 +02:00
}
// NewPackageBlob creates a package blob instance
func NewPackageBlob ( hsr packages_module . HashedSizeReader ) * packages_model . PackageBlob {
hashMD5 , hashSHA1 , hashSHA256 , hashSHA512 := hsr . Sums ( )
return & packages_model . PackageBlob {
Size : hsr . Size ( ) ,
2022-11-28 19:19:18 +08:00
HashMD5 : hex . EncodeToString ( hashMD5 ) ,
HashSHA1 : hex . EncodeToString ( hashSHA1 ) ,
HashSHA256 : hex . EncodeToString ( hashSHA256 ) ,
HashSHA512 : hex . EncodeToString ( hashSHA512 ) ,
2022-03-30 10:42:47 +02:00
}
}
2022-11-09 07:34:27 +01:00
func addFileToPackageVersion ( ctx context . Context , pv * packages_model . PackageVersion , pvi * PackageInfo , pfci * PackageFileCreationInfo ) ( * packages_model . PackageFile , * packages_model . PackageBlob , bool , error ) {
2023-01-29 18:34:29 +01:00
if err := CheckSizeQuotaExceeded ( ctx , pfci . Creator , pvi . Owner , pvi . PackageType , pfci . Data . Size ( ) ) ; err != nil {
2022-11-09 07:34:27 +01:00
return nil , nil , false , err
}
2023-05-02 18:31:35 +02:00
return addFileToPackageVersionUnchecked ( ctx , pv , pfci )
}
func addFileToPackageVersionUnchecked ( ctx context . Context , pv * packages_model . PackageVersion , pfci * PackageFileCreationInfo ) ( * packages_model . PackageFile , * packages_model . PackageBlob , bool , error ) {
log . Trace ( "Adding package file: %v, %s" , pv . ID , pfci . Filename )
2022-03-30 10:42:47 +02:00
pb , exists , err := packages_model . GetOrInsertBlob ( ctx , NewPackageBlob ( pfci . Data ) )
if err != nil {
log . Error ( "Error inserting package blob: %v" , err )
return nil , nil , false , err
}
if ! exists {
contentStore := packages_module . NewContentStore ( )
if err := contentStore . Save ( packages_module . BlobHash256Key ( pb . HashSHA256 ) , pfci . Data , pfci . Data . Size ( ) ) ; err != nil {
log . Error ( "Error saving package blob in content store: %v" , err )
return nil , nil , false , err
}
}
if pfci . OverwriteExisting {
pf , err := packages_model . GetFileForVersionByName ( ctx , pv . ID , pfci . Filename , pfci . CompositeKey )
if err != nil && err != packages_model . ErrPackageFileNotExist {
return nil , pb , ! exists , err
}
if pf != nil {
// Short circuit if blob is the same
if pf . BlobID == pb . ID {
return pf , pb , ! exists , nil
}
if err := packages_model . DeleteAllProperties ( ctx , packages_model . PropertyTypeFile , pf . ID ) ; err != nil {
return nil , pb , ! exists , err
}
if err := packages_model . DeleteFileByID ( ctx , pf . ID ) ; err != nil {
return nil , pb , ! exists , err
}
}
}
pf := & packages_model . PackageFile {
VersionID : pv . ID ,
BlobID : pb . ID ,
Name : pfci . Filename ,
LowerName : strings . ToLower ( pfci . Filename ) ,
CompositeKey : pfci . CompositeKey ,
IsLead : pfci . IsLead ,
}
if pf , err = packages_model . TryInsertFile ( ctx , pf ) ; err != nil {
if err != packages_model . ErrDuplicatePackageFile {
log . Error ( "Error inserting package file: %v" , err )
}
return nil , pb , ! exists , err
}
for name , value := range pfci . Properties {
if _ , err := packages_model . InsertProperty ( ctx , packages_model . PropertyTypeFile , pf . ID , name , value ) ; err != nil {
log . Error ( "Error setting package file property: %v" , err )
return pf , pb , ! exists , err
}
}
return pf , pb , ! exists , nil
}
2023-01-29 18:34:29 +01:00
// CheckCountQuotaExceeded checks if the owner has more than the allowed packages
// The check is skipped if the doer is an admin.
func CheckCountQuotaExceeded ( ctx context . Context , doer , owner * user_model . User ) error {
2022-11-09 07:34:27 +01:00
if doer . IsAdmin {
return nil
}
if setting . Packages . LimitTotalOwnerCount > - 1 {
totalCount , err := packages_model . CountVersions ( ctx , & packages_model . PackageSearchOptions {
OwnerID : owner . ID ,
2024-03-02 16:42:31 +01:00
IsInternal : optional . Some ( false ) ,
2022-11-09 07:34:27 +01:00
} )
if err != nil {
log . Error ( "CountVersions failed: %v" , err )
return err
}
if totalCount > setting . Packages . LimitTotalOwnerCount {
return ErrQuotaTotalCount
}
}
return nil
}
2023-01-29 18:34:29 +01:00
// CheckSizeQuotaExceeded checks if the upload size is bigger than the allowed size
// The check is skipped if the doer is an admin.
func CheckSizeQuotaExceeded ( ctx context . Context , doer , owner * user_model . User , packageType packages_model . Type , uploadSize int64 ) error {
2022-11-09 07:34:27 +01:00
if doer . IsAdmin {
return nil
}
var typeSpecificSize int64
switch packageType {
2023-05-12 19:27:50 +02:00
case packages_model . TypeAlpine :
typeSpecificSize = setting . Packages . LimitSizeAlpine
2023-02-05 11:12:31 +01:00
case packages_model . TypeCargo :
typeSpecificSize = setting . Packages . LimitSizeCargo
2023-02-06 02:49:21 +01:00
case packages_model . TypeChef :
typeSpecificSize = setting . Packages . LimitSizeChef
2022-11-09 07:34:27 +01:00
case packages_model . TypeComposer :
typeSpecificSize = setting . Packages . LimitSizeComposer
case packages_model . TypeConan :
typeSpecificSize = setting . Packages . LimitSizeConan
2023-02-01 19:30:39 +01:00
case packages_model . TypeConda :
typeSpecificSize = setting . Packages . LimitSizeConda
2022-11-09 07:34:27 +01:00
case packages_model . TypeContainer :
typeSpecificSize = setting . Packages . LimitSizeContainer
2023-05-22 04:57:49 +02:00
case packages_model . TypeCran :
typeSpecificSize = setting . Packages . LimitSizeCran
2023-05-02 18:31:35 +02:00
case packages_model . TypeDebian :
typeSpecificSize = setting . Packages . LimitSizeDebian
2022-11-09 07:34:27 +01:00
case packages_model . TypeGeneric :
typeSpecificSize = setting . Packages . LimitSizeGeneric
2023-05-14 17:38:40 +02:00
case packages_model . TypeGo :
typeSpecificSize = setting . Packages . LimitSizeGo
2022-11-09 07:34:27 +01:00
case packages_model . TypeHelm :
typeSpecificSize = setting . Packages . LimitSizeHelm
case packages_model . TypeMaven :
typeSpecificSize = setting . Packages . LimitSizeMaven
case packages_model . TypeNpm :
typeSpecificSize = setting . Packages . LimitSizeNpm
case packages_model . TypeNuGet :
typeSpecificSize = setting . Packages . LimitSizeNuGet
case packages_model . TypePub :
typeSpecificSize = setting . Packages . LimitSizePub
case packages_model . TypePyPI :
typeSpecificSize = setting . Packages . LimitSizePyPI
2023-05-05 22:33:37 +02:00
case packages_model . TypeRpm :
typeSpecificSize = setting . Packages . LimitSizeRpm
2022-11-09 07:34:27 +01:00
case packages_model . TypeRubyGems :
typeSpecificSize = setting . Packages . LimitSizeRubyGems
2023-03-13 21:28:39 +01:00
case packages_model . TypeSwift :
typeSpecificSize = setting . Packages . LimitSizeSwift
2022-11-09 07:34:27 +01:00
case packages_model . TypeVagrant :
typeSpecificSize = setting . Packages . LimitSizeVagrant
}
if typeSpecificSize > - 1 && typeSpecificSize < uploadSize {
return ErrQuotaTypeSize
}
if setting . Packages . LimitTotalOwnerSize > - 1 {
2023-11-14 23:03:56 +08:00
totalSize , err := packages_model . CalculateFileSize ( ctx , & packages_model . PackageFileSearchOptions {
OwnerID : owner . ID ,
} )
2022-11-09 07:34:27 +01:00
if err != nil {
2023-01-18 23:52:04 +08:00
log . Error ( "CalculateFileSize failed: %v" , err )
2022-11-09 07:34:27 +01:00
return err
}
if totalSize + uploadSize > setting . Packages . LimitTotalOwnerSize {
return ErrQuotaTotalSize
}
}
return nil
}
2023-05-05 22:33:37 +02:00
// GetOrCreateInternalPackageVersion gets or creates an internal package
// Some package types need such internal packages for housekeeping.
2023-09-25 15:17:37 +02:00
func GetOrCreateInternalPackageVersion ( ctx context . Context , ownerID int64 , packageType packages_model . Type , name , version string ) ( * packages_model . PackageVersion , error ) {
2023-05-05 22:33:37 +02:00
var pv * packages_model . PackageVersion
2023-09-25 15:17:37 +02:00
return pv , db . WithTx ( ctx , func ( ctx context . Context ) error {
2023-05-05 22:33:37 +02:00
p := & packages_model . Package {
OwnerID : ownerID ,
Type : packageType ,
Name : name ,
LowerName : name ,
IsInternal : true ,
}
var err error
if p , err = packages_model . TryInsertPackage ( ctx , p ) ; err != nil {
if err != packages_model . ErrDuplicatePackage {
log . Error ( "Error inserting package: %v" , err )
return err
}
}
pv = & packages_model . PackageVersion {
PackageID : p . ID ,
CreatorID : ownerID ,
Version : version ,
LowerVersion : version ,
IsInternal : true ,
MetadataJSON : "null" ,
}
if pv , err = packages_model . GetOrInsertVersion ( ctx , pv ) ; err != nil {
if err != packages_model . ErrDuplicatePackageVersion {
log . Error ( "Error inserting package version: %v" , err )
return err
}
}
return nil
} )
}
2022-03-30 10:42:47 +02:00
// RemovePackageVersionByNameAndVersion deletes a package version and all associated files
2023-09-25 15:17:37 +02:00
func RemovePackageVersionByNameAndVersion ( ctx context . Context , doer * user_model . User , pvi * PackageInfo ) error {
pv , err := packages_model . GetVersionByNameAndVersion ( ctx , pvi . Owner . ID , pvi . PackageType , pvi . Name , pvi . Version )
2022-03-30 10:42:47 +02:00
if err != nil {
return err
}
2023-09-25 15:17:37 +02:00
return RemovePackageVersion ( ctx , doer , pv )
2022-03-30 10:42:47 +02:00
}
// RemovePackageVersion deletes the package version and all associated files
2023-09-25 15:17:37 +02:00
func RemovePackageVersion ( ctx context . Context , doer * user_model . User , pv * packages_model . PackageVersion ) error {
dbCtx , committer , err := db . TxContext ( ctx )
2022-03-30 10:42:47 +02:00
if err != nil {
return err
}
defer committer . Close ( )
2023-09-25 15:17:37 +02:00
pd , err := packages_model . GetPackageDescriptor ( dbCtx , pv )
2022-03-30 10:42:47 +02:00
if err != nil {
return err
}
log . Trace ( "Deleting package: %v" , pv . ID )
2023-09-25 15:17:37 +02:00
if err := DeletePackageVersionAndReferences ( dbCtx , pv ) ; err != nil {
2022-03-30 10:42:47 +02:00
return err
}
if err := committer . Commit ( ) ; err != nil {
return err
}
2023-09-25 15:17:37 +02:00
notify_service . PackageDelete ( ctx , doer , pd )
2022-03-30 10:42:47 +02:00
return nil
}
2023-05-12 19:27:50 +02:00
// RemovePackageFileAndVersionIfUnreferenced deletes the package file and the version if there are no referenced files afterwards
2023-09-25 15:17:37 +02:00
func RemovePackageFileAndVersionIfUnreferenced ( ctx context . Context , doer * user_model . User , pf * packages_model . PackageFile ) error {
2023-05-12 19:27:50 +02:00
var pd * packages_model . PackageDescriptor
2023-09-25 15:17:37 +02:00
if err := db . WithTx ( ctx , func ( ctx context . Context ) error {
2023-05-12 19:27:50 +02:00
if err := DeletePackageFile ( ctx , pf ) ; err != nil {
return err
}
has , err := packages_model . HasVersionFileReferences ( ctx , pf . VersionID )
if err != nil {
return err
}
if ! has {
pv , err := packages_model . GetVersionByID ( ctx , pf . VersionID )
if err != nil {
return err
}
pd , err = packages_model . GetPackageDescriptor ( ctx , pv )
if err != nil {
return err
}
if err := DeletePackageVersionAndReferences ( ctx , pv ) ; err != nil {
return err
}
}
return nil
} ) ; err != nil {
return err
}
if pd != nil {
2023-09-25 15:17:37 +02:00
notify_service . PackageDelete ( ctx , doer , pd )
2023-05-12 19:27:50 +02:00
}
return nil
}
2022-03-30 10:42:47 +02:00
// DeletePackageVersionAndReferences deletes the package version and its properties and files
func DeletePackageVersionAndReferences ( ctx context . Context , pv * packages_model . PackageVersion ) error {
if err := packages_model . DeleteAllProperties ( ctx , packages_model . PropertyTypeVersion , pv . ID ) ; err != nil {
return err
}
pfs , err := packages_model . GetFilesByVersionID ( ctx , pv . ID )
if err != nil {
return err
}
for _ , pf := range pfs {
if err := DeletePackageFile ( ctx , pf ) ; err != nil {
return err
}
}
return packages_model . DeleteVersionByID ( ctx , pv . ID )
}
// DeletePackageFile deletes the package file and its properties
func DeletePackageFile ( ctx context . Context , pf * packages_model . PackageFile ) error {
if err := packages_model . DeleteAllProperties ( ctx , packages_model . PropertyTypeFile , pf . ID ) ; err != nil {
return err
}
return packages_model . DeleteFileByID ( ctx , pf . ID )
}
// GetFileStreamByPackageNameAndVersion returns the content of the specific package file
2023-07-03 15:33:28 +02:00
func GetFileStreamByPackageNameAndVersion ( ctx context . Context , pvi * PackageInfo , pfi * PackageFileInfo ) ( io . ReadSeekCloser , * url . URL , * packages_model . PackageFile , error ) {
2022-03-30 10:42:47 +02:00
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 )
pv , err := packages_model . GetVersionByNameAndVersion ( ctx , pvi . Owner . ID , pvi . PackageType , pvi . Name , pvi . Version )
if err != nil {
if err == packages_model . ErrPackageNotExist {
2023-07-03 15:33:28 +02:00
return nil , nil , nil , err
2022-03-30 10:42:47 +02:00
}
log . Error ( "Error getting package: %v" , err )
2023-07-03 15:33:28 +02:00
return nil , nil , nil , err
2022-03-30 10:42:47 +02:00
}
return GetFileStreamByPackageVersion ( ctx , pv , pfi )
}
// GetFileStreamByPackageVersion returns the content of the specific package file
2023-07-03 15:33:28 +02:00
func GetFileStreamByPackageVersion ( ctx context . Context , pv * packages_model . PackageVersion , pfi * PackageFileInfo ) ( io . ReadSeekCloser , * url . URL , * packages_model . PackageFile , error ) {
2022-08-04 23:14:47 +02:00
pf , err := packages_model . GetFileForVersionByName ( ctx , pv . ID , pfi . Filename , pfi . CompositeKey )
2022-03-30 10:42:47 +02:00
if err != nil {
2023-07-03 15:33:28 +02:00
return nil , nil , nil , err
2022-03-30 10:42:47 +02:00
}
2022-04-01 01:08:32 +02:00
return GetPackageFileStream ( ctx , pf )
2022-03-30 10:42:47 +02:00
}
// GetPackageFileStream returns the content of the specific package file
2023-07-03 15:33:28 +02:00
func GetPackageFileStream ( ctx context . Context , pf * packages_model . PackageFile ) ( io . ReadSeekCloser , * url . URL , * packages_model . PackageFile , error ) {
2022-03-30 10:42:47 +02:00
pb , err := packages_model . GetBlobByID ( ctx , pf . BlobID )
if err != nil {
2023-07-03 15:33:28 +02:00
return nil , nil , nil , err
}
return GetPackageBlobStream ( ctx , pf , pb )
}
// GetPackageBlobStream returns the content of the specific package blob
// If the storage supports direct serving and it's enabled, only the direct serving url is returned.
func GetPackageBlobStream ( ctx context . Context , pf * packages_model . PackageFile , pb * packages_model . PackageBlob ) ( io . ReadSeekCloser , * url . URL , * packages_model . PackageFile , error ) {
key := packages_module . BlobHash256Key ( pb . HashSHA256 )
cs := packages_module . NewContentStore ( )
var s io . ReadSeekCloser
var u * url . URL
var err error
if cs . ShouldServeDirect ( ) {
u , err = cs . GetServeDirectURL ( key , pf . Name )
if err != nil && ! errors . Is ( err , storage . ErrURLNotSupported ) {
log . Error ( "Error getting serve direct url: %v" , err )
}
}
if u == nil {
s , err = cs . Get ( key )
2022-03-30 10:42:47 +02:00
}
if err == nil {
if pf . IsLead {
2022-04-01 01:08:32 +02:00
if err := packages_model . IncrementDownloadCounter ( ctx , pf . VersionID ) ; err != nil {
2022-03-30 10:42:47 +02:00
log . Error ( "Error incrementing download counter: %v" , err )
}
}
}
2023-07-03 15:33:28 +02:00
return s , u , pf , err
2022-03-30 10:42:47 +02:00
}
2022-07-14 08:22:09 +01:00
// RemoveAllPackages for User
func RemoveAllPackages ( ctx context . Context , userID int64 ) ( int , error ) {
count := 0
for {
pkgVersions , _ , err := packages_model . SearchVersions ( ctx , & packages_model . PackageSearchOptions {
Paginator : & db . ListOptions {
PageSize : repo_model . RepositoryListDefaultPageSize ,
Page : 1 ,
} ,
2022-07-27 03:59:10 +02:00
OwnerID : userID ,
2024-03-02 16:42:31 +01:00
IsInternal : optional . None [ bool ] ( ) ,
2022-07-14 08:22:09 +01:00
} )
if err != nil {
return count , fmt . Errorf ( "GetOwnedPackages[%d]: %w" , userID , err )
}
if len ( pkgVersions ) == 0 {
break
}
for _ , pv := range pkgVersions {
if err := DeletePackageVersionAndReferences ( ctx , pv ) ; err != nil {
return count , fmt . Errorf ( "unable to delete package %d:%s[%d]. Error: %w" , pv . PackageID , pv . Version , pv . ID , err )
}
count ++
}
}
return count , nil
}