mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-22 19:01:32 +00:00
Merge branch 'main' of https://github.com/LemmyNet/lemmy into magic-filetypes
This commit is contained in:
commit
d1371ed48f
32 changed files with 405 additions and 203 deletions
|
@ -91,6 +91,36 @@ steps:
|
|||
when:
|
||||
- event: pull_request
|
||||
|
||||
cargo_clippy:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy --workspace --tests --all-targets -- -D warnings
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_test:
|
||||
image: *rust_image
|
||||
environment:
|
||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||
RUST_BACKTRACE: "1"
|
||||
CARGO_HOME: .cargo_home
|
||||
LEMMY_TEST_FAST_FEDERATION: "1"
|
||||
LEMMY_CONFIG_LOCATION: ../../config/config.hjson
|
||||
commands:
|
||||
- cargo test --workspace --no-fail-fast
|
||||
when: *slow_check_paths
|
||||
|
||||
check_ts_bindings:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- ./scripts/ts_bindings_check.sh
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
# make sure api builds with default features (used by other crates relying on lemmy api)
|
||||
check_api_common_default_features:
|
||||
image: *rust_image
|
||||
|
@ -138,15 +168,6 @@ steps:
|
|||
- diff tmp.schema crates/db_schema/src/schema.rs
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_clippy:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy --workspace --tests --all-targets -- -D warnings
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_build:
|
||||
image: *rust_image
|
||||
environment:
|
||||
|
@ -156,27 +177,6 @@ steps:
|
|||
- mv target/debug/lemmy_server target/lemmy_server
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_test:
|
||||
image: *rust_image
|
||||
environment:
|
||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||
RUST_BACKTRACE: "1"
|
||||
CARGO_HOME: .cargo_home
|
||||
LEMMY_TEST_FAST_FEDERATION: "1"
|
||||
LEMMY_CONFIG_LOCATION: ../../config/config.hjson
|
||||
commands:
|
||||
- cargo test --workspace --no-fail-fast
|
||||
when: *slow_check_paths
|
||||
|
||||
check_ts_bindings:
|
||||
image: *rust_image
|
||||
environment:
|
||||
CARGO_HOME: .cargo_home
|
||||
commands:
|
||||
- ./scripts/ts_bindings_check.sh
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
check_diesel_migration:
|
||||
# TODO: use willsquire/diesel-cli image when shared libraries become optional in lemmy_server
|
||||
image: *rust_image
|
||||
|
|
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accept-language"
|
||||
|
@ -779,17 +779,6 @@ dependencies = [
|
|||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfb"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"fnv",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -2358,15 +2347,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847"
|
||||
dependencies = [
|
||||
"cfb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
|
@ -2527,8 +2507,6 @@ dependencies = [
|
|||
"encoding_rs",
|
||||
"enum-map",
|
||||
"futures",
|
||||
"getrandom",
|
||||
"infer",
|
||||
"jsonwebtoken",
|
||||
"lemmy_db_schema",
|
||||
"lemmy_db_views",
|
||||
|
@ -2536,6 +2514,7 @@ dependencies = [
|
|||
"lemmy_db_views_moderator",
|
||||
"lemmy_utils",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"moka",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
|
@ -2603,6 +2582,7 @@ dependencies = [
|
|||
"moka",
|
||||
"pretty_assertions",
|
||||
"reqwest 0.12.8",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
|
@ -3167,6 +3147,16 @@ version = "0.3.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -5320,6 +5310,12 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
|
|
|
@ -131,7 +131,7 @@ chrono = { version = "0.4.38", features = [
|
|||
], default-features = false }
|
||||
serde_json = { version = "1.0.132", features = ["preserve_order"] }
|
||||
base64 = "0.22.1"
|
||||
uuid = { version = "1.11.0", features = ["serde", "v4"] }
|
||||
uuid = { version = "1.11.0", features = ["serde"] }
|
||||
async-trait = "0.1.83"
|
||||
captcha = "0.0.9"
|
||||
anyhow = { version = "1.0.93", features = [
|
||||
|
|
|
@ -41,6 +41,9 @@ afterAll(async () => {
|
|||
});
|
||||
|
||||
test("Upload image and delete it", async () => {
|
||||
const healthz = await fetch(alphaUrl + "/pictrs/healthz");
|
||||
expect(healthz.status).toBe(200);
|
||||
|
||||
// Before running this test, you need to delete all previous images in the DB
|
||||
await deleteAllImages(alpha);
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ full = [
|
|||
"futures",
|
||||
"jsonwebtoken",
|
||||
"mime",
|
||||
"moka",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
@ -58,7 +59,7 @@ uuid = { workspace = true, optional = true }
|
|||
tokio = { workspace = true, optional = true }
|
||||
reqwest = { workspace = true, optional = true }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
moka.workspace = true
|
||||
moka = { workspace = true, optional = true }
|
||||
anyhow.workspace = true
|
||||
actix-web = { workspace = true, optional = true }
|
||||
enum-map = { workspace = true }
|
||||
|
@ -70,11 +71,6 @@ webpage = { version = "2.0", default-features = false, features = [
|
|||
], optional = true }
|
||||
encoding_rs = { version = "0.8.35", optional = true }
|
||||
jsonwebtoken = { version = "9.3.0", optional = true }
|
||||
# necessary for wasmt compilation
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
|
||||
[package.metadata.cargo-shear]
|
||||
ignored = ["getrandom"]
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -45,6 +45,7 @@ html2md = "0.2.14"
|
|||
html2text = "0.12.6"
|
||||
stringreader = "0.1.1"
|
||||
enum_delegate = "0.2.0"
|
||||
semver = "1.0.23"
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||
"to": ["http://ds9.lemmy.ml/u/lemmy_alpha"],
|
||||
"object": {
|
||||
"type": "ChatMessage",
|
||||
"type": "Note",
|
||||
"id": "http://enterprise.lemmy.ml/private_message/1",
|
||||
"attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||
"to": ["http://ds9.lemmy.ml/u/lemmy_alpha"],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"id": "https://enterprise.lemmy.ml/private_message/1621",
|
||||
"type": "ChatMessage",
|
||||
"type": "Note",
|
||||
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
||||
"to": ["https://queer.hacktivis.me/users/lanodan"],
|
||||
"content": "<p>Hello hello, testing</p>\n",
|
49
crates/apub/assets/mastodon/activities/private_message.json
Normal file
49
crates/apub/assets/mastodon/activities/private_message.json
Normal file
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "https://mastodon.world/users/nutomic/statuses/110854468010322301",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": "https://mastodon.world/users/nutomic/statuses/110854464248188528",
|
||||
"published": "2023-08-08T14:29:04Z",
|
||||
"url": "https://mastodon.world/@nutomic/110854468010322301",
|
||||
"attributedTo": "https://mastodon.world/users/nutomic",
|
||||
"to": ["https://ds9.lemmy.ml/u/nutomic"],
|
||||
"cc": [],
|
||||
"sensitive": false,
|
||||
"atomUri": "https://mastodon.world/users/nutomic/statuses/110854468010322301",
|
||||
"inReplyToAtomUri": "https://mastodon.world/users/nutomic/statuses/110854464248188528",
|
||||
"conversation": "tag:mastodon.world,2023-08-08:objectId=121377096:objectType=Conversation",
|
||||
"content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://ds9.lemmy.ml/u/nutomic\" class=\"u-url mention\">@<span>nutomic@ds9.lemmy.ml</span></a></span> 444</p>",
|
||||
"contentMap": {
|
||||
"es": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://ds9.lemmy.ml/u/nutomic\" class=\"u-url mention\">@<span>nutomic@ds9.lemmy.ml</span></a></span> 444</p>"
|
||||
},
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://ds9.lemmy.ml/u/nutomic",
|
||||
"name": "@nutomic@ds9.lemmy.ml"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "https://mastodon.world/users/nutomic/statuses/110854468010322301/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "https://mastodon.world/users/nutomic/statuses/110854468010322301/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "https://mastodon.world/users/nutomic/statuses/110854468010322301/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://queer.hacktivis.me/schemas/litepub-0.1.jsonld",
|
||||
{
|
||||
"@language": "und"
|
||||
}
|
||||
],
|
||||
"attributedTo": "https://queer.hacktivis.me/users/lanodan",
|
||||
"content": "Hi!",
|
||||
"id": "https://queer.hacktivis.me/objects/2",
|
||||
"published": "2020-02-12T14:08:20Z",
|
||||
"to": ["https://enterprise.lemmy.ml/u/picard"],
|
||||
"type": "ChatMessage"
|
||||
}
|
|
@ -64,16 +64,17 @@ impl ActivityHandler for RawAnnouncableActivities {
|
|||
|
||||
// verify and receive activity
|
||||
activity.verify(context).await?;
|
||||
activity.clone().receive(context).await?;
|
||||
let actor_id = activity.actor().clone().into();
|
||||
activity.receive(context).await?;
|
||||
|
||||
// if community is local, send activity to followers
|
||||
if let Some(community) = community {
|
||||
if community.local {
|
||||
let actor_id = activity.actor().clone().into();
|
||||
verify_person_in_community(&actor_id, &community, context).await?;
|
||||
AnnounceActivity::send(self, &community, context).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ use lemmy_utils::{
|
|||
error::{LemmyError, LemmyResult},
|
||||
utils::mention::scrape_text_for_mentions,
|
||||
};
|
||||
use serde_json::{from_value, to_value};
|
||||
use url::Url;
|
||||
|
||||
impl CreateOrUpdateNote {
|
||||
|
@ -98,7 +99,8 @@ impl CreateOrUpdateNote {
|
|||
inboxes.add_inbox(person.shared_inbox_or_inbox());
|
||||
}
|
||||
|
||||
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
|
||||
let activity =
|
||||
AnnouncableActivities::CreateOrUpdateNoteWrapper(from_value(to_value(create_or_update)?)?);
|
||||
send_activity_in_community(activity, &person, &community, inboxes, false, &context).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod comment;
|
||||
pub(crate) mod note_wrapper;
|
||||
pub mod post;
|
||||
pub mod private_message;
|
||||
|
|
66
crates/apub/src/activities/create_or_update/note_wrapper.rs
Normal file
66
crates/apub/src/activities/create_or_update/note_wrapper.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use crate::{
|
||||
objects::{community::ApubCommunity, note_wrapper::is_public},
|
||||
protocol::{
|
||||
activities::create_or_update::{
|
||||
note::CreateOrUpdateNote,
|
||||
note_wrapper::CreateOrUpdateNoteWrapper,
|
||||
private_message::CreateOrUpdatePrivateMessage,
|
||||
},
|
||||
InCommunity,
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{config::Data, traits::ActivityHandler};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::{FederationError, LemmyError, LemmyResult};
|
||||
use serde_json::{from_value, to_value};
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CreateOrUpdateNoteWrapper {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn id(&self) -> &Url {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn actor(&self) -> &Url {
|
||||
&self.actor
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||
let val = to_value(self)?;
|
||||
if is_public(&self.to, &self.cc) {
|
||||
CreateOrUpdateNote::verify(&from_value(val)?, context).await?;
|
||||
} else {
|
||||
CreateOrUpdatePrivateMessage::verify(&from_value(val)?, context).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||
let is_public = is_public(&self.to, &self.cc);
|
||||
let val = to_value(self)?;
|
||||
if is_public {
|
||||
CreateOrUpdateNote::receive(from_value(val)?, context).await?;
|
||||
} else {
|
||||
CreateOrUpdatePrivateMessage::receive(from_value(val)?, context).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for CreateOrUpdateNoteWrapper {
|
||||
#[tracing::instrument(skip(self, context))]
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
if is_public(&self.to, &self.cc) {
|
||||
let comment: CreateOrUpdateNote = from_value(to_value(self)?)?;
|
||||
comment.community(context).await
|
||||
} else {
|
||||
Err(FederationError::ObjectIsNotPublic.into())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
insert_received_activity,
|
||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||
protocol::activities::{
|
||||
create_or_update::chat_message::CreateOrUpdateChatMessage,
|
||||
create_or_update::private_message::CreateOrUpdatePrivateMessage,
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ pub(crate) async fn send_create_or_update_pm(
|
|||
kind.clone(),
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let create_or_update = CreateOrUpdateChatMessage {
|
||||
let create_or_update = CreateOrUpdatePrivateMessage {
|
||||
id: id.clone(),
|
||||
actor: actor.id().into(),
|
||||
to: [recipient.id().into()],
|
||||
|
@ -44,7 +44,7 @@ pub(crate) async fn send_create_or_update_pm(
|
|||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CreateOrUpdateChatMessage {
|
||||
impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ use lemmy_db_schema::{
|
|||
community::{Community, CommunityUpdateForm},
|
||||
person::Person,
|
||||
post::{Post, PostUpdateForm},
|
||||
private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||
private_message::{PrivateMessage as DbPrivateMessage, PrivateMessageUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ pub(crate) async fn send_apub_delete_in_community(
|
|||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn send_apub_delete_private_message(
|
||||
actor: &ApubPerson,
|
||||
pm: PrivateMessage,
|
||||
pm: DbPrivateMessage,
|
||||
deleted: bool,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
|
@ -298,7 +298,7 @@ async fn receive_delete_action(
|
|||
}
|
||||
}
|
||||
DeletableObjects::PrivateMessage(pm) => {
|
||||
PrivateMessage::update(
|
||||
DbPrivateMessage::update(
|
||||
&mut context.pool(),
|
||||
pm.id,
|
||||
&PrivateMessageUpdateForm {
|
||||
|
|
|
@ -11,11 +11,7 @@ use crate::{
|
|||
report::Report,
|
||||
update::UpdateCommunity,
|
||||
},
|
||||
create_or_update::{
|
||||
chat_message::CreateOrUpdateChatMessage,
|
||||
note::CreateOrUpdateNote,
|
||||
page::CreateOrUpdatePage,
|
||||
},
|
||||
create_or_update::{note_wrapper::CreateOrUpdateNoteWrapper, page::CreateOrUpdatePage},
|
||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
following::{
|
||||
accept::AcceptFollow,
|
||||
|
@ -48,47 +44,17 @@ pub enum SharedInboxActivities {
|
|||
AcceptFollow(AcceptFollow),
|
||||
RejectFollow(RejectFollow),
|
||||
UndoFollow(UndoFollow),
|
||||
CreateOrUpdatePrivateMessage(CreateOrUpdateChatMessage),
|
||||
Report(Report),
|
||||
AnnounceActivity(AnnounceActivity),
|
||||
/// This is a catch-all and needs to be last
|
||||
RawAnnouncableActivities(RawAnnouncableActivities),
|
||||
}
|
||||
|
||||
/// List of activities which the group inbox can handle.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[enum_delegate::implement(ActivityHandler)]
|
||||
pub enum GroupInboxActivities {
|
||||
Follow(Follow),
|
||||
UndoFollow(UndoFollow),
|
||||
Report(Report),
|
||||
/// This is a catch-all and needs to be last
|
||||
AnnouncableActivities(RawAnnouncableActivities),
|
||||
}
|
||||
|
||||
/// List of activities which the person inbox can handle.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[enum_delegate::implement(ActivityHandler)]
|
||||
pub enum PersonInboxActivities {
|
||||
Follow(Follow),
|
||||
AcceptFollow(AcceptFollow),
|
||||
RejectFollow(RejectFollow),
|
||||
UndoFollow(UndoFollow),
|
||||
CreateOrUpdatePrivateMessage(CreateOrUpdateChatMessage),
|
||||
Delete(Delete),
|
||||
UndoDelete(UndoDelete),
|
||||
AnnounceActivity(AnnounceActivity),
|
||||
/// User can also receive some "announcable" activities, eg a comment mention.
|
||||
AnnouncableActivities(AnnouncableActivities),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[enum_delegate::implement(ActivityHandler)]
|
||||
pub enum AnnouncableActivities {
|
||||
CreateOrUpdateComment(CreateOrUpdateNote),
|
||||
CreateOrUpdateNoteWrapper(CreateOrUpdateNoteWrapper),
|
||||
CreateOrUpdatePost(CreateOrUpdatePage),
|
||||
Vote(Vote),
|
||||
UndoVote(UndoVote),
|
||||
|
@ -111,7 +77,7 @@ impl InCommunity for AnnouncableActivities {
|
|||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
use AnnouncableActivities::*;
|
||||
match self {
|
||||
CreateOrUpdateComment(a) => a.community(context).await,
|
||||
CreateOrUpdateNoteWrapper(a) => a.community(context).await,
|
||||
CreateOrUpdatePost(a) => a.community(context).await,
|
||||
Vote(a) => a.community(context).await,
|
||||
UndoVote(a) => a.community(context).await,
|
||||
|
@ -133,40 +99,32 @@ impl InCommunity for AnnouncableActivities {
|
|||
mod tests {
|
||||
|
||||
use crate::{
|
||||
activity_lists::{GroupInboxActivities, PersonInboxActivities, SharedInboxActivities},
|
||||
activity_lists::SharedInboxActivities,
|
||||
protocol::tests::{test_json, test_parse_lemmy_item},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_group_inbox() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<GroupInboxActivities>("assets/lemmy/activities/following/follow.json")?;
|
||||
test_parse_lemmy_item::<GroupInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_person_inbox() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<PersonInboxActivities>(
|
||||
"assets/lemmy/activities/following/accept.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<PersonInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<PersonInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_private_message.json",
|
||||
)?;
|
||||
test_json::<PersonInboxActivities>("assets/mastodon/activities/follow.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_inbox() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/deletion/delete_user.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/following/accept.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_comment.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_private_message.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/following/follow.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_comment.json",
|
||||
)?;
|
||||
test_json::<SharedInboxActivities>("assets/mastodon/activities/follow.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ pub(crate) mod tests {
|
|||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
|
||||
let json: Note = file_to_json_object("assets/lemmy/objects/note.json")?;
|
||||
let json: Note = file_to_json_object("assets/lemmy/objects/comment.json")?;
|
||||
ApubComment::verify(&json, &url, &context).await?;
|
||||
let comment = ApubComment::from_json(json.clone(), &context).await?;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use std::fmt::Debug;
|
|||
pub mod comment;
|
||||
pub mod community;
|
||||
pub mod instance;
|
||||
pub mod note_wrapper;
|
||||
pub mod person;
|
||||
pub mod post;
|
||||
pub mod private_message;
|
||||
|
|
85
crates/apub/src/objects/note_wrapper.rs
Normal file
85
crates/apub/src/objects/note_wrapper.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
use super::comment::ApubComment;
|
||||
use crate::{
|
||||
objects::private_message::ApubPrivateMessage,
|
||||
protocol::objects::note_wrapper::NoteWrapper,
|
||||
};
|
||||
use activitypub_federation::{config::Data, kinds::public, traits::Object};
|
||||
use chrono::{DateTime, Utc};
|
||||
use lemmy_api_common::{context::LemmyContext, LemmyErrorType};
|
||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||
use serde_json::{from_value, to_value};
|
||||
use url::Url;
|
||||
|
||||
/// Private messages and public comments are quite awkward in Activitypub, because the json
|
||||
/// format looks identical. They only way to differentiate them is to check for the presence
|
||||
/// or absence of `https://www.w3.org/ns/activitystreams#Public` in `to` or `cc` which this
|
||||
/// wrapper does.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ApubNote {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubNote {
|
||||
type DataType = LemmyContext;
|
||||
type Kind = NoteWrapper;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_id(
|
||||
_object_id: Url,
|
||||
_context: &Data<Self::DataType>,
|
||||
) -> LemmyResult<Option<Self>> {
|
||||
Err(LemmyErrorType::Unknown("not implemented".to_string()).into())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, _context: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||
Err(LemmyErrorType::Unknown("not implemented".to_string()).into())
|
||||
}
|
||||
|
||||
async fn verify(
|
||||
note: &NoteWrapper,
|
||||
expected_domain: &Url,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
let val = to_value(note)?;
|
||||
if is_public(¬e.to, ¬e.cc) {
|
||||
ApubComment::verify(&from_value(val)?, expected_domain, context).await?;
|
||||
} else {
|
||||
ApubPrivateMessage::verify(&from_value(val)?, expected_domain, context).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn from_json(note: NoteWrapper, context: &Data<LemmyContext>) -> LemmyResult<ApubNote> {
|
||||
let is_public = is_public(¬e.to, ¬e.cc);
|
||||
let val = to_value(note)?;
|
||||
if is_public {
|
||||
ApubComment::from_json(from_value(val)?, context).await?;
|
||||
} else {
|
||||
ApubPrivateMessage::from_json(from_value(val)?, context).await?;
|
||||
}
|
||||
Ok(ApubNote {})
|
||||
}
|
||||
|
||||
async fn into_json(self, _context: &Data<Self::DataType>) -> LemmyResult<NoteWrapper> {
|
||||
Err(LemmyErrorType::Unknown("not implemented".to_string()).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_public(to: &Option<Vec<Url>>, cc: &Option<Vec<Url>>) -> bool {
|
||||
if let Some(to) = to {
|
||||
if to.contains(&public()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(cc) = cc {
|
||||
if cc.contains(&public()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
fetcher::markdown_links::markdown_rewrite_remote_links,
|
||||
objects::read_from_string_or_source,
|
||||
protocol::{
|
||||
objects::chat_message::{ChatMessage, ChatMessageType},
|
||||
objects::private_message::{PrivateMessage, PrivateMessageType},
|
||||
Source,
|
||||
},
|
||||
};
|
||||
|
@ -25,10 +25,11 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
instance::Instance,
|
||||
local_site::LocalSite,
|
||||
person::Person,
|
||||
person_block::PersonBlock,
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
private_message::{PrivateMessage as DbPrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -37,21 +38,22 @@ use lemmy_utils::{
|
|||
error::{FederationError, LemmyError, LemmyErrorType, LemmyResult},
|
||||
utils::markdown::markdown_to_html,
|
||||
};
|
||||
use semver::{Version, VersionReq};
|
||||
use std::ops::Deref;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ApubPrivateMessage(pub(crate) PrivateMessage);
|
||||
pub struct ApubPrivateMessage(pub(crate) DbPrivateMessage);
|
||||
|
||||
impl Deref for ApubPrivateMessage {
|
||||
type Target = PrivateMessage;
|
||||
type Target = DbPrivateMessage;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivateMessage> for ApubPrivateMessage {
|
||||
fn from(pm: PrivateMessage) -> Self {
|
||||
impl From<DbPrivateMessage> for ApubPrivateMessage {
|
||||
fn from(pm: DbPrivateMessage) -> Self {
|
||||
ApubPrivateMessage(pm)
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +61,7 @@ impl From<PrivateMessage> for ApubPrivateMessage {
|
|||
#[async_trait::async_trait]
|
||||
impl Object for ApubPrivateMessage {
|
||||
type DataType = LemmyContext;
|
||||
type Kind = ChatMessage;
|
||||
type Kind = PrivateMessage;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
|
||||
|
@ -72,7 +74,7 @@ impl Object for ApubPrivateMessage {
|
|||
context: &Data<Self::DataType>,
|
||||
) -> LemmyResult<Option<Self>> {
|
||||
Ok(
|
||||
PrivateMessage::read_from_apub_id(&mut context.pool(), object_id)
|
||||
DbPrivateMessage::read_from_apub_id(&mut context.pool(), object_id)
|
||||
.await?
|
||||
.map(Into::into),
|
||||
)
|
||||
|
@ -84,15 +86,26 @@ impl Object for ApubPrivateMessage {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_json(self, context: &Data<Self::DataType>) -> LemmyResult<ChatMessage> {
|
||||
async fn into_json(self, context: &Data<Self::DataType>) -> LemmyResult<PrivateMessage> {
|
||||
let creator_id = self.creator_id;
|
||||
let creator = Person::read(&mut context.pool(), creator_id).await?;
|
||||
|
||||
let recipient_id = self.recipient_id;
|
||||
let recipient = Person::read(&mut context.pool(), recipient_id).await?;
|
||||
|
||||
let note = ChatMessage {
|
||||
r#type: ChatMessageType::ChatMessage,
|
||||
let instance = Instance::read(&mut context.pool(), recipient.instance_id).await?;
|
||||
let mut kind = PrivateMessageType::Note;
|
||||
|
||||
// Deprecated: For Lemmy versions before 0.20, send private messages with old type
|
||||
if let (Some(software), Some(version)) = (instance.software, &instance.version) {
|
||||
let req = VersionReq::parse("<0.20")?;
|
||||
if software == "lemmy" && req.matches(&Version::parse(version)?) {
|
||||
kind = PrivateMessageType::ChatMessage
|
||||
}
|
||||
}
|
||||
|
||||
let note = PrivateMessage {
|
||||
kind,
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: creator.actor_id.into(),
|
||||
to: [recipient.actor_id.into()],
|
||||
|
@ -107,7 +120,7 @@ impl Object for ApubPrivateMessage {
|
|||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
note: &ChatMessage,
|
||||
note: &PrivateMessage,
|
||||
expected_domain: &Url,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> LemmyResult<()> {
|
||||
|
@ -128,7 +141,7 @@ impl Object for ApubPrivateMessage {
|
|||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_json(
|
||||
note: ChatMessage,
|
||||
note: PrivateMessage,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> LemmyResult<ApubPrivateMessage> {
|
||||
let creator = note.attributed_to.dereference(context).await?;
|
||||
|
@ -161,7 +174,7 @@ impl Object for ApubPrivateMessage {
|
|||
local: Some(false),
|
||||
};
|
||||
let timestamp = note.updated.or(note.published).unwrap_or_else(Utc::now);
|
||||
let pm = PrivateMessage::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
||||
let pm = DbPrivateMessage::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
||||
Ok(pm.into())
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +226,7 @@ mod tests {
|
|||
let context = LemmyContext::init_test_context().await;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json")?;
|
||||
let json: PrivateMessage = file_to_json_object("assets/lemmy/objects/private_message.json")?;
|
||||
ApubPrivateMessage::verify(&json, &url, &context).await?;
|
||||
let pm = ApubPrivateMessage::from_json(json.clone(), &context).await?;
|
||||
|
||||
|
@ -225,27 +238,7 @@ mod tests {
|
|||
let to_apub = pm.into_json(&context).await?;
|
||||
assert_json_include!(actual: json, expected: to_apub);
|
||||
|
||||
PrivateMessage::delete(&mut context.pool(), pm_id).await?;
|
||||
cleanup(data, &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_pleroma_pm() -> LemmyResult<()> {
|
||||
let context = LemmyContext::init_test_context().await;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2")?;
|
||||
let json = file_to_json_object("assets/pleroma/objects/chat_message.json")?;
|
||||
ApubPrivateMessage::verify(&json, &pleroma_url, &context).await?;
|
||||
let pm = ApubPrivateMessage::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(pm.ap_id, pleroma_url.into());
|
||||
assert_eq!(pm.content.len(), 3);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
PrivateMessage::delete(&mut context.pool(), pm.id).await?;
|
||||
DbPrivateMessage::delete(&mut context.pool(), pm_id).await?;
|
||||
cleanup(data, &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
pub mod chat_message;
|
||||
pub mod note;
|
||||
pub(crate) mod note_wrapper;
|
||||
pub mod page;
|
||||
pub mod private_message;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::note_wrapper::CreateOrUpdateNoteWrapper;
|
||||
use crate::protocol::{
|
||||
activities::create_or_update::{
|
||||
chat_message::CreateOrUpdateChatMessage,
|
||||
note::CreateOrUpdateNote,
|
||||
page::CreateOrUpdatePage,
|
||||
private_message::CreateOrUpdatePrivateMessage,
|
||||
},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
|
@ -23,9 +25,15 @@ mod tests {
|
|||
"assets/lemmy/activities/create_or_update/update_page.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdateNote>(
|
||||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
"assets/lemmy/activities/create_or_update/create_comment.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdateChatMessage>(
|
||||
test_parse_lemmy_item::<CreateOrUpdatePrivateMessage>(
|
||||
"assets/lemmy/activities/create_or_update/create_private_message.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdateNoteWrapper>(
|
||||
"assets/lemmy/activities/create_or_update/create_comment.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdateNoteWrapper>(
|
||||
"assets/lemmy/activities/create_or_update/create_private_message.json",
|
||||
)?;
|
||||
Ok(())
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
use crate::protocol::objects::note_wrapper::NoteWrapper;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateOrUpdateNoteWrapper {
|
||||
object: NoteWrapper,
|
||||
pub(crate) id: Url,
|
||||
pub(crate) actor: Url,
|
||||
pub(crate) to: Option<Vec<Url>>,
|
||||
pub(crate) cc: Option<Vec<Url>>,
|
||||
#[serde(flatten)]
|
||||
other: Map<String, Value>,
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
|
||||
protocol::{activities::CreateOrUpdateType, objects::private_message::PrivateMessage},
|
||||
};
|
||||
use activitypub_federation::{fetch::object_id::ObjectId, protocol::helpers::deserialize_one};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -8,12 +8,12 @@ use url::Url;
|
|||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateOrUpdateChatMessage {
|
||||
pub struct CreateOrUpdatePrivateMessage {
|
||||
pub(crate) id: Url,
|
||||
pub(crate) actor: ObjectId<ApubPerson>,
|
||||
#[serde(deserialize_with = "deserialize_one")]
|
||||
pub(crate) to: [ObjectId<ApubPerson>; 1],
|
||||
pub(crate) object: ChatMessage,
|
||||
pub(crate) object: PrivateMessage,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: CreateOrUpdateType,
|
||||
}
|
|
@ -116,6 +116,7 @@ pub(crate) mod tests {
|
|||
// parse file into hashmap, which ensures that every field is included
|
||||
let raw = file_to_json_object::<HashMap<String, serde_json::Value>>(path)?;
|
||||
// assert that all fields are identical, otherwise print diff
|
||||
//dbg!(&parsed, &raw);
|
||||
assert_json_include!(actual: &parsed, expected: raw);
|
||||
Ok(parsed)
|
||||
}
|
||||
|
|
|
@ -8,12 +8,13 @@ use lemmy_utils::error::LemmyResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod chat_message;
|
||||
pub(crate) mod group;
|
||||
pub(crate) mod instance;
|
||||
pub(crate) mod note;
|
||||
pub(crate) mod note_wrapper;
|
||||
pub(crate) mod page;
|
||||
pub(crate) mod person;
|
||||
pub(crate) mod private_message;
|
||||
pub(crate) mod tombstone;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
|
@ -101,14 +102,15 @@ impl LanguageTag {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::note_wrapper::NoteWrapper;
|
||||
use crate::protocol::{
|
||||
objects::{
|
||||
chat_message::ChatMessage,
|
||||
group::Group,
|
||||
instance::Instance,
|
||||
note::Note,
|
||||
page::Page,
|
||||
person::Person,
|
||||
private_message::PrivateMessage,
|
||||
tombstone::Tombstone,
|
||||
},
|
||||
tests::{test_json, test_parse_lemmy_item},
|
||||
|
@ -121,8 +123,10 @@ mod tests {
|
|||
test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json")?;
|
||||
test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json")?;
|
||||
test_parse_lemmy_item::<Page>("assets/lemmy/objects/page.json")?;
|
||||
test_parse_lemmy_item::<Note>("assets/lemmy/objects/note.json")?;
|
||||
test_parse_lemmy_item::<ChatMessage>("assets/lemmy/objects/chat_message.json")?;
|
||||
test_parse_lemmy_item::<Note>("assets/lemmy/objects/comment.json")?;
|
||||
test_parse_lemmy_item::<PrivateMessage>("assets/lemmy/objects/private_message.json")?;
|
||||
test_parse_lemmy_item::<NoteWrapper>("assets/lemmy/objects/comment.json")?;
|
||||
test_parse_lemmy_item::<NoteWrapper>("assets/lemmy/objects/private_message.json")?;
|
||||
test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -131,7 +135,6 @@ mod tests {
|
|||
fn test_parse_objects_pleroma() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/pleroma/objects/person.json")?;
|
||||
test_json::<Note>("assets/pleroma/objects/note.json")?;
|
||||
test_json::<ChatMessage>("assets/pleroma/objects/chat_message.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
14
crates/apub/src/protocol/objects/note_wrapper.rs
Normal file
14
crates/apub/src/protocol/objects/note_wrapper.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use activitypub_federation::kinds::object::NoteType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct NoteWrapper {
|
||||
pub(crate) r#type: NoteType,
|
||||
pub(crate) to: Option<Vec<Url>>,
|
||||
pub(crate) cc: Option<Vec<Url>>,
|
||||
#[serde(flatten)]
|
||||
other: Map<String, Value>,
|
||||
}
|
|
@ -16,8 +16,9 @@ use serde_with::skip_serializing_none;
|
|||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChatMessage {
|
||||
pub(crate) r#type: ChatMessageType,
|
||||
pub struct PrivateMessage {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: PrivateMessageType,
|
||||
pub(crate) id: ObjectId<ApubPrivateMessage>,
|
||||
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
||||
#[serde(deserialize_with = "deserialize_one")]
|
||||
|
@ -31,8 +32,10 @@ pub struct ChatMessage {
|
|||
pub(crate) updated: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ChatMessageType {
|
||||
pub enum PrivateMessageType {
|
||||
/// For compatibility with Lemmy 0.19 and earlier
|
||||
/// https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages
|
||||
ChatMessage,
|
||||
Note,
|
||||
}
|
|
@ -75,7 +75,7 @@ tokio = { workspace = true, optional = true }
|
|||
tokio-postgres = { workspace = true, optional = true }
|
||||
tokio-postgres-rustls = { workspace = true, optional = true }
|
||||
rustls = { workspace = true, optional = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
uuid.workspace = true
|
||||
i-love-jesus = { workspace = true, optional = true }
|
||||
anyhow = { workspace = true }
|
||||
diesel-bind-if-some = { workspace = true, optional = true }
|
||||
|
|
|
@ -38,7 +38,8 @@ pub fn config(
|
|||
)
|
||||
// This has optional query params: /image/{filename}?format=jpg&thumbnail=256
|
||||
.service(web::resource("/pictrs/image/{filename}").route(web::get().to(full_res)))
|
||||
.service(web::resource("/pictrs/image/delete/{token}/{filename}").route(web::get().to(delete)));
|
||||
.service(web::resource("/pictrs/image/delete/{token}/{filename}").route(web::get().to(delete)))
|
||||
.service(web::resource("/pictrs/healthz").route(web::get().to(healthz)));
|
||||
}
|
||||
|
||||
trait ProcessUrl {
|
||||
|
@ -250,6 +251,25 @@ async fn delete(
|
|||
Ok(HttpResponse::build(convert_status(res.status())).body(BodyStream::new(res.bytes_stream())))
|
||||
}
|
||||
|
||||
async fn healthz(
|
||||
req: HttpRequest,
|
||||
client: web::Data<ClientWithMiddleware>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> LemmyResult<HttpResponse> {
|
||||
let pictrs_config = context.settings().pictrs_config()?;
|
||||
let url = format!("{}healthz", pictrs_config.url);
|
||||
|
||||
let mut client_req = adapt_request(&req, &client, url);
|
||||
|
||||
if let Some(addr) = req.head().peer_addr {
|
||||
client_req = client_req.header("X-Forwarded-For", addr.to_string());
|
||||
}
|
||||
|
||||
let res = client_req.send().await?;
|
||||
|
||||
Ok(HttpResponse::build(convert_status(res.status())).body(BodyStream::new(res.bytes_stream())))
|
||||
}
|
||||
|
||||
pub async fn image_proxy(
|
||||
Query(params): Query<ImageProxyParams>,
|
||||
req: HttpRequest,
|
||||
|
|
Loading…
Reference in a new issue