fedimovies/src/activitypub/handlers/move_person.rs

104 lines
3.1 KiB
Rust

use tokio_postgres::GenericClient;
use crate::activitypub::{
activity::Activity,
builders::{
follow::prepare_follow,
undo_follow::prepare_undo_follow,
},
fetcher::helpers::get_or_import_profile_by_actor_id,
receiver::find_object_id,
vocabulary::PERSON,
};
use crate::config::Config;
use crate::errors::{DatabaseError, ValidationError};
use crate::models::{
relationships::queries::{
create_follow_request,
get_followers,
unfollow,
},
users::queries::get_user_by_id,
};
use super::HandlerResult;
pub async fn handle_move_person(
config: &Config,
db_client: &mut impl GenericClient,
activity: Activity,
) -> HandlerResult {
let object_id = find_object_id(&activity.object)?;
if object_id != activity.actor {
return Err(ValidationError("actor ID mismatch").into());
};
let target_value = activity.target
.ok_or(ValidationError("target is missing"))?;
let target_id = find_object_id(&target_value)?;
let instance = config.instance();
let media_dir = config.media_dir();
let old_profile = get_or_import_profile_by_actor_id(
db_client,
&instance,
&media_dir,
&activity.actor,
).await?;
let old_actor = old_profile.actor_json.unwrap();
let new_profile = get_or_import_profile_by_actor_id(
db_client,
&instance,
&media_dir,
&target_id,
).await?;
let new_actor = new_profile.actor_json.unwrap();
let maybe_also_known_as = new_actor.also_known_as.as_ref()
.and_then(|aliases| aliases.first());
if maybe_also_known_as != Some(&old_actor.id) {
return Err(ValidationError("target ID is not an alias").into());
};
let followers = get_followers(db_client, &old_profile.id).await?;
let mut activities = vec![];
for follower in followers {
let follower = get_user_by_id(db_client, &follower.id).await?;
// Unfollow old profile
let maybe_follow_request_id = unfollow(
db_client,
&follower.id,
&old_profile.id,
).await?;
// The target is remote profile, so follow request must exist
let follow_request_id = maybe_follow_request_id.unwrap();
activities.push(prepare_undo_follow(
&instance,
&follower,
&old_actor,
&follow_request_id,
));
// Follow new profile
match create_follow_request(
db_client,
&follower.id,
&new_profile.id,
).await {
Ok(follow_request) => {
activities.push(prepare_follow(
&instance,
&follower,
&new_actor,
&follow_request.id,
));
},
Err(DatabaseError::AlreadyExists(_)) => (), // already following
Err(other_error) => return Err(other_error.into()),
};
};
tokio::spawn(async move {
for activity in activities {
activity.deliver_or_log().await;
};
});
Ok(Some(PERSON))
}