Implement create activity

This commit is contained in:
Felix 2020-04-09 21:04:31 +02:00
parent 1836d017f9
commit f7330bf062
7 changed files with 91 additions and 27 deletions

View file

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::apub::activities::post_create;
use diesel::PgConnection; use diesel::PgConnection;
use std::str::FromStr; use std::str::FromStr;
@ -153,6 +154,8 @@ impl Perform<PostResponse> for Oper<CreatePost> {
Err(_e) => return Err(APIError::err("couldnt_create_post").into()), Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
}; };
post_create(&inserted_post, conn)?;
// They like their own post by default // They like their own post by default
let like_form = PostLikeForm { let like_form = PostLikeForm {
post_id: inserted_post.id, post_id: inserted_post.id,

View file

@ -0,0 +1,36 @@
use crate::apub::{get_apub_protocol_string, get_following_instances};
use crate::db::post::Post;
use crate::db::user::User_;
use crate::db::Crud;
use activitystreams::activity::Create;
use activitystreams::context;
use diesel::PgConnection;
use failure::Error;
use isahc::prelude::*;
pub fn post_create(post: &Post, conn: &PgConnection) -> Result<(), Error> {
let user = User_::read(conn, post.creator_id)?;
let page = post.as_page(conn)?;
let mut create = Create::new();
create.object_props.set_context_xsd_any_uri(context())?;
// TODO: seems like the create activity needs its own id (and be fetchable there)
create
.object_props
.set_id(page.object_props.get_id().unwrap().to_string())?;
create.create_props.set_actor_xsd_any_uri(user.actor_id)?;
create.create_props.set_object_base_box(page)?;
let json = serde_json::to_string(&create)?;
for i in get_following_instances() {
let inbox = format!(
"{}://{}/federation/inbox",
get_apub_protocol_string(),
i.domain
);
let res = Request::post(inbox)
.header("Content-Type", "application/json")
.body(json.to_owned())?
.send()?;
dbg!(res);
}
Ok(())
}

25
server/src/apub/inbox.rs Normal file
View file

@ -0,0 +1,25 @@
use crate::db::post::{Post, PostForm};
use crate::db::Crud;
use activitystreams::activity::Create;
use activitystreams::object::Page;
use actix_web::{web, HttpResponse};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use failure::Error;
pub async fn inbox(
create: web::Json<Create>,
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
) -> Result<HttpResponse, Error> {
let page = create
.create_props
.get_object_base_box()
.unwrap()
.to_owned()
.to_concrete::<Page>()?;
let post = PostForm::from_page(&page, &db.get().unwrap())?;
Post::create(&db.get().unwrap(), &post)?;
// TODO: send the new post out via websocket
dbg!(&post);
Ok(HttpResponse::Ok().finish())
}

View file

@ -1,4 +1,6 @@
pub mod activities;
pub mod community; pub mod community;
pub mod inbox;
pub mod post; pub mod post;
pub mod puller; pub mod puller;
pub mod user; pub mod user;

View file

@ -1,6 +1,7 @@
use crate::apub::puller::fetch_remote_user; use crate::apub::puller::{fetch_remote_community, fetch_remote_user};
use crate::apub::{create_apub_response, make_apub_endpoint, EndpointType}; use crate::apub::{create_apub_response, make_apub_endpoint, EndpointType};
use crate::convert_datetime; use crate::convert_datetime;
use crate::db::community::Community;
use crate::db::post::{Post, PostForm}; use crate::db::post::{Post, PostForm};
use crate::db::user::User_; use crate::db::user::User_;
use crate::db::Crud; use crate::db::Crud;
@ -34,6 +35,7 @@ impl Post {
let mut page = Page::default(); let mut page = Page::default();
let oprops: &mut ObjectProperties = page.as_mut(); let oprops: &mut ObjectProperties = page.as_mut();
let creator = User_::read(conn, self.creator_id)?; let creator = User_::read(conn, self.creator_id)?;
let community = Community::read(conn, self.community_id)?;
oprops oprops
// Not needed when the Post is embedded in a collection (like for community outbox) // Not needed when the Post is embedded in a collection (like for community outbox)
@ -41,6 +43,7 @@ impl Post {
.set_id(base_url)? .set_id(base_url)?
.set_name_xsd_string(self.name.to_owned())? .set_name_xsd_string(self.name.to_owned())?
.set_published(convert_datetime(self.published))? .set_published(convert_datetime(self.published))?
.set_to_xsd_any_uri(community.actor_id)?
.set_attributed_to_xsd_any_uri(make_apub_endpoint(EndpointType::User, &creator.name))?; .set_attributed_to_xsd_any_uri(make_apub_endpoint(EndpointType::User, &creator.name))?;
if let Some(body) = &self.body { if let Some(body) = &self.body {
@ -65,14 +68,17 @@ impl Post {
impl PostForm { impl PostForm {
pub fn from_page(page: &Page, conn: &PgConnection) -> Result<PostForm, Error> { pub fn from_page(page: &Page, conn: &PgConnection) -> Result<PostForm, Error> {
let oprops = &page.object_props; let oprops = &page.object_props;
let apub_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?; let creator_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?;
let creator = fetch_remote_user(&apub_id, conn)?; let creator = fetch_remote_user(&creator_id, conn)?;
let community_id = Url::parse(&oprops.get_to_xsd_any_uri().unwrap().to_string())?;
let community = fetch_remote_community(&community_id, conn)?;
Ok(PostForm { Ok(PostForm {
name: oprops.get_name_xsd_string().unwrap().to_string(), name: oprops.get_name_xsd_string().unwrap().to_string(),
url: oprops.get_url_xsd_any_uri().map(|u| u.to_string()), url: oprops.get_url_xsd_any_uri().map(|u| u.to_string()),
body: oprops.get_content_xsd_string().map(|c| c.to_string()), body: oprops.get_content_xsd_string().map(|c| c.to_string()),
creator_id: creator.id, creator_id: creator.id,
community_id: -1, community_id: community.id,
removed: None, removed: None,
locked: None, locked: None,
published: oprops published: oprops

View file

@ -91,8 +91,7 @@ fn fetch_remote_community_posts(
PostForm::from_page(&page, conn) PostForm::from_page(&page, conn)
}) })
.map(|pf: Result<PostForm, Error>| -> Result<Post, Error> { .map(|pf: Result<PostForm, Error>| -> Result<Post, Error> {
let mut pf2 = pf?; let pf2 = pf?;
pf2.community_id = community.id;
let existing = Post::read_from_apub_id(conn, &pf2.ap_id); let existing = Post::read_from_apub_id(conn, &pf2.ap_id);
match existing { match existing {
Err(NotFound {}) => Ok(Post::create(conn, &pf2)?), Err(NotFound {}) => Ok(Post::create(conn, &pf2)?),
@ -104,6 +103,7 @@ fn fetch_remote_community_posts(
) )
} }
// TODO: can probably merge these two methods?
pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result<User_, Error> { pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result<User_, Error> {
let person = fetch_remote_object::<PersonExt>(apub_id)?; let person = fetch_remote_object::<PersonExt>(apub_id)?;
let uf = UserForm::from_person(&person)?; let uf = UserForm::from_person(&person)?;
@ -114,6 +114,16 @@ pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result<User_, Er
Err(e) => return Err(Error::from(e)), Err(e) => return Err(Error::from(e)),
}) })
} }
pub fn fetch_remote_community(apub_id: &Url, conn: &PgConnection) -> Result<Community, Error> {
let group = fetch_remote_object::<GroupExt>(apub_id)?;
let cf = CommunityForm::from_group(&group, conn)?;
let existing = Community::read_from_actor_id(conn, &cf.actor_id);
Ok(match existing {
Err(NotFound {}) => Community::create(conn, &cf)?,
Ok(u) => Community::update(conn, u.id, &cf)?,
Err(e) => return Err(Error::from(e)),
})
}
// TODO: in the future, this should only be done when an instance is followed for the first time // TODO: in the future, this should only be done when an instance is followed for the first time
// after that, we should rely in the inbox, and fetch on demand when needed // after that, we should rely in the inbox, and fetch on demand when needed

View file

@ -1,12 +1,6 @@
use crate::api::community::ListCommunities;
use crate::api::Oper;
use crate::api::Perform;
use crate::apub; use crate::apub;
use crate::settings::Settings; use crate::settings::Settings;
use actix_web::web::Query; use actix_web::web;
use actix_web::{web, HttpResponse};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
pub fn config(cfg: &mut web::ServiceConfig) { pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation.enabled { if Settings::get().federation.enabled {
@ -16,6 +10,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
"/federation/communities", "/federation/communities",
web::get().to(apub::community::get_apub_community_list), web::get().to(apub::community::get_apub_community_list),
) )
// TODO: need a proper actor that has this inbox
.route("/federation/inbox", web::post().to(apub::inbox::inbox))
.route( .route(
"/federation/c/{community_name}", "/federation/c/{community_name}",
web::get().to(apub::community::get_apub_community_http), web::get().to(apub::community::get_apub_community_http),
@ -35,20 +31,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route( .route(
"/federation/p/{post_id}", "/federation/p/{post_id}",
web::get().to(apub::user::get_apub_user), web::get().to(apub::user::get_apub_user),
)
// TODO: we should be able to remove this but somehow that breaks the remote community list
.route(
"/api/v1/communities/list",
web::get().to(
|query: Query<ListCommunities>, db: web::Data<Pool<ConnectionManager<PgConnection>>>| {
let res = Oper::new(query.into_inner())
.perform(&db.get().unwrap())
.unwrap();
HttpResponse::Ok()
.content_type("application/json")
.body(serde_json::to_string(&res).unwrap())
},
),
); );
} }
} }