diff --git a/models/federation/federation.go b/models/federation/federation.go index 6b867e8ea..ee2e64af9 100644 --- a/models/federation/federation.go +++ b/models/federation/federation.go @@ -3,10 +3,13 @@ package federation import ( "context" "strings" + "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "xorm.io/builder" ) // HookTask represents a hook task. @@ -161,3 +164,24 @@ func CreatUser(ctx context.Context, u *user.User) error { return committer.Commit() } + +func GetRemoteUsersWithNoLocalFollowers(ctx context.Context, olderThan time.Duration, page int) ([]user.User, error) { + limit := 40 + offset := page * limit + var users []user.User + + err := db.GetEngine(ctx). + Table("user"). + Where("num_followers = 0"). + And(builder.Lt{"user.created_unix": time.Now().Add(-olderThan).Unix()}). + Join("inner", "federated_user", "federated_user.user_id = user.id"). + Limit(limit, offset). + Find(&users) + + if err != nil { + log.Trace("Error: GetRemoteUserWithNoLocalFollowers: %w", err) + return nil, err + } + return users, nil + +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 647c46b5a..8a1ed3a41 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2921,6 +2921,7 @@ dashboard.start_schedule_tasks = Start schedule tasks dashboard.sync_branch.started = Branches Sync started dashboard.sync_tag.started = Tags Sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer +dashboard.remote_actor_cleanup = Clean remote actors with no local followers users.user_manage_panel = Manage user accounts users.new_account = Create User Account diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go index 3869382d2..ea7dc3e8b 100644 --- a/services/cron/tasks_basic.go +++ b/services/cron/tasks_basic.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" + forgefed_service "code.gitea.io/gitea/services/forgefed" "code.gitea.io/gitea/services/migrations" mirror_service "code.gitea.io/gitea/services/mirror" packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup" @@ -187,7 +188,27 @@ func initBasicTasks() { if setting.Packages.Enabled { registerCleanupPackages() } + if setting.Actions.Enabled { registerActionsCleanup() } + + if setting.Federation.Enabled { + registerCleanupRemotePersonsWithNoFollowers() + } + +} + +func registerCleanupRemotePersonsWithNoFollowers() { + RegisterTaskFatal("remote_actor_cleanup", &OlderThanConfig{ + BaseConfig: BaseConfig{ + Enabled: true, + RunAtStart: true, + Schedule: "@midnight", + }, + OlderThan: 24 * time.Hour, + }, func(ctx context.Context, _ *user_model.User, config Config) error { + acConfig := config.(*OlderThanConfig) + return forgefed_service.CleanUpRemotePersons(ctx, acConfig.OlderThan) + }) } diff --git a/services/forgefed/actor.go b/services/forgefed/actor.go index e6b0eb8a8..ab10a41e3 100644 --- a/services/forgefed/actor.go +++ b/services/forgefed/actor.go @@ -6,9 +6,12 @@ import ( "fmt" "io" "net/http" + "time" "code.gitea.io/gitea/models/federation" "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + user_service "code.gitea.io/gitea/services/user" ap "github.com/go-ap/activitypub" ) @@ -94,3 +97,28 @@ func SavePerson(ctx context.Context, person *ap.Person) (*user.User, error) { return u, nil } + +// Clean up remote actors (persons) without any followers in local instance +func CleanUpRemotePersons(ctx context.Context, olderThan time.Duration) error { + page := 0 + for { + users, err := federation.GetRemoteUsersWithNoLocalFollowers(ctx, olderThan, page) + if len(users) == 0 { + break + } + if err != nil { + log.Trace("Error: CleanUpRemotePersons: %v", err) + return err + } + + for _, u := range users { + err = user_service.DeleteUser(ctx, &u, false) + if err != nil { + log.Trace("Error: CleanUpRemotePersons: %v", err) + return err + } + } + page += 1 + } + return nil +}