feat: get remote person's avatar and assign to user #6

Merged
realaravinth merged 2 commits from task-605 into federation 2024-03-29 14:11:43 +05:30
4 changed files with 134 additions and 0 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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)
})
}

View file

@ -43,6 +43,21 @@ func GetActor(id string) (*ap.Actor, error) {
return actorObj, nil
}
func GetPersonAvatar(ctx context.Context, person *ap.Person) ([]byte, error) {
avatarObj := new(ap.Image)
ap.CopyItemProperties(avatarObj, person.Icon)
log.Info("Getting avatar from link : %s", avatarObj.URL.GetLink().String())
r, err := http.Get(avatarObj.URL.GetLink().String())
if err != nil {
log.Error("Got error while fetching avatar fn: %w", err)
return nil, err
}
defer r.Body.Close()
return io.ReadAll(r.Body)
}
func SavePerson(ctx context.Context, person *ap.Person) (*user.User, error) {
fmt.Println(person.ID.String())
@ -91,6 +106,16 @@ func SavePerson(ctx context.Context, person *ap.Person) (*user.User, error) {
return nil, err
}
avatar, err := GetPersonAvatar(ctx, person)
if err != nil {
log.Error("Got error while fetching avatar: %w", err)
return nil, err
}
if u.IsUploadAvatarChanged(avatar) {
_ = user_service.UploadAvatar(ctx, u, avatar)
}
if err = federation.CreateFederatedUser(ctx, u, &federatedHost); err != nil {
return nil, err
}
@ -98,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
@ -122,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
}