Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions models/packages/package_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ func DeleteFileByID(ctx context.Context, fileID int64) error {
return err
}

// DeleteFilesByPackageID deletes all files of a specific package
// Versions must not be deleted prior to this call
func DeleteFilesByPackageID(ctx context.Context, packageID int64) error {
deleteStmt := builder.Delete(builder.In("version_id", builder.Select("package_version.id").From("package_version").Where(builder.Eq{"package_id": packageID}))).From("package_file")
_, err := db.GetEngine(ctx).Exec(deleteStmt)
return err
}

// DeleteFilesByVersionID deletes all files of a specific version
func DeleteFilesByVersionID(ctx context.Context, versionID int64) error {
_, err := db.GetEngine(ctx).Where("version_id = ?", versionID).Delete(&PackageFile{})
return err
}

func UpdateFile(ctx context.Context, pf *PackageFile, cols []string) error {
_, err := db.GetEngine(ctx).ID(pf.ID).Cols(cols...).Update(pf)
return err
Expand Down
38 changes: 38 additions & 0 deletions models/packages/package_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,44 @@ func DeleteAllProperties(ctx context.Context, refType PropertyType, refID int64)
return err
}

// DeletePropertiesByPackageID deletes properties of a typed linked to the package
// Use to avoid for loops in mass deletion of properties
func DeletePropertiesByPackageID(ctx context.Context, refType PropertyType, packageID int64) error {
Comment thread
TheFox0x7 marked this conversation as resolved.
var deleteStmt *builder.Builder

switch refType {
case PropertyTypeFile:
deleteStmt = builder.Delete(
// Delete all properties that are attached to a file and are in ids from a subquery
// which returns ids from the package_file table joined on package_version to link it with package id
builder.Eq{"ref_type": PropertyTypeFile}, builder.In("ref_id",
builder.Select("package_file.id").From("package_file").
LeftJoin("package_version", "package_file.version_id = package_version.id").
Where(builder.Eq{"package_version.package_id": packageID}))).From("package_property")
case PropertyTypeVersion:
// Delete all properties that are attached to a version and are in ids from subquery to the package_version filtered by package id
deleteStmt = builder.Delete(
builder.Eq{"ref_type": PropertyTypeVersion}, builder.In("ref_id",
builder.Select("package_version.id").From("package_version").
Where(builder.Eq{"package_version.package_id": packageID}))).From("package_property")
case PropertyTypePackage:
// Delete all properties that are attached to a package and their reference links to the given package ID
deleteStmt = builder.Delete(
builder.Eq{"ref_type": PropertyTypePackage}, builder.Eq{"ref_id": packageID}).
From("package_property")
}

_, err := db.GetEngine(ctx).Exec(deleteStmt)
return err
}

// DeleteFilePropertiesByVersionID deletes all file properties linked to specific version
func DeleteFilePropertiesByVersionID(ctx context.Context, versionID int64) error {
deleteStmt := builder.Delete(builder.Eq{"ref_type": PropertyTypeFile}, builder.In("ref_id", builder.Select("id").From("package_file").Where(builder.Eq{"version_id": versionID}))).From("package_property")
_, err := db.GetEngine(ctx).Exec(deleteStmt)
return err
}

// DeletePropertyByID deletes a property
func DeletePropertyByID(ctx context.Context, propertyID int64) error {
_, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{})
Expand Down
6 changes: 6 additions & 0 deletions models/packages/package_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ func DeleteVersionByID(ctx context.Context, versionID int64) error {
return err
}

// DeleteVersionsByPackageID deletes all versions of a specific package
func DeleteVersionsByPackageID(ctx context.Context, packageID int64) error {
_, err := db.GetEngine(ctx).Where(builder.Eq{"package_id": packageID}).Delete(&PackageVersion{})
return err
}

