From ba901981f0e46887dc0ef704cb86686ee7f6675e Mon Sep 17 00:00:00 2001 From: April John Date: Sat, 3 Aug 2024 03:49:07 +0200 Subject: [PATCH] update --- src/activities/follow.rs | 41 ++++++------ src/entities/follow_relation.rs | 8 ++- src/lysand/http.rs | 66 +++++++++++++++---- src/lysand/inbox.rs | 109 ++++++++++++++++++++++++++++++++ src/lysand/mod.rs | 1 + src/main.rs | 5 +- src/utils.rs | 4 ++ 7 files changed, 199 insertions(+), 35 deletions(-) create mode 100644 src/lysand/inbox.rs diff --git a/src/activities/follow.rs b/src/activities/follow.rs index fe175e0..be716c1 100644 --- a/src/activities/follow.rs +++ b/src/activities/follow.rs @@ -6,13 +6,16 @@ use activitypub_federation::{ traits::{ActivityHandler, Actor, Object}, }; use activitystreams_kinds::activity::{AcceptType, FollowType}; -use sea_orm::{ActiveModelTrait, Set}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityOrSelect, EntityTrait, QueryFilter, Set}; use serde::{Deserialize, Serialize}; use url::Url; use crate::{ database::StateHandle, - entities::{follow_relation, post, user}, + entities::{ + follow_relation::{self, Entity}, + post, prelude, user, + }, error, utils::{generate_follow_accept_id, generate_random_object_id}, DB, @@ -20,11 +23,11 @@ use crate::{ #[derive(Deserialize, Serialize, Debug)] pub struct Follow { - actor: ObjectId, - object: ObjectId, + pub actor: ObjectId, + pub object: ObjectId, #[serde(rename = "type")] - kind: FollowType, - id: Url, + pub kind: FollowType, + pub id: Url, } impl Follow { @@ -154,19 +157,21 @@ async fn accept_follow( } async fn save_follow( - local_user: user::Model, + followee: user::Model, follower: user::Model, ) -> Result { - let url = Url::parse(&follower.url)?; - let follow_relation = follow_relation::ActiveModel { - followee_id: Set(local_user.id.clone()), - follower_id: Set(follower.id.clone()), - followee_host: Set(None), - follower_host: Set(Some(url.host_str().unwrap().to_string())), - followee_inbox: Set(Some(local_user.inbox.clone())), - follower_inbox: Set(Some(follower.inbox.clone())), - ..Default::default() - }; - let model = follow_relation.insert(DB.get().unwrap()).await?; + let db = DB.get().unwrap(); + let query = prelude::FollowRelation::find() + .filter(follow_relation::Column::FollowerId.eq(follower.id.as_str())) + .filter(follow_relation::Column::FolloweeId.eq(followee.id.as_str())) + .one(db) + .await?; + if query.is_none() { + return Err(crate::error::Error(anyhow::anyhow!("oopsie woopise"))); + } + // modify db entry + let res = prelude::FollowRelation::update(query.unwrap()); + + Ok(model) } diff --git a/src/entities/follow_relation.rs b/src/entities/follow_relation.rs index ea2074a..c0c9c5c 100644 --- a/src/entities/follow_relation.rs +++ b/src/entities/follow_relation.rs @@ -6,9 +6,15 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "follow_relation")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, + pub id: String, pub followee_id: String, pub follower_id: String, + pub ap_json: String, + pub ap_accept_json: Option, + pub ap_id: Option, + pub ap_accept_id: Option, + pub accept_id: Option, + pub remote: bool, pub followee_host: Option, pub follower_host: Option, pub followee_inbox: Option, diff --git a/src/lysand/http.rs b/src/lysand/http.rs index 9e2eb1b..082cc03 100644 --- a/src/lysand/http.rs +++ b/src/lysand/http.rs @@ -5,7 +5,7 @@ use activitypub_federation::{ FEDERATION_CONTENT_TYPE, }; use activitystreams_kinds::{activity::CreateType, object}; -use actix_web::{get, web, HttpResponse}; +use actix_web::{get, post, web, HttpResponse}; use sea_orm::{query, ColumnTrait, EntityTrait, QueryFilter}; use url::Url; @@ -58,19 +58,7 @@ async fn query_post( } if let Some(user) = query.user_url.clone() { - let opt_model = prelude::User::find() - .filter(user::Column::Url.eq(user.as_str())) - .one(db) - .await?; - let target; - if let Some(model) = opt_model { - target = model; - } else { - target = ObjectId::::from(user) - .dereference(&data.to_request_data()) - .await?; - } - let lysand_user = lysand_user_from_db(target).await?; + let lysand_user = lysand_url_to_user(user).await?; return Ok(HttpResponse::Ok() .content_type("application/json") @@ -95,6 +83,16 @@ async fn query_post( .json(lysand_post_from_db(target).await?)) } +#[post("/apbridge/lysand/inbox")] +async fn lysand_inbox( + body: web::Bytes, + state: web::Data, +) -> actix_web::Result { + + + Ok(HttpResponse::Created().finish()) +} + #[get("/apbridge/object/{post}")] async fn fetch_post( path: web::Path, @@ -199,3 +197,43 @@ async fn create_activity( .content_type(FEDERATION_CONTENT_TYPE) .json(create_with_context)) } + +pub async fn lysand_url_to_user(url: Url) -> anyhow::Result { + let db = DB.get().unwrap(); + let data = FEDERATION_CONFIG.get().unwrap(); + + let opt_model = prelude::User::find() + .filter(user::Column::Url.eq(url.as_str())) + .one(db) + .await?; + let target; + if let Some(model) = opt_model { + target = model; + } else { + target = ObjectId::::from(url) + .dereference(&data.to_request_data()) + .await.unwrap(); + } + + Ok(lysand_user_from_db(target).await?) +} + +pub async fn lysand_url_to_user_and_model(url: Url) -> anyhow::Result<(super::objects::User, user::Model)> { + let db = DB.get().unwrap(); + let data = FEDERATION_CONFIG.get().unwrap(); + + let opt_model = prelude::User::find() + .filter(user::Column::Url.eq(url.as_str())) + .one(db) + .await?; + let target; + if let Some(model) = opt_model { + target = model; + } else { + target = ObjectId::::from(url) + .dereference(&data.to_request_data()) + .await.unwrap(); + } + + Ok((lysand_user_from_db(target.clone()).await?, target)) +} \ No newline at end of file diff --git a/src/lysand/inbox.rs b/src/lysand/inbox.rs new file mode 100644 index 0000000..2624ce5 --- /dev/null +++ b/src/lysand/inbox.rs @@ -0,0 +1,109 @@ + +use activitypub_federation::{activity_sending::SendActivityTask, fetch::object_id::ObjectId, protocol::context::WithContext}; +use activitystreams_kinds::activity::FollowType; +use anyhow::Result; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityOrSelect, EntityTrait, QueryFilter, Set}; +use serde::Deserialize; +use url::Url; +use crate::{activities::follow::Follow, entities::{self, follow_relation, prelude::{self, FollowRelation}, user}, utils::generate_follow_req_id, DB, FEDERATION_CONFIG}; + +use super::{conversion::lysand_user_from_db, http::{lysand_url_to_user, lysand_url_to_user_and_model}, objects::LysandType}; + +pub async fn inbox_entry(json: &str) -> Result<()> { + // Deserialize the JSON string into a dynamic value + let value: serde_json::Value = serde_json::from_str(json).unwrap(); + + // Extract the "type" field from the JSON + if let Some(json_type) = value.get("type") { + // Match the "type" field with the corresponding LysandType + match json_type.as_str() { + Some("Note") => { + let note: super::objects::Note = serde_json::from_str(json)?; + + } + Some("Patch") => { + let patch: super::objects::Patch = serde_json::from_str(json)?; + + } + Some("Follow") => { + let follow_req: super::objects::Follow = serde_json::from_str(json)?; + + } + Some("FollowAccept") => { + let follow_accept: super::objects::FollowResult = serde_json::from_str(json)?; + + } + Some("FollowReject") => { + let follow_rej: super::objects::FollowResult = serde_json::from_str(json)?; + + } + // Add more cases for other types as needed + _ => { + return Err(anyhow::anyhow!("Unknown 'type' field in JSON, it is {}", json_type)); + } + } + } else { + return Err(anyhow::anyhow!("Missing 'type' field in JSON")); + } + Ok(()) +} + +async fn follow_request(follow: super::objects::Follow) -> Result<()> { + // Check if the user is already following the requester + let db = DB.get().unwrap(); + let query = FollowRelation::find() + .filter(follow_relation::Column::FollowerId.eq(follow.author.to_string().as_str())) + .filter(follow_relation::Column::FolloweeId.eq(follow.followee.to_string().as_str())) + .one(db) + .await?; + if query.is_some() { + return Err(anyhow::anyhow!("User is already follow requesting / following the followee")); + } + let data = FEDERATION_CONFIG.get().unwrap(); + let author = lysand_url_to_user_and_model(follow.author.into()).await?; + let followee = lysand_url_to_user_and_model(follow.followee.into()).await?; + let serial_ap_author = serde_json::from_str::(&(author.1.ap_json.clone()).unwrap())?; + let serial_ap_followee = serde_json::from_str::(&(followee.1.ap_json.clone()).unwrap())?; + + let id = uuid::Uuid::now_v7().to_string(); + + let followee_object: ObjectId = serial_ap_followee.url.into(); + let localuser_object: ObjectId = serial_ap_author.url.into(); + + println!("Sending follow request to {}", &followee.0.display_name.unwrap_or(followee.0.username)); + let create = Follow { + actor: localuser_object.clone(), + object: followee_object.clone(), + kind: FollowType::Follow, + id: generate_follow_req_id(data.domain(), id.clone().as_str())?, + }; + + let ap_json = serde_json::to_string(&create)?; + + let create_with_context = WithContext::new_default(create); + + let follow_db_entry = follow_relation::ActiveModel { + id: Set(id.clone()), + followee_id: Set(followee.0.id.to_string()), + follower_id: Set(author.0.id.to_string()), + ap_id: Set(Some(id.clone())), + ap_json: Set(ap_json), + remote: Set(false), + ..Default::default() + }; + follow_db_entry.insert(db).await?; + + let sends = SendActivityTask::prepare( + &create_with_context, + &data.local_user().await.unwrap(), + vec![serial_ap_followee.inbox], + &data.to_request_data(), + ) + .await?; + + for send in sends { + send.sign_and_send(&data.to_request_data()).await?; + } + + Ok(()) +} \ No newline at end of file diff --git a/src/lysand/mod.rs b/src/lysand/mod.rs index ff9c29c..c258f10 100644 --- a/src/lysand/mod.rs +++ b/src/lysand/mod.rs @@ -4,3 +4,4 @@ pub mod http; pub mod objects; pub mod superx; pub mod test; +pub mod inbox; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 61dc1b8..245f2e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -141,8 +141,8 @@ async fn follow_manually( webfinger_resolve_actor::(path.as_str(), &data.to_request_data()) .await?; - let followee_object: ObjectId = Url::parse(&followee.id)?.into(); - let localuser_object: ObjectId = Url::parse(&local_user.id)?.into(); + let followee_object: ObjectId = Url::parse(&followee.url)?.into(); + let localuser_object: ObjectId = Url::parse(&local_user.url)?.into(); Follow::send( localuser_object, @@ -289,6 +289,7 @@ async fn main() -> actix_web::Result<(), anyhow::Error> { .service(follow_manually) .route("/{user}", web::get().to(http_get_user)) .route("/{user}/inbox", web::post().to(http_post_user_inbox)) + .route("/apbridge/{user}/inbox", web::post().to(http_post_user_inbox)) .route("/.well-known/webfinger", web::get().to(webfinger)) .service(index) .service(fetch_post) diff --git a/src/utils.rs b/src/utils.rs index 2603a32..5e9a30d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -18,6 +18,10 @@ pub fn generate_follow_accept_id(domain: &str, db_id: &str) -> Result Result { + Url::parse(&format!("https://{}/apbridge/followreq/{}", domain, db_id)) +} + pub fn generate_lysand_post_url(domain: &str, db_id: &str) -> Result { Url::parse(&format!( "https://{}/apbridge/lysand/object/{}",