forked from nutomic/lemmy
Add correct ActivityPub types conversion for Community and Post.
This commit is contained in:
parent
48ef6ed4a9
commit
844a97a6a5
8 changed files with 339 additions and 98 deletions
|
@ -328,7 +328,7 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
||||||
CommentLike::remove(&conn, &like_form)?;
|
CommentLike::remove(&conn, &like_form)?;
|
||||||
|
|
||||||
// Only add the like if the score isnt 0
|
// Only add the like if the score isnt 0
|
||||||
let do_add = &like_form.score != &0 && (&like_form.score == &1 || &like_form.score == &-1);
|
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
||||||
if do_add {
|
if do_add {
|
||||||
let _inserted_like = match CommentLike::like(&conn, &like_form) {
|
let _inserted_like = match CommentLike::like(&conn, &like_form) {
|
||||||
Ok(like) => like,
|
Ok(like) => like,
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
extern crate activitypub;
|
|
||||||
use self::activitypub::{actor::Person, context};
|
|
||||||
use crate::db::user::User_;
|
|
||||||
|
|
||||||
impl User_ {
|
|
||||||
pub fn person(&self) -> Person {
|
|
||||||
use crate::{to_datetime_utc, Settings};
|
|
||||||
let base_url = &format!("{}/user/{}", Settings::get().api_endpoint(), self.name);
|
|
||||||
let mut person = Person::default();
|
|
||||||
person.object_props.set_context_object(context()).ok();
|
|
||||||
person.object_props.set_id_string(base_url.to_string()).ok();
|
|
||||||
person
|
|
||||||
.object_props
|
|
||||||
.set_name_string(self.name.to_owned())
|
|
||||||
.ok();
|
|
||||||
person
|
|
||||||
.object_props
|
|
||||||
.set_published_utctime(to_datetime_utc(self.published))
|
|
||||||
.ok();
|
|
||||||
if let Some(i) = self.updated {
|
|
||||||
person
|
|
||||||
.object_props
|
|
||||||
.set_updated_utctime(to_datetime_utc(i))
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
// person.object_props.summary = self.summary;
|
|
||||||
|
|
||||||
person
|
|
||||||
.ap_actor_props
|
|
||||||
.set_inbox_string(format!("{}/inbox", &base_url))
|
|
||||||
.ok();
|
|
||||||
person
|
|
||||||
.ap_actor_props
|
|
||||||
.set_outbox_string(format!("{}/outbox", &base_url))
|
|
||||||
.ok();
|
|
||||||
person
|
|
||||||
.ap_actor_props
|
|
||||||
.set_following_string(format!("{}/following", &base_url))
|
|
||||||
.ok();
|
|
||||||
person
|
|
||||||
.ap_actor_props
|
|
||||||
.set_liked_string(format!("{}/liked", &base_url))
|
|
||||||
.ok();
|
|
||||||
if let Some(i) = &self.preferred_username {
|
|
||||||
person
|
|
||||||
.ap_actor_props
|
|
||||||
.set_preferred_username_string(i.to_string())
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
person
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::User_;
|
|
||||||
use crate::db::{ListingType, SortType};
|
|
||||||
use crate::naive_now;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_person() {
|
|
||||||
let expected_user = User_ {
|
|
||||||
id: 52,
|
|
||||||
name: "thom".into(),
|
|
||||||
fedi_name: "rrf".into(),
|
|
||||||
preferred_username: None,
|
|
||||||
password_encrypted: "here".into(),
|
|
||||||
email: None,
|
|
||||||
icon: None,
|
|
||||||
published: naive_now(),
|
|
||||||
admin: false,
|
|
||||||
banned: false,
|
|
||||||
updated: None,
|
|
||||||
show_nsfw: false,
|
|
||||||
theme: "darkly".into(),
|
|
||||||
default_sort_type: SortType::Hot as i16,
|
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
|
||||||
lang: "browser".into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let person = expected_user.person();
|
|
||||||
assert_eq!(
|
|
||||||
"rrr/api/v1/user/thom",
|
|
||||||
person.object_props.id_string().unwrap()
|
|
||||||
);
|
|
||||||
let json = serde_json::to_string_pretty(&person).unwrap();
|
|
||||||
println!("{}", json);
|
|
||||||
}
|
|
||||||
}
|
|
112
server/src/apub/community.rs
Normal file
112
server/src/apub/community.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
use crate::apub::make_apub_endpoint;
|
||||||
|
use crate::db::community::Community;
|
||||||
|
use crate::db::community_view::CommunityFollowerView;
|
||||||
|
use crate::db::establish_connection;
|
||||||
|
use crate::to_datetime_utc;
|
||||||
|
use activitypub::{actor::Group, collection::UnorderedCollection, context};
|
||||||
|
use actix_web::body::Body;
|
||||||
|
use actix_web::web::Path;
|
||||||
|
use actix_web::HttpResponse;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
impl Community {
|
||||||
|
pub fn as_group(&self) -> Group {
|
||||||
|
let base_url = make_apub_endpoint("c", &self.name);
|
||||||
|
|
||||||
|
let mut group = Group::default();
|
||||||
|
|
||||||
|
group.object_props.set_context_object(context()).ok();
|
||||||
|
group.object_props.set_id_string(base_url.to_string()).ok();
|
||||||
|
group
|
||||||
|
.object_props
|
||||||
|
.set_name_string(self.name.to_owned())
|
||||||
|
.ok();
|
||||||
|
group
|
||||||
|
.object_props
|
||||||
|
.set_published_utctime(to_datetime_utc(self.published))
|
||||||
|
.ok();
|
||||||
|
if let Some(updated) = self.updated {
|
||||||
|
group
|
||||||
|
.object_props
|
||||||
|
.set_updated_utctime(to_datetime_utc(updated))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(description) = &self.description {
|
||||||
|
group
|
||||||
|
.object_props
|
||||||
|
.set_summary_string(description.to_string())
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
group
|
||||||
|
.ap_actor_props
|
||||||
|
.set_inbox_string(format!("{}/inbox", &base_url))
|
||||||
|
.ok();
|
||||||
|
group
|
||||||
|
.ap_actor_props
|
||||||
|
.set_outbox_string(format!("{}/outbox", &base_url))
|
||||||
|
.ok();
|
||||||
|
group
|
||||||
|
.ap_actor_props
|
||||||
|
.set_followers_string(format!("{}/followers", &base_url))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
group
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn followers_as_collection(&self) -> UnorderedCollection {
|
||||||
|
let base_url = make_apub_endpoint("c", &self.name);
|
||||||
|
|
||||||
|
let mut collection = UnorderedCollection::default();
|
||||||
|
collection.object_props.set_context_object(context()).ok();
|
||||||
|
collection
|
||||||
|
.object_props
|
||||||
|
.set_id_string(base_url.to_string())
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let connection = establish_connection();
|
||||||
|
//As we are an object, we validated that the community id was valid
|
||||||
|
let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap();
|
||||||
|
|
||||||
|
let ap_followers = community_followers
|
||||||
|
.iter()
|
||||||
|
.map(|follower| make_apub_endpoint("u", &follower.user_name))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
collection
|
||||||
|
.collection_props
|
||||||
|
.set_items_string_vec(ap_followers)
|
||||||
|
.unwrap();
|
||||||
|
collection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct CommunityQuery {
|
||||||
|
community_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_apub_community(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
||||||
|
let connection = establish_connection();
|
||||||
|
|
||||||
|
if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type("application/activity+json")
|
||||||
|
.body(serde_json::to_string(&community.as_group()).unwrap())
|
||||||
|
} else {
|
||||||
|
HttpResponse::NotFound().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_apub_community_followers(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
||||||
|
let connection = establish_connection();
|
||||||
|
|
||||||
|
if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type("application/activity+json")
|
||||||
|
.body(serde_json::to_string(&community.followers_as_collection()).unwrap())
|
||||||
|
} else {
|
||||||
|
HttpResponse::NotFound().finish()
|
||||||
|
}
|
||||||
|
}
|
100
server/src/apub/mod.rs
Normal file
100
server/src/apub/mod.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
pub mod community;
|
||||||
|
pub mod post;
|
||||||
|
pub mod user;
|
||||||
|
use crate::Settings;
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::db::community::Community;
|
||||||
|
use crate::db::post::Post;
|
||||||
|
use crate::db::user::User_;
|
||||||
|
use crate::db::{ListingType, SortType};
|
||||||
|
use crate::{naive_now, Settings};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_person() {
|
||||||
|
let user = User_ {
|
||||||
|
id: 52,
|
||||||
|
name: "thom".into(),
|
||||||
|
fedi_name: "rrf".into(),
|
||||||
|
preferred_username: None,
|
||||||
|
password_encrypted: "here".into(),
|
||||||
|
email: None,
|
||||||
|
icon: None,
|
||||||
|
published: naive_now(),
|
||||||
|
admin: false,
|
||||||
|
banned: false,
|
||||||
|
updated: None,
|
||||||
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
|
default_sort_type: SortType::Hot as i16,
|
||||||
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
|
lang: "browser".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let person = user.as_person();
|
||||||
|
assert_eq!(
|
||||||
|
format!("https://{}/federation/u/thom", Settings::get().hostname),
|
||||||
|
person.object_props.id_string().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_community() {
|
||||||
|
let community = Community {
|
||||||
|
id: 42,
|
||||||
|
name: "Test".into(),
|
||||||
|
title: "Test Title".into(),
|
||||||
|
description: Some("Test community".into()),
|
||||||
|
category_id: 32,
|
||||||
|
creator_id: 52,
|
||||||
|
removed: false,
|
||||||
|
published: naive_now(),
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
deleted: false,
|
||||||
|
nsfw: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let group = community.as_group();
|
||||||
|
assert_eq!(
|
||||||
|
format!("https://{}/federation/c/Test", Settings::get().hostname),
|
||||||
|
group.object_props.id_string().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_post() {
|
||||||
|
let post = Post {
|
||||||
|
id: 62,
|
||||||
|
name: "A test post".into(),
|
||||||
|
url: None,
|
||||||
|
body: None,
|
||||||
|
creator_id: 52,
|
||||||
|
community_id: 42,
|
||||||
|
published: naive_now(),
|
||||||
|
removed: false,
|
||||||
|
locked: false,
|
||||||
|
stickied: false,
|
||||||
|
nsfw: false,
|
||||||
|
deleted: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let page = post.as_page();
|
||||||
|
assert_eq!(
|
||||||
|
format!("https://{}/federation/post/62", Settings::get().hostname),
|
||||||
|
page.object_props.id_string().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_apub_endpoint<S: Display, T: Display>(point: S, value: T) -> String {
|
||||||
|
format!(
|
||||||
|
"https://{}/federation/{}/{}",
|
||||||
|
Settings::get().hostname,
|
||||||
|
point,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
}
|
38
server/src/apub/post.rs
Normal file
38
server/src/apub/post.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::apub::make_apub_endpoint;
|
||||||
|
use crate::db::post::Post;
|
||||||
|
use crate::to_datetime_utc;
|
||||||
|
use activitypub::{context, object::Page};
|
||||||
|
|
||||||
|
impl Post {
|
||||||
|
pub fn as_page(&self) -> Page {
|
||||||
|
let base_url = make_apub_endpoint("post", self.id);
|
||||||
|
let mut page = Page::default();
|
||||||
|
|
||||||
|
page.object_props.set_context_object(context()).ok();
|
||||||
|
page.object_props.set_id_string(base_url.to_string()).ok();
|
||||||
|
page.object_props.set_name_string(self.name.to_owned()).ok();
|
||||||
|
|
||||||
|
if let Some(body) = &self.body {
|
||||||
|
page.object_props.set_content_string(body.to_owned()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(url) = &self.url {
|
||||||
|
page.object_props.set_url_string(url.to_owned()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
//page.object_props.set_attributed_to_string
|
||||||
|
|
||||||
|
page
|
||||||
|
.object_props
|
||||||
|
.set_published_utctime(to_datetime_utc(self.published))
|
||||||
|
.ok();
|
||||||
|
if let Some(updated) = self.updated {
|
||||||
|
page
|
||||||
|
.object_props
|
||||||
|
.set_updated_utctime(to_datetime_utc(updated))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
page
|
||||||
|
}
|
||||||
|
}
|
74
server/src/apub/user.rs
Normal file
74
server/src/apub/user.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use crate::apub::make_apub_endpoint;
|
||||||
|
use crate::db::establish_connection;
|
||||||
|
use crate::db::user::User_;
|
||||||
|
use crate::to_datetime_utc;
|
||||||
|
use activitypub::{actor::Person, context};
|
||||||
|
use actix_web::body::Body;
|
||||||
|
use actix_web::web::Path;
|
||||||
|
use actix_web::HttpResponse;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
impl User_ {
|
||||||
|
pub fn as_person(&self) -> Person {
|
||||||
|
let base_url = make_apub_endpoint("u", &self.name);
|
||||||
|
let mut person = Person::default();
|
||||||
|
person.object_props.set_context_object(context()).ok();
|
||||||
|
person.object_props.set_id_string(base_url.to_string()).ok();
|
||||||
|
person
|
||||||
|
.object_props
|
||||||
|
.set_name_string(self.name.to_owned())
|
||||||
|
.ok();
|
||||||
|
person
|
||||||
|
.object_props
|
||||||
|
.set_published_utctime(to_datetime_utc(self.published))
|
||||||
|
.ok();
|
||||||
|
if let Some(updated) = self.updated {
|
||||||
|
person
|
||||||
|
.object_props
|
||||||
|
.set_updated_utctime(to_datetime_utc(updated))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
person
|
||||||
|
.ap_actor_props
|
||||||
|
.set_inbox_string(format!("{}/inbox", &base_url))
|
||||||
|
.ok();
|
||||||
|
person
|
||||||
|
.ap_actor_props
|
||||||
|
.set_outbox_string(format!("{}/outbox", &base_url))
|
||||||
|
.ok();
|
||||||
|
person
|
||||||
|
.ap_actor_props
|
||||||
|
.set_following_string(format!("{}/following", &base_url))
|
||||||
|
.ok();
|
||||||
|
person
|
||||||
|
.ap_actor_props
|
||||||
|
.set_liked_string(format!("{}/liked", &base_url))
|
||||||
|
.ok();
|
||||||
|
if let Some(i) = &self.preferred_username {
|
||||||
|
person
|
||||||
|
.ap_actor_props
|
||||||
|
.set_preferred_username_string(i.to_string())
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
person
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct UserQuery {
|
||||||
|
user_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_apub_user(info: Path<UserQuery>) -> HttpResponse<Body> {
|
||||||
|
let connection = establish_connection();
|
||||||
|
|
||||||
|
if let Ok(user) = User_::find_by_email_or_username(&connection, &info.user_name) {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type("application/activity+json")
|
||||||
|
.body(serde_json::to_string(&user.as_person()).unwrap())
|
||||||
|
} else {
|
||||||
|
HttpResponse::NotFound().finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,9 +121,6 @@ impl Settings {
|
||||||
email_config,
|
email_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn api_endpoint(&self) -> String {
|
|
||||||
format!("{}/api/v1", self.hostname)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime<Utc> {
|
pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime<Utc> {
|
||||||
|
@ -210,10 +207,6 @@ pub fn send_email(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{extract_usernames, has_slurs, is_email_regex, remove_slurs, Settings};
|
use crate::{extract_usernames, has_slurs, is_email_regex, remove_slurs, Settings};
|
||||||
#[test]
|
|
||||||
fn test_api() {
|
|
||||||
assert_eq!(Settings::get().api_endpoint(), "rrr/api/v1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_email() {
|
fn test_email() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ use actix::prelude::*;
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
|
use lemmy_server::apub;
|
||||||
use lemmy_server::db::establish_connection;
|
use lemmy_server::db::establish_connection;
|
||||||
use lemmy_server::feeds;
|
use lemmy_server::feeds;
|
||||||
use lemmy_server::nodeinfo;
|
use lemmy_server::nodeinfo;
|
||||||
|
@ -243,6 +244,19 @@ fn main() {
|
||||||
// RSS
|
// RSS
|
||||||
.route("/feeds/{type}/{name}.xml", web::get().to(feeds::get_feed))
|
.route("/feeds/{type}/{name}.xml", web::get().to(feeds::get_feed))
|
||||||
.route("/feeds/all.xml", web::get().to(feeds::get_all_feed))
|
.route("/feeds/all.xml", web::get().to(feeds::get_all_feed))
|
||||||
|
// Federation
|
||||||
|
.route(
|
||||||
|
"/federation/c/{community_name}",
|
||||||
|
web::get().to(apub::community::get_apub_community),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/federation/c/{community_name}/followers",
|
||||||
|
web::get().to(apub::community::get_apub_community_followers),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/federation/u/{user_name}",
|
||||||
|
web::get().to(apub::user::get_apub_user),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.bind((settings.bind, settings.port))
|
.bind((settings.bind, settings.port))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
Loading…
Reference in a new issue