diff --git a/Cargo.lock b/Cargo.lock
index 120f68278..3af6e1bd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1785,6 +1785,7 @@ dependencies = [
"serde",
"serde_json",
"serde_with",
+ "serial_test",
"sha2",
"strum",
"strum_macros",
diff --git a/config/config.hjson b/config/config.hjson
index 252fca250..361c06bef 100644
--- a/config/config.hjson
+++ b/config/config.hjson
@@ -1,5 +1,8 @@
# See the documentation for available config fields and descriptions:
# https://join-lemmy.org/docs/en/administration/configuration.html
{
- hostname: lemmy-alpha
+ hostname: lemmy-alphan
+ federation: {
+ enabled: true
+ }
}
diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml
index 2bc69c4a6..45ee1c312 100644
--- a/crates/apub/Cargo.toml
+++ b/crates/apub/Cargo.toml
@@ -50,3 +50,5 @@ thiserror = "1.0.29"
background-jobs = "0.9.0"
reqwest = { version = "0.11.4", features = ["json"] }
+[dev-dependencies]
+serial_test = "0.5.1"
diff --git a/crates/apub/assets/lemmy-person.json b/crates/apub/assets/lemmy-person.json
new file mode 100644
index 000000000..fb1afbe9a
--- /dev/null
+++ b/crates/apub/assets/lemmy-person.json
@@ -0,0 +1,46 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ {
+ "moderators": "as:moderators",
+ "sensitive": "as:sensitive",
+ "pt": "https://join-lemmy.org#",
+ "sc": "http://schema.org#",
+ "stickied": "as:stickied",
+ "matrixUserId": {
+ "type": "sc:Text",
+ "id": "as:alsoKnownAs"
+ },
+ "comments_enabled": {
+ "type": "sc:Boolean",
+ "id": "pt:commentsEnabled"
+ }
+ },
+ "https://w3id.org/security/v1"
+ ],
+ "type": "Person",
+ "id": "https://lemmy.ml/u/nutomic",
+ "preferredUsername": "nutomic",
+ "content": "
Lemmy maintainer. Interested in politics, video games, and many other things.
\n",
+ "mediaType": "text/html",
+ "source": {
+ "content": "Lemmy maintainer. Interested in politics, video games, and many other things.",
+ "mediaType": "text/markdown"
+ },
+ "icon": {
+ "type": "Image",
+ "url": "https://lemmy.ml/pictrs/image/ed9ej7.jpg"
+ },
+ "inbox": "https://lemmy.ml/u/nutomic/inbox",
+ "outbox": "https://lemmy.ml/u/nutomic/outbox",
+ "endpoints": {
+ "sharedInbox": "https://lemmy.ml/inbox"
+ },
+ "publicKey": {
+ "id": "https://lemmy.ml/u/nutomic#main-key",
+ "owner": "https://lemmy.ml/u/nutomic",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0lP99/s5Vv+XbPdkeqIJ\nwoD4GFnHmBnBHdEKChEUWfWj1TtioC/rGNoXFQeXQA3Amhy4nxSceiDnUgwkkuQY\nv0MtIW58NzgknEavtllxL+LSds5pg3gANaDIk8UiWTkqXTg0GnlJMpCK1Chen0l/\nszL6DEvUyTSuS5ZYDXFgewF89Pe7U0S15V5U2Harv7AgJYDyxmUL0D1pGuUCRqcE\nl5MTHJjrXeNnH1w2g8aly8YlO/Cr0L51rFg/lBF23vni7ZLv8HbmWh6YpaAf1R8h\nE45zKR7OHqymdjzrg1ITBwovefpwMkVgnJ+Wdr4HPnFlBSkXPoZeM11+Z8L0anzA\nXwIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "published": "2020-01-17T01:38:22.348392+00:00",
+ "updated": "2021-08-13T00:11:15.941990+00:00"
+}
diff --git a/crates/apub/assets/pleroma-person.json b/crates/apub/assets/pleroma-person.json
new file mode 100644
index 000000000..bc9008bab
--- /dev/null
+++ b/crates/apub/assets/pleroma-person.json
@@ -0,0 +1,79 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://queer.hacktivis.me/schemas/litepub-0.1.jsonld",
+ {
+ "@language": "und"
+ }
+ ],
+ "alsoKnownAs": [],
+ "attachment": [],
+ "capabilities": {
+ "acceptsChatMessages": true
+ },
+ "discoverable": false,
+ "endpoints": {
+ "oauthAuthorizationEndpoint": "https://queer.hacktivis.me/oauth/authorize",
+ "oauthRegistrationEndpoint": "https://queer.hacktivis.me/api/v1/apps",
+ "oauthTokenEndpoint": "https://queer.hacktivis.me/oauth/token",
+ "sharedInbox": "https://queer.hacktivis.me/inbox",
+ "uploadMedia": "https://queer.hacktivis.me/api/ap/upload_media"
+ },
+ "featured": "https://queer.hacktivis.me/users/lanodan/collections/featured",
+ "followers": "https://queer.hacktivis.me/users/lanodan/followers",
+ "following": "https://queer.hacktivis.me/users/lanodan/following",
+ "icon": {
+ "type": "Image",
+ "url": "https://queer.hacktivis.me/media/d23cf9b0-5586-4592-aca5-9a52777a6042/avatar_HD.png"
+ },
+ "id": "https://queer.hacktivis.me/users/lanodan",
+ "image": {
+ "type": "Image",
+ "url": "https://queer.hacktivis.me/media/37b6ce56-8c24-4e64-bd70-a76e84ab0c69/53a48a3a49ed5e5637a84e4f3663df17f8d764244bbc1027ba03cfc446e8b7bd.jpg"
+ },
+ "inbox": "https://queer.hacktivis.me/users/lanodan/inbox",
+ "manuallyApprovesFollowers": false,
+ "name": "Haelwenn /элвэн/ :bzh: ",
+ "outbox": "https://queer.hacktivis.me/users/lanodan/outbox",
+ "preferredUsername": "lanodan",
+ "publicKey": {
+ "id": "https://queer.hacktivis.me/users/lanodan#main-key",
+ "owner": "https://queer.hacktivis.me/users/lanodan",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsWOgdjSMc010qvxC3njI\nXJlFWMJ5gJ8QXCW/PajYdsHPM6d+jxBNJ6zp9/tIRa2m7bWHTSkuHQ7QthOpt6vu\n+dAWpKRLS607SPLItn/qUcyXvgN+H8shfyhMxvkVs9jXdtlBsLUVE7UNpN0dxzqe\nI79QWbf7o4amgaIWGRYB+OYMnIxKt+GzIkivZdSVSYjfxNnBYkMCeUxm5EpPIxKS\nP5bBHAVRRambD5NUmyKILuC60/rYuc/C+vmgpY2HCWFS2q6o34dPr9enwL6t4b3m\nS1t/EJHk9rGaaDqSGkDEfyQI83/7SDebWKuETMKKFLZi1vMgQIFuOYCIhN6bIiZm\npQIDAQAB\n-----END PUBLIC KEY-----\n\n"
+ },
+ "summary": "---
Website: https://hacktivis.me/
Lang: Français(natif), English(fluent), LSF(🤏~👌), русский (еле-еле),
Politics: Anarchist as in DIY/DIWO, freedom of association, anti-authoritarian, anti-identitarianism
Pronouns: meh, pick any, have fun
Timezone: Let's say Mars, I have a non-24h cycle
```
🦊🦄⚧🂡ⓥ :anarchy: 👿🐧 :gentoo:
Pleroma maintainer (mostly backend)
BadWolf developer
Gentoo contributor
Dayjob: yogoko.fr
That person which uses HJKL in games
Just because computer bad: X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
banner from: https://soc.flyingcube.tech/objects/56f79be2-9013-4559-9826-f7dc392417db
Federation-bots: #nobot",
+ "tag": [
+ {
+ "icon": {
+ "type": "Image",
+ "url": "https://queer.hacktivis.me/emoji/custom/symbols/anarchy.png"
+ },
+ "id": "https://queer.hacktivis.me/emoji/custom/symbols/anarchy.png",
+ "name": ":anarchy:",
+ "type": "Emoji",
+ "updated": "1970-01-01T00:00:00Z"
+ },
+ {
+ "icon": {
+ "type": "Image",
+ "url": "https://queer.hacktivis.me/emoji/custom/mastodon.xyz/bzh.png"
+ },
+ "id": "https://queer.hacktivis.me/emoji/custom/mastodon.xyz/bzh.png",
+ "name": ":bzh:",
+ "type": "Emoji",
+ "updated": "1970-01-01T00:00:00Z"
+ },
+ {
+ "icon": {
+ "type": "Image",
+ "url": "https://queer.hacktivis.me/emoji/custom/gentoo.png"
+ },
+ "id": "https://queer.hacktivis.me/emoji/custom/gentoo.png",
+ "name": ":gentoo:",
+ "type": "Emoji",
+ "updated": "1970-01-01T00:00:00Z"
+ }
+ ],
+ "type": "Person",
+ "url": "https://queer.hacktivis.me/users/lanodan"
+}
diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs
index 5745778c7..9f0552b02 100644
--- a/crates/apub/src/objects/comment.rs
+++ b/crates/apub/src/objects/comment.rs
@@ -60,7 +60,7 @@ pub struct Note {
media_type: MediaTypeHtml,
source: Source,
in_reply_to: CommentInReplyToMigration,
- published: DateTime,
+ published: Option>,
updated: Option>,
#[serde(flatten)]
unparsed: Unparsed,
@@ -230,7 +230,7 @@ impl ToApub for ApubComment {
media_type: MediaTypeMarkdown::Markdown,
},
in_reply_to: CommentInReplyToMigration::Old(in_reply_to_vec),
- published: convert_datetime(self.published),
+ published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
};
@@ -282,7 +282,7 @@ impl FromApub for ApubComment {
content: content_slurs_removed,
removed: None,
read: None,
- published: Some(note.published.naive_local()),
+ published: note.published.map(|u| u.to_owned().naive_local()),
updated: note.updated.map(|u| u.to_owned().naive_local()),
deleted: None,
ap_id,
diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs
index 56a86cc85..e46dd7d00 100644
--- a/crates/apub/src/objects/community.rs
+++ b/crates/apub/src/objects/community.rs
@@ -69,7 +69,7 @@ pub struct Group {
followers: Url,
endpoints: Endpoints,
public_key: PublicKey,
- published: DateTime,
+ published: Option>,
updated: Option>,
#[serde(flatten)]
unparsed: Unparsed,
@@ -101,7 +101,7 @@ impl Group {
title,
description,
removed: None,
- published: Some(group.published.naive_local()),
+ published: group.published.map(|u| u.naive_local()),
updated: group.updated.map(|u| u.naive_local()),
deleted: None,
nsfw: Some(group.sensitive.unwrap_or(false)),
@@ -232,7 +232,7 @@ impl ToApub for ApubCommunity {
..Default::default()
},
public_key: self.get_public_key()?,
- published: convert_datetime(self.published),
+ published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
};
diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs
index c5f20c292..147215e91 100644
--- a/crates/apub/src/objects/mod.rs
+++ b/crates/apub/src/objects/mod.rs
@@ -53,3 +53,80 @@ where
Err(anyhow!("Cant convert object to tombstone if it wasnt deleted").into())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use actix::Actor;
+ use diesel::{
+ r2d2::{ConnectionManager, Pool},
+ PgConnection,
+ };
+ use lemmy_apub_lib::activity_queue::create_activity_queue;
+ use lemmy_db_schema::{
+ establish_unpooled_connection,
+ get_database_url_from_env,
+ source::secret::Secret,
+ };
+ use lemmy_utils::{
+ rate_limit::{rate_limiter::RateLimiter, RateLimit},
+ request::build_user_agent,
+ settings::structs::Settings,
+ };
+ use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
+ use reqwest::Client;
+ use serde::de::DeserializeOwned;
+ use std::{fs::File, io::BufReader, sync::Arc};
+ use tokio::sync::Mutex;
+
+ // TODO: would be nice if we didnt have to use a full context for tests.
+ // or at least write a helper function so this code is shared with main.rs
+ pub(crate) fn init_context() -> LemmyContext {
+ // call this to run migrations
+ establish_unpooled_connection();
+ let settings = Settings::init().unwrap();
+ let rate_limiter = RateLimit {
+ rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
+ rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(),
+ };
+ let client = Client::builder()
+ .user_agent(build_user_agent(&settings))
+ .build()
+ .unwrap();
+ let activity_queue = create_activity_queue();
+ let secret = Secret {
+ id: 0,
+ jwt_secret: "".to_string(),
+ };
+ let db_url = match get_database_url_from_env() {
+ Ok(url) => url,
+ Err(_) => settings.get_database_url(),
+ };
+ let manager = ConnectionManager::::new(&db_url);
+ let pool = Pool::builder()
+ .max_size(settings.database.pool_size)
+ .build(manager)
+ .unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
+ async fn x() -> Result {
+ Ok("".to_string())
+ }
+ let chat_server = ChatServer::startup(
+ pool.clone(),
+ rate_limiter.clone(),
+ |_, _, _, _| Box::pin(x()),
+ |_, _, _, _| Box::pin(x()),
+ client.clone(),
+ activity_queue.clone(),
+ settings.clone(),
+ secret.clone(),
+ )
+ .start();
+ LemmyContext::create(pool, chat_server, client, activity_queue, settings, secret)
+ }
+
+ pub(crate) fn file_to_json_object(path: &str) -> T {
+ let file = File::open(path).unwrap();
+ let reader = BufReader::new(file);
+ serde_json::from_reader(reader).unwrap()
+ }
+}
diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs
index 53503d007..775adeca5 100644
--- a/crates/apub/src/objects/person.rs
+++ b/crates/apub/src/objects/person.rs
@@ -67,7 +67,7 @@ pub struct Person {
outbox: Url,
endpoints: Endpoints,
public_key: PublicKey,
- published: DateTime,
+ published: Option>,
updated: Option>,
#[serde(flatten)]
unparsed: Unparsed,
@@ -192,7 +192,7 @@ impl ToApub for ApubPerson {
icon,
image,
matrix_user_id: self.matrix_user_id.clone(),
- published: convert_datetime(self.published),
+ published: Some(convert_datetime(self.published)),
outbox: generate_outbox_url(&self.actor_id)?.into(),
endpoints: Endpoints {
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
@@ -245,7 +245,7 @@ impl FromApub for ApubPerson {
deleted: None,
avatar: Some(person.icon.clone().map(|i| i.url.into())),
banner: Some(person.image.clone().map(|i| i.url.into())),
- published: Some(person.published.naive_local()),
+ published: person.published.map(|u| u.clone().naive_local()),
updated: person.updated.map(|u| u.clone().naive_local()),
actor_id,
bio: Some(bio),
@@ -266,3 +266,43 @@ impl FromApub for ApubPerson {
Ok(person.into())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::objects::tests::{file_to_json_object, init_context};
+ use serial_test::serial;
+
+ #[actix_rt::test]
+ #[serial]
+ async fn test_fetch_lemmy_person() {
+ let json = file_to_json_object("assets/lemmy-person.json");
+ let url = Url::parse("https://lemmy.ml/u/nutomic").unwrap();
+ let person = ApubPerson::from_apub(&json, &init_context(), &url, &mut 0)
+ .await
+ .unwrap();
+
+ assert_eq!(person.actor_id.clone().into_inner(), url);
+ assert_eq!(person.name, "nutomic");
+ assert!(person.public_key.is_some());
+ assert_eq!(person.local, false);
+ assert!(person.bio.is_some());
+ }
+
+ #[actix_rt::test]
+ #[serial]
+ async fn test_fetch_pleroma_person() {
+ let json = file_to_json_object("assets/pleroma-person.json");
+ let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
+ let person = ApubPerson::from_apub(&json, &init_context(), &url, &mut 0)
+ .await
+ .unwrap();
+
+ assert_eq!(person.actor_id.clone().into_inner(), url);
+ assert_eq!(person.name, "lanodan");
+ assert!(person.public_key.is_some());
+ assert_eq!(person.local, false);
+ // TODO: pleroma uses summary for user profile, while we use content
+ //assert!(person.bio.is_some());
+ }
+}
diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs
index dc6d96518..97419cfff 100644
--- a/crates/apub/src/objects/post.rs
+++ b/crates/apub/src/objects/post.rs
@@ -61,7 +61,7 @@ pub struct Page {
pub(crate) comments_enabled: Option,
sensitive: Option,
pub(crate) stickied: Option,
- published: DateTime,
+ published: Option>,
updated: Option>,
#[serde(flatten)]
unparsed: Unparsed,
@@ -196,7 +196,7 @@ impl ToApub for ApubPost {
comments_enabled: Some(!self.locked),
sensitive: Some(self.nsfw),
stickied: Some(self.stickied),
- published: convert_datetime(self.published),
+ published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
};
@@ -260,7 +260,7 @@ impl FromApub for ApubPost {
community_id: community.id,
removed: None,
locked: page.comments_enabled.map(|e| !e),
- published: Some(page.published.naive_local()),
+ published: page.published.map(|u| u.naive_local()),
updated: page.updated.map(|u| u.naive_local()),
deleted: None,
nsfw: page.sensitive,
diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs
index cde1966ed..ee6aff74d 100644
--- a/crates/apub/src/objects/private_message.rs
+++ b/crates/apub/src/objects/private_message.rs
@@ -46,7 +46,7 @@ pub struct Note {
content: String,
media_type: MediaTypeHtml,
source: Source,
- published: DateTime,
+ published: Option>,
updated: Option>,
#[serde(flatten)]
unparsed: Unparsed,
@@ -146,7 +146,7 @@ impl ToApub for ApubPrivateMessage {
content: self.content.clone(),
media_type: MediaTypeMarkdown::Markdown,
},
- published: convert_datetime(self.published),
+ published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
unparsed: Default::default(),
};
@@ -185,7 +185,7 @@ impl FromApub for ApubPrivateMessage {
creator_id: creator.id,
recipient_id: recipient.id,
content: note.source.content.clone(),
- published: Some(note.published.naive_local()),
+ published: note.published.map(|u| u.to_owned().naive_local()),
updated: note.updated.map(|u| u.to_owned().naive_local()),
deleted: None,
read: None,
diff --git a/crates/websocket/src/lib.rs b/crates/websocket/src/lib.rs
index fbf9d25a3..b7ab5bf28 100644
--- a/crates/websocket/src/lib.rs
+++ b/crates/websocket/src/lib.rs
@@ -16,12 +16,12 @@ pub mod routes;
pub mod send;
pub struct LemmyContext {
- pub pool: DbPool,
- pub chat_server: Addr,
- pub client: Client,
- pub activity_queue: QueueHandle,
- pub settings: Settings,
- pub secret: Secret,
+ pool: DbPool,
+ chat_server: Addr,
+ client: Client,
+ activity_queue: QueueHandle,
+ settings: Settings,
+ secret: Secret,
}
impl LemmyContext {
diff --git a/scripts/update_apub_test_files.sh b/scripts/update_apub_test_files.sh
new file mode 100755
index 000000000..b62fc3b6c
--- /dev/null
+++ b/scripts/update_apub_test_files.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -e
+
+curl -H "Accept: application/activity+json" https://lemmy.ml/u/nutomic | jq \
+ > crates/apub/assets/lemmy-person.json
+curl -H "Accept: application/activity+json" https://queer.hacktivis.me/users/lanodan | jq \
+ > crates/apub/assets/pleroma-person.json
\ No newline at end of file