Merge branch 'main' into content_removal_remote_users

content_removal_remote_users
Dessalines 2024-03-01 12:00:44 -05:00 committed by GitHub
commit 8da51cd6c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 85 additions and 64 deletions

1
Cargo.lock generated
View File

@ -2755,6 +2755,7 @@ dependencies = [
"diesel", "diesel",
"diesel-async", "diesel-async",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_utils",
"pretty_assertions", "pretty_assertions",
"serde", "serde",
"serde_with", "serde_with",

View File

@ -422,17 +422,19 @@ pub async fn send_password_reset_email(
// Generate a random token // Generate a random token
let token = uuid::Uuid::new_v4().to_string(); let token = uuid::Uuid::new_v4().to_string();
// Insert the row
let local_user_id = user.local_user.id;
PasswordResetRequest::create_token(pool, local_user_id, token.clone()).await?;
let email = &user.local_user.email.clone().expect("email"); let email = &user.local_user.email.clone().expect("email");
let lang = get_interface_language(user); let lang = get_interface_language(user);
let subject = &lang.password_reset_subject(&user.person.name); let subject = &lang.password_reset_subject(&user.person.name);
let protocol_and_hostname = settings.get_protocol_and_hostname(); let protocol_and_hostname = settings.get_protocol_and_hostname();
let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token); let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
let body = &lang.password_reset_body(reset_link, &user.person.name); let body = &lang.password_reset_body(reset_link, &user.person.name);
send_email(subject, email, &user.person.name, body, settings).await send_email(subject, email, &user.person.name, body, settings).await?;
// Insert the row after successful send, to avoid using daily reset limit while
// email sending is broken.
let local_user_id = user.local_user.id;
PasswordResetRequest::create_token(pool, local_user_id, token.clone()).await?;
Ok(())
} }
/// Send a verification email /// Send a verification email

View File

