mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Compare commits
6 Commits
100d28a25f
...
ec52ac1bd6
Author | SHA1 | Date | |
---|---|---|---|
|
ec52ac1bd6 | ||
|
dc7b391313 | ||
|
dbeeb755d1 | ||
|
0533bcf822 | ||
|
a0646913a9 | ||
|
a4b022523f |
@ -76,7 +76,7 @@ impl LemmyContext {
|
|||||||
.app_data(context)
|
.app_data(context)
|
||||||
.debug(true)
|
.debug(true)
|
||||||
// Dont allow any network fetches
|
// Dont allow any network fetches
|
||||||
.http_fetch_limit(10)
|
.http_fetch_limit(0)
|
||||||
.build()
|
.build()
|
||||||
.await
|
.await
|
||||||
.expect("build federation config")
|
.expect("build federation config")
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::fetcher::{
|
use crate::fetcher::{
|
||||||
|
post_or_comment::PostOrComment,
|
||||||
search::{search_query_to_object_id, search_query_to_object_id_local, SearchableObjects},
|
search::{search_query_to_object_id, search_query_to_object_id_local, SearchableObjects},
|
||||||
user_or_community::UserOrCommunity,
|
user_or_community::UserOrCommunity,
|
||||||
};
|
};
|
||||||
@ -46,21 +47,22 @@ async fn convert_response(
|
|||||||
local_user_view: Option<LocalUserView>,
|
local_user_view: Option<LocalUserView>,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<Json<ResolveObjectResponse>> {
|
) -> LemmyResult<Json<ResolveObjectResponse>> {
|
||||||
use SearchableObjects::*;
|
|
||||||
let removed_or_deleted;
|
let removed_or_deleted;
|
||||||
let mut res = ResolveObjectResponse::default();
|
let mut res = ResolveObjectResponse::default();
|
||||||
let local_user = local_user_view.map(|l| l.local_user);
|
let local_user = local_user_view.map(|l| l.local_user);
|
||||||
|
|
||||||
match object {
|
match object {
|
||||||
Post(p) => {
|
SearchableObjects::PostOrComment(pc) => match *pc {
|
||||||
removed_or_deleted = p.deleted || p.removed;
|
PostOrComment::Post(p) => {
|
||||||
res.post = Some(PostView::read(pool, p.id, local_user.as_ref(), false).await?)
|
removed_or_deleted = p.deleted || p.removed;
|
||||||
}
|
res.post = Some(PostView::read(pool, p.id, local_user.as_ref(), false).await?)
|
||||||
Comment(c) => {
|
}
|
||||||
removed_or_deleted = c.deleted || c.removed;
|
PostOrComment::Comment(c) => {
|
||||||
res.comment = Some(CommentView::read(pool, c.id, local_user.as_ref()).await?)
|
removed_or_deleted = c.deleted || c.removed;
|
||||||
}
|
res.comment = Some(CommentView::read(pool, c.id, local_user.as_ref()).await?)
|
||||||
PersonOrCommunity(p) => match *p {
|
}
|
||||||
|
},
|
||||||
|
SearchableObjects::PersonOrCommunity(pc) => match *pc {
|
||||||
UserOrCommunity::User(u) => {
|
UserOrCommunity::User(u) => {
|
||||||
removed_or_deleted = u.deleted;
|
removed_or_deleted = u.deleted;
|
||||||
res.person = Some(PersonView::read(pool, u.id).await?)
|
res.person = Some(PersonView::read(pool, u.id).await?)
|
||||||
|
@ -310,7 +310,7 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[expect(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use crate::api::user_settings_backup::{export_settings, import_settings, UserSettingsBackup};
|
use crate::api::user_settings_backup::{export_settings, import_settings, UserSettingsBackup};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
@ -332,7 +332,7 @@ mod tests {
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
async fn create_user(
|
pub(crate) async fn create_user(
|
||||||
name: String,
|
name: String,
|
||||||
bio: Option<String>,
|
bio: Option<String>,
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
|
184
crates/apub/src/fetcher/markdown_links.rs
Normal file
184
crates/apub/src/fetcher/markdown_links.rs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
use super::{search::SearchableObjects, user_or_community::UserOrCommunity};
|
||||||
|
use crate::fetcher::post_or_comment::PostOrComment;
|
||||||
|
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
utils::{generate_local_apub_endpoint, EndpointType},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{newtypes::InstanceId, source::instance::Instance};
|
||||||
|
use lemmy_utils::{
|
||||||
|
error::LemmyResult,
|
||||||
|
utils::markdown::image_links::{markdown_find_links, markdown_handle_title},
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub async fn markdown_rewrite_remote_links_opt(
|
||||||
|
src: Option<String>,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> Option<String> {
|
||||||
|
match src {
|
||||||
|
Some(t) => Some(markdown_rewrite_remote_links(t, context).await),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Goes through all remote markdown links and attempts to resolve them as Activitypub objects.
|
||||||
|
/// If successful, the link is rewritten to a local link, so it can be viewed without leaving the
|
||||||
|
/// local instance.
|
||||||
|
///
|
||||||
|
/// As it relies on ObjectId::dereference, it can only be used for incoming federated objects, not
|
||||||
|
/// for the API.
|
||||||
|
pub async fn markdown_rewrite_remote_links(
|
||||||
|
mut src: String,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> String {
|
||||||
|
let links_offsets = markdown_find_links(&src);
|
||||||
|
|
||||||
|
// Go through the collected links in reverse order
|
||||||
|
for (start, end) in links_offsets.into_iter().rev() {
|
||||||
|
let (url, extra) = markdown_handle_title(&src, start, end);
|
||||||
|
|
||||||
|
// TODO: needs cleanup
|
||||||
|
|
||||||
|
if let Some(local_url) = to_local_url(url, context).await {
|
||||||
|
let mut local_url = local_url.to_string();
|
||||||
|
// restore title
|
||||||
|
if let Some(extra) = extra {
|
||||||
|
local_url = format!("{local_url} {extra}");
|
||||||
|
}
|
||||||
|
src.replace_range(start..end, local_url.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn to_local_url(url: &str, context: &Data<LemmyContext>) -> Option<Url> {
|
||||||
|
let local_domain = &context.settings().get_protocol_and_hostname();
|
||||||
|
let object_id = ObjectId::<SearchableObjects>::parse(url).ok()?;
|
||||||
|
if object_id.inner().domain() == Some(local_domain) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let dereferenced = object_id.dereference(context).await.ok()?;
|
||||||
|
match dereferenced {
|
||||||
|
SearchableObjects::PostOrComment(pc) => match *pc {
|
||||||
|
PostOrComment::Post(post) => {
|
||||||
|
generate_local_apub_endpoint(EndpointType::Post, &post.id.to_string(), local_domain)
|
||||||
|
}
|
||||||
|
PostOrComment::Comment(comment) => {
|
||||||
|
generate_local_apub_endpoint(EndpointType::Comment, &comment.id.to_string(), local_domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ok()
|
||||||
|
.map(Into::into),
|
||||||
|
SearchableObjects::PersonOrCommunity(pc) => match *pc {
|
||||||
|
UserOrCommunity::User(user) => {
|
||||||
|
format_actor_url(&user.name, "u", user.instance_id, context).await
|
||||||
|
}
|
||||||
|
UserOrCommunity::Community(community) => {
|
||||||
|
format_actor_url(&community.name, "c", community.instance_id, context).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ok(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn format_actor_url(
|
||||||
|
name: &str,
|
||||||
|
kind: &str,
|
||||||
|
instance_id: InstanceId,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> LemmyResult<Url> {
|
||||||
|
let local_protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
|
let local_hostname = &context.settings().hostname;
|
||||||
|
let instance = Instance::read(&mut context.pool(), instance_id).await?;
|
||||||
|
let url = if &instance.domain != local_hostname {
|
||||||
|
format!(
|
||||||
|
"{local_protocol_and_hostname}/{kind}/{name}@{}",
|
||||||
|
instance.domain
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("{local_protocol_and_hostname}/{kind}/{name}")
|
||||||
|
};
|
||||||
|
Ok(Url::parse(&url)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::api::user_settings_backup::tests::create_user;
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
source::{
|
||||||
|
community::{Community, CommunityInsertForm},
|
||||||
|
post::{Post, PostInsertForm},
|
||||||
|
},
|
||||||
|
traits::Crud,
|
||||||
|
};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_markdown_rewrite_remote_links() -> LemmyResult<()> {
|
||||||
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
|
||||||
|
let community = Community::create(
|
||||||
|
&mut context.pool(),
|
||||||
|
&CommunityInsertForm::new(
|
||||||
|
instance.id,
|
||||||
|
"my_community".to_string(),
|
||||||
|
"My Community".to_string(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let user = create_user("john".to_string(), None, &context).await?;
|
||||||
|
let post_form = PostInsertForm {
|
||||||
|
..PostInsertForm::new("My post".to_string(), user.person.id, community.id)
|
||||||
|
};
|
||||||
|
let post = Post::create(&mut context.pool(), &post_form).await?;
|
||||||
|
|
||||||
|
let tests: Vec<_> = vec![
|
||||||
|
(
|
||||||
|
"rewrite remote link",
|
||||||
|
format!("[link]({})", post.ap_id),
|
||||||
|
"[link](https://lemmy-alpha/post/1)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"rewrite community link",
|
||||||
|
format!("[link]({})", community.actor_id),
|
||||||
|
"[link](https://lemmy-alpha/c/my_community@example.com)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite local post link",
|
||||||
|
"[link](https://lemmy-alpha/post/2)".to_string(),
|
||||||
|
"[link](https://lemmy-alpha/post/2)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite local community link",
|
||||||
|
"[link](https://lemmy-alpha/c/test)".to_string(),
|
||||||
|
"[link](https://lemmy-alpha/c/test)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite non-fediverse link",
|
||||||
|
"[link](https://example.com/)".to_string(),
|
||||||
|
"[link](https://example.com/)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite invalid url",
|
||||||
|
"[link](example-com)".to_string(),
|
||||||
|
"[link](example-com)",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
for (msg, input, expected) in &tests {
|
||||||
|
let result = markdown_rewrite_remote_links(input.to_string(), &context).await;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result, expected,
|
||||||
|
"Testing {}, with original input '{}'",
|
||||||
|
msg, input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ use lemmy_db_schema::traits::ApubActor;
|
|||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
|
|
||||||
pub(crate) mod post_links;
|
pub(crate) mod markdown_links;
|
||||||
pub mod post_or_comment;
|
pub mod post_or_comment;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod site_or_community_or_user;
|
pub mod site_or_community_or_user;
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
use crate::fetcher::post_or_comment::PostOrComment;
|
|
||||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
|
||||||
use lemmy_api_common::{
|
|
||||||
context::LemmyContext,
|
|
||||||
utils::{generate_local_apub_endpoint, EndpointType},
|
|
||||||
};
|
|
||||||
use lemmy_utils::utils::markdown::image_links::{markdown_find_links, markdown_handle_title};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub async fn markdown_rewrite_remote_post_links_opt(
|
|
||||||
src: Option<String>,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Option<String> {
|
|
||||||
match src {
|
|
||||||
Some(t) => Some(markdown_rewrite_remote_post_links(t, context).await),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: call this logic for comment.text etc
|
|
||||||
/// TODO: as it uses ObjectId::dereference, it can currently only be used in apub crate
|
|
||||||
pub async fn markdown_rewrite_remote_post_links(
|
|
||||||
mut src: String,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> String {
|
|
||||||
let links_offsets = markdown_find_links(&src);
|
|
||||||
|
|
||||||
// Go through the collected links in reverse order
|
|
||||||
for (start, end) in links_offsets.into_iter().rev() {
|
|
||||||
let (url, extra) = markdown_handle_title(&src, start, end);
|
|
||||||
|
|
||||||
// TODO: needs cleanup
|
|
||||||
|
|
||||||
if let Some(local_url) = to_local_url(&url, &context).await {
|
|
||||||
let mut local_url = local_url.to_string();
|
|
||||||
// restore title
|
|
||||||
if let Some(extra) = extra {
|
|
||||||
local_url = format!("{local_url} {extra}");
|
|
||||||
}
|
|
||||||
src.replace_range(start..end, local_url.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
src
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: also resolve user and community links
|
|
||||||
pub(crate) async fn to_local_url(url: &str, context: &Data<LemmyContext>) -> Option<Url> {
|
|
||||||
let local_domain = &context.settings().get_protocol_and_hostname();
|
|
||||||
let object_id = ObjectId::<PostOrComment>::parse(url).ok()?;
|
|
||||||
if object_id.inner().domain() == Some(local_domain) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let dereferenced = object_id.dereference(context).await;
|
|
||||||
dereferenced
|
|
||||||
.map(|d| match d {
|
|
||||||
PostOrComment::Post(post) => {
|
|
||||||
generate_local_apub_endpoint(EndpointType::Post, &post.id.to_string(), local_domain).ok()
|
|
||||||
}
|
|
||||||
PostOrComment::Comment(comment) => {
|
|
||||||
generate_local_apub_endpoint(EndpointType::Comment, &comment.id.to_string(), local_domain)
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.map(|u| u.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[expect(clippy::unwrap_used)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_markdown_rewrite_remote_post_links() {
|
|
||||||
let tests: Vec<_> = vec![
|
|
||||||
(
|
|
||||||
"rewrite remote link",
|
|
||||||
"[link](https://feddit.org/post/3172593)",
|
|
||||||
"[link](https://lemmy-alpha/post/1)",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"dont rewrite local link",
|
|
||||||
"[link](https://lemmy-alpha/post/2)",
|
|
||||||
"[link](https://lemmy-alpha/post/2)",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"dont rewrite non-fediverse link",
|
|
||||||
"[link](https://example.com/)",
|
|
||||||
"[link](https://example.com/)",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"dont rewrite invalid url",
|
|
||||||
"[link](example-com)",
|
|
||||||
"[link](example-com)",
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
let context = LemmyContext::init_test_context().await;
|
|
||||||
for &(msg, input, expected) in &tests {
|
|
||||||
let result = markdown_rewrite_remote_post_links(input.to_string(), &context).await;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
result, expected,
|
|
||||||
"Testing {}, with original input '{}'",
|
|
||||||
msg, input
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,5 @@
|
|||||||
use crate::{
|
use super::post_or_comment::{PageOrNote, PostOrComment};
|
||||||
fetcher::user_or_community::{PersonOrGroup, UserOrCommunity},
|
use crate::fetcher::user_or_community::{PersonOrGroup, UserOrCommunity};
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
||||||
protocol::objects::{note::Note, page::Page},
|
|
||||||
};
|
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
||||||
@ -54,16 +51,14 @@ pub(crate) async fn search_query_to_object_id_local(
|
|||||||
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum SearchableObjects {
|
pub(crate) enum SearchableObjects {
|
||||||
Post(ApubPost),
|
PostOrComment(Box<PostOrComment>),
|
||||||
Comment(ApubComment),
|
|
||||||
PersonOrCommunity(Box<UserOrCommunity>),
|
PersonOrCommunity(Box<UserOrCommunity>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub(crate) enum SearchableKinds {
|
pub(crate) enum SearchableKinds {
|
||||||
Page(Box<Page>),
|
PageOrNote(Box<PageOrNote>),
|
||||||
Note(Note),
|
|
||||||
PersonOrGroup(Box<PersonOrGroup>),
|
PersonOrGroup(Box<PersonOrGroup>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +70,7 @@ impl Object for SearchableObjects {
|
|||||||
|
|
||||||
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
|
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
|
||||||
match self {
|
match self {
|
||||||
SearchableObjects::Post(p) => p.last_refreshed_at(),
|
SearchableObjects::PostOrComment(p) => p.last_refreshed_at(),
|
||||||
SearchableObjects::Comment(c) => c.last_refreshed_at(),
|
|
||||||
SearchableObjects::PersonOrCommunity(p) => p.last_refreshed_at(),
|
SearchableObjects::PersonOrCommunity(p) => p.last_refreshed_at(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,13 +89,9 @@ impl Object for SearchableObjects {
|
|||||||
if let Some(uc) = uc {
|
if let Some(uc) = uc {
|
||||||
return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc))));
|
return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc))));
|
||||||
}
|
}
|
||||||
let p = ApubPost::read_from_id(object_id.clone(), context).await?;
|
let pc = PostOrComment::read_from_id(object_id.clone(), context).await?;
|
||||||
if let Some(p) = p {
|
if let Some(pc) = pc {
|
||||||
return Ok(Some(SearchableObjects::Post(p)));
|
return Ok(Some(SearchableObjects::PostOrComment(Box::new(pc))));
|
||||||
}
|
|
||||||
let c = ApubComment::read_from_id(object_id, context).await?;
|
|
||||||
if let Some(c) = c {
|
|
||||||
return Ok(Some(SearchableObjects::Comment(c)));
|
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -109,25 +99,16 @@ impl Object for SearchableObjects {
|
|||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn delete(self, data: &Data<Self::DataType>) -> LemmyResult<()> {
|
async fn delete(self, data: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||||
match self {
|
match self {
|
||||||
SearchableObjects::Post(p) => p.delete(data).await,
|
SearchableObjects::PostOrComment(pc) => pc.delete(data).await,
|
||||||
SearchableObjects::Comment(c) => c.delete(data).await,
|
SearchableObjects::PersonOrCommunity(pc) => pc.delete(data).await,
|
||||||
SearchableObjects::PersonOrCommunity(pc) => match *pc {
|
|
||||||
UserOrCommunity::User(p) => p.delete(data).await,
|
|
||||||
UserOrCommunity::Community(c) => c.delete(data).await,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> {
|
async fn into_json(self, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> {
|
||||||
|
use SearchableObjects::*;
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
SearchableObjects::Post(p) => SearchableKinds::Page(Box::new(p.into_json(data).await?)),
|
PostOrComment(pc) => SearchableKinds::PageOrNote(Box::new(pc.into_json(data).await?)),
|
||||||
SearchableObjects::Comment(c) => SearchableKinds::Note(c.into_json(data).await?),
|
PersonOrCommunity(pc) => SearchableKinds::PersonOrGroup(Box::new(pc.into_json(data).await?)),
|
||||||
SearchableObjects::PersonOrCommunity(pc) => {
|
|
||||||
SearchableKinds::PersonOrGroup(Box::new(match *pc {
|
|
||||||
UserOrCommunity::User(p) => PersonOrGroup::Person(p.into_json(data).await?),
|
|
||||||
UserOrCommunity::Community(c) => PersonOrGroup::Group(c.into_json(data).await?),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,24 +118,20 @@ impl Object for SearchableObjects {
|
|||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
|
use SearchableKinds::*;
|
||||||
match apub {
|
match apub {
|
||||||
SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await,
|
PageOrNote(pn) => PostOrComment::verify(pn, expected_domain, data).await,
|
||||||
SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await,
|
PersonOrGroup(pg) => UserOrCommunity::verify(pg, expected_domain, data).await,
|
||||||
SearchableKinds::PersonOrGroup(pg) => match pg.as_ref() {
|
|
||||||
PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await,
|
|
||||||
PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn from_json(apub: Self::Kind, context: &Data<LemmyContext>) -> LemmyResult<Self> {
|
async fn from_json(apub: Self::Kind, context: &Data<LemmyContext>) -> LemmyResult<Self> {
|
||||||
use SearchableKinds as SAT;
|
use SearchableKinds::*;
|
||||||
use SearchableObjects as SO;
|
use SearchableObjects as SO;
|
||||||
Ok(match apub {
|
Ok(match apub {
|
||||||
SAT::Page(p) => SO::Post(ApubPost::from_json(*p, context).await?),
|
PageOrNote(pg) => SO::PostOrComment(Box::new(PostOrComment::from_json(*pg, context).await?)),
|
||||||
SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?),
|
PersonOrGroup(pg) => {
|
||||||
SAT::PersonOrGroup(pg) => {
|
|
||||||
SO::PersonOrCommunity(Box::new(UserOrCommunity::from_json(*pg, context).await?))
|
SO::PersonOrCommunity(Box::new(UserOrCommunity::from_json(*pg, context).await?))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
activities::{verify_is_public, verify_person_in_community},
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links,
|
||||||
mentions::collect_non_local_mentions,
|
mentions::collect_non_local_mentions,
|
||||||
objects::{read_from_string_or_source, verify_is_remote_object},
|
objects::{read_from_string_or_source, verify_is_remote_object},
|
||||||
protocol::{
|
protocol::{
|
||||||
@ -169,6 +170,7 @@ impl Object for ApubComment {
|
|||||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let content = markdown_rewrite_remote_links(content, context).await;
|
||||||
let language_id =
|
let language_id =
|
||||||
LanguageTag::to_language_id_single(note.language, &mut context.pool()).await?;
|
LanguageTag::to_language_id_single(note.language, &mut context.pool()).await?;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
activities::GetActorType,
|
activities::GetActorType,
|
||||||
check_apub_id_valid,
|
check_apub_id_valid,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links_opt,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
||||||
protocol::{
|
protocol::{
|
||||||
@ -148,6 +149,7 @@ impl Object for ApubCommunity {
|
|||||||
let description = read_from_string_or_source_opt(&group.summary, &None, &group.source);
|
let description = read_from_string_or_source_opt(&group.summary, &None, &group.source);
|
||||||
let description =
|
let description =
|
||||||
process_markdown_opt(&description, slur_regex, &url_blocklist, context).await?;
|
process_markdown_opt(&description, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let description = markdown_rewrite_remote_links_opt(description, context).await;
|
||||||
let icon = proxy_image_link_opt_apub(group.icon.map(|i| i.url), context).await?;
|
let icon = proxy_image_link_opt_apub(group.icon.map(|i| i.url), context).await?;
|
||||||
let banner = proxy_image_link_opt_apub(group.image.map(|i| i.url), context).await?;
|
let banner = proxy_image_link_opt_apub(group.image.map(|i| i.url), context).await?;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use super::verify_is_remote_object;
|
|||||||
use crate::{
|
use crate::{
|
||||||
activities::GetActorType,
|
activities::GetActorType,
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links_opt,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::read_from_string_or_source_opt,
|
objects::read_from_string_or_source_opt,
|
||||||
protocol::{
|
protocol::{
|
||||||
@ -151,6 +152,7 @@ impl Object for ApubSite {
|
|||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let sidebar = read_from_string_or_source_opt(&apub.content, &None, &apub.source);
|
let sidebar = read_from_string_or_source_opt(&apub.content, &None, &apub.source);
|
||||||
let sidebar = process_markdown_opt(&sidebar, slur_regex, &url_blocklist, context).await?;
|
let sidebar = process_markdown_opt(&sidebar, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let sidebar = markdown_rewrite_remote_links_opt(sidebar, context).await;
|
||||||
let icon = proxy_image_link_opt_apub(apub.icon.map(|i| i.url), context).await?;
|
let icon = proxy_image_link_opt_apub(apub.icon.map(|i| i.url), context).await?;
|
||||||
let banner = proxy_image_link_opt_apub(apub.image.map(|i| i.url), context).await?;
|
let banner = proxy_image_link_opt_apub(apub.image.map(|i| i.url), context).await?;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use super::verify_is_remote_object;
|
|||||||
use crate::{
|
use crate::{
|
||||||
activities::GetActorType,
|
activities::GetActorType,
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links_opt,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
||||||
protocol::{
|
protocol::{
|
||||||
@ -156,6 +157,7 @@ impl Object for ApubPerson {
|
|||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
|
let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
|
||||||
let bio = process_markdown_opt(&bio, slur_regex, &url_blocklist, context).await?;
|
let bio = process_markdown_opt(&bio, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let bio = markdown_rewrite_remote_links_opt(bio, context).await;
|
||||||
let avatar = proxy_image_link_opt_apub(person.icon.map(|i| i.url), context).await?;
|
let avatar = proxy_image_link_opt_apub(person.icon.map(|i| i.url), context).await?;
|
||||||
let banner = proxy_image_link_opt_apub(person.image.map(|i| i.url), context).await?;
|
let banner = proxy_image_link_opt_apub(person.image.map(|i| i.url), context).await?;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
activities::{verify_is_public, verify_person_in_community},
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
fetcher::post_links::{markdown_rewrite_remote_post_links_opt, to_local_url},
|
fetcher::markdown_links::{markdown_rewrite_remote_links_opt, to_local_url},
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
||||||
protocol::{
|
protocol::{
|
||||||
@ -228,8 +228,8 @@ impl Object for ApubPost {
|
|||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
|
|
||||||
if let Some(ref mut url) = url {
|
if let Some(ref mut url) = url {
|
||||||
is_url_blocked(&url, &url_blocklist)?;
|
is_url_blocked(url, &url_blocklist)?;
|
||||||
is_valid_url(&url)?;
|
is_valid_url(url)?;
|
||||||
if let Some(local_url) = to_local_url(url.as_str(), context).await {
|
if let Some(local_url) = to_local_url(url.as_str(), context).await {
|
||||||
*url = local_url;
|
*url = local_url;
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ impl Object for ApubPost {
|
|||||||
|
|
||||||
let body = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source);
|
let body = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source);
|
||||||
let body = process_markdown_opt(&body, slur_regex, &url_blocklist, context).await?;
|
let body = process_markdown_opt(&body, slur_regex, &url_blocklist, context).await?;
|
||||||
let body = markdown_rewrite_remote_post_links_opt(body, context).await;
|
let body = markdown_rewrite_remote_links_opt(body, context).await;
|
||||||
let language_id =
|
let language_id =
|
||||||
LanguageTag::to_language_id_single(page.language, &mut context.pool()).await?;
|
LanguageTag::to_language_id_single(page.language, &mut context.pool()).await?;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use super::verify_is_remote_object;
|
use super::verify_is_remote_object;
|
||||||
use crate::{
|
use crate::{
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links,
|
||||||
objects::read_from_string_or_source,
|
objects::read_from_string_or_source,
|
||||||
protocol::{
|
protocol::{
|
||||||
objects::chat_message::{ChatMessage, ChatMessageType},
|
objects::chat_message::{ChatMessage, ChatMessageType},
|
||||||
@ -134,6 +135,7 @@ impl Object for ApubPrivateMessage {
|
|||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let content = read_from_string_or_source(¬e.content, &None, ¬e.source);
|
let content = read_from_string_or_source(¬e.content, &None, ¬e.source);
|
||||||
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let content = markdown_rewrite_remote_links(content, context).await;
|
||||||
|
|
||||||
let form = PrivateMessageInsertForm {
|
let form = PrivateMessageInsertForm {
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
|
@ -67,6 +67,11 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub async fn read(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
instance::table.find(instance_id).first(conn).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn update(
|
pub async fn update(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
instance_id: InstanceId,
|
instance_id: InstanceId,
|
||||||
|
@ -39,7 +39,7 @@ pub fn markdown_rewrite_image_links(mut src: String) -> (String, Vec<Url>) {
|
|||||||
(src, links)
|
(src, links)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn markdown_handle_title(src: &String, start: usize, end: usize) -> (&str, Option<&str>) {
|
pub fn markdown_handle_title(src: &str, start: usize, end: usize) -> (&str, Option<&str>) {
|
||||||
let content = src.get(start..end).unwrap_or_default();
|
let content = src.get(start..end).unwrap_or_default();
|
||||||
// necessary for custom emojis which look like `![name](url "title")`
|
// necessary for custom emojis which look like `![name](url "title")`
|
||||||
let (url, extra) = if content.contains(' ') {
|
let (url, extra) = if content.contains(' ') {
|
||||||
@ -95,7 +95,6 @@ impl UrlAndTitle for Link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[expect(clippy::unwrap_used)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user