diff --git a/models/federation/federation.go b/models/federation/federation.go index ee2e64af9..aebed0d61 100644 --- a/models/federation/federation.go +++ b/models/federation/federation.go @@ -185,3 +185,28 @@ func GetRemoteUsersWithNoLocalFollowers(ctx context.Context, olderThan time.Dura return users, nil } + +func GetRemotePersons(ctx context.Context, page int) ([]FederatedUser, error) { + limit := 1 + offset := page * limit + var federatedUsers []FederatedUser + + err := db.GetEngine(ctx). + Table("federated_user"). + Limit(limit, offset). + Find(&federatedUsers) + + // TODO: this doesn't work, so fetching federated_user and then getting user. How to make this work? + // var users []user.User + // err := db.GetEngine(ctx). + // Table("user"). + // Join("inner", "federated_user", "federated_user.user_id = user.id"). + // Limit(limit, offset). + // Find(&users) + + if err != nil { + log.Trace("Error: GetRemotePersons: %w", err) + return nil, err + } + return federatedUsers, nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8a1ed3a41..dc11fcb05 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2922,6 +2922,7 @@ 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 +dashboard.remote_actor_update = Update remote actors' data 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 ea7dc3e8b..c23dbf05f 100644 --- a/services/cron/tasks_basic.go +++ b/services/cron/tasks_basic.go @@ -195,6 +195,7 @@ func initBasicTasks() { if setting.Federation.Enabled { registerCleanupRemotePersonsWithNoFollowers() + registerUpdateRemotePersons() } } @@ -212,3 +213,15 @@ func registerCleanupRemotePersonsWithNoFollowers() { return forgefed_service.CleanUpRemotePersons(ctx, acConfig.OlderThan) }) } + +func registerUpdateRemotePersons() { + RegisterTaskFatal("remote_actor_update", &OlderThanConfig{ + BaseConfig: BaseConfig{ + Enabled: true, + RunAtStart: true, + Schedule: "@every 6h", + }, + }, func(ctx context.Context, _ *user_model.User, config Config) error { + return forgefed_service.UpdatePersonActor(ctx) + }) +} diff --git a/services/forgefed/actor.go b/services/forgefed/actor.go index a709260b9..f967e6500 100644 --- a/services/forgefed/actor.go +++ b/services/forgefed/actor.go @@ -123,6 +123,20 @@ func SavePerson(ctx context.Context, person *ap.Person) (*user.User, error) { return u, nil } +func GetActorFromUser(ctx context.Context, u *user.User) (*ap.Actor, error) { + + alias := u.Name + + webfingerRes, err := WebFingerLookup(alias) + if err != nil { + return nil, err + } + + actorID := webfingerRes.GetActorLink().Href + + return GetActor(actorID) +} + // Clean up remote actors (persons) without any followers in local instance func CleanUpRemotePersons(ctx context.Context, olderThan time.Duration) error { page := 0 @@ -147,3 +161,59 @@ func CleanUpRemotePersons(ctx context.Context, olderThan time.Duration) error { } return nil } + +func UpdatePersonActor(ctx context.Context) error { + // NOTE: change of any of these don't matter at this point since we are + // ignoring actor's PreferredUsername and using their address to generate + // username and email. Ask suggestions from other devs. + // + // fmt.Println(person.ID.String()) + // hostname, err := GetHostnameFromResource(person.ID.String()) + // + // + // u := new(user.User) + // u.Name = "@" + person.PreferredUsername.String() + "@" + hostname + // //panic(u.Name) + // u.Email = person.PreferredUsername.String() + "@" + hostname + // u.Website = person.URL.GetID().String() + // u.KeepEmailPrivate = true + + page := 0 + for { + federatedUsers, err := federation.GetRemotePersons(ctx, page) + if len(federatedUsers) == 0 { + break + } + if err != nil { + log.Trace("Error: UpdatePersonActor: %v", err) + return err + } + + for _, f := range federatedUsers { + log.Info("Updating users, got %s", f.ExternalID) + u, err := user.GetUserByName(ctx, f.ExternalID) + if err != nil { + log.Error("Got error while getting user: %w", err) + return err + } + + person, err := GetActorFromUser(ctx, u) + if err != nil { + log.Error("Got error while fetching actor: %w", err) + return err + } + + avatar, err := GetPersonAvatar(ctx, person) + if err != nil { + log.Error("Got error while fetching avatar: %w", err) + return err + } + + if u.IsUploadAvatarChanged(avatar) { + _ = user_service.UploadAvatar(ctx, u, avatar) + } + } + page += 1 + } + return nil +}