diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 2ffa130751..e9a7496a98 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -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", diff --git a/routers/web/user/package.go b/routers/web/user/package.go index ffbfaa229b..ab46f284d3 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -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 + + if err := packages_service.RemovePackage(ctx, ctx.Doer, 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")) + } else { + ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) + } + 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) diff --git a/routers/web/web.go b/routers/web/web.go index 72d2c27eaf..32aa4368cc 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1072,16 +1072,18 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) { m.Get("", user.ListPackages) m.Group("/{type}/{name}", func() { m.Get("", user.RedirectToLastVersion) - m.Get("/versions", user.ListPackageVersions) - m.Group("/{version}", func() { - m.Get("", user.ViewPackageVersion) - 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)) - }) + // use `~` as a separator; otherwise it might clash with a user package named `-` or `settings` + m.Group("/~/settings", func() { + 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) }) }, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) } diff --git a/services/context/package.go b/services/context/package.go index 0e9210515b..4aee80b8a6 100644 --- a/services/context/package.go +++ b/services/context/package.go @@ -70,22 +70,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 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 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, + } } } diff --git a/services/packages/packages.go b/services/packages/packages.go index 3b4e11e041..85269dbe32 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -629,6 +629,25 @@ 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, doer *user_model.User, p *packages_model.Package) error { + pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ + PackageID: p.ID, + IsInternal: optional.None[bool](), + }) + if err != nil { + return err + } + + for _, pv := range pvs { + if err := RemovePackageVersion(ctx, doer, pv); err != nil { + return err + } + } + + return packages_model.DeletePackageByID(ctx, p.ID) +} + // RemoveAllPackages for User func RemoveAllPackages(ctx context.Context, userID int64) (int, error) { count := 0 diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index 0124f26820..4545575618 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -10,7 +10,7 @@ {{template "user/overview/header" .}} {{end}} {{template "base/alert" .}} -

{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}}) / {{ctx.Locale.Tr "repo.settings"}}

+

{{.PackageDescriptor.Package.Name}} / {{ctx.Locale.Tr "repo.settings"}}

{{ctx.Locale.Tr "packages.settings.link"}}

@@ -45,7 +45,7 @@
- {{ctx.Locale.Tr "packages.settings.delete.notice" .PackageDescriptor.Package.Name .PackageDescriptor.Version.Version}} + {{ctx.Locale.Tr "packages.settings.delete.notice.package" .PackageDescriptor.Package.Name}}
diff --git a/templates/package/shared/view.tmpl b/templates/package/shared/view.tmpl index cfdc114e1b..6e0b08fb00 100644 --- a/templates/package/shared/view.tmpl +++ b/templates/package/shared/view.tmpl @@ -99,7 +99,24 @@
{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues"}}
{{end}} {{if .CanWritePackages}} -
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
+
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
+
+ {{svg "octicon-trash"}} + {{ctx.Locale.Tr "packages.settings.delete"}} + +
{{end}}
{{end}}