2
0
Files
gitcaddy-server/routers/web/admin/packages.go
logikonline b2adcdf969
Some checks failed
Build and Release / Create Release (push) Successful in 0s
Build and Release / Unit Tests (push) Successful in 3m12s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 5m8s
Build and Release / Lint (push) Successful in 5m19s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 3m11s
Build and Release / Build Binaries (amd64, darwin, macos) (push) Successful in 5m38s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 9h5m9s
Build and Release / Build Binary (linux/arm64) (push) Successful in 8m29s
Build and Release / Build Binaries (arm64, darwin, macos) (push) Failing after 10m38s
feat(packages): add bulk visibility management for packages
Add ability to bulk set packages as private or public in both admin and repository package views. Includes new bulk action buttons, visibility grouping in repository view, and corresponding backend handlers for processing visibility changes. Admin can manage all packages while repository owners can manage their own packages.
2026-02-07 15:02:16 -05:00

239 lines
6.7 KiB
Go

// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package admin
import (
"fmt"
"net/http"
"net/url"
"time"
"code.gitcaddy.com/server/v3/models/db"
packages_model "code.gitcaddy.com/server/v3/models/packages"
"code.gitcaddy.com/server/v3/modules/optional"
"code.gitcaddy.com/server/v3/modules/setting"
"code.gitcaddy.com/server/v3/modules/templates"
"code.gitcaddy.com/server/v3/services/context"
packages_service "code.gitcaddy.com/server/v3/services/packages"
packages_cleanup_service "code.gitcaddy.com/server/v3/services/packages/cleanup"
)
const (
tplPackagesList templates.TplName = "admin/packages/list"
)
// Packages shows all packages
func Packages(ctx *context.Context) {
page := max(ctx.FormInt("page"), 1)
query := ctx.FormTrim("q")
packageType := ctx.FormTrim("type")
sort := ctx.FormTrim("sort")
pvs, total, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
Type: packages_model.Type(packageType),
Name: packages_model.SearchValue{Value: query},
Sort: sort,
IsInternal: optional.Some(false),
Paginator: &db.ListOptions{
PageSize: setting.UI.PackagesPagingNum,
Page: page,
},
})
if err != nil {
ctx.ServerError("SearchVersions", err)
return
}
pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
if err != nil {
ctx.ServerError("GetPackageDescriptors", err)
return
}
totalBlobSize, err := packages_model.GetTotalBlobSize(ctx)
if err != nil {
ctx.ServerError("GetTotalBlobSize", err)
return
}
totalUnreferencedBlobSize, err := packages_model.GetTotalUnreferencedBlobSize(ctx)
if err != nil {
ctx.ServerError("CalculateBlobSize", err)
return
}
ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["PageIsAdminPackages"] = true
ctx.Data["Query"] = query
ctx.Data["PackageType"] = packageType
ctx.Data["AvailableTypes"] = packages_model.TypeList
ctx.Data["SortType"] = sort
ctx.Data["PackageDescriptors"] = pds
ctx.Data["TotalCount"] = total
ctx.Data["TotalBlobSize"] = totalBlobSize - totalUnreferencedBlobSize
ctx.Data["TotalUnreferencedBlobSize"] = totalUnreferencedBlobSize
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplPackagesList)
}
// DeletePackageVersion deletes a package version
func DeletePackageVersion(ctx *context.Context) {
pv, err := packages_model.GetVersionByID(ctx, ctx.FormInt64("id"))
if err != nil {
ctx.ServerError("GetRepositoryByID", err)
return
}
if err := packages_service.RemovePackageVersion(ctx, ctx.Doer, pv); err != nil {
ctx.ServerError("RemovePackageVersion", err)
return
}
ctx.Flash.Success(ctx.Tr("packages.settings.delete.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")))
}
func CleanupExpiredData(ctx *context.Context) {
if err := packages_cleanup_service.CleanupExpiredData(ctx, time.Duration(0)); err != nil {
ctx.ServerError("CleanupExpiredData", err)
return
}
ctx.Flash.Success(ctx.Tr("admin.packages.cleanup.success"))
ctx.Redirect(setting.AppSubURL + "/-/admin/packages")
}
// BulkSetGlobal sets/unsets global flag on multiple packages
func BulkSetGlobal(ctx *context.Context) {
packageIDs := ctx.FormStrings("ids[]")
isGlobal := ctx.FormBool("is_global")
ids := make([]int64, 0, len(packageIDs))
for _, idStr := range packageIDs {
var id int64
if _, err := fmt.Sscanf(idStr, "%d", &id); err == nil && id > 0 {
ids = append(ids, id)
}
}
if len(ids) == 0 {
ctx.Flash.Error(ctx.Tr("admin.packages.bulk.no_selection"))
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
return
}
succeeded, failed, _ := packages_model.SetPackagesIsGlobal(ctx, ids, isGlobal)
if failed > 0 {
ctx.Flash.Warning(ctx.Tr("admin.packages.bulk.global.partial", succeeded, failed))
} else {
if isGlobal {
ctx.Flash.Success(ctx.Tr("admin.packages.bulk.global.enabled", succeeded))
} else {
ctx.Flash.Success(ctx.Tr("admin.packages.bulk.global.disabled", succeeded))
}
}
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
}
// BulkAutoMatch automatically matches packages to repositories
func BulkAutoMatch(ctx *context.Context) {
packageIDs := ctx.FormStrings("ids[]")
ids := make([]int64, 0, len(packageIDs))
for _, idStr := range packageIDs {
var id int64
if _, err := fmt.Sscanf(idStr, "%d", &id); err == nil && id > 0 {
ids = append(ids, id)
}
}
if len(ids) == 0 {
ctx.Flash.Error(ctx.Tr("admin.packages.bulk.no_selection"))
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
return
}
matched := 0
for _, id := range ids {
repoID, err := packages_model.AutoMatchPackageToRepository(ctx, id)
if err == nil && repoID > 0 {
matched++
}
}
if matched > 0 {
ctx.Flash.Success(ctx.Tr("admin.packages.bulk.automatch.success", matched))
} else {
ctx.Flash.Warning(ctx.Tr("admin.packages.bulk.automatch.none"))
}
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
}
// BulkSetPrivate sets/unsets private flag on multiple packages
func BulkSetPrivate(ctx *context.Context) {
packageIDs := ctx.FormStrings("ids[]")
isPrivate := ctx.FormBool("is_private")
ids := make([]int64, 0, len(packageIDs))
for _, idStr := range packageIDs {
var id int64
if _, err := fmt.Sscanf(idStr, "%d", &id); err == nil && id > 0 {
ids = append(ids, id)
}
}
if len(ids) == 0 {
ctx.Flash.Error(ctx.Tr("admin.packages.bulk.no_selection"))
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
return
}
succeeded := 0
for _, id := range ids {
if err := packages_model.SetPackageIsPrivate(ctx, id, isPrivate); err == nil {
succeeded++
}
}
if isPrivate {
ctx.Flash.Success(ctx.Tr("admin.packages.bulk.private.enabled", succeeded))
} else {
ctx.Flash.Success(ctx.Tr("admin.packages.bulk.private.disabled", succeeded))
}
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
}
// SingleAutoMatch automatically matches a single package to a repository
func SingleAutoMatch(ctx *context.Context) {
packageID := ctx.FormInt64("id")
if packageID == 0 {
ctx.Flash.Error(ctx.Tr("admin.packages.automatch.invalid"))
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
return
}
repoID, err := packages_model.AutoMatchPackageToRepository(ctx, packageID)
if err != nil {
ctx.Flash.Error(ctx.Tr("admin.packages.automatch.error"))
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
return
}
if repoID > 0 {
ctx.Flash.Success(ctx.Tr("admin.packages.automatch.success"))
} else {
ctx.Flash.Warning(ctx.Tr("admin.packages.automatch.no_match"))
}
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages")
}