mirror of
https://github.com/versia-pub/activitypub.git
synced 2025-12-06 06:38:20 +01:00
update
This commit is contained in:
parent
b495fbe80e
commit
ba901981f0
|
|
@ -6,13 +6,16 @@ use activitypub_federation::{
|
||||||
traits::{ActivityHandler, Actor, Object},
|
traits::{ActivityHandler, Actor, Object},
|
||||||
};
|
};
|
||||||
use activitystreams_kinds::activity::{AcceptType, FollowType};
|
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 serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::StateHandle,
|
database::StateHandle,
|
||||||
entities::{follow_relation, post, user},
|
entities::{
|
||||||
|
follow_relation::{self, Entity},
|
||||||
|
post, prelude, user,
|
||||||
|
},
|
||||||
error,
|
error,
|
||||||
utils::{generate_follow_accept_id, generate_random_object_id},
|
utils::{generate_follow_accept_id, generate_random_object_id},
|
||||||
DB,
|
DB,
|
||||||
|
|
@ -20,11 +23,11 @@ use crate::{
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Follow {
|
pub struct Follow {
|
||||||
actor: ObjectId<user::Model>,
|
pub actor: ObjectId<user::Model>,
|
||||||
object: ObjectId<user::Model>,
|
pub object: ObjectId<user::Model>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: FollowType,
|
pub kind: FollowType,
|
||||||
id: Url,
|
pub id: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Follow {
|
impl Follow {
|
||||||
|
|
@ -154,19 +157,21 @@ async fn accept_follow(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_follow(
|
async fn save_follow(
|
||||||
local_user: user::Model,
|
followee: user::Model,
|
||||||
follower: user::Model,
|
follower: user::Model,
|
||||||
) -> Result<follow_relation::Model, crate::error::Error> {
|
) -> Result<follow_relation::Model, crate::error::Error> {
|
||||||
let url = Url::parse(&follower.url)?;
|
let db = DB.get().unwrap();
|
||||||
let follow_relation = follow_relation::ActiveModel {
|
let query = prelude::FollowRelation::find()
|
||||||
followee_id: Set(local_user.id.clone()),
|
.filter(follow_relation::Column::FollowerId.eq(follower.id.as_str()))
|
||||||
follower_id: Set(follower.id.clone()),
|
.filter(follow_relation::Column::FolloweeId.eq(followee.id.as_str()))
|
||||||
followee_host: Set(None),
|
.one(db)
|
||||||
follower_host: Set(Some(url.host_str().unwrap().to_string())),
|
.await?;
|
||||||
followee_inbox: Set(Some(local_user.inbox.clone())),
|
if query.is_none() {
|
||||||
follower_inbox: Set(Some(follower.inbox.clone())),
|
return Err(crate::error::Error(anyhow::anyhow!("oopsie woopise")));
|
||||||
..Default::default()
|
}
|
||||||
};
|
// modify db entry
|
||||||
let model = follow_relation.insert(DB.get().unwrap()).await?;
|
let res = prelude::FollowRelation::update(query.unwrap());
|
||||||
|
|
||||||
|
|
||||||
Ok(model)
|
Ok(model)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,15 @@ use sea_orm::entity::prelude::*;
|
||||||
#[sea_orm(table_name = "follow_relation")]
|
#[sea_orm(table_name = "follow_relation")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key)]
|
#[sea_orm(primary_key)]
|
||||||
pub id: i32,
|
pub id: String,
|
||||||
pub followee_id: String,
|
pub followee_id: String,
|
||||||
pub follower_id: String,
|
pub follower_id: String,
|
||||||
|
pub ap_json: String,
|
||||||
|
pub ap_accept_json: Option<String>,
|
||||||
|
pub ap_id: Option<String>,
|
||||||
|
pub ap_accept_id: Option<String>,
|
||||||
|
pub accept_id: Option<String>,
|
||||||
|
pub remote: bool,
|
||||||
pub followee_host: Option<String>,
|
pub followee_host: Option<String>,
|
||||||
pub follower_host: Option<String>,
|
pub follower_host: Option<String>,
|
||||||
pub followee_inbox: Option<String>,
|
pub followee_inbox: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use activitypub_federation::{
|
||||||
FEDERATION_CONTENT_TYPE,
|
FEDERATION_CONTENT_TYPE,
|
||||||
};
|
};
|
||||||
use activitystreams_kinds::{activity::CreateType, object};
|
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 sea_orm::{query, ColumnTrait, EntityTrait, QueryFilter};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -58,19 +58,7 @@ async fn query_post(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(user) = query.user_url.clone() {
|
if let Some(user) = query.user_url.clone() {
|
||||||
let opt_model = prelude::User::find()
|
let lysand_user = lysand_url_to_user(user).await?;
|
||||||
.filter(user::Column::Url.eq(user.as_str()))
|
|
||||||
.one(db)
|
|
||||||
.await?;
|
|
||||||
let target;
|
|
||||||
if let Some(model) = opt_model {
|
|
||||||
target = model;
|
|
||||||
} else {
|
|
||||||
target = ObjectId::<user::Model>::from(user)
|
|
||||||
.dereference(&data.to_request_data())
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
let lysand_user = lysand_user_from_db(target).await?;
|
|
||||||
|
|
||||||
return Ok(HttpResponse::Ok()
|
return Ok(HttpResponse::Ok()
|
||||||
.content_type("application/json")
|
.content_type("application/json")
|
||||||
|
|
@ -95,6 +83,16 @@ async fn query_post(
|
||||||
.json(lysand_post_from_db(target).await?))
|
.json(lysand_post_from_db(target).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/apbridge/lysand/inbox")]
|
||||||
|
async fn lysand_inbox(
|
||||||
|
body: web::Bytes,
|
||||||
|
state: web::Data<State>,
|
||||||
|
) -> actix_web::Result<HttpResponse, error::Error> {
|
||||||
|
|
||||||
|
|
||||||
|
Ok(HttpResponse::Created().finish())
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/apbridge/object/{post}")]
|
#[get("/apbridge/object/{post}")]
|
||||||
async fn fetch_post(
|
async fn fetch_post(
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
|
|
@ -199,3 +197,43 @@ async fn create_activity(
|
||||||
.content_type(FEDERATION_CONTENT_TYPE)
|
.content_type(FEDERATION_CONTENT_TYPE)
|
||||||
.json(create_with_context))
|
.json(create_with_context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn lysand_url_to_user(url: Url) -> anyhow::Result<super::objects::User> {
|
||||||
|
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::<user::Model>::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::<user::Model>::from(url)
|
||||||
|
.dereference(&data.to_request_data())
|
||||||
|
.await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((lysand_user_from_db(target.clone()).await?, target))
|
||||||
|
}
|
||||||
109
src/lysand/inbox.rs
Normal file
109
src/lysand/inbox.rs
Normal file
|
|
@ -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::<crate::objects::person::Person>(&(author.1.ap_json.clone()).unwrap())?;
|
||||||
|
let serial_ap_followee = serde_json::from_str::<crate::objects::person::Person>(&(followee.1.ap_json.clone()).unwrap())?;
|
||||||
|
|
||||||
|
let id = uuid::Uuid::now_v7().to_string();
|
||||||
|
|
||||||
|
let followee_object: ObjectId<user::Model> = serial_ap_followee.url.into();
|
||||||
|
let localuser_object: ObjectId<user::Model> = 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(())
|
||||||
|
}
|
||||||
|
|
@ -4,3 +4,4 @@ pub mod http;
|
||||||
pub mod objects;
|
pub mod objects;
|
||||||
pub mod superx;
|
pub mod superx;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
pub mod inbox;
|
||||||
|
|
@ -141,8 +141,8 @@ async fn follow_manually(
|
||||||
webfinger_resolve_actor::<State, user::Model>(path.as_str(), &data.to_request_data())
|
webfinger_resolve_actor::<State, user::Model>(path.as_str(), &data.to_request_data())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let followee_object: ObjectId<user::Model> = Url::parse(&followee.id)?.into();
|
let followee_object: ObjectId<user::Model> = Url::parse(&followee.url)?.into();
|
||||||
let localuser_object: ObjectId<user::Model> = Url::parse(&local_user.id)?.into();
|
let localuser_object: ObjectId<user::Model> = Url::parse(&local_user.url)?.into();
|
||||||
|
|
||||||
Follow::send(
|
Follow::send(
|
||||||
localuser_object,
|
localuser_object,
|
||||||
|
|
@ -289,6 +289,7 @@ async fn main() -> actix_web::Result<(), anyhow::Error> {
|
||||||
.service(follow_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("/apbridge/{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))
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(fetch_post)
|
.service(fetch_post)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ pub fn generate_follow_accept_id(domain: &str, db_id: &str) -> Result<Url, Parse
|
||||||
Url::parse(&format!("https://{}/apbridge/follow/{}", domain, db_id))
|
Url::parse(&format!("https://{}/apbridge/follow/{}", domain, db_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_follow_req_id(domain: &str, db_id: &str) -> Result<Url, ParseError> {
|
||||||
|
Url::parse(&format!("https://{}/apbridge/followreq/{}", domain, db_id))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_lysand_post_url(domain: &str, db_id: &str) -> Result<Url, ParseError> {
|
pub fn generate_lysand_post_url(domain: &str, db_id: &str) -> Result<Url, ParseError> {
|
||||||
Url::parse(&format!(
|
Url::parse(&format!(
|
||||||
"https://{}/apbridge/lysand/object/{}",
|
"https://{}/apbridge/lysand/object/{}",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue