Get inbox working properly
This commit is contained in:
parent
ddf171f128
commit
88b17e90ac
9 changed files with 164 additions and 49 deletions
2
docker/federation/docker-compose.yml
vendored
2
docker/federation/docker-compose.yml
vendored
|
@ -28,6 +28,7 @@ services:
|
||||||
- LEMMY_FEDERATION__TLS_ENABLED=false
|
- LEMMY_FEDERATION__TLS_ENABLED=false
|
||||||
- LEMMY_PORT=8540
|
- LEMMY_PORT=8540
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
|
- RUST_LOG=actix_web=debug
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres_alpha
|
- postgres_alpha
|
||||||
|
@ -58,6 +59,7 @@ services:
|
||||||
- LEMMY_FEDERATION__TLS_ENABLED=false
|
- LEMMY_FEDERATION__TLS_ENABLED=false
|
||||||
- LEMMY_PORT=8550
|
- LEMMY_PORT=8550
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
|
- RUST_LOG=actix_web=debug
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres_beta
|
- postgres_beta
|
||||||
|
|
8
docker/federation/run-federation-test.bash
vendored
8
docker/federation/run-federation-test.bash
vendored
|
@ -1,9 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
pushd ../../ui/ || exit
|
if [ "$1" = "-yarn" ]; then
|
||||||
yarn build
|
pushd ../../ui/ || exit
|
||||||
popd || exit
|
yarn build
|
||||||
|
popd || exit
|
||||||
|
fi
|
||||||
|
|
||||||
pushd ../../server/ || exit
|
pushd ../../server/ || exit
|
||||||
cargo build
|
cargo build
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::apub::activities::post_create;
|
use crate::apub::activities::{post_create, post_update};
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct CreatePost {
|
pub struct CreatePost {
|
||||||
name: String,
|
name: String,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
|
@ -150,12 +150,12 @@ impl Perform<PostResponse> for Oper<CreatePost> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match Post::update_ap_id(&conn, inserted_post.id) {
|
let updated_post = match Post::update_ap_id(&conn, inserted_post.id) {
|
||||||
Ok(post) => post,
|
Ok(post) => post,
|
||||||
Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
|
Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
post_create(&inserted_post, &user, conn)?;
|
post_create(&updated_post, &user, conn)?;
|
||||||
|
|
||||||
// They like their own post by default
|
// They like their own post by default
|
||||||
let like_form = PostLikeForm {
|
let like_form = PostLikeForm {
|
||||||
|
@ -369,7 +369,8 @@ impl Perform<PostResponse> for Oper<EditPost> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a site ban
|
// Check for a site ban
|
||||||
if UserView::read(&conn, user_id)?.banned {
|
let user = User_::read(&conn, user_id)?;
|
||||||
|
if user.banned {
|
||||||
return Err(APIError::err("site_ban").into());
|
return Err(APIError::err("site_ban").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +401,7 @@ impl Perform<PostResponse> for Oper<EditPost> {
|
||||||
published: None,
|
published: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _updated_post = match Post::update(&conn, data.edit_id, &post_form) {
|
let updated_post = match Post::update(&conn, data.edit_id, &post_form) {
|
||||||
Ok(post) => post,
|
Ok(post) => post,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_type = if e.to_string() == "value too long for type character varying(200)" {
|
let err_type = if e.to_string() == "value too long for type character varying(200)" {
|
||||||
|
@ -442,6 +443,8 @@ impl Perform<PostResponse> for Oper<EditPost> {
|
||||||
ModStickyPost::create(&conn, &form)?;
|
ModStickyPost::create(&conn, &form)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post_update(&updated_post, &user, conn)?;
|
||||||
|
|
||||||
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
|
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
|
||||||
|
|
||||||
Ok(PostResponse { post: post_view })
|
Ok(PostResponse { post: post_view })
|
||||||
|
|
|
@ -3,30 +3,36 @@ use crate::db::community::Community;
|
||||||
use crate::db::post::Post;
|
use crate::db::post::Post;
|
||||||
use crate::db::user::User_;
|
use crate::db::user::User_;
|
||||||
use crate::db::Crud;
|
use crate::db::Crud;
|
||||||
use activitystreams::activity::Create;
|
use activitystreams::activity::{Create, Update};
|
||||||
use activitystreams::context;
|
use activitystreams::object::properties::ObjectProperties;
|
||||||
|
use activitystreams::{context, public};
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
|
use failure::_core::fmt::Debug;
|
||||||
use isahc::prelude::*;
|
use isahc::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
fn populate_object_props(
|
||||||
let page = post.as_page(conn)?;
|
props: &mut ObjectProperties,
|
||||||
let community = Community::read(conn, post.community_id)?;
|
addressed_to: &str,
|
||||||
let mut create = Create::new();
|
object_id: &str,
|
||||||
create.object_props.set_context_xsd_any_uri(context())?;
|
) -> Result<(), Error> {
|
||||||
create
|
props
|
||||||
.object_props
|
.set_context_xsd_any_uri(context())?
|
||||||
// TODO: seems like the create activity needs its own id (and be fetchable there)
|
// TODO: the activity needs a seperate id from the object
|
||||||
.set_id(page.object_props.get_id().unwrap().to_string())?
|
.set_id(object_id)?
|
||||||
// TODO: should to/cc go on the Create, or on the Post? or on both?
|
// TODO: should to/cc go on the Create, or on the Post? or on both?
|
||||||
// TODO: handle privacy on the receiving side (at least ignore anything thats not public)
|
// TODO: handle privacy on the receiving side (at least ignore anything thats not public)
|
||||||
.set_to_xsd_any_uri("https://www.w3.org/ns/activitystreams#Public")?
|
.set_to_xsd_any_uri(public())?
|
||||||
.set_cc_xsd_any_uri(format!("{}/followers", community.actor_id))?;
|
.set_cc_xsd_any_uri(addressed_to)?;
|
||||||
create
|
Ok(())
|
||||||
.create_props
|
}
|
||||||
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?;
|
|
||||||
create.create_props.set_object_base_box(page)?;
|
fn send_activity<A>(activity: &A) -> Result<(), Error>
|
||||||
let json = serde_json::to_string(&create)?;
|
where
|
||||||
|
A: Serialize + Debug,
|
||||||
|
{
|
||||||
|
let json = serde_json::to_string(&activity)?;
|
||||||
for i in get_following_instances() {
|
for i in get_following_instances() {
|
||||||
// TODO: need to send this to the inbox of following users
|
// TODO: need to send this to the inbox of following users
|
||||||
let inbox = format!(
|
let inbox = format!(
|
||||||
|
@ -42,3 +48,37 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
let mut create = Create::new();
|
||||||
|
populate_object_props(
|
||||||
|
&mut create.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&post.ap_id,
|
||||||
|
)?;
|
||||||
|
create
|
||||||
|
.create_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(page)?;
|
||||||
|
send_activity(&create)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let page = post.as_page(conn)?;
|
||||||
|
let community = Community::read(conn, post.community_id)?;
|
||||||
|
let mut update = Update::new();
|
||||||
|
populate_object_props(
|
||||||
|
&mut update.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&post.ap_id,
|
||||||
|
)?;
|
||||||
|
update
|
||||||
|
.update_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(page)?;
|
||||||
|
send_activity(&update)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -79,9 +79,9 @@ impl Community {
|
||||||
|
|
||||||
actor_props
|
actor_props
|
||||||
.set_preferred_username(self.title.to_owned())?
|
.set_preferred_username(self.title.to_owned())?
|
||||||
.set_inbox(format!("{}/inbox", &self.actor_id))?
|
.set_inbox(self.get_inbox_url())?
|
||||||
.set_outbox(format!("{}/outbox", &self.actor_id))?
|
.set_outbox(self.get_outbox_url())?
|
||||||
.set_followers(format!("{}/followers", &self.actor_id))?;
|
.set_followers(self.get_followers_url())?;
|
||||||
|
|
||||||
let public_key = PublicKey {
|
let public_key = PublicKey {
|
||||||
id: format!("{}#main-key", self.actor_id),
|
id: format!("{}#main-key", self.actor_id),
|
||||||
|
@ -91,6 +91,16 @@ impl Community {
|
||||||
|
|
||||||
Ok(group.extend(actor_props).extend(public_key.to_ext()))
|
Ok(group.extend(actor_props).extend(public_key.to_ext()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_followers_url(&self) -> String {
|
||||||
|
format!("{}/followers", &self.actor_id)
|
||||||
|
}
|
||||||
|
pub fn get_inbox_url(&self) -> String {
|
||||||
|
format!("{}/inbox", &self.actor_id)
|
||||||
|
}
|
||||||
|
pub fn get_outbox_url(&self) -> String {
|
||||||
|
format!("{}/outbox", &self.actor_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommunityForm {
|
impl CommunityForm {
|
||||||
|
|
|
@ -78,8 +78,7 @@ fn fetch_remote_community_posts(
|
||||||
community: &Community,
|
community: &Community,
|
||||||
conn: &PgConnection,
|
conn: &PgConnection,
|
||||||
) -> Result<Vec<Post>, Error> {
|
) -> Result<Vec<Post>, Error> {
|
||||||
// TODO: need to add outbox field to Community
|
let outbox_url = Url::parse(&community.get_outbox_url())?;
|
||||||
let outbox_url = Url::parse(&format!("{}/outbox", community.actor_id))?;
|
|
||||||
let outbox = fetch_remote_object::<OrderedCollection>(&outbox_url)?;
|
let outbox = fetch_remote_object::<OrderedCollection>(&outbox_url)?;
|
||||||
let items = outbox.collection_props.get_many_items_base_boxes();
|
let items = outbox.collection_props.get_many_items_base_boxes();
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,88 @@
|
||||||
use crate::db::post::{Post, PostForm};
|
use crate::db::post::{Post, PostForm};
|
||||||
use crate::db::Crud;
|
use crate::db::Crud;
|
||||||
use activitystreams::activity::Create;
|
|
||||||
use activitystreams::object::Page;
|
use activitystreams::object::Page;
|
||||||
|
use activitystreams::{
|
||||||
|
object::{Object, ObjectBox},
|
||||||
|
primitives::XsdAnyUri,
|
||||||
|
Base, BaseBox, PropRefs,
|
||||||
|
};
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use diesel::r2d2::{ConnectionManager, Pool};
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// TODO: need a proper actor that has this inbox
|
// TODO: need a proper actor that has this inbox
|
||||||
|
|
||||||
pub async fn create_inbox(
|
pub async fn inbox(
|
||||||
create: web::Json<Create>,
|
input: web::Json<AcceptedObjects>,
|
||||||
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let page = create
|
let input = input.into_inner();
|
||||||
.create_props
|
let conn = &db.get().unwrap();
|
||||||
.get_object_base_box()
|
match input.kind {
|
||||||
.unwrap()
|
ValidTypes::Create => handle_create(&input, conn),
|
||||||
.to_owned()
|
ValidTypes::Update => handle_update(&input, conn),
|
||||||
.to_concrete::<Page>()?;
|
}
|
||||||
let post = PostForm::from_page(&page, &db.get().unwrap())?;
|
}
|
||||||
Post::create(&db.get().unwrap(), &post)?;
|
|
||||||
|
fn handle_create(create: &AcceptedObjects, conn: &PgConnection) -> Result<HttpResponse, Error> {
|
||||||
|
let page = create.object.to_owned().to_concrete::<Page>()?;
|
||||||
|
let post = PostForm::from_page(&page, conn)?;
|
||||||
|
Post::create(conn, &post)?;
|
||||||
// TODO: send the new post out via websocket
|
// TODO: send the new post out via websocket
|
||||||
dbg!(&post);
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_update(update: &AcceptedObjects, conn: &PgConnection) -> Result<HttpResponse, Error> {
|
||||||
|
let page = update.object.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)?;
|
||||||
|
// TODO: send the new post out via websocket
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct AcceptedObjects {
|
||||||
|
pub id: XsdAnyUri,
|
||||||
|
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub kind: ValidTypes,
|
||||||
|
|
||||||
|
pub actor: XsdAnyUri,
|
||||||
|
|
||||||
|
pub object: BaseBox,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
ext: HashMap<String, serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub enum ValidTypes {
|
||||||
|
Create,
|
||||||
|
Update,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>,
|
||||||
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl Post {
|
||||||
// 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)
|
||||||
.set_context_xsd_any_uri(context())?
|
.set_context_xsd_any_uri(context())?
|
||||||
.set_id(base_url)?
|
.set_id(base_url)?
|
||||||
// Use summary field to be consistent with mastodon content warning.
|
// Use summary field to be consistent with mastodon content warning.
|
||||||
// https://mastodon.xyz/@Louisa/103987265222901387.json
|
// https://mastodon.xyz/@Louisa/103987265222901387.json
|
||||||
.set_summary_xsd_string(self.name.to_owned())?
|
.set_summary_xsd_string(self.name.to_owned())?
|
||||||
.set_published(convert_datetime(self.published))?
|
.set_published(convert_datetime(self.published))?
|
||||||
|
|
|
@ -11,10 +11,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
web::get().to(apub::community::get_apub_community_list),
|
web::get().to(apub::community::get_apub_community_list),
|
||||||
)
|
)
|
||||||
// TODO: this needs to be moved to the actors (eg /federation/u/{}/inbox)
|
// TODO: this needs to be moved to the actors (eg /federation/u/{}/inbox)
|
||||||
.route(
|
.route("/federation/inbox", web::post().to(apub::inbox::inbox))
|
||||||
"/federation/inbox",
|
.route("/federation/inbox", web::post().to(apub::inbox::inbox))
|
||||||
web::post().to(apub::inbox::create_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),
|
||||||
|
|
Reference in a new issue