Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 20 additions & 2 deletions models/packages/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"net/url"

"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
Expand Down Expand Up @@ -53,8 +54,11 @@ func (l PackagePropertyList) GetByName(name string) string {

// PackageDescriptor describes a package
type PackageDescriptor struct {
Package *Package
Owner *user_model.User
// basic package info
Package *Package
Owner *user_model.User

// package version info
Repository *repo_model.Repository
Version *PackageVersion
SemVer *version.Version
Expand All @@ -77,6 +81,11 @@ func (pd *PackageDescriptor) PackageWebLink() string {
return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
}

// PackageSettingsLink returns the relative package settings link
func (pd *PackageDescriptor) PackageSettingsLink() string {
return fmt.Sprintf("%s/-/packages/settings/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
}

// VersionWebLink returns the relative package version web link
func (pd *PackageDescriptor) VersionWebLink() string {
return fmt.Sprintf("%s/%s", pd.PackageWebLink(), url.PathEscape(pd.Version.LowerVersion))
Expand Down Expand Up @@ -267,6 +276,15 @@ func GetPackageDescriptors(ctx context.Context, pvs []*PackageVersion) ([]*Packa
return getPackageDescriptors(ctx, pvs, cache.NewEphemeralCache())
}

// GetAllPackageDescriptors gets all package descriptors for a package
func GetAllPackageDescriptors(ctx context.Context, p *Package) ([]*PackageDescriptor, error) {
pvs := make([]*PackageVersion, 0, 10)
if err := db.GetEngine(ctx).Where("package_id = ?", p.ID).Find(&pvs); err != nil {
return nil, err
}
return getPackageDescriptors(ctx, pvs, cache.NewEphemeralCache())
}

func getPackageDescriptors(ctx context.Context, pvs []*PackageVersion, c *cache.EphemeralCache) ([]*PackageDescriptor, error) {
pds := make([]*PackageDescriptor, 0, len(pvs))
for _, pv := range pvs {
Expand Down
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
41 changes: 41 additions & 0 deletions models/packages/package_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package packages

import (
"context"
"errors"

"code.gitea.io/gitea/models/db"

Expand Down Expand Up @@ -86,6 +87,46 @@ 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")
default:
return errors.New("invalid ref type")
}

_, 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
6 changes: 6 additions & 0 deletions options/locale/locale_en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3506,6 +3506,7 @@
"packages.dependencies": "Dependencies",
"packages.keywords": "Keywords",
"packages.details": "Details",
"packages.name": "Package Name",
"packages.details.author": "Author",
"packages.details.project_site": "Project Site",
"packages.details.repository_site": "Repository Site",
Expand Down Expand Up @@ -3614,8 +3615,13 @@
"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.version.success": "The package version has been deleted.",
"packages.settings.delete.error": "Failed to delete the package.",
"packages.settings.delete.version": "Delete version",
"packages.settings.delete.confirm": "Enter package name to confirm",
"packages.settings.delete.invalid_package_name": "The package name you entered is incorrect.",
"packages.owner.settings.cargo.title": "Cargo Registry Index",
"packages.owner.settings.cargo.initialize": "Initialize Index",
"packages.owner.settings.cargo.initialize.description": "A special index Git repository is needed to use the Cargo registry. Using this option will (re-)create the repository and configure it automatically.",
Expand Down
3 changes: 2 additions & 1 deletion routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1578,10 +1578,11 @@ func Routes() *web.Router {
m.Group("/packages/{username}", func() {
m.Group("/{type}/{name}", func() {
m.Get("/", packages.ListPackageVersions)
m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)

m.Group("/{version}", func() {
m.Get("", packages.GetPackage)
m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackageVersion)
m.Get("/files", packages.ListPackageFiles)
})

Expand Down
37 changes: 36 additions & 1 deletion routers/api/v1/packages/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func GetPackage(ctx *context.APIContext) {

// DeletePackage deletes a package
func DeletePackage(ctx *context.APIContext) {
// swagger:operation DELETE /packages/{owner}/{type}/{name}/{version} package deletePackage
// swagger:operation DELETE /packages/{owner}/{type}/{name} package deletePackage
// ---
// summary: Delete a package
// parameters:
Expand All @@ -137,6 +137,41 @@ func DeletePackage(ctx *context.APIContext) {
// description: name of the package
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"

err := packages_service.RemovePackage(ctx, ctx.Doer, ctx.Package.Descriptor.Package)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}

// DeletePackageVersion deletes a package version
func DeletePackageVersion(ctx *context.APIContext) {
// swagger:operation DELETE /packages/{owner}/{type}/{name}/{version} package deletePackageVersion
// ---
// summary: Delete a package version
// parameters:
// - name: owner
// in: path
// description: owner of the package
// type: string
// required: true
// - name: type
// in: path
// description: type of the package
// type: string
// required: true
// - name: name
// in: path
// description: name of the package
// type: string
// required: true
// - name: version
// in: path
// description: version of the package
Expand Down
2 changes: 1 addition & 1 deletion routers/web/admin/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func DeletePackageVersion(ctx *context.Context) {
return
}

ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
ctx.Flash.Success(ctx.Tr("packages.settings.delete.version.success"))
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type")))
}

Expand Down
35 changes: 30 additions & 5 deletions routers/web/user/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,18 +491,43 @@ 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 ctx.FormString("package_name") != pd.Package.Name {
ctx.Flash.Error(ctx.Tr("packages.settings.delete.invalid_package_name"))
ctx.Redirect(pd.PackageSettingsLink())
return
}

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"))
Comment thread
wxiaoguang marked this conversation as resolved.
} else {
ctx.Flash.Success(ctx.Tr("packages.settings.delete.version.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 +537,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
9 changes: 5 additions & 4 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1071,14 +1071,15 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
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))
})
})
m.Group("/settings/{type}/{name}", func() {
m.Get("", user.PackageSettings)
m.Post("", web.Bind(forms.PackageSettingForm{}), user.PackageSettingsPost)
}, reqPackageAccess(perm.AccessModeWrite))
}, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
}

Expand Down
Loading