feat: Add generate_follow_accept_id function to utils.rs and follow_manually endpoint to main.rs

This commit is contained in:
aprilthepink 2024-05-05 18:18:39 +02:00
parent 3ebe83c52f
commit 36d10f774a
3 changed files with 150 additions and 10 deletions

View file

@ -1,12 +1,20 @@
use activitypub_federation::{config::Data, fetch::object_id::ObjectId, traits::ActivityHandler}; use activitypub_federation::{
use activitystreams_kinds::activity::FollowType; activity_sending::SendActivityTask,
config::Data,
fetch::object_id::ObjectId,
protocol::context::WithContext,
traits::{ActivityHandler, Actor, Object},
};
use activitystreams_kinds::activity::{AcceptType, FollowType};
use sea_orm::{ActiveModelTrait, Set}; use sea_orm::{ActiveModelTrait, Set};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
use crate::{ use crate::{
database::StateHandle, database::StateHandle,
entities::{follow_relation, prelude::FollowRelation, user}, entities::{follow_relation, post, user},
error,
utils::{generate_follow_accept_id, generate_object_id},
DB, DB,
}; };
@ -19,6 +27,73 @@ pub struct Follow {
id: Url, id: Url,
} }
impl Follow {
pub async fn send(
local_user: ObjectId<user::Model>,
followee: ObjectId<user::Model>,
inbox: Url,
data: &Data<StateHandle>,
) -> Result<(), error::Error> {
print!("Sending follow request to {}", &followee);
let create = Follow {
actor: local_user.clone(),
object: followee.clone(),
kind: FollowType::Follow,
id: generate_object_id(data.domain())?,
};
let create_with_context = WithContext::new_default(create);
let sends = SendActivityTask::prepare(
&create_with_context,
&data.local_user().await?,
vec![inbox],
data,
)
.await?;
for send in sends {
send.sign_and_send(data).await?;
}
Ok(())
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Accept {
actor: ObjectId<user::Model>,
object: Follow,
#[serde(rename = "type")]
kind: AcceptType,
id: Url,
}
impl Accept {
pub async fn send(
follow_relation: follow_relation::Model,
follow_req: Follow,
inbox: Url,
data: &Data<StateHandle>,
) -> Result<(), error::Error> {
print!("Sending accept to {}", &follow_relation.follower_id);
let create = Accept {
actor: follow_req.object.clone(),
object: follow_req,
kind: AcceptType::Accept,
id: generate_follow_accept_id(data.domain(), follow_relation.id)?,
};
let create_with_context = WithContext::new_default(create);
let sends = SendActivityTask::prepare(
&create_with_context,
&data.local_user().await?,
vec![inbox],
data,
)
.await?;
for send in sends {
send.sign_and_send(data).await?;
}
Ok(())
}
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl ActivityHandler for Follow { impl ActivityHandler for Follow {
type DataType = StateHandle; type DataType = StateHandle;
@ -37,17 +112,51 @@ impl ActivityHandler for Follow {
} }
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> { async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let local_user = self.object.dereference(data).await?; accept_follow(self, data).await?;
let follower = self.actor.dereference(data).await?;
save_follow(local_user, follower).await?;
Ok(()) Ok(())
} }
} }
#[async_trait::async_trait]
impl ActivityHandler for Accept {
type DataType = StateHandle;
type Error = crate::error::Error;
fn id(&self) -> &Url {
&self.id
}
fn actor(&self) -> &Url {
self.actor.inner()
}
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let user = self.actor.dereference(data).await?;
let follower = self.object.actor.dereference(data).await?;
save_follow(user, follower).await?;
Ok(())
}
}
async fn accept_follow(
follow_req: Follow,
data: &Data<StateHandle>,
) -> Result<(), crate::error::Error> {
let local_user = follow_req.actor.dereference(data).await?;
let follower = follow_req.object.dereference(data).await?;
let follow_relation = save_follow(local_user, follower.clone()).await?;
Accept::send(follow_relation, follow_req, follower.inbox().clone(), data).await?;
Ok(())
}
async fn save_follow( async fn save_follow(
local_user: user::Model, local_user: user::Model,
follower: user::Model, follower: user::Model,
) -> Result<(), crate::error::Error> { ) -> Result<follow_relation::Model, crate::error::Error> {
let url = Url::parse(&follower.url)?; let url = Url::parse(&follower.url)?;
let follow_relation = follow_relation::ActiveModel { let follow_relation = follow_relation::ActiveModel {
followee_id: Set(local_user.id.clone()), followee_id: Set(local_user.id.clone()),
@ -58,6 +167,6 @@ async fn save_follow(
follower_inbox: Set(Some(follower.inbox.clone())), follower_inbox: Set(Some(follower.inbox.clone())),
..Default::default() ..Default::default()
}; };
follow_relation.insert(DB.get().unwrap()).await?; let model = follow_relation.insert(DB.get().unwrap()).await?;
Ok(()) Ok(model)
} }

View file

@ -28,13 +28,13 @@ use tokio::signal;
use tracing::{info, instrument::WithSubscriber}; use tracing::{info, instrument::WithSubscriber};
use url::Url; use url::Url;
use crate::entities::user;
use crate::utils::generate_object_id; use crate::utils::generate_object_id;
use crate::{ use crate::{
activities::create_post::CreatePost, activities::create_post::CreatePost,
database::{Config, State}, database::{Config, State},
objects::post::{Mention, Note}, objects::post::{Mention, Note},
}; };
use crate::{activities::follow::Follow, entities::user};
use lazy_static::lazy_static; use lazy_static::lazy_static;
mod activities; mod activities;
@ -105,6 +105,31 @@ async fn post_manually(
Ok(HttpResponse::Ok().json(Response { health: true })) Ok(HttpResponse::Ok().json(Response { health: true }))
} }
#[get("/test/follow/{user}")]
async fn follow_manually(
path: web::Path<String>,
state: web::Data<State>,
) -> actix_web::Result<HttpResponse, error::Error> {
let local_user = state.local_user().await?;
let data = FEDERATION_CONFIG.get().unwrap();
let followee =
webfinger_resolve_actor::<State, user::Model>(path.as_str(), &data.to_request_data())
.await?;
let followee_object: ObjectId<user::Model> = Url::parse(&followee.id)?.into();
let localuser_object: ObjectId<user::Model> = Url::parse(&local_user.id)?.into();
Follow::send(
localuser_object,
followee_object,
followee.shared_inbox_or_inbox(),
&data.to_request_data(),
)
.await?;
Ok(HttpResponse::Ok().json(Response { health: true }))
}
const DOMAIN_DEF: &str = "example.com"; const DOMAIN_DEF: &str = "example.com";
const LOCAL_USER_NAME: &str = "example"; const LOCAL_USER_NAME: &str = "example";
@ -209,6 +234,7 @@ async fn main() -> actix_web::Result<(), anyhow::Error> {
.wrap(prometheus.clone()) .wrap(prometheus.clone())
.wrap(FederationMiddleware::new(data.clone())) .wrap(FederationMiddleware::new(data.clone()))
.service(post_manually) .service(post_manually)
.service(follow_manually)
.route("/{user}", web::get().to(http_get_user)) .route("/{user}", web::get().to(http_get_user))
.route("/{user}/inbox", web::post().to(http_post_user_inbox)) .route("/{user}/inbox", web::post().to(http_post_user_inbox))
.route("/.well-known/webfinger", web::get().to(webfinger)) .route("/.well-known/webfinger", web::get().to(webfinger))

View file

@ -11,3 +11,8 @@ pub fn generate_object_id(domain: &str) -> Result<Url, ParseError> {
.collect(); .collect();
Url::parse(&format!("https://{}/objects/{}", domain, id)) Url::parse(&format!("https://{}/objects/{}", domain, id))
} }
/// Generate a follow accept id
pub fn generate_follow_accept_id(domain: &str, db_id: i32) -> Result<Url, ParseError> {
Url::parse(&format!("https://{}/activities/follow/{}", domain, db_id))
}