diff --git a/src/http.rs b/src/http.rs index 9b99203..6ea3e0e 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,8 +1,5 @@ use crate::{ - database::StateHandle, - entities::user, - error::Error, - objects::person::{DbUser, PersonAcceptedActivities}, + database::StateHandle, entities::user, error::Error, lysand::{self, conversion::receive_lysand_note}, objects::person::{DbUser, PersonAcceptedActivities} }; use activitypub_federation::{ actix_web::{inbox::receive_activity, signing_actor}, @@ -36,6 +33,11 @@ pub fn listen(config: &FederationConfig) -> Result<(), Error> { Ok(()) } +pub fn lysand_inbox(note: web::Json, id: web::Path, data: Data) -> Result { + tokio::spawn(receive_lysand_note(note.into_inner(), id.into_inner(), data.app_data().clone())); + Ok(HttpResponse::Created().finish()) +} + /// Handles requests to fetch system user json over HTTP /*pub async fn http_get_system_user(data: Data) -> Result { let json_user = data.system_user.clone().into_json(&data).await?; diff --git a/src/lysand/conversion.rs b/src/lysand/conversion.rs new file mode 100644 index 0000000..ff4770a --- /dev/null +++ b/src/lysand/conversion.rs @@ -0,0 +1,46 @@ +use activitypub_federation::fetch::object_id::ObjectId; +use activitystreams_kinds::public; +use sea_orm::{EntityTrait, QueryFilter}; +use url::Url; + +use crate::{database::State, entities::{self, post, prelude}, objects::post::Mention, utils::{generate_object_id, generate_user_id}, FEDERATION_CONFIG}; + +use super::objects::Note; + +pub async fn receive_lysand_note(note: Note, db_id: String, db: State) { + let author: entities::user::Model = todo!(); + let user_res = prelude::User::find_by_id(db_id).one(db.database_connection.as_ref()).await; + if user_res.is_err() { + println!("{}", user_res.unwrap_err()); + return; + } + if let Some(target) = user_res.ok().unwrap() { + let data = FEDERATION_CONFIG.get().unwrap(); + let id: ObjectId = generate_object_id(data.domain(), ¬e.id.to_string()).unwrap().into(); + let user_id = generate_user_id(data.domain(), &target.id.to_string()).unwrap(); + let to = match note.visibility.unwrap_or(super::objects::VisibilityType::Public) { + super::objects::VisibilityType::Public => vec![public(), Url::parse(&author.inbox).unwrap()], + super::objects::VisibilityType::Followers => vec![Url::parse(&author.inbox).unwrap()], + super::objects::VisibilityType::Direct => vec![user_id], + super::objects::VisibilityType::Unlisted => vec![Url::parse(&author.inbox).unwrap()], + }; + let cc = match note.visibility.unwrap_or(super::objects::VisibilityType::Public) { + super::objects::VisibilityType::Unlisted => Some(vec![public()]), + _ => None + }; + let mut tag: Vec = Vec::new(); + for l_tag in note.mentions.unwrap_or_default() { + tag.push(Mention { href: l_tag, //todo convert to ap url + kind: Default::default(), }) + } + let ap_note = crate::objects::post::Note { + kind: Default::default(), + id, + sensitive: note.is_sensitive.unwrap_or(false), + cc, + to, + tag, + + } + } +} \ No newline at end of file diff --git a/src/lysand/mod.rs b/src/lysand/mod.rs index c1ee410..58e6578 100644 --- a/src/lysand/mod.rs +++ b/src/lysand/mod.rs @@ -1,3 +1,4 @@ pub mod objects; pub mod superx; pub mod test; +pub mod conversion; \ No newline at end of file diff --git a/src/lysand/objects.rs b/src/lysand/objects.rs index 08d09ee..da9bfa3 100644 --- a/src/lysand/objects.rs +++ b/src/lysand/objects.rs @@ -58,7 +58,7 @@ pub enum CategoryType { Image, Video, Audio, - Messaging + Messaging, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -66,7 +66,7 @@ pub enum VisibilityType { Public, Unlisted, Followers, - Direct + Direct, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -191,7 +191,7 @@ pub struct LinkPreview { title: String, link: Url, image: Option, - icon: Option + icon: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -225,4 +225,4 @@ pub struct Outbox { pub next: Option, pub prev: Option, pub items: Vec, -} \ No newline at end of file +} diff --git a/src/lysand/test.rs b/src/lysand/test.rs index 733b3c4..a45032b 100644 --- a/src/lysand/test.rs +++ b/src/lysand/test.rs @@ -22,10 +22,7 @@ pub async fn main() -> anyhow::Result<()> { let user_json = serde_json::to_string_pretty(&SortAlphabetically(&user))?; println!("{}", user_json); - let response_outbox = client - .get(user.outbox.as_str()) - .send() - .await?; + let response_outbox = client.get(user.outbox.as_str()).send().await?; let outbox_json = response_outbox.text().await?; let outbox = super::superx::deserialize_outbox(outbox_json).await?; diff --git a/src/main.rs b/src/main.rs index 66dbcf5..27ceb1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,12 +76,12 @@ async fn post_manually( ) -> actix_web::Result { let local_user = state.local_user().await?; let data = FEDERATION_CONFIG.get().unwrap(); - let creator = + let target = webfinger_resolve_actor::(path.0.as_str(), &data.to_request_data()) .await?; let mention = Mention { - href: Url::parse(&creator.id)?, + href: Url::parse(&target.id)?, kind: Default::default(), }; let id: ObjectId = generate_object_id(data.domain())?.into(); @@ -89,16 +89,16 @@ async fn post_manually( kind: Default::default(), id, sensitive: false, - attributed_to: Url::parse(&data.local_user().await?.id).unwrap().into(), + attributed_to: Url::parse(&local_user.id).unwrap().into(), to: vec![public()], - content: format!("{} {}", path.1, creator.name), + content: format!("{} {}", path.1, target.name), tag: vec![mention], in_reply_to: None, }; CreatePost::send( note, - creator.shared_inbox_or_inbox(), + target.shared_inbox_or_inbox(), &data.to_request_data(), ) .await?; @@ -131,16 +131,18 @@ async fn follow_manually( Ok(HttpResponse::Ok().json(Response { health: true })) } -const DOMAIN_DEF: &str = "example.com"; +const DOMAIN_DEF: &str = "social.lysand.org"; const LOCAL_USER_NAME: &str = "example"; lazy_static! { - static ref SERVER_URL: String = env::var("LISTEN").unwrap_or("127.0.0.1:8080".to_string()); + static ref SERVER_URL: String = env::var("LISTEN").unwrap_or("0.0.0.0:8080".to_string()); static ref DATABASE_URL: String = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); static ref USERNAME: String = env::var("LOCAL_USER_NAME").unwrap_or(LOCAL_USER_NAME.to_string()); - static ref API_DOMAIN: String = env::var("API_DOMAIN").unwrap_or(DOMAIN_DEF.to_string()); - static ref FEDERATED_DOMAIN: String = env::var("FEDERATED_DOMAIN").unwrap_or(DOMAIN_DEF.to_string()); + static ref API_DOMAIN: String = env::var("API_DOMAIN").expect("not set API_DOMAIN"); + static ref LYSAND_DOMAIN: String = env::var("LYSAND_DOMAIN").expect("not set LYSAND_DOMAIN"); + static ref FEDERATED_DOMAIN: String = + env::var("FEDERATED_DOMAIN").unwrap_or(API_DOMAIN.to_string()); } static DB: OnceLock = OnceLock::new(); @@ -204,7 +206,7 @@ async fn main() -> actix_web::Result<(), anyhow::Error> { }; let data = FederationConfig::builder() - .domain(env::var("FEDERATED_DOMAIN").expect("FEDERATED_DOMAIN must be set")) + .domain(FEDERATED_DOMAIN.to_string()) .app_data(state.clone()) .http_signature_compat(true) .signed_fetch_actor(&state.local_user().await.unwrap()) @@ -214,18 +216,9 @@ async fn main() -> actix_web::Result<(), anyhow::Error> { let _ = FEDERATION_CONFIG.set(data.clone()); let mut labels = HashMap::new(); - labels.insert( - "domain".to_string(), - env::var("FEDERATED_DOMAIN") - .expect("FEDERATED_DOMAIN must be set") - .to_string(), - ); - labels.insert( - "name".to_string(), - env::var("LOCAL_USER_NAME") - .expect("LOCAL_USER_NAME must be set") - .to_string(), - ); + labels.insert("domain".to_string(), FEDERATED_DOMAIN.to_string()); + labels.insert("name".to_string(), USERNAME.to_string()); + labels.insert("api_domain".to_string(), API_DOMAIN.to_string()); let prometheus = PrometheusMetricsBuilder::new("api") .endpoint("/metrics") diff --git a/src/objects/post.rs b/src/objects/post.rs index 09fbb83..ee2a83e 100644 --- a/src/objects/post.rs +++ b/src/objects/post.rs @@ -40,6 +40,7 @@ pub struct Note { pub(crate) in_reply_to: Option>, pub(crate) tag: Vec, pub(crate) sensitive: bool, + pub(crate) cc: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/utils.rs b/src/utils.rs index 04f4195..55fd266 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,18 +1,21 @@ -use rand::{distributions::Alphanumeric, thread_rng, Rng}; use url::{ParseError, Url}; -/// Just generate random url as object id. In a real project, you probably want to use -/// an url which contains the database id for easy retrieval (or store the random id in db). -pub fn generate_object_id(domain: &str) -> Result { - let id: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(7) - .map(char::from) - .collect(); - Url::parse(&format!("https://{}/objects/{}", domain, id)) +pub fn generate_object_id(domain: &str, uuid: &str) -> Result { + let id: String = uuid::Uuid::new_v4().to_string(); + Url::parse(&format!("https://{}/apbridge/object/{}", domain, id)) +} + +pub fn generate_user_id(domain: &str, uuid: &str) -> Result { + let id: String = uuid::Uuid::new_v4().to_string(); + Url::parse(&format!("https://{}/apbridge/user/{}", domain, id)) +} + +pub fn generate_random_object_id(domain: &str) -> Result { + let id: String = uuid::Uuid::new_v4().to_string(); + Url::parse(&format!("https://{}/apbridge/object/{}", domain, id)) } /// Generate a follow accept id pub fn generate_follow_accept_id(domain: &str, db_id: i32) -> Result { - Url::parse(&format!("https://{}/activities/follow/{}", domain, db_id)) + Url::parse(&format!("https://{}/apbridge/activity/follow/{}", domain, db_id)) }