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