mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Allow package cleanup from admin page (#25307)
Until now expired package data gets deleted daily by a cronjob. The admin page shows the size of all packages and the size of unreferenced data. The users (#25035, #20631) expect the deletion of this data if they run the cronjob from the admin page but the job only deletes data older than 24h. This PR adds a new button which deletes all expired data.  --------- Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		@@ -2833,6 +2833,7 @@ repos.lfs_size = LFS Size
 | 
				
			|||||||
packages.package_manage_panel = Package Management
 | 
					packages.package_manage_panel = Package Management
 | 
				
			||||||
packages.total_size = Total Size: %s
 | 
					packages.total_size = Total Size: %s
 | 
				
			||||||
packages.unreferenced_size = Unreferenced Size: %s
 | 
					packages.unreferenced_size = Unreferenced Size: %s
 | 
				
			||||||
 | 
					packages.cleanup = Clean up expired data
 | 
				
			||||||
packages.owner = Owner
 | 
					packages.owner = Owner
 | 
				
			||||||
packages.creator = Creator
 | 
					packages.creator = Creator
 | 
				
			||||||
packages.name = Name
 | 
					packages.name = Name
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ package admin
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	packages_model "code.gitea.io/gitea/models/packages"
 | 
						packages_model "code.gitea.io/gitea/models/packages"
 | 
				
			||||||
@@ -14,6 +15,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	packages_service "code.gitea.io/gitea/services/packages"
 | 
						packages_service "code.gitea.io/gitea/services/packages"
 | 
				
			||||||
 | 
						packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -99,3 +101,13 @@ func DeletePackageVersion(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
 | 
						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")))
 | 
						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("packages.cleanup.success"))
 | 
				
			||||||
 | 
						ctx.Redirect(setting.AppSubURL + "/admin/packages")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -597,6 +597,7 @@ func registerRoutes(m *web.Route) {
 | 
				
			|||||||
		m.Group("/packages", func() {
 | 
							m.Group("/packages", func() {
 | 
				
			||||||
			m.Get("", admin.Packages)
 | 
								m.Get("", admin.Packages)
 | 
				
			||||||
			m.Post("/delete", admin.DeletePackageVersion)
 | 
								m.Post("/delete", admin.DeletePackageVersion)
 | 
				
			||||||
 | 
								m.Post("/cleanup", admin.CleanupExpiredData)
 | 
				
			||||||
		}, packagesEnabled)
 | 
							}, packagesEnabled)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("/hooks", func() {
 | 
							m.Group("/hooks", func() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -152,7 +152,7 @@ func registerCleanupPackages() {
 | 
				
			|||||||
		OlderThan: 24 * time.Hour,
 | 
							OlderThan: 24 * time.Hour,
 | 
				
			||||||
	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 | 
						}, func(ctx context.Context, _ *user_model.User, config Config) error {
 | 
				
			||||||
		realConfig := config.(*OlderThanConfig)
 | 
							realConfig := config.(*OlderThanConfig)
 | 
				
			||||||
		return packages_cleanup_service.Cleanup(ctx, realConfig.OlderThan)
 | 
							return packages_cleanup_service.CleanupTask(ctx, realConfig.OlderThan)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,9 +20,17 @@ import (
 | 
				
			|||||||
	debian_service "code.gitea.io/gitea/services/packages/debian"
 | 
						debian_service "code.gitea.io/gitea/services/packages/debian"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Cleanup removes expired package data
 | 
					// Task method to execute cleanup rules and cleanup expired package data
 | 
				
			||||||
func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
 | 
					func CleanupTask(ctx context.Context, olderThan time.Duration) error {
 | 
				
			||||||
	ctx, committer, err := db.TxContext(taskCtx)
 | 
						if err := ExecuteCleanupRules(ctx); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CleanupExpiredData(ctx, olderThan)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ExecuteCleanupRules(outerCtx context.Context) error {
 | 
				
			||||||
 | 
						ctx, committer, err := db.TxContext(outerCtx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -30,7 +38,7 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
 | 
						err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case <-taskCtx.Done():
 | 
							case <-outerCtx.Done():
 | 
				
			||||||
			return db.ErrCancelledf("While processing package cleanup rules")
 | 
								return db.ErrCancelledf("While processing package cleanup rules")
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -122,6 +130,16 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return committer.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
 | 
				
			||||||
 | 
						ctx, committer, err := db.TxContext(outerCtx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer committer.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := container_service.Cleanup(ctx, olderThan); err != nil {
 | 
						if err := container_service.Cleanup(ctx, olderThan); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,12 @@
 | 
				
			|||||||
			{{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}},
 | 
								{{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}},
 | 
				
			||||||
			{{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}},
 | 
								{{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}},
 | 
				
			||||||
			{{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})
 | 
								{{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})
 | 
				
			||||||
 | 
								<div class="ui right">
 | 
				
			||||||
 | 
									<form method="post" action="/admin/packages/cleanup">
 | 
				
			||||||
 | 
										{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
										<button class="ui primary tiny button">{{.locale.Tr "admin.packages.cleanup"}}</button>
 | 
				
			||||||
 | 
									</form>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
		</h4>
 | 
							</h4>
 | 
				
			||||||
		<div class="ui attached segment">
 | 
							<div class="ui attached segment">
 | 
				
			||||||
			<form class="ui form ignore-dirty">
 | 
								<form class="ui form ignore-dirty">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -475,7 +475,7 @@ func TestPackageCleanup(t *testing.T) {
 | 
				
			|||||||
		_, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion)
 | 
							_, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = packages_cleanup_service.Cleanup(db.DefaultContext, duration)
 | 
							err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pbs, err = packages_model.FindExpiredUnreferencedBlobs(db.DefaultContext, duration)
 | 
							pbs, err = packages_model.FindExpiredUnreferencedBlobs(db.DefaultContext, duration)
 | 
				
			||||||
@@ -610,7 +610,7 @@ func TestPackageCleanup(t *testing.T) {
 | 
				
			|||||||
				pcr, err := packages_model.InsertCleanupRule(db.DefaultContext, c.Rule)
 | 
									pcr, err := packages_model.InsertCleanupRule(db.DefaultContext, c.Rule)
 | 
				
			||||||
				assert.NoError(t, err)
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				err = packages_cleanup_service.Cleanup(db.DefaultContext, duration)
 | 
									err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration)
 | 
				
			||||||
				assert.NoError(t, err)
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				for _, v := range c.Versions {
 | 
									for _, v := range c.Versions {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user