use super::*;
+use crate::apub::activities::follow_community;
use crate::apub::{format_community_name, gen_keypair_str, make_apub_endpoint, EndpointType};
use diesel::PgConnection;
use std::str::FromStr;
let user_id = claims.id;
- let community_follower_form = CommunityFollowerForm {
- community_id: data.community_id,
- user_id,
- };
-
- if data.follow {
- match CommunityFollower::follow(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
+ let community = Community::read(conn, data.community_id)?;
+ if community.local {
+ let community_follower_form = CommunityFollowerForm {
+ community_id: data.community_id,
+ user_id,
};
+
+ if data.follow {
+ match CommunityFollower::follow(&conn, &community_follower_form) {
+ Ok(user) => user,
+ Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
+ };
+ } else {
+ match CommunityFollower::ignore(&conn, &community_follower_form) {
+ Ok(user) => user,
+ Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
+ };
+ }
} else {
- match CommunityFollower::ignore(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
- };
+ // TODO: still have to implement unfollow
+ let user = User_::read(conn, user_id)?;
+ follow_community(&community, &user, conn)?;
+ // TODO: this needs to return a "pending" state, until Accept is received from the remote server
}
let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
use crate::db::post::Post;
use crate::db::user::User_;
use crate::db::Crud;
-use activitystreams::activity::{Create, Update};
+use activitystreams::activity::{Accept, Create, Follow, Update};
use activitystreams::object::properties::ObjectProperties;
+use activitystreams::BaseBox;
use activitystreams::{context, public};
use diesel::PgConnection;
use failure::Error;
Ok(())
}
-fn send_activity<A>(activity: &A) -> Result<(), Error>
+fn send_activity<A>(activity: &A, to: Vec<String>) -> Result<(), Error>
where
A: Serialize + Debug,
{
let json = serde_json::to_string(&activity)?;
- for i in get_following_instances() {
- // TODO: need to send this to the inbox of following users
- let inbox = format!(
- "{}://{}/federation/inbox",
- get_apub_protocol_string(),
- i.domain
- );
- let res = Request::post(inbox)
+ println!("sending data {}", json);
+ for t in to {
+ println!("to: {}", t);
+ let res = Request::post(t)
.header("Content-Type", "application/json")
.body(json.to_owned())?
.send()?;
Ok(())
}
+fn get_followers(_community: &Community) -> Vec<String> {
+ // TODO: this is wrong, needs to go to the (non-local) followers of the community
+ get_following_instances()
+ .iter()
+ .map(|i| {
+ format!(
+ "{}://{}/federation/inbox",
+ get_apub_protocol_string(),
+ i.domain
+ )
+ })
+ .collect()
+}
+
pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
let page = post.as_page(conn)?;
let community = Community::read(conn, post.community_id)?;
.create_props
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(page)?;
- send_activity(&create)?;
+ send_activity(&create, get_followers(&community))?;
Ok(())
}
.update_props
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(page)?;
- send_activity(&update)?;
+ send_activity(&update, get_followers(&community))?;
+ Ok(())
+}
+
+pub fn follow_community(
+ community: &Community,
+ user: &User_,
+ _conn: &PgConnection,
+) -> Result<(), Error> {
+ let mut follow = Follow::new();
+ follow
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ // TODO: needs proper id
+ .set_id(user.actor_id.clone())?;
+ follow
+ .follow_props
+ .set_actor_xsd_any_uri(user.actor_id.clone())?
+ .set_object_xsd_any_uri(community.actor_id.clone())?;
+ let to = format!("{}/inbox", community.actor_id);
+ send_activity(&follow, vec![to])?;
+ Ok(())
+}
+
+pub fn accept_follow(follow: &Follow) -> Result<(), Error> {
+ let mut accept = Accept::new();
+ accept
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ // TODO: needs proper id
+ .set_id(
+ follow
+ .follow_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string(),
+ )?;
+ accept
+ .accept_props
+ .set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
+ let to = format!(
+ "{}/inbox",
+ follow
+ .follow_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string()
+ );
+ send_activity(&accept, vec![to])?;
Ok(())
}
.set_id(self.actor_id.to_owned())?
.set_name_xsd_string(self.name.to_owned())?
.set_published(convert_datetime(self.published))?
- .set_attributed_to_xsd_any_uri(make_apub_endpoint(EndpointType::User, &creator.name))?;
+ .set_attributed_to_xsd_any_uri(creator.actor_id)?;
if let Some(u) = self.updated.to_owned() {
oprops.set_updated(convert_datetime(u))?;
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
) -> Result<HttpResponse<Body>, Error> {
let community = Community::read_from_name(&&db.get()?, info.community_name.to_owned())?;
- let base_url = make_apub_endpoint(EndpointType::Community, &community.name);
let connection = establish_unpooled_connection();
//As we are an object, we validated that the community id was valid
let oprops: &mut ObjectProperties = collection.as_mut();
oprops
.set_context_xsd_any_uri(context())?
- .set_id(base_url)?;
+ .set_id(community.actor_id)?;
collection
.collection_props
.set_total_items(community_followers.len() as u64)?;
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
) -> Result<HttpResponse<Body>, Error> {
let community = Community::read_from_name(&&db.get()?, info.community_name.to_owned())?;
- let base_url = make_apub_endpoint(EndpointType::Community, &community.name);
let conn = establish_unpooled_connection();
//As we are an object, we validated that the community id was valid
let oprops: &mut ObjectProperties = collection.as_mut();
oprops
.set_context_xsd_any_uri(context())?
- .set_id(base_url)?;
+ .set_id(community.actor_id)?;
collection
.collection_props
.set_many_items_base_boxes(
+use crate::apub::activities::accept_follow;
+use crate::apub::fetcher::fetch_remote_user;
+use crate::db::community::{Community, CommunityFollower, CommunityFollowerForm};
use crate::db::post::{Post, PostForm};
use crate::db::Crud;
+use crate::db::Followable;
+use activitystreams::activity::{Accept, Create, Follow, Update};
use activitystreams::object::Page;
-use activitystreams::{
- object::{Object, ObjectBox},
- primitives::XsdAnyUri,
- Base, BaseBox, PropRefs,
-};
use actix_web::{web, HttpResponse};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use failure::Error;
-use std::collections::HashMap;
+use url::Url;
// TODO: need a proper actor that has this inbox
input: web::Json<AcceptedObjects>,
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
) -> Result<HttpResponse, Error> {
+ // TODO: make sure that things are received in the correct inbox
+ // (by using seperate handler functions and checking the user/community name in the path)
let input = input.into_inner();
let conn = &db.get().unwrap();
- match input.kind {
- ValidTypes::Create => handle_create(&input, conn),
- ValidTypes::Update => handle_update(&input, conn),
+ match input {
+ AcceptedObjects::Create(c) => handle_create(&c, conn),
+ AcceptedObjects::Update(u) => handle_update(&u, conn),
+ AcceptedObjects::Follow(f) => handle_follow(&f, conn),
+ AcceptedObjects::Accept(a) => handle_accept(&a, conn),
}
}
-fn handle_create(create: &AcceptedObjects, conn: &PgConnection) -> Result<HttpResponse, Error> {
- let page = create.object.to_owned().to_concrete::<Page>()?;
+fn handle_create(create: &Create, conn: &PgConnection) -> Result<HttpResponse, Error> {
+ let page = create
+ .create_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .to_concrete::<Page>()?;
let post = PostForm::from_page(&page, conn)?;
Post::create(conn, &post)?;
// TODO: send the new post out via websocket
Ok(HttpResponse::Ok().finish())
}
-fn handle_update(update: &AcceptedObjects, conn: &PgConnection) -> Result<HttpResponse, Error> {
- let page = update.object.to_owned().to_concrete::<Page>()?;
+fn handle_update(update: &Update, conn: &PgConnection) -> Result<HttpResponse, Error> {
+ let page = update
+ .update_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .to_concrete::<Page>()?;
let post = PostForm::from_page(&page, conn)?;
let id = Post::read_from_apub_id(conn, &post.ap_id)?.id;
Post::update(conn, id, &post)?;
Ok(HttpResponse::Ok().finish())
}
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct AcceptedObjects {
- pub id: XsdAnyUri,
+fn handle_follow(follow: &Follow, conn: &PgConnection) -> Result<HttpResponse, Error> {
+ println!("received follow: {:?}", &follow);
- #[serde(rename = "type")]
- pub kind: ValidTypes,
-
- pub actor: XsdAnyUri,
-
- pub object: BaseBox,
-
- #[serde(flatten)]
- ext: HashMap<String, serde_json::Value>,
+ // TODO: make sure this is a local community
+ let community_uri = follow
+ .follow_props
+ .get_object_xsd_any_uri()
+ .unwrap()
+ .to_string();
+ let community = Community::read_from_actor_id(conn, &community_uri)?;
+ let user_uri = follow
+ .follow_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string();
+ let user = fetch_remote_user(&Url::parse(&user_uri)?, conn)?;
+ // TODO: insert ID of the user into follows of the community
+ let community_follower_form = CommunityFollowerForm {
+ community_id: community.id,
+ user_id: user.id,
+ };
+ CommunityFollower::follow(&conn, &community_follower_form)?;
+ accept_follow(&follow)?;
+ Ok(HttpResponse::Ok().finish())
}
-#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "PascalCase")]
-pub enum ValidTypes {
- Create,
- Update,
+fn handle_accept(accept: &Accept, _conn: &PgConnection) -> Result<HttpResponse, Error> {
+ println!("received accept: {:?}", &accept);
+ // TODO: at this point, indicate to the user that they are following the community
+ Ok(HttpResponse::Ok().finish())
}
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
-#[serde(rename_all = "camelCase")]
-pub enum ValidObjects {
- Id(XsdAnyUri),
- Object(AnyExistingObject),
-}
-
-#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, PropRefs)]
-#[serde(rename_all = "camelCase")]
-#[prop_refs(Object)]
-pub struct AnyExistingObject {
- pub id: XsdAnyUri,
-
- #[serde(rename = "type")]
- pub kind: String,
-
- #[serde(flatten)]
- ext: HashMap<String, serde_json::Value>,
+#[derive(serde::Deserialize)]
+pub enum AcceptedObjects {
+ Create(Create),
+ Update(Update),
+ Follow(Follow),
+ Accept(Accept),
}
+use crate::apub::create_apub_response;
use crate::apub::fetcher::{fetch_remote_community, fetch_remote_user};
-use crate::apub::{create_apub_response, make_apub_endpoint, EndpointType};
use crate::convert_datetime;
use crate::db::community::Community;
use crate::db::post::{Post, PostForm};
impl Post {
pub fn as_page(&self, conn: &PgConnection) -> Result<Page, Error> {
- let base_url = make_apub_endpoint(EndpointType::Post, &self.id.to_string());
let mut page = Page::default();
let oprops: &mut ObjectProperties = page.as_mut();
let creator = User_::read(conn, self.creator_id)?;
oprops
// Not needed when the Post is embedded in a collection (like for community outbox)
.set_context_xsd_any_uri(context())?
- .set_id(base_url)?
+ .set_id(self.ap_id.to_owned())?
// Use summary field to be consistent with mastodon content warning.
// https://mastodon.xyz/@Louisa/103987265222901387.json
.set_summary_xsd_string(self.name.to_owned())?
.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(creator.actor_id)?;
if let Some(body) = &self.body {
oprops.set_content_xsd_string(body.to_owned())?;
// Set up websocket server
let server = ChatServer::startup(pool.clone()).start();
- // TODO: its probably failing because the other instance is not up yet
- // need to make a new thread and wait a bit before fetching
thread::spawn(move || {
// some work here
sleep(Duration::from_secs(5));
)
// TODO: this needs to be moved to the actors (eg /federation/u/{}/inbox)
.route("/federation/inbox", web::post().to(apub::inbox::inbox))
- .route("/federation/inbox", web::post().to(apub::inbox::inbox))
+ .route(
+ "/federation/c/{_}/inbox",
+ web::post().to(apub::inbox::inbox),
+ )
+ .route(
+ "/federation/u/{_}/inbox",
+ web::post().to(apub::inbox::inbox),
+ )
.route(
"/federation/c/{community_name}",
web::get().to(apub::community::get_apub_community_http),