mirror of https://github.com/LemmyNet/lemmy.git
Compare commits
9 Commits
076262317b
...
f51764327d
Author | SHA1 | Date |
---|---|---|
Dull Bananas | f51764327d | |
dullbananas | 71667d9297 | |
dullbananas | 78702b59fd | |
renovate[bot] | 117a8b42c8 | |
dullbananas | fd58b4f809 | |
Dessalines | a7c39226e2 | |
renovate[bot] | d90e8f8550 | |
Richard Schwab | 2c57f42022 | |
dullbananas | 9120207314 |
|
@ -977,9 +977,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.7"
|
version = "4.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -987,9 +987,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.7"
|
version = "4.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -999,9 +999,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.5"
|
version = "4.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -3292,7 +3292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.5",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -5216,9 +5216,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.117"
|
version = "1.0.120"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.2.6",
|
"indexmap 2.2.6",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
|
|
@ -8,20 +8,18 @@ use lemmy_api_common::{
|
||||||
utils::{
|
utils::{
|
||||||
check_community_user_action,
|
check_community_user_action,
|
||||||
check_post_deleted_or_removed,
|
check_post_deleted_or_removed,
|
||||||
generate_local_apub_endpoint,
|
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
process_markdown,
|
process_markdown,
|
||||||
update_read_comments,
|
update_read_comments,
|
||||||
EndpointType,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
impls::actor_language::default_post_language,
|
impls::actor_language::default_post_language,
|
||||||
source::{
|
source::{
|
||||||
actor_language::CommunityLanguage,
|
actor_language::CommunityLanguage,
|
||||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm},
|
||||||
comment_reply::{CommentReply, CommentReplyUpdateForm},
|
comment_reply::{CommentReply, CommentReplyUpdateForm},
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
person_mention::{PersonMention, PersonMentionUpdateForm},
|
person_mention::{PersonMention, PersonMentionUpdateForm},
|
||||||
|
@ -126,25 +124,7 @@ pub async fn create_comment(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
|
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
|
||||||
|
|
||||||
// Necessary to update the ap_id
|
|
||||||
let inserted_comment_id = inserted_comment.id;
|
let inserted_comment_id = inserted_comment.id;
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
|
||||||
|
|
||||||
let apub_id = generate_local_apub_endpoint(
|
|
||||||
EndpointType::Comment,
|
|
||||||
&inserted_comment_id.to_string(),
|
|
||||||
&protocol_and_hostname,
|
|
||||||
)?;
|
|
||||||
let updated_comment = Comment::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
inserted_comment_id,
|
|
||||||
&CommentUpdateForm {
|
|
||||||
ap_id: Some(apub_id),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
|
|
||||||
|
|
||||||
// Scan the comment for user mentions, add those rows
|
// Scan the comment for user mentions, add those rows
|
||||||
let mentions = scrape_text_for_mentions(&content);
|
let mentions = scrape_text_for_mentions(&content);
|
||||||
|
@ -170,7 +150,7 @@ pub async fn create_comment(
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
|
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
|
||||||
|
|
||||||
ActivityChannel::submit_activity(
|
ActivityChannel::submit_activity(
|
||||||
SendActivityData::CreateComment(updated_comment.clone()),
|
SendActivityData::CreateComment(inserted_comment.clone()),
|
||||||
&context,
|
&context,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -8,13 +8,11 @@ use lemmy_api_common::{
|
||||||
send_activity::SendActivityData,
|
send_activity::SendActivityData,
|
||||||
utils::{
|
utils::{
|
||||||
check_community_user_action,
|
check_community_user_action,
|
||||||
generate_local_apub_endpoint,
|
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
honeypot_check,
|
honeypot_check,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
mark_post_as_read,
|
mark_post_as_read,
|
||||||
process_markdown_opt,
|
process_markdown_opt,
|
||||||
EndpointType,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -23,7 +21,7 @@ use lemmy_db_schema::{
|
||||||
actor_language::CommunityLanguage,
|
actor_language::CommunityLanguage,
|
||||||
community::Community,
|
community::Community,
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
post::{Post, PostInsertForm, PostLike, PostLikeForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
utils::diesel_url_create,
|
utils::diesel_url_create,
|
||||||
|
@ -147,26 +145,8 @@ pub async fn create_post(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
||||||
|
|
||||||
let inserted_post_id = inserted_post.id;
|
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
|
||||||
let apub_id = generate_local_apub_endpoint(
|
|
||||||
EndpointType::Post,
|
|
||||||
&inserted_post_id.to_string(),
|
|
||||||
&protocol_and_hostname,
|
|
||||||
)?;
|
|
||||||
let updated_post = Post::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
inserted_post_id,
|
|
||||||
&PostUpdateForm {
|
|
||||||
ap_id: Some(apub_id),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
|
||||||
|
|
||||||
generate_post_link_metadata(
|
generate_post_link_metadata(
|
||||||
updated_post.clone(),
|
inserted_post.clone(),
|
||||||
custom_thumbnail.map(Into::into),
|
custom_thumbnail.map(Into::into),
|
||||||
|post| Some(SendActivityData::CreatePost(post)),
|
|post| Some(SendActivityData::CreatePost(post)),
|
||||||
Some(local_site),
|
Some(local_site),
|
||||||
|
@ -189,11 +169,11 @@ pub async fn create_post(
|
||||||
|
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
if let Some(url) = updated_post.url.clone() {
|
if let Some(url) = inserted_post.url.clone() {
|
||||||
if community.visibility == CommunityVisibility::Public {
|
if community.visibility == CommunityVisibility::Public {
|
||||||
spawn_try_task(async move {
|
spawn_try_task(async move {
|
||||||
let mut webmention =
|
let mut webmention =
|
||||||
Webmention::new::<Url>(updated_post.ap_id.clone().into(), url.clone().into())?;
|
Webmention::new::<Url>(inserted_post.ap_id.clone().into(), url.clone().into())?;
|
||||||
webmention.set_checked(true);
|
webmention.set_checked(true);
|
||||||
match webmention
|
match webmention
|
||||||
.send()
|
.send()
|
||||||
|
|
|
@ -6,19 +6,17 @@ use lemmy_api_common::{
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_person_block,
|
check_person_block,
|
||||||
generate_local_apub_endpoint,
|
|
||||||
get_interface_language,
|
get_interface_language,
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
process_markdown,
|
process_markdown,
|
||||||
send_email_to_user,
|
send_email_to_user,
|
||||||
EndpointType,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm},
|
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
|
@ -58,24 +56,6 @@ pub async fn create_private_message(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
||||||
|
|
||||||
let inserted_private_message_id = inserted_private_message.id;
|
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
|
||||||
let apub_id = generate_local_apub_endpoint(
|
|
||||||
EndpointType::PrivateMessage,
|
|
||||||
&inserted_private_message_id.to_string(),
|
|
||||||
&protocol_and_hostname,
|
|
||||||
)?;
|
|
||||||
PrivateMessage::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
inserted_private_message.id,
|
|
||||||
&PrivateMessageUpdateForm {
|
|
||||||
ap_id: Some(apub_id),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
|
||||||
|
|
||||||
let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id)
|
let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or(LemmyErrorType::CouldntFindPrivateMessage)?;
|
.ok_or(LemmyErrorType::CouldntFindPrivateMessage)?;
|
||||||
|
|
|
@ -566,6 +566,10 @@ BEGIN
|
||||||
IF NOT (NEW.path ~ ('*.' || id)::lquery) THEN
|
IF NOT (NEW.path ~ ('*.' || id)::lquery) THEN
|
||||||
NEW.path = NEW.path || id;
|
NEW.path = NEW.path || id;
|
||||||
END IF;
|
END IF;
|
||||||
|
-- Set local ap_id
|
||||||
|
IF NEW.local THEN
|
||||||
|
NEW.ap_id = coalesce(NEW.ap_id, r.local_url ('/comment/' || id));
|
||||||
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
@ -575,3 +579,39 @@ CREATE TRIGGER change_values
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION r.comment_change_values ();
|
EXECUTE FUNCTION r.comment_change_values ();
|
||||||
|
|
||||||
|
CREATE FUNCTION r.post_change_values ()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
-- Set local ap_id
|
||||||
|
IF NEW.local THEN
|
||||||
|
NEW.ap_id = coalesce(NEW.ap_id, r.local_url ('/post/' || NEW.id::text));
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER change_values
|
||||||
|
BEFORE INSERT ON post
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION r.post_change_values ();
|
||||||
|
|
||||||
|
CREATE FUNCTION r.private_message_change_values ()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
-- Set local ap_id
|
||||||
|
IF NEW.local THEN
|
||||||
|
NEW.ap_id = coalesce(NEW.ap_id, r.local_url ('/private_message/' || NEW.id::text));
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER change_values
|
||||||
|
BEFORE INSERT ON private_message
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION r.private_message_change_values ();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ CREATE FUNCTION r.controversy_rank (upvotes numeric, downvotes numeric)
|
||||||
0
|
0
|
||||||
ELSE
|
ELSE
|
||||||
(
|
(
|
||||||
upvotes + downvotes) * CASE WHEN upvotes > downvotes THEN
|
upvotes + downvotes) ^ CASE WHEN upvotes > downvotes THEN
|
||||||
downvotes::float / upvotes::float
|
downvotes::float / upvotes::float
|
||||||
ELSE
|
ELSE
|
||||||
upvotes::float / downvotes::float
|
upvotes::float / downvotes::float
|
||||||
|
@ -57,6 +57,13 @@ BEGIN
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
CREATE FUNCTION r.local_url (url_path text)
|
||||||
|
RETURNS text
|
||||||
|
LANGUAGE sql
|
||||||
|
STABLE PARALLEL SAFE RETURN (
|
||||||
|
current_setting('lemmy.protocol_and_hostname') || url_path
|
||||||
|
);
|
||||||
|
|
||||||
-- This function creates statement-level triggers for all operation types. It's designed this way
|
-- This function creates statement-level triggers for all operation types. It's designed this way
|
||||||
-- because of these limitations:
|
-- because of these limitations:
|
||||||
-- * A trigger that uses transition tables can only handle 1 operation type.
|
-- * A trigger that uses transition tables can only handle 1 operation type.
|
||||||
|
|
|
@ -250,6 +250,7 @@ mod tests {
|
||||||
use diesel_ltree::Ltree;
|
use diesel_ltree::Ltree;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
@ -300,7 +301,12 @@ mod tests {
|
||||||
path: Ltree(format!("0.{}", inserted_comment.id)),
|
path: Ltree(format!("0.{}", inserted_comment.id)),
|
||||||
published: inserted_comment.published,
|
published: inserted_comment.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
ap_id: inserted_comment.ap_id.clone(),
|
ap_id: Url::parse(&format!(
|
||||||
|
"https://lemmy-alpha/comment/{}",
|
||||||
|
inserted_comment.id
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
distinguished: false,
|
distinguished: false,
|
||||||
local: true,
|
local: true,
|
||||||
language_id: LanguageId::default(),
|
language_id: LanguageId::default(),
|
||||||
|
|
|
@ -420,6 +420,7 @@ mod tests {
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
@ -477,7 +478,9 @@ mod tests {
|
||||||
embed_description: None,
|
embed_description: None,
|
||||||
embed_video_url: None,
|
embed_video_url: None,
|
||||||
thumbnail_url: None,
|
thumbnail_url: None,
|
||||||
ap_id: inserted_post.ap_id.clone(),
|
ap_id: Url::parse(&format!("https://lemmy-alpha/post/{}", inserted_post.id))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
local: true,
|
local: true,
|
||||||
language_id: Default::default(),
|
language_id: Default::default(),
|
||||||
featured_community: false,
|
featured_community: false,
|
||||||
|
|
|
@ -100,6 +100,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
@ -138,7 +139,12 @@ mod tests {
|
||||||
read: false,
|
read: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
published: inserted_private_message.published,
|
published: inserted_private_message.published,
|
||||||
ap_id: inserted_private_message.ap_id.clone(),
|
ap_id: Url::parse(&format!(
|
||||||
|
"https://lemmy-alpha/private_message/{}",
|
||||||
|
inserted_private_message.id
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
local: true,
|
local: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -85,12 +85,6 @@ impl fmt::Display for PrivateMessageId {
|
||||||
/// The person mention id.
|
/// The person mention id.
|
||||||
pub struct PersonMentionId(i32);
|
pub struct PersonMentionId(i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
/// The person block id.
|
|
||||||
pub struct PersonBlockId(i32);
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
|
|
@ -40,7 +40,8 @@ use diesel_async::{
|
||||||
AsyncDieselConnectionManager,
|
AsyncDieselConnectionManager,
|
||||||
ManagerConfig,
|
ManagerConfig,
|
||||||
},
|
},
|
||||||
SimpleAsyncConnection,
|
AsyncConnection,
|
||||||
|
RunQueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_bind_if_some::BindIfSome;
|
use diesel_bind_if_some::BindIfSome;
|
||||||
use futures_util::{future::BoxFuture, Future, FutureExt};
|
use futures_util::{future::BoxFuture, Future, FutureExt};
|
||||||
|
@ -343,34 +344,50 @@ pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult<Option<DbUrl>> {
|
||||||
|
|
||||||
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
|
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
|
||||||
let fut = async {
|
let fut = async {
|
||||||
rustls::crypto::ring::default_provider()
|
// We only support TLS with sslmode=require currently
|
||||||
.install_default()
|
let mut conn = if config.contains("sslmode=require") {
|
||||||
.expect("Failed to install rustls crypto provider");
|
rustls::crypto::ring::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.expect("Failed to install rustls crypto provider");
|
||||||
|
|
||||||
let rustls_config = DangerousClientConfigBuilder {
|
let rustls_config = DangerousClientConfigBuilder {
|
||||||
cfg: ClientConfig::builder(),
|
cfg: ClientConfig::builder(),
|
||||||
}
|
|
||||||
.with_custom_certificate_verifier(Arc::new(NoCertVerifier {}))
|
|
||||||
.with_no_client_auth();
|
|
||||||
|
|
||||||
let tls = tokio_postgres_rustls::MakeRustlsConnect::new(rustls_config);
|
|
||||||
let (client, conn) = tokio_postgres::connect(config, tls)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
|
|
||||||
tokio::spawn(async move {
|
|
||||||
if let Err(e) = conn.await {
|
|
||||||
error!("Database connection failed: {e}");
|
|
||||||
}
|
}
|
||||||
});
|
.with_custom_certificate_verifier(Arc::new(NoCertVerifier {}))
|
||||||
let mut conn = AsyncPgConnection::try_from(client).await?;
|
.with_no_client_auth();
|
||||||
// * Change geqo_threshold back to default value if it was changed, so it's higher than the
|
|
||||||
// collapse limits
|
let tls = tokio_postgres_rustls::MakeRustlsConnect::new(rustls_config);
|
||||||
// * Change collapse limits from 8 to 11 so the query planner can find a better table join order
|
let (client, conn) = tokio_postgres::connect(config, tls)
|
||||||
// for more complicated queries
|
.await
|
||||||
conn
|
.map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
|
||||||
.batch_execute("SET geqo_threshold=12;SET from_collapse_limit=11;SET join_collapse_limit=11;")
|
tokio::spawn(async move {
|
||||||
.await
|
if let Err(e) = conn.await {
|
||||||
.map_err(ConnectionError::CouldntSetupConfiguration)?;
|
error!("Database connection failed: {e}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AsyncPgConnection::try_from(client).await?
|
||||||
|
} else {
|
||||||
|
AsyncPgConnection::establish(config).await?
|
||||||
|
};
|
||||||
|
|
||||||
|
diesel::select((
|
||||||
|
// Change geqo_threshold back to default value if it was changed, so it's higher than the
|
||||||
|
// collapse limits
|
||||||
|
functions::set_config("geqo_threshold", "12", false),
|
||||||
|
// Change collapse limits from 8 to 11 so the query planner can find a better table join
|
||||||
|
// order for more complicated queries
|
||||||
|
functions::set_config("from_collapse_limit", "11", false),
|
||||||
|
functions::set_config("join_collapse_limit", "11", false),
|
||||||
|
// Set `lemmy.protocol_and_hostname` so triggers can use it
|
||||||
|
functions::set_config(
|
||||||
|
"lemmy.protocol_and_hostname",
|
||||||
|
SETTINGS.get_protocol_and_hostname(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.execute(&mut conn)
|
||||||
|
.await
|
||||||
|
.map_err(ConnectionError::CouldntSetupConfiguration)?;
|
||||||
Ok(conn)
|
Ok(conn)
|
||||||
};
|
};
|
||||||
fut.boxed()
|
fut.boxed()
|
||||||
|
@ -429,17 +446,11 @@ impl ServerCertVerifier for NoCertVerifier {
|
||||||
|
|
||||||
pub async fn build_db_pool() -> LemmyResult<ActualDbPool> {
|
pub async fn build_db_pool() -> LemmyResult<ActualDbPool> {
|
||||||
let db_url = SETTINGS.get_database_url();
|
let db_url = SETTINGS.get_database_url();
|
||||||
// We only support TLS with sslmode=require currently
|
// diesel-async does not support any TLS connections out of the box, so we need to manually
|
||||||
let tls_enabled = db_url.contains("sslmode=require");
|
// provide a setup function which handles creating the connection
|
||||||
let manager = if tls_enabled {
|
let mut config = ManagerConfig::default();
|
||||||
// diesel-async does not support any TLS connections out of the box, so we need to manually
|
config.custom_setup = Box::new(establish_connection);
|
||||||
// provide a setup function which handles creating the connection
|
let manager = AsyncDieselConnectionManager::<AsyncPgConnection>::new_with_config(&db_url, config);
|
||||||
let mut config = ManagerConfig::default();
|
|
||||||
config.custom_setup = Box::new(establish_connection);
|
|
||||||
AsyncDieselConnectionManager::<AsyncPgConnection>::new_with_config(&db_url, config)
|
|
||||||
} else {
|
|
||||||
AsyncDieselConnectionManager::<AsyncPgConnection>::new(&db_url)
|
|
||||||
};
|
|
||||||
let pool = Pool::builder(manager)
|
let pool = Pool::builder(manager)
|
||||||
.max_size(SETTINGS.database.pool_size)
|
.max_size(SETTINGS.database.pool_size)
|
||||||
.runtime(Runtime::Tokio1)
|
.runtime(Runtime::Tokio1)
|
||||||
|
@ -496,7 +507,7 @@ static EMAIL_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||||
});
|
});
|
||||||
|
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
use diesel::sql_types::{BigInt, Text, Timestamptz};
|
use diesel::sql_types::{BigInt, Bool, Text, Timestamptz};
|
||||||
|
|
||||||
sql_function! {
|
sql_function! {
|
||||||
#[sql_name = "r.hot_rank"]
|
#[sql_name = "r.hot_rank"]
|
||||||
|
@ -519,6 +530,8 @@ pub mod functions {
|
||||||
|
|
||||||
// really this function is variadic, this just adds the two-argument version
|
// really this function is variadic, this just adds the two-argument version
|
||||||
sql_function!(fn coalesce<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(x: diesel::sql_types::Nullable<T>, y: T) -> T);
|
sql_function!(fn coalesce<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(x: diesel::sql_types::Nullable<T>, y: T) -> T);
|
||||||
|
|
||||||
|
sql_function!(fn set_config(setting_name: Text, new_value: Text, is_local: Bool) -> Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*";
|
pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*";
|
||||||
|
@ -528,8 +541,7 @@ pub fn now() -> AsExprOf<diesel::dsl::now, diesel::sql_types::Timestamptz> {
|
||||||
diesel::dsl::now.into_sql::<Timestamptz>()
|
diesel::dsl::now.into_sql::<Timestamptz>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AsRecordOutput<T> = dsl::AsExprOf<T, sql_types::Record<<T as Expression>::SqlType>>;
|
/// Trait alias for a type that can be converted to an SQL tuple using `IntoSql::into_sql`
|
||||||
|
|
||||||
pub trait AsRecord: Expression + AsExpression<sql_types::Record<Self::SqlType>>
|
pub trait AsRecord: Expression + AsExpression<sql_types::Record<Self::SqlType>>
|
||||||
where
|
where
|
||||||
Self::SqlType: 'static,
|
Self::SqlType: 'static,
|
||||||
|
@ -541,8 +553,25 @@ impl<T: Expression + AsExpression<sql_types::Record<T::SqlType>>> AsRecord for T
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Output of `IntoSql::into_sql` for a type that implements `AsRecord`
|
||||||
|
pub type AsRecordOutput<T> = dsl::AsExprOf<T, sql_types::Record<<T as Expression>::SqlType>>;
|
||||||
|
|
||||||
|
/// Output of `t.on((l0, l1).into_sql().eq((r0, r1)))`
|
||||||
type OnTupleEq<T, L0, L1, R0, R1> = dsl::On<T, dsl::Eq<AsRecordOutput<(L0, L1)>, (R0, R1)>>;
|
type OnTupleEq<T, L0, L1, R0, R1> = dsl::On<T, dsl::Eq<AsRecordOutput<(L0, L1)>, (R0, R1)>>;
|
||||||
|
|
||||||
|
/// Creates an `ON` clause for a table where a person ID and another column are used as the
|
||||||
|
/// primary key. Use with the `QueryDsl::left_join` method.
|
||||||
|
///
|
||||||
|
/// This example modifies a query to make columns in `community_actions` available:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// community::table
|
||||||
|
/// .left_join(actions(
|
||||||
|
/// community_actions::table,
|
||||||
|
/// my_person_id,
|
||||||
|
/// community::id,
|
||||||
|
/// ))
|
||||||
|
/// ```
|
||||||
pub fn actions<T, P, C, K0, K1>(
|
pub fn actions<T, P, C, K0, K1>(
|
||||||
actions_table: T,
|
actions_table: T,
|
||||||
person_id: Option<P>,
|
person_id: Option<P>,
|
||||||
|
@ -572,8 +601,7 @@ pub fn actions_alias<T, P, C, K0, K1>(
|
||||||
) -> OnTupleEq<Alias<T>, AliasedField<T, K0>, AliasedField<T, K1>, P, C>
|
) -> OnTupleEq<Alias<T>, AliasedField<T, K0>, AliasedField<T, K1>, P, C>
|
||||||
where
|
where
|
||||||
Alias<T>: QuerySource + Copy,
|
Alias<T>: QuerySource + Copy,
|
||||||
T: AliasSource + Default,
|
T: AliasSource<Target: Table<PrimaryKey = (K0, K1)>> + Default,
|
||||||
T::Target: Table<PrimaryKey = (K0, K1)>,
|
|
||||||
K0: Column<Table = T::Target>,
|
K0: Column<Table = T::Target>,
|
||||||
K1: Column<Table = T::Target>,
|
K1: Column<Table = T::Target>,
|
||||||
(AliasedField<T, K0>, AliasedField<T, K1>): AsRecord,
|
(AliasedField<T, K0>, AliasedField<T, K1>): AsRecord,
|
||||||
|
@ -593,34 +621,32 @@ where
|
||||||
/// `table_name::table.filter(table_name::action_name.is_not_null())`.
|
/// `table_name::table.filter(table_name::action_name.is_not_null())`.
|
||||||
pub fn action_query<C>(column: C) -> dsl::Filter<C::Table, dsl::IsNotNull<C>>
|
pub fn action_query<C>(column: C) -> dsl::Filter<C::Table, dsl::IsNotNull<C>>
|
||||||
where
|
where
|
||||||
C: Column,
|
C: Column<Table: Default + FilterDsl<dsl::IsNotNull<C>>, SqlType: SingleValue>,
|
||||||
C::Table: Default + FilterDsl<dsl::IsNotNull<C>>,
|
|
||||||
C::SqlType: SingleValue,
|
|
||||||
{
|
{
|
||||||
action_query_with_fn(column, |t| t)
|
action_query_with_fn(column, |t| t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `find_action(table_name::action_name, key)` is the same as
|
||||||
|
/// `table_name::table.find(key).filter(table_name::action_name.is_not_null())`.
|
||||||
pub fn find_action<C, K>(
|
pub fn find_action<C, K>(
|
||||||
column: C,
|
column: C,
|
||||||
key: K,
|
key: K,
|
||||||
) -> dsl::Filter<dsl::Find<C::Table, K>, dsl::IsNotNull<C>>
|
) -> dsl::Filter<dsl::Find<C::Table, K>, dsl::IsNotNull<C>>
|
||||||
where
|
where
|
||||||
C: Column,
|
C:
|
||||||
C::Table: Default + FindDsl<K>,
|
Column<Table: Default + FindDsl<K, Output: FilterDsl<dsl::IsNotNull<C>>>, SqlType: SingleValue>,
|
||||||
dsl::Find<C::Table, K>: FilterDsl<dsl::IsNotNull<C>>,
|
|
||||||
C::SqlType: SingleValue,
|
|
||||||
{
|
{
|
||||||
action_query_with_fn(column, |t| t.find(key))
|
action_query_with_fn(column, |t| t.find(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `action_query_with_fn(table_name::action_name, f)` is the same as
|
||||||
|
/// `f(table_name::table).filter(table_name::action_name.is_not_null())`.
|
||||||
fn action_query_with_fn<C, Q>(
|
fn action_query_with_fn<C, Q>(
|
||||||
column: C,
|
column: C,
|
||||||
f: impl FnOnce(C::Table) -> Q,
|
f: impl FnOnce(C::Table) -> Q,
|
||||||
) -> dsl::Filter<Q, dsl::IsNotNull<C>>
|
) -> dsl::Filter<Q, dsl::IsNotNull<C>>
|
||||||
where
|
where
|
||||||
C: Column,
|
C: Column<Table: Default, SqlType: SingleValue>,
|
||||||
C::Table: Default,
|
|
||||||
C::SqlType: SingleValue,
|
|
||||||
Q: FilterDsl<dsl::IsNotNull<C>>,
|
Q: FilterDsl<dsl::IsNotNull<C>>,
|
||||||
{
|
{
|
||||||
f(C::Table::default()).filter(column.is_not_null())
|
f(C::Table::default()).filter(column.is_not_null())
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ALTER TABLE comment
|
||||||
|
ALTER COLUMN ap_id SET DEFAULT generate_unique_changeme ();
|
||||||
|
|
||||||
|
ALTER TABLE post
|
||||||
|
ALTER COLUMN ap_id SET DEFAULT generate_unique_changeme ();
|
||||||
|
|
||||||
|
ALTER TABLE private_message
|
||||||
|
ALTER COLUMN ap_id SET DEFAULT generate_unique_changeme ();
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ALTER TABLE comment
|
||||||
|
ALTER COLUMN ap_id DROP DEFAULT;
|
||||||
|
|
||||||
|
ALTER TABLE post
|
||||||
|
ALTER COLUMN ap_id DROP DEFAULT;
|
||||||
|
|
||||||
|
ALTER TABLE private_message
|
||||||
|
ALTER COLUMN ap_id DROP DEFAULT;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
UPDATE
|
||||||
|
post_aggregates
|
||||||
|
SET
|
||||||
|
controversy_rank = CASE WHEN downvotes <= 0
|
||||||
|
OR upvotes <= 0 THEN
|
||||||
|
0
|
||||||
|
ELSE
|
||||||
|
(upvotes + downvotes) * CASE WHEN upvotes > downvotes THEN
|
||||||
|
downvotes::float / upvotes::float
|
||||||
|
ELSE
|
||||||
|
upvotes::float / downvotes::float
|
||||||
|
END
|
||||||
|
END
|
||||||
|
WHERE
|
||||||
|
upvotes > 0
|
||||||
|
AND downvotes > 0;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
UPDATE
|
||||||
|
post_aggregates
|
||||||
|
SET
|
||||||
|
controversy_rank = CASE WHEN downvotes <= 0
|
||||||
|
OR upvotes <= 0 THEN
|
||||||
|
0
|
||||||
|
ELSE
|
||||||
|
(upvotes + downvotes) ^ CASE WHEN upvotes > downvotes THEN
|
||||||
|
downvotes::float / upvotes::float
|
||||||
|
ELSE
|
||||||
|
upvotes::float / downvotes::float
|
||||||
|
END
|
||||||
|
END
|
||||||
|
WHERE
|
||||||
|
upvotes > 0
|
||||||
|
AND downvotes > 0;
|
||||||
|
|
|
@ -12,6 +12,6 @@ cargo +nightly fmt
|
||||||
taplo format
|
taplo format
|
||||||
|
|
||||||
# Format sql files
|
# Format sql files
|
||||||
find migrations -type f -name '*.sql' -exec pg_format -i {} +
|
find migrations crates/db_schema/replaceable_schema -type f -name '*.sql' -exec pg_format -i {} +
|
||||||
|
|
||||||
cargo clippy --workspace --fix --allow-staged --allow-dirty --tests --all-targets --all-features -- -D warnings
|
cargo clippy --workspace --fix --allow-staged --allow-dirty --tests --all-targets --all-features -- -D warnings
|
||||||
|
|
31
src/lib.rs
31
src/lib.rs
|
@ -55,7 +55,7 @@ use prometheus_metrics::serve_prometheus;
|
||||||
use reqwest_middleware::ClientBuilder;
|
use reqwest_middleware::ClientBuilder;
|
||||||
use reqwest_tracing::TracingMiddleware;
|
use reqwest_tracing::TracingMiddleware;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{env, ops::Deref};
|
use std::{env, ops::Deref, time::Duration};
|
||||||
use tokio::signal::unix::SignalKind;
|
use tokio::signal::unix::SignalKind;
|
||||||
use tracing::subscriber::set_global_default;
|
use tracing::subscriber::set_global_default;
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
|
@ -64,6 +64,13 @@ use tracing_log::LogTracer;
|
||||||
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer, Registry};
|
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer, Registry};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
/// Timeout for HTTP requests while sending activities. A longer timeout provides better
|
||||||
|
/// compatibility with other ActivityPub software that might allocate more time for synchronous
|
||||||
|
/// processing of incoming activities. This timeout should be slightly longer than the time we
|
||||||
|
/// expect a remote server to wait before aborting processing on its own to account for delays from
|
||||||
|
/// establishing the HTTP connection and sending the request itself.
|
||||||
|
const ACTIVITY_SENDING_TIMEOUT: Duration = Duration::from_secs(125);
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(
|
#[command(
|
||||||
version,
|
version,
|
||||||
|
@ -173,8 +180,8 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
serve_prometheus(prometheus, context.clone())?;
|
serve_prometheus(prometheus, context.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut federation_config = FederationConfig::builder();
|
let mut federation_config_builder = FederationConfig::builder();
|
||||||
federation_config
|
federation_config_builder
|
||||||
.domain(SETTINGS.hostname.clone())
|
.domain(SETTINGS.hostname.clone())
|
||||||
.app_data(context.clone())
|
.app_data(context.clone())
|
||||||
.client(client.clone())
|
.client(client.clone())
|
||||||
|
@ -184,9 +191,9 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
.url_verifier(Box::new(VerifyUrlData(context.inner_pool().clone())));
|
.url_verifier(Box::new(VerifyUrlData(context.inner_pool().clone())));
|
||||||
if local_site.federation_signed_fetch {
|
if local_site.federation_signed_fetch {
|
||||||
let site: ApubSite = site_view.site.into();
|
let site: ApubSite = site_view.site.into();
|
||||||
federation_config.signed_fetch_actor(&site);
|
federation_config_builder.signed_fetch_actor(&site);
|
||||||
}
|
}
|
||||||
let federation_config = federation_config.build().await?;
|
let federation_config = federation_config_builder.build().await?;
|
||||||
|
|
||||||
MATCH_OUTGOING_ACTIVITIES
|
MATCH_OUTGOING_ACTIVITIES
|
||||||
.set(Box::new(move |d, c| {
|
.set(Box::new(move |d, c| {
|
||||||
|
@ -209,13 +216,23 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let federate = (!args.disable_activity_sending).then(|| {
|
|
||||||
|
// This FederationConfig instance is exclusively used to send activities, so we can safely
|
||||||
|
// increase the timeout without affecting timeouts for resolving objects anywhere.
|
||||||
|
let federation_sender_config = if !args.disable_activity_sending {
|
||||||
|
let mut federation_sender_config = federation_config_builder.clone();
|
||||||
|
federation_sender_config.request_timeout(ACTIVITY_SENDING_TIMEOUT);
|
||||||
|
Some(federation_sender_config.build().await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let federate = federation_sender_config.map(|cfg| {
|
||||||
SendManager::run(
|
SendManager::run(
|
||||||
Opts {
|
Opts {
|
||||||
process_index: args.federate_process_index,
|
process_index: args.federate_process_index,
|
||||||
process_count: args.federate_process_count,
|
process_count: args.federate_process_count,
|
||||||
},
|
},
|
||||||
federation_config,
|
cfg,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let mut interrupt = tokio::signal::unix::signal(SignalKind::interrupt())?;
|
let mut interrupt = tokio::signal::unix::signal(SignalKind::interrupt())?;
|
||||||
|
|
Loading…
Reference in New Issue