// HasVersionFileReferences checks if there are associated files
func HasVersionFileReferences(ctx context.Context, versionID int64) (bool, error) {
return db.GetEngine(ctx).Get(&PackageFile{
Expand Down
1 change: 1 addition & 0 deletions options/locale/locale_en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3607,6 +3607,7 @@
"packages.settings.delete": "Delete package",
"packages.settings.delete.description": "Deleting a package is permanent and cannot be undone.",
"packages.settings.delete.notice": "You are about to delete %s (%s). This operation is irreversible, are you sure?",
"packages.settings.delete.notice.package": "You are about to delete %s and all its versions. This operation is irreversible, are you sure?",
"packages.settings.delete.success": "The package has been deleted.",
"packages.settings.delete.error": "Failed to delete the package.",
"packages.owner.settings.cargo.title": "Cargo Registry Index",
Expand Down
29 changes: 24 additions & 5 deletions routers/web/user/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,18 +491,37 @@ func packageSettingsPostActionLink(ctx *context.Context, form *forms.PackageSett
}

func packageSettingsPostActionDelete(ctx *context.Context) {
err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version)
if err != nil {
pd := ctx.Package.Descriptor
Comment thread
TheFox0x7 marked this conversation as resolved.

if err := packages_service.RemovePackage(ctx, pd.Package); err != nil {
log.Error("Error deleting package: %v", err)
ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
} else {
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
}

ctx.Redirect(ctx.Package.Owner.HomeLink() + "/-/packages")
}

// PackageVersionDelete deletes a package version
func PackageVersionDelete(ctx *context.Context) {
pd := ctx.Package.Descriptor
if pd.Version == nil {
ctx.NotFound(nil)
return
}

if err := packages_service.RemovePackageVersion(ctx, ctx.Doer, pd.Version); err != nil {
log.Error("Error deleting package version: %v", err)
ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
Comment thread
wxiaoguang marked this conversation as resolved.
} else {
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
}
Comment thread
TheFox0x7 marked this conversation as resolved.

redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
// redirect to the package if there are still versions available
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has {
redirectURL = ctx.Package.Descriptor.PackageWebLink()
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: pd.Package.ID, IsInternal: optional.Some(false)}); has {
redirectURL = pd.PackageWebLink()
}

ctx.Redirect(redirectURL)
Expand All @@ -512,7 +531,7 @@ func packageSettingsPostActionDelete(ctx *context.Context) {
func DownloadPackageFile(ctx *context.Context) {
pf, err := packages_model.GetFileForVersionByID(ctx, ctx.Package.Descriptor.Version.ID, ctx.PathParamInt64("fileid"))
if err != nil {
if err == packages_model.ErrPackageFileNotExist {
if errors.Is(err, packages_model.ErrPackageFileNotExist) {
ctx.NotFound(err)
} else {
ctx.ServerError("GetFileForVersionByID", err)
Expand Down
10 changes: 6 additions & 4 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,15 +1072,17 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Get("", user.ListPackages)
m.Group("/{type}/{name}", func() {
m.Get("", user.RedirectToLastVersion)
// use `~` as a separator; otherwise it might clash with a user package named `-` or `settings`
m.Group("/~/settings", func() {
Comment thread
TheFox0x7 marked this conversation as resolved.
Outdated
m.Get("", user.PackageSettings)
m.Post("", web.Bind(forms.PackageSettingForm{}), user.PackageSettingsPost)
}, reqPackageAccess(perm.AccessModeWrite))
m.Get("/versions", user.ListPackageVersions)
m.Group("/{version}", func() {
m.Get("", user.ViewPackageVersion)
m.Post("", reqPackageAccess(perm.AccessModeWrite), user.PackageVersionDelete)
m.Get("/{version_sub}", user.ViewPackageVersion)
m.Get("/files/{fileid}", user.DownloadPackageFile)
m.Group("/settings", func() {
m.Get("", user.PackageSettings)
m.Post("", web.Bind(forms.PackageSettingForm{}), user.PackageSettingsPost)
}, reqPackageAccess(perm.AccessModeWrite))
})
})
}, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
Expand Down
46 changes: 32 additions & 14 deletions services/context/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package context

import (
"errors"
"fmt"
"net/http"

Expand Down Expand Up @@ -70,22 +71,39 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, any)) *Package

packageType := ctx.PathParam("type")
name := ctx.PathParam("name")
version := ctx.PathParam("version")
if packageType != "" && name != "" && version != "" {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
if err != nil {
if err == packages_model.ErrPackageNotExist {
errCb(http.StatusNotFound, fmt.Errorf("GetVersionByNameAndVersion: %w", err))
} else {
errCb(http.StatusInternalServerError, fmt.Errorf("GetVersionByNameAndVersion: %w", err))
if packageType != "" && name != "" {
version := ctx.PathParam("version")
if version != "" {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
if err != nil {
if errors.Is(err, packages_model.ErrPackageNotExist) {
errCb(http.StatusNotFound, fmt.Errorf("GetVersionByNameAndVersion: %w", err))
} else {
errCb(http.StatusInternalServerError, fmt.Errorf("GetVersionByNameAndVersion: %w", err))
}
return pkg
}
return pkg
}

pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
errCb(http.StatusInternalServerError, fmt.Errorf("GetPackageDescriptor: %w", err))
return pkg
pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
errCb(http.StatusInternalServerError, fmt.Errorf("GetPackageDescriptor: %w", err))
return pkg
}
} else {
p, err := packages_model.GetPackageByName(ctx, pkg.Owner.ID, packages_model.Type(packageType), name)
if err != nil {
if errors.Is(err, packages_model.ErrPackageNotExist) {
errCb(http.StatusNotFound, fmt.Errorf("GetPackageByName: %w", err))
} else {
errCb(http.StatusInternalServerError, fmt.Errorf("GetPackageByName: %w", err))
}
return pkg
}

pkg.Descriptor = &packages_model.PackageDescriptor{
Package: p,
Owner: pkg.Owner,
}
}
}

Expand Down
39 changes: 31 additions & 8 deletions services/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,16 +532,11 @@ func DeletePackageVersionAndReferences(ctx context.Context, pv *packages_model.P
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 {
if err := packages_model.DeleteFilePropertiesByVersionID(ctx, pv.ID); err != nil {
return err
}

for _, pf := range pfs {
if err := DeletePackageFile(ctx, pf); err != nil {
return err
}
if err := packages_model.DeleteFilesByVersionID(ctx, pv.ID); err != nil {
return err
}

return packages_model.DeleteVersionByID(ctx, pv.ID)
Comment thread
TheFox0x7 marked this conversation as resolved.
Expand Down Expand Up @@ -629,6 +624,34 @@ func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb
return s, u, pf, nil
}

// RemovePackage deletes the package and all its versions
func RemovePackage(ctx context.Context, p *packages_model.Package) error {
Comment thread
TheFox0x7 marked this conversation as resolved.
Outdated
return db.WithTx(ctx, func(ctx context.Context) error {
err := packages_model.DeletePropertiesByPackageID(ctx, packages_model.PropertyTypePackage, p.ID)
if err != nil {
return err
}
err = packages_model.DeletePropertiesByPackageID(ctx, packages_model.PropertyTypeFile, p.ID)
if err != nil {
return err
}
err = packages_model.DeletePropertiesByPackageID(ctx, packages_model.PropertyTypeVersion, p.ID)
if err != nil {
return err
}
err = packages_model.DeleteFilesByPackageID(ctx, p.ID)
if err != nil {
return err
}
err = packages_model.DeleteVersionsByPackageID(ctx, p.ID)
if err != nil {
return err
}

return packages_model.DeletePackageByID(ctx, p.ID)
})
Comment thread
wxiaoguang marked this conversation as resolved.
}

Comment thread
TheFox0x7 marked this conversation as resolved.
// RemoveAllPackages for User
func RemoveAllPackages(ctx context.Context, userID int64) (int, error) {
Comment thread
TheFox0x7 marked this conversation as resolved.
count := 0
Expand Down
4 changes: 2 additions & 2 deletions templates/package/settings.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{{template "user/overview/header" .}}
{{end}}
{{template "base/alert" .}}
<p><a href="{{.PackageDescriptor.VersionWebLink}}">{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</a> / <strong>{{ctx.Locale.Tr "repo.settings"}}</strong></p>
<p><a href="{{.PackageDescriptor.PackageWebLink}}">{{.PackageDescriptor.Package.Name}}</a> / <strong>{{ctx.Locale.Tr "repo.settings"}}</strong></p>
<h4 class="ui top attached header">
{{ctx.Locale.Tr "packages.settings.link"}}
</h4>
Expand Down Expand Up @@ -45,7 +45,7 @@
</div>
<div class="content">
<div class="ui warning message tw-break-anywhere">
{{ctx.Locale.Tr "packages.settings.delete.notice" .PackageDescriptor.Package.Name .PackageDescriptor.Version.Version}}
{{ctx.Locale.Tr "packages.settings.delete.notice.package" .PackageDescriptor.Package.Name}}
</div>
<form class="ui form" action="{{.Link}}" method="post">
<input type="hidden" name="action" value="delete">
Expand Down
19 changes: 18 additions & 1 deletion templates/package/shared/view.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,24 @@
<div class="item">{{svg "octicon-issue-opened"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{ctx.Locale.Tr "repo.issues"}}</a></div>
{{end}}
{{if .CanWritePackages}}
<div class="item">{{svg "octicon-tools"}} <a href="{{$packageVersionLink}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
<div class="item">{{svg "octicon-tools"}} <a href="{{.PackageDescriptor.PackageWebLink}}/~/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
<div class="item">
{{svg "octicon-trash"}}
<a class="show-modal" data-modal="#delete-package-version-modal">{{ctx.Locale.Tr "packages.settings.delete"}}</a>
<div class="ui tiny modal" id="delete-package-version-modal">
<div class="header">
{{ctx.Locale.Tr "packages.settings.delete"}}
</div>
<div class="content">
<div class="ui warning message tw-break-anywhere">
{{ctx.Locale.Tr "packages.settings.delete.notice" .PackageDescriptor.Package.Name .PackageDescriptor.Version.Version}}
</div>
<form class="ui form" action="{{$packageVersionLink}}" method="post">
{{template "base/modal_actions_confirm" .}}
</form>
</div>
</div>
</div>
{{end}}
</div>
{{end}}
Expand Down