mirror of https://github.com/LemmyNet/lemmy.git
Merge branch 'main' into error-expose
commit
5bb2e4fbfd
|
@ -2,7 +2,7 @@
|
|||
# See https://github.com/woodpecker-ci/woodpecker/issues/1677
|
||||
|
||||
variables:
|
||||
- &rust_image "rust:1.75"
|
||||
- &rust_image "rust:1.76"
|
||||
- &install_pnpm "corepack enable pnpm"
|
||||
- &slow_check_paths
|
||||
- path:
|
||||
|
|
|
@ -28,6 +28,9 @@ pub struct CreatePost {
|
|||
pub honeypot: Option<String>,
|
||||
pub nsfw: Option<bool>,
|
||||
pub language_id: Option<LanguageId>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
/// Instead of fetching a thumbnail, use a custom one.
|
||||
pub custom_thumbnail: Option<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
@ -114,6 +117,9 @@ pub struct EditPost {
|
|||
pub body: Option<String>,
|
||||
pub nsfw: Option<bool>,
|
||||
pub language_id: Option<LanguageId>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
/// Instead of fetching a thumbnail, use a custom one.
|
||||
pub custom_thumbnail: Option<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -57,10 +57,12 @@ pub async fn create_post(
|
|||
|
||||
let data_url = data.url.as_ref();
|
||||
let url = data_url.map(clean_url_params); // TODO no good way to handle a "clear"
|
||||
let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params);
|
||||
|
||||
is_valid_post_title(&data.name)?;
|
||||
is_valid_body_field(&body, true)?;
|
||||
check_url_scheme(&data.url)?;
|
||||
check_url_scheme(&url)?;
|
||||
check_url_scheme(&custom_thumbnail)?;
|
||||
|
||||
check_community_user_action(
|
||||
&local_user_view.person,
|
||||
|
@ -84,9 +86,17 @@ pub async fn create_post(
|
|||
}
|
||||
}
|
||||
|
||||
// Only generate the thumbnail if there's no custom thumbnail provided,
|
||||
// otherwise it will save it in pictrs
|
||||
let generate_thumbnail = custom_thumbnail.is_none();
|
||||
|
||||
// Fetch post links and pictrs cached image
|
||||
let metadata = fetch_link_metadata_opt(url.as_ref(), true, &context).await;
|
||||
let metadata = fetch_link_metadata_opt(url.as_ref(), generate_thumbnail, &context).await;
|
||||
let url = proxy_image_link_opt_apub(url, &context).await?;
|
||||
let thumbnail_url = proxy_image_link_opt_apub(custom_thumbnail, &context)
|
||||
.await?
|
||||
.map(Into::into)
|
||||
.or(metadata.thumbnail);
|
||||
|
||||
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||
// language, it already only returns allowed languages.
|
||||
|
@ -121,7 +131,7 @@ pub async fn create_post(
|
|||
.embed_description(metadata.opengraph_data.description)
|
||||
.embed_video_url(metadata.opengraph_data.embed_video_url)
|
||||
.language_id(language_id)
|
||||
.thumbnail_url(metadata.thumbnail)
|
||||
.thumbnail_url(thumbnail_url)
|
||||
.build();
|
||||
|
||||
let inserted_post = Post::create(&mut context.pool(), &post_form)
|
||||
|
|
|
@ -43,6 +43,7 @@ pub async fn update_post(
|
|||
// TODO No good way to handle a clear.
|
||||
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
|
||||
let url = data.url.as_ref().map(clean_url_params);
|
||||
let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params);
|
||||
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs_opt(&data.name, &slur_regex)?;
|
||||
|
@ -53,7 +54,8 @@ pub async fn update_post(
|
|||
}
|
||||
|
||||
is_valid_body_field(&body, true)?;
|
||||
check_url_scheme(&data.url)?;
|
||||
check_url_scheme(&url)?;
|
||||
check_url_scheme(&custom_thumbnail)?;
|
||||
|
||||
let post_id = data.post_id;
|
||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||
|
@ -70,10 +72,14 @@ pub async fn update_post(
|
|||
Err(LemmyErrorType::NoPostEditAllowed)?
|
||||
}
|
||||
|
||||
// Fetch post links and Pictrs cached image if url was updated
|
||||
let (embed_title, embed_description, embed_video_url, thumbnail_url) = match &url {
|
||||
// Fetch post links and thumbnail if url was updated
|
||||
let (embed_title, embed_description, embed_video_url, metadata_thumbnail) = match &url {
|
||||
Some(url) => {
|
||||
let metadata = fetch_link_metadata(url, true, &context).await?;
|
||||
// Only generate the thumbnail if there's no custom thumbnail provided,
|
||||
// otherwise it will save it in pictrs
|
||||
let generate_thumbnail = custom_thumbnail.is_none();
|
||||
|
||||
let metadata = fetch_link_metadata(url, generate_thumbnail, &context).await?;
|
||||
(
|
||||
Some(metadata.opengraph_data.title),
|
||||
Some(metadata.opengraph_data.description),
|
||||
|
@ -83,11 +89,21 @@ pub async fn update_post(
|
|||
}
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let url = match url {
|
||||
Some(url) => Some(proxy_image_link_opt_apub(Some(url), &context).await?),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let custom_thumbnail = match custom_thumbnail {
|
||||
Some(custom_thumbnail) => {
|
||||
Some(proxy_image_link_opt_apub(Some(custom_thumbnail), &context).await?)
|
||||
}
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let thumbnail_url = custom_thumbnail.or(metadata_thumbnail);
|
||||
|
||||
let language_id = data.language_id;
|
||||
CommunityLanguage::is_allowed_community_language(
|
||||
&mut context.pool(),
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
use crate::{
|
||||
newtypes::{DbUrl, LocalUserId, PersonId},
|
||||
schema::local_user::dsl::{
|
||||
accepted_application,
|
||||
email,
|
||||
email_verified,
|
||||
local_user,
|
||||
password_encrypted,
|
||||
},
|
||||
schema::{local_user, person, registration_application},
|
||||
source::{
|
||||
actor_language::{LocalUserLanguage, SiteLanguage},
|
||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||
|
@ -15,11 +9,18 @@ use crate::{
|
|||
utils::{
|
||||
functions::{coalesce, lower},
|
||||
get_conn,
|
||||
now,
|
||||
DbPool,
|
||||
},
|
||||
};
|
||||
use bcrypt::{hash, DEFAULT_COST};
|
||||
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel::{
|
||||
dsl::{insert_into, not, IntervalDsl},
|
||||
result::Error,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
impl LocalUser {
|
||||
|
@ -31,16 +32,16 @@ impl LocalUser {
|
|||
let conn = &mut get_conn(pool).await?;
|
||||
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
|
||||
|
||||
diesel::update(local_user.find(local_user_id))
|
||||
.set((password_encrypted.eq(password_hash),))
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set((local_user::password_encrypted.eq(password_hash),))
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_all_users_email_verified(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user)
|
||||
.set(email_verified.eq(true))
|
||||
diesel::update(local_user::table)
|
||||
.set(local_user::email_verified.eq(true))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
@ -49,18 +50,43 @@ impl LocalUser {
|
|||
pool: &mut DbPool<'_>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user)
|
||||
.set(accepted_application.eq(true))
|
||||
diesel::update(local_user::table)
|
||||
.set(local_user::accepted_application.eq(true))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn is_email_taken(pool: &mut DbPool<'_>, email_: &str) -> Result<bool, Error> {
|
||||
pub async fn delete_old_denied_local_users(pool: &mut DbPool<'_>) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
// Make sure:
|
||||
// - The deny reason exists
|
||||
// - The app is older than a week
|
||||
// - The accepted_application is false
|
||||
let old_denied_registrations = registration_application::table
|
||||
.filter(registration_application::deny_reason.is_not_null())
|
||||
.filter(registration_application::published.lt(now() - 1.week()))
|
||||
.select(registration_application::local_user_id);
|
||||
|
||||
// Delete based on join logic is here:
|
||||
// https://stackoverflow.com/questions/60836040/how-do-i-perform-a-delete-with-sub-query-in-diesel-against-a-postgres-database
|
||||
let local_users = local_user::table
|
||||
.filter(local_user::id.eq_any(old_denied_registrations))
|
||||
.filter(not(local_user::accepted_application))
|
||||
.select(local_user::person_id);
|
||||
|
||||
// Delete the person rows, which should automatically clear the local_user ones
|
||||
let persons = person::table.filter(person::id.eq_any(local_users));
|
||||
|
||||
diesel::delete(persons).execute(conn).await
|
||||
}
|
||||
|
||||
pub async fn is_email_taken(pool: &mut DbPool<'_>, email: &str) -> Result<bool, Error> {
|
||||
use diesel::dsl::{exists, select};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
local_user.filter(lower(coalesce(email, "")).eq(email_.to_lowercase())),
|
||||
))
|
||||
select(exists(local_user::table.filter(
|
||||
lower(coalesce(local_user::email, "")).eq(email.to_lowercase()),
|
||||
)))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
@ -78,7 +104,6 @@ impl LocalUser {
|
|||
community_follower,
|
||||
instance,
|
||||
instance_block,
|
||||
person,
|
||||
person_block,
|
||||
post,
|
||||
post_saved,
|
||||
|
@ -140,6 +165,15 @@ impl LocalUser {
|
|||
}
|
||||
}
|
||||
|
||||
impl LocalUserInsertForm {
|
||||
pub fn test_form(person_id: PersonId) -> Self {
|
||||
Self::builder()
|
||||
.person_id(person_id)
|
||||
.password_encrypted(String::new())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserBackupLists {
|
||||
pub followed_communities: Vec<DbUrl>,
|
||||
pub saved_posts: Vec<DbUrl>,
|
||||
|
@ -162,7 +196,7 @@ impl Crud for LocalUser {
|
|||
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
|
||||
form_with_encrypted_password.password_encrypted = password_hash;
|
||||
|
||||
let local_user_ = insert_into(local_user)
|
||||
let local_user_ = insert_into(local_user::table)
|
||||
.values(form_with_encrypted_password)
|
||||
.get_result::<Self>(conn)
|
||||
.await?;
|
||||
|
@ -185,7 +219,7 @@ impl Crud for LocalUser {
|
|||
form: &Self::UpdateForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user.find(local_user_id))
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
newtypes::{CommunityId, DbUrl, PersonId},
|
||||
newtypes::{CommunityId, DbUrl, InstanceId, PersonId},
|
||||
schema::{instance, local_user, person, person_follower},
|
||||
source::person::{
|
||||
Person,
|
||||
|
@ -86,6 +86,16 @@ impl Person {
|
|||
}
|
||||
}
|
||||
|
||||
impl PersonInsertForm {
|
||||
pub fn test_form(instance_id: InstanceId, name: &str) -> Self {
|
||||
Self::builder()
|
||||
.name(name.to_owned())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(instance_id)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ApubActor for Person {
|
||||
async fn read_from_apub_id(
|
||||
|
|
|
@ -698,7 +698,7 @@ mod tests {
|
|||
use lemmy_db_schema::{
|
||||
aggregates::structs::PostAggregates,
|
||||
impls::actor_language::UNDETERMINED_ID,
|
||||
newtypes::{InstanceId, LanguageId, PersonId},
|
||||
newtypes::LanguageId,
|
||||
source::{
|
||||
actor_language::LocalUserLanguage,
|
||||
comment::{Comment, CommentInsertForm},
|
||||
|
@ -757,37 +757,22 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn default_person_insert_form(instance_id: InstanceId, name: &str) -> PersonInsertForm {
|
||||
PersonInsertForm::builder()
|
||||
.name(name.to_owned())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(instance_id)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn default_local_user_form(person_id: PersonId) -> LocalUserInsertForm {
|
||||
LocalUserInsertForm::builder()
|
||||
.person_id(person_id)
|
||||
.password_encrypted(String::new())
|
||||
.build()
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||
|
||||
let new_person = default_person_insert_form(inserted_instance.id, "tegan");
|
||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "tegan");
|
||||
|
||||
let inserted_person = Person::create(pool, &new_person).await?;
|
||||
|
||||
let local_user_form = LocalUserInsertForm {
|
||||
admin: Some(true),
|
||||
..default_local_user_form(inserted_person.id)
|
||||
..LocalUserInsertForm::test_form(inserted_person.id)
|
||||
};
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form).await?;
|
||||
|
||||
let new_bot = PersonInsertForm {
|
||||
bot_account: Some(true),
|
||||
..default_person_insert_form(inserted_instance.id, "mybot")
|
||||
..PersonInsertForm::test_form(inserted_instance.id, "mybot")
|
||||
};
|
||||
|
||||
let inserted_bot = Person::create(pool, &new_bot).await?;
|
||||
|
@ -802,12 +787,15 @@ mod tests {
|
|||
let inserted_community = Community::create(pool, &new_community).await?;
|
||||
|
||||
// Test a person block, make sure the post query doesn't include their post
|
||||
let blocked_person = default_person_insert_form(inserted_instance.id, "john");
|
||||
let blocked_person = PersonInsertForm::test_form(inserted_instance.id, "john");
|
||||
|
||||
let inserted_blocked_person = Person::create(pool, &blocked_person).await?;
|
||||
|
||||
let inserted_blocked_local_user =
|
||||
LocalUser::create(pool, &default_local_user_form(inserted_blocked_person.id)).await?;
|
||||
let inserted_blocked_local_user = LocalUser::create(
|
||||
pool,
|
||||
&LocalUserInsertForm::test_form(inserted_blocked_person.id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let post_from_blocked_person = PostInsertForm::builder()
|
||||
.name(POST_BY_BLOCKED_PERSON.to_string())
|
||||
|
|
|
@ -12,7 +12,7 @@ use diesel_async::RunQueryDsl;
|
|||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
newtypes::{PersonId, PrivateMessageId},
|
||||
schema::{person, person_block, private_message},
|
||||
schema::{instance_block, person, person_block, private_message},
|
||||
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
@ -34,6 +34,13 @@ fn queries<'a>() -> Queries<
|
|||
.and(person_block::person_id.eq(aliases::person1.field(person::id))),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
instance_block::table.on(
|
||||
person::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(aliases::person1.field(person::id))),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let selection = (
|
||||
|
@ -55,7 +62,9 @@ fn queries<'a>() -> Queries<
|
|||
let mut query = all_joins(private_message::table.into_boxed())
|
||||
.select(selection)
|
||||
// Dont show replies from blocked users
|
||||
.filter(person_block::person_id.is_null());
|
||||
.filter(person_block::person_id.is_null())
|
||||
// Dont show replies from blocked instances
|
||||
.filter(instance_block::person_id.is_null());
|
||||
|
||||
// If its unread, I only want the ones to me
|
||||
if options.unread_only {
|
||||
|
@ -116,6 +125,8 @@ impl PrivateMessageView {
|
|||
use diesel::dsl::count;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
private_message::table
|
||||
// Necessary to get the senders instance_id
|
||||
.inner_join(person::table.on(private_message::creator_id.eq(person::id)))
|
||||
.left_join(
|
||||
person_block::table.on(
|
||||
private_message::creator_id
|
||||
|
@ -123,8 +134,17 @@ impl PrivateMessageView {
|
|||
.and(person_block::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
instance_block::table.on(
|
||||
person::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
// Dont count replies from blocked users
|
||||
.filter(person_block::person_id.is_null())
|
||||
// Dont count replies from blocked instances
|
||||
.filter(instance_block::person_id.is_null())
|
||||
.filter(private_message::read.eq(false))
|
||||
.filter(private_message::recipient_id.eq(my_person_id))
|
||||
.filter(private_message::deleted.eq(false))
|
||||
|
@ -160,24 +180,30 @@ mod tests {
|
|||
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
||||
use lemmy_db_schema::{
|
||||
assert_length,
|
||||
newtypes::InstanceId,
|
||||
source::{
|
||||
instance::Instance,
|
||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::{Blockable, Crud},
|
||||
utils::build_db_pool_for_tests,
|
||||
utils::{build_db_pool_for_tests, DbPool},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serial_test::serial;
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_crud() {
|
||||
struct Data {
|
||||
instance: Instance,
|
||||
timmy: Person,
|
||||
jess: Person,
|
||||
sara: Person,
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||
let message_content = String::new();
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
|
||||
let instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
||||
.await
|
||||
|
@ -243,6 +269,32 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(Data {
|
||||
instance,
|
||||
timmy,
|
||||
jess,
|
||||
sara,
|
||||
})
|
||||
}
|
||||
|
||||
async fn cleanup(instance_id: InstanceId, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||
// This also deletes all persons and private messages thanks to sql `on delete cascade`
|
||||
Instance::delete(pool, instance_id).await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn read_private_messages() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let Data {
|
||||
timmy,
|
||||
jess,
|
||||
sara,
|
||||
instance,
|
||||
} = init_data(pool).await?;
|
||||
|
||||
let timmy_messages = PrivateMessageQuery {
|
||||
unread_only: false,
|
||||
creator_id: None,
|
||||
|
@ -303,6 +355,21 @@ mod tests {
|
|||
assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id);
|
||||
assert_eq!(timmy_sara_unread_messages[0].recipient.id, timmy.id);
|
||||
|
||||
cleanup(instance.id, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn ensure_person_block() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let Data {
|
||||
timmy,
|
||||
sara,
|
||||
instance,
|
||||
jess: _,
|
||||
} = init_data(pool).await?;
|
||||
|
||||
// Make sure blocks are working
|
||||
let timmy_blocks_sara_form = PersonBlockForm {
|
||||
person_id: timmy.id,
|
||||
|
@ -336,7 +403,52 @@ mod tests {
|
|||
.unwrap();
|
||||
assert_eq!(timmy_unread_messages, 1);
|
||||
|
||||
// This also deletes all persons and private messages thanks to sql `on delete cascade`
|
||||
Instance::delete(pool, instance.id).await.unwrap();
|
||||
cleanup(instance.id, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn ensure_instance_block() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let Data {
|
||||
timmy,
|
||||
jess: _,
|
||||
sara,
|
||||
instance,
|
||||
} = init_data(pool).await?;
|
||||
// Make sure instance_blocks are working
|
||||
let timmy_blocks_instance_form = InstanceBlockForm {
|
||||
person_id: timmy.id,
|
||||
instance_id: sara.instance_id,
|
||||
};
|
||||
|
||||
let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let expected_instance_block = InstanceBlock {
|
||||
person_id: timmy.id,
|
||||
instance_id: sara.instance_id,
|
||||
published: inserted_instance_block.published,
|
||||
};
|
||||
assert_eq!(expected_instance_block, inserted_instance_block);
|
||||
|
||||
let timmy_messages = PrivateMessageQuery {
|
||||
unread_only: true,
|
||||
creator_id: None,
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool, timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_length!(0, &timmy_messages);
|
||||
|
||||
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(timmy_unread_messages, 0);
|
||||
cleanup(instance.id, pool).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ use once_cell::sync::Lazy;
|
|||
use rss::{
|
||||
extension::{dublincore::DublinCoreExtension, ExtensionBuilder, ExtensionMap},
|
||||
Channel,
|
||||
EnclosureBuilder,
|
||||
Guid,
|
||||
Item,
|
||||
};
|
||||
|
@ -495,7 +496,6 @@ fn create_post_items(
|
|||
let mut items: Vec<Item> = Vec::new();
|
||||
|
||||
for p in posts {
|
||||
// TODO add images
|
||||
let post_url = format!("{}/post/{}", protocol_and_hostname, p.post.id);
|
||||
let community_url = format!(
|
||||
"{}/c/{}",
|
||||
|
@ -520,12 +520,21 @@ fn create_post_items(
|
|||
p.counts.comments);
|
||||
|
||||
// If its a url post, add it to the description
|
||||
let link = Some(if let Some(url) = p.post.url {
|
||||
// and see if we can parse it as a media enclosure.
|
||||
let enclosure_opt = p.post.url.map(|url| {
|
||||
let link_html = format!("<br><a href=\"{url}\">{url}</a>");
|
||||
description.push_str(&link_html);
|
||||
url.to_string()
|
||||
} else {
|
||||
post_url.clone()
|
||||
|
||||
let mime_type = p
|
||||
.post
|
||||
.url_content_type
|
||||
.unwrap_or_else(|| "application/octet-stream".to_string());
|
||||
let mut enclosure_bld = EnclosureBuilder::default();
|
||||
|
||||
enclosure_bld.url(url.as_str().to_string());
|
||||
enclosure_bld.mime_type(mime_type);
|
||||
enclosure_bld.length("0".to_string());
|
||||
enclosure_bld.build()
|
||||
});
|
||||
|
||||
if let Some(body) = p.post.body {
|
||||
|
@ -558,8 +567,9 @@ fn create_post_items(
|
|||
guid,
|
||||
description: Some(sanitize_xml(description)),
|
||||
dublin_core_ext,
|
||||
link,
|
||||
link: Some(post_url.clone()),
|
||||
extensions,
|
||||
enclosure: enclosure_opt,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# syntax=docker/dockerfile:1.6
|
||||
ARG RUST_VERSION=1.75
|
||||
ARG RUST_VERSION=1.76
|
||||
ARG CARGO_BUILD_FEATURES=default
|
||||
ARG RUST_RELEASE_MODE=debug
|
||||
|
||||
|
|
|
@ -22,7 +22,10 @@ use lemmy_db_schema::{
|
|||
received_activity,
|
||||
sent_activity,
|
||||
},
|
||||
source::instance::{Instance, InstanceForm},
|
||||
source::{
|
||||
instance::{Instance, InstanceForm},
|
||||
local_user::LocalUser,
|
||||
},
|
||||
utils::{get_conn, naive_now, now, DbPool, DELETED_REPLACEMENT_TEXT},
|
||||
};
|
||||
use lemmy_routes::nodeinfo::NodeInfo;
|
||||
|
@ -79,24 +82,19 @@ pub async fn setup(context: LemmyContext) -> Result<(), LemmyError> {
|
|||
});
|
||||
|
||||
let context_1 = context.clone();
|
||||
// Overwrite deleted & removed posts and comments every day
|
||||
// Daily tasks:
|
||||
// - Overwrite deleted & removed posts and comments every day
|
||||
// - Delete old denied users
|
||||
// - Update instance software
|
||||
scheduler.every(CTimeUnits::days(1)).run(move || {
|
||||
let context = context_1.clone();
|
||||
|
||||
async move {
|
||||
overwrite_deleted_posts_and_comments(&mut context.pool()).await;
|
||||
}
|
||||
});
|
||||
|
||||
let context_1 = context.clone();
|
||||
// Update the Instance Software
|
||||
scheduler.every(CTimeUnits::days(1)).run(move || {
|
||||
let context = context_1.clone();
|
||||
|
||||
async move {
|
||||
delete_old_denied_users(&mut context.pool()).await;
|
||||
update_instance_software(&mut context.pool(), context.client())
|
||||
.await
|
||||
.map_err(|e| warn!("Failed to update instance software: {e}"))
|
||||
.inspect_err(|e| warn!("Failed to update instance software: {e}"))
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
|
@ -115,6 +113,7 @@ async fn startup_jobs(pool: &mut DbPool<'_>) {
|
|||
update_banned_when_expired(pool).await;
|
||||
clear_old_activities(pool).await;
|
||||
overwrite_deleted_posts_and_comments(pool).await;
|
||||
delete_old_denied_users(pool).await;
|
||||
}
|
||||
|
||||
/// Update the hot_rank columns for the aggregates tables
|
||||
|
@ -277,10 +276,10 @@ async fn delete_expired_captcha_answers(pool: &mut DbPool<'_>) {
|
|||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| {
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.map_err(|e| error!("Failed to clear old captcha answers: {e}"))
|
||||
.inspect_err(|e| error!("Failed to clear old captcha answers: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -301,7 +300,7 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) {
|
|||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to clear old sent activities: {e}"))
|
||||
.inspect_err(|e| error!("Failed to clear old sent activities: {e}"))
|
||||
.ok();
|
||||
|
||||
diesel::delete(
|
||||
|
@ -310,8 +309,8 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) {
|
|||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| info!("Done."))
|
||||
.map_err(|e| error!("Failed to clear old received activities: {e}"))
|
||||
.inspect(|_| info!("Done."))
|
||||
.inspect_err(|e| error!("Failed to clear old received activities: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -320,6 +319,16 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
async fn delete_old_denied_users(pool: &mut DbPool<'_>) {
|
||||
LocalUser::delete_old_denied_local_users(pool)
|
||||
.await
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.inspect(|e| error!("Failed to deleted old denied users: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// overwrite posts and comments 30d after deletion
|
||||
async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) {
|
||||
info!("Overwriting deleted posts...");
|
||||
|
@ -339,10 +348,10 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) {
|
|||
))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| {
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.map_err(|e| error!("Failed to overwrite deleted posts: {e}"))
|
||||
.inspect_err(|e| error!("Failed to overwrite deleted posts: {e}"))
|
||||
.ok();
|
||||
|
||||
info!("Overwriting deleted comments...");
|
||||
|
@ -355,10 +364,10 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) {
|
|||
.set(comment::content.eq(DELETED_REPLACEMENT_TEXT))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| {
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.map_err(|e| error!("Failed to overwrite deleted comments: {e}"))
|
||||
.inspect_err(|e| error!("Failed to overwrite deleted comments: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -390,14 +399,14 @@ async fn active_counts(pool: &mut DbPool<'_>) {
|
|||
sql_query(update_site_stmt)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to update site stats: {e}"))
|
||||
.inspect_err(|e| error!("Failed to update site stats: {e}"))
|
||||
.ok();
|
||||
|
||||
let update_community_stmt = format!("update community_aggregates ca set users_active_{} = mv.count_ from community_aggregates_activity('{}') mv where ca.community_id = mv.community_id_", i.1, i.0);
|
||||
sql_query(update_community_stmt)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to update community stats: {e}"))
|
||||
.inspect_err(|e| error!("Failed to update community stats: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
|
@ -424,7 +433,7 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) {
|
|||
.set(person::banned.eq(false))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to update person.banned when expires: {e}"))
|
||||
.inspect_err(|e| error!("Failed to update person.banned when expires: {e}"))
|
||||
.ok();
|
||||
|
||||
diesel::delete(
|
||||
|
@ -432,7 +441,7 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) {
|
|||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to remove community_ban expired rows: {e}"))
|
||||
.inspect_err(|e| error!("Failed to remove community_ban expired rows: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
Loading…
Reference in New Issue