@ -104,6 +104,7 @@ pub async fn search(
users = PersonQuery { users = PersonQuery {
sort, sort,
search_term: (Some(q)), search_term: (Some(q)),
listing_type: (listing_type),
page: (page), page: (page),
limit: (limit), limit: (limit),
} }
@ -174,6 +175,7 @@ pub async fn search(
PersonQuery { PersonQuery {
sort, sort,
search_term: (Some(q)), search_term: (Some(q)),
listing_type: (listing_type),
page: (page), page: (page),
limit: (limit), limit: (limit),
} }

View File

@ -40,6 +40,7 @@ serial_test = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
url.workspace = true url.workspace = true
lemmy_utils.workspace = true
[package.metadata.cargo-machete] [package.metadata.cargo-machete]
ignored = ["strum"] ignored = ["strum"]

View File

@ -23,6 +23,7 @@ use lemmy_db_schema::{
Queries, Queries,
ReadFn, ReadFn,
}, },
ListingType,
SortType, SortType,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -115,6 +116,15 @@ fn queries<'a>(
let (limit, offset) = limit_and_offset(options.page, options.limit)?; let (limit, offset) = limit_and_offset(options.page, options.limit)?;
query = query.limit(limit).offset(offset); query = query.limit(limit).offset(offset);
if let Some(listing_type) = options.listing_type {
query = match listing_type {
// return nothing as its not possible to follow users
ListingType::Subscribed => query.limit(0),
ListingType::Local => query.filter(person::local.eq(true)),
_ => query,
};
}
} }
} }
query.load::<PersonView>(&mut conn).await query.load::<PersonView>(&mut conn).await
@ -141,6 +151,7 @@ impl PersonView {
pub struct PersonQuery { pub struct PersonQuery {
pub sort: Option<SortType>, pub sort: Option<SortType>,
pub search_term: Option<String>, pub search_term: Option<String>,
pub listing_type: Option<ListingType>,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
} }
@ -168,6 +179,7 @@ mod tests {
traits::Crud, traits::Crud,
utils::build_db_pool_for_tests, utils::build_db_pool_for_tests,
}; };
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
@ -178,64 +190,59 @@ mod tests {
bob_local_user: LocalUser, bob_local_user: LocalUser,
} }
async fn init_data(pool: &mut DbPool<'_>) -> Data { async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
.await
.unwrap();
let alice_form = PersonInsertForm::builder() let alice_form = PersonInsertForm::builder()
.name("alice".to_string()) .name("alice".to_string())
.public_key("pubkey".to_string()) .public_key("pubkey".to_string())
.instance_id(inserted_instance.id) .instance_id(inserted_instance.id)
.local(Some(true))
.build(); .build();
let alice = Person::create(pool, &alice_form).await.unwrap(); let alice = Person::create(pool, &alice_form).await?;
let alice_local_user_form = LocalUserInsertForm::builder() let alice_local_user_form = LocalUserInsertForm::builder()
.person_id(alice.id) .person_id(alice.id)
.password_encrypted(String::new()) .password_encrypted(String::new())
.build(); .build();
let alice_local_user = LocalUser::create(pool, &alice_local_user_form) let alice_local_user = LocalUser::create(pool, &alice_local_user_form).await?;
.await
.unwrap();
let bob_form = PersonInsertForm::builder() let bob_form = PersonInsertForm::builder()
.name("bob".to_string()) .name("bob".to_string())
.bot_account(Some(true)) .bot_account(Some(true))
.public_key("pubkey".to_string()) .public_key("pubkey".to_string())
.instance_id(inserted_instance.id) .instance_id(inserted_instance.id)
.local(Some(false))
.build(); .build();
let bob = Person::create(pool, &bob_form).await.unwrap(); let bob = Person::create(pool, &bob_form).await?;
let bob_local_user_form = LocalUserInsertForm::builder() let bob_local_user_form = LocalUserInsertForm::builder()
.person_id(bob.id) .person_id(bob.id)
.password_encrypted(String::new()) .password_encrypted(String::new())
.build(); .build();
let bob_local_user = LocalUser::create(pool, &bob_local_user_form).await.unwrap(); let bob_local_user = LocalUser::create(pool, &bob_local_user_form).await?;
Data { Ok(Data {
alice, alice,
alice_local_user, alice_local_user,
bob, bob,
bob_local_user, bob_local_user,
} })
} }
async fn cleanup(data: Data, pool: &mut DbPool<'_>) { async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> {
LocalUser::delete(pool, data.alice_local_user.id) LocalUser::delete(pool, data.alice_local_user.id).await?;
.await LocalUser::delete(pool, data.bob_local_user.id).await?;
.unwrap(); Person::delete(pool, data.alice.id).await?;
LocalUser::delete(pool, data.bob_local_user.id) Person::delete(pool, data.bob.id).await?;
.await Instance::delete(pool, data.bob.instance_id).await?;
.unwrap(); Ok(())
Person::delete(pool, data.alice.id).await.unwrap();
Person::delete(pool, data.bob.id).await.unwrap();
Instance::delete(pool, data.bob.instance_id).await.unwrap();
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn exclude_deleted() { async fn exclude_deleted() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await; let data = init_data(pool).await?;
Person::update( Person::update(
pool, pool,
@ -245,8 +252,7 @@ mod tests {
..Default::default() ..Default::default()
}, },
) )
.await .await?;
.unwrap();
let read = PersonView::read(pool, data.alice.id).await; let read = PersonView::read(pool, data.alice.id).await;
assert_eq!(read.err(), Some(NotFound)); assert_eq!(read.err(), Some(NotFound));
@ -256,20 +262,19 @@ mod tests {
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
.await .await?;
.unwrap();
assert_length!(1, list); assert_length!(1, list);
assert_eq!(list[0].person.id, data.bob.id); assert_eq!(list[0].person.id, data.bob.id);
cleanup(data, pool).await; cleanup(data, pool).await
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn list_banned() { async fn list_banned() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await; let data = init_data(pool).await?;
Person::update( Person::update(
pool, pool,
@ -279,22 +284,21 @@ mod tests {
..Default::default() ..Default::default()
}, },
) )
.await .await?;
.unwrap();
let list = PersonView::banned(pool).await.unwrap(); let list = PersonView::banned(pool).await?;
assert_length!(1, list); assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id); assert_eq!(list[0].person.id, data.alice.id);
cleanup(data, pool).await; cleanup(data, pool).await
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn list_admins() { async fn list_admins() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await; let data = init_data(pool).await?;
LocalUser::update( LocalUser::update(
pool, pool,
@ -304,22 +308,45 @@ mod tests {
..Default::default() ..Default::default()
}, },
) )
.await .await?;
.unwrap();
let list = PersonView::admins(pool).await.unwrap(); let list = PersonView::admins(pool).await?;
assert_length!(1, list); assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id); assert_eq!(list[0].person.id, data.alice.id);
let is_admin = PersonView::read(pool, data.alice.id) let is_admin = PersonView::read(pool, data.alice.id).await?.is_admin;
.await
.unwrap()
.is_admin;
assert!(is_admin); assert!(is_admin);
let is_admin = PersonView::read(pool, data.bob.id).await.unwrap().is_admin; let is_admin = PersonView::read(pool, data.bob.id).await?.is_admin;
assert!(!is_admin); assert!(!is_admin);
cleanup(data, pool).await; cleanup(data, pool).await
}
#[tokio::test]
#[serial]
async fn listing_type() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into();
let data = init_data(pool).await?;
let list = PersonQuery {
listing_type: Some(ListingType::Local),
..Default::default()
}
.list(pool)
.await?;
assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id);
let list = PersonQuery {
listing_type: Some(ListingType::All),
..Default::default()
}
.list(pool)
.await?;
assert_length!(2, list);
cleanup(data, pool).await
} }
} }

View File

@ -158,14 +158,6 @@ impl InstanceWorker {
latest_id latest_id
}; };
if id >= latest_id { if id >= latest_id {
if id > latest_id {
tracing::error!(
"{}: last successful id {} is higher than latest id {} in database (did the db get cleared?)",
self.instance.domain,
id.0,
latest_id.0
);
}
// no more work to be done, wait before rechecking // no more work to be done, wait before rechecking
tokio::select! { tokio::select! {
() = sleep(*WORK_FINISHED_RECHECK_DELAY) => {}, () = sleep(*WORK_FINISHED_RECHECK_DELAY) => {},

View File

@ -209,11 +209,7 @@ cfg_if! {
impl fmt::Display for LemmyError { impl fmt::Display for LemmyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: ", &self.error_type)?; write!(f, "{}: ", &self.error_type)?;
// print anyhow including trace writeln!(f, "{}", self.inner)?;
// https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
// this will print the anyhow trace (only if it exists)
// and if RUST_BACKTRACE=1, also a full backtrace
writeln!(f, "{:?}", self.inner)?;
fmt::Display::fmt(&self.context, f) fmt::Display::fmt(&self.context, f)
} }
} }