mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Compare commits
No commits in common. "a5eaad7afd1fd27a7432beff67ae10182db20b93" and "622ce6c976adf69667abf8ad5b9dfbd0897d9998" have entirely different histories.
a5eaad7afd
...
622ce6c976
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2538,7 +2538,6 @@ dependencies = [
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"futures",
|
||||
"lemmy_api_common",
|
||||
"lemmy_db_schema",
|
||||
|
@ -21,16 +21,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
||||
"@typescript-eslint/parser": "^8.1.0",
|
||||
"eslint": "^9.9.0",
|
||||
"@types/node": "^22.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.20.0-alpha.12",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.1.0"
|
||||
"typescript-eslint": "^8.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,16 @@ importers:
|
||||
specifier: ^29.5.12
|
||||
version: 29.5.12
|
||||
'@types/node':
|
||||
specifier: ^22.3.0
|
||||
specifier: ^22.0.2
|
||||
version: 22.3.0
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^8.1.0
|
||||
specifier: ^8.0.0
|
||||
version: 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.1.0
|
||||
specifier: ^8.0.0
|
||||
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
|
||||
eslint:
|
||||
specifier: ^9.9.0
|
||||
specifier: ^9.8.0
|
||||
version: 9.9.0
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.1.3
|
||||
@ -42,7 +42,7 @@ importers:
|
||||
specifier: ^5.5.4
|
||||
version: 5.5.4
|
||||
typescript-eslint:
|
||||
specifier: ^8.1.0
|
||||
specifier: ^8.0.0
|
||||
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
|
||||
|
||||
packages:
|
||||
|
@ -628,7 +628,7 @@ test("Enforce community ban for federated user", async () => {
|
||||
// Alpha tries to make post on beta, but it fails because of ban
|
||||
await expect(
|
||||
createPost(alpha, betaCommunity.community.id),
|
||||
).rejects.toStrictEqual(Error("person_is_banned_from_community"));
|
||||
).rejects.toStrictEqual(Error("banned_from_community"));
|
||||
|
||||
// Unban alpha
|
||||
let unBanAlpha = await banPersonFromCommunity(
|
||||
|
@ -52,12 +52,15 @@ pub async fn add_mod_to_community(
|
||||
// moderator. This is necessary because otherwise the action would be rejected
|
||||
// by the community's home instance.
|
||||
if local_user_view.local_user.admin && !community.local {
|
||||
CommunityModeratorView::check_is_community_moderator(
|
||||
let is_mod = CommunityModeratorView::is_community_moderator(
|
||||
&mut context.pool(),
|
||||
community.id,
|
||||
local_user_view.person.id,
|
||||
)
|
||||
.await?;
|
||||
if !is_mod {
|
||||
Err(LemmyErrorType::NotAModerator)?
|
||||
}
|
||||
}
|
||||
|
||||
// Update in local database
|
||||
|
@ -265,6 +265,8 @@ pub async fn local_user_view_from_jwt(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -63,7 +63,9 @@ pub async fn save_user_settings(
|
||||
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
|
||||
// if email was changed, check that it is not taken and send verification mail
|
||||
if previous_email.deref() != email {
|
||||
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
|
||||
return Err(LemmyErrorType::EmailAlreadyExists)?;
|
||||
}
|
||||
send_verification_email(
|
||||
&local_user_view,
|
||||
email,
|
||||
@ -130,6 +132,7 @@ pub async fn save_user_settings(
|
||||
send_notifications_to_email: data.send_notifications_to_email,
|
||||
show_nsfw: data.show_nsfw,
|
||||
blur_nsfw: data.blur_nsfw,
|
||||
auto_expand: data.auto_expand,
|
||||
show_bot_accounts: data.show_bot_accounts,
|
||||
default_post_sort_type,
|
||||
default_comment_sort_type,
|
||||
|
@ -34,7 +34,7 @@ use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType, CACHE_DURATION_API};
|
||||
use serial_test::serial;
|
||||
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance, LocalUserView)> {
|
||||
let pool = &mut context.pool();
|
||||
|
||||
@ -109,7 +109,7 @@ async fn signup(
|
||||
Ok((local_user, application))
|
||||
}
|
||||
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
async fn get_application_statuses(
|
||||
context: &Data<LemmyContext>,
|
||||
admin: LocalUserView,
|
||||
@ -138,9 +138,10 @@ async fn get_application_statuses(
|
||||
Ok((application_count, unread_applications, all_applications))
|
||||
}
|
||||
|
||||
#[serial]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[tokio::test]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[serial]
|
||||
async fn test_application_approval() -> LemmyResult<()> {
|
||||
let context = LemmyContext::init_test_context().await;
|
||||
let pool = &mut context.pool();
|
||||
|
@ -42,7 +42,7 @@ pub async fn get_sitemap(context: Data<LemmyContext>) -> LemmyResult<HttpRespons
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
|
||||
use crate::sitemap::generate_urlset;
|
||||
|
@ -29,8 +29,12 @@ impl Claims {
|
||||
let claims =
|
||||
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
|
||||
let user_id = LocalUserId(claims.claims.sub.parse()?);
|
||||
LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
|
||||
Ok(user_id)
|
||||
let is_valid = LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
|
||||
if !is_valid {
|
||||
Err(LemmyErrorType::NotLoggedIn)?
|
||||
} else {
|
||||
Ok(user_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn generate(
|
||||
@ -69,7 +73,8 @@ impl Claims {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{claims::Claims, context::LemmyContext};
|
||||
|
@ -19,12 +19,11 @@ pub struct CreateOAuthProvider {
|
||||
pub client_id: String,
|
||||
pub client_secret: String,
|
||||
pub scopes: String,
|
||||
pub auto_verify_email: Option<bool>,
|
||||
pub account_linking_enabled: Option<bool>,
|
||||
pub enabled: Option<bool>,
|
||||
pub auto_verify_email: bool,
|
||||
pub account_linking_enabled: bool,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
|
@ -84,8 +84,8 @@ pub struct CaptchaResponse {
|
||||
pub struct SaveUserSettings {
|
||||
/// Show nsfw posts.
|
||||
pub show_nsfw: Option<bool>,
|
||||
/// Blur nsfw posts.
|
||||
pub blur_nsfw: Option<bool>,
|
||||
pub auto_expand: Option<bool>,
|
||||
/// Your user's theme.
|
||||
pub theme: Option<String>,
|
||||
/// The default post listing type, usually "local"
|
||||
|
@ -30,8 +30,6 @@ pub struct CreatePost {
|
||||
pub language_id: Option<LanguageId>,
|
||||
/// Instead of fetching a thumbnail, use a custom one.
|
||||
pub custom_thumbnail: Option<String>,
|
||||
/// Time when this post should be scheduled. Null means publish immediately.
|
||||
pub scheduled_publish_time: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@ -126,8 +124,6 @@ pub struct EditPost {
|
||||
pub language_id: Option<LanguageId>,
|
||||
/// Instead of fetching a thumbnail, use a custom one.
|
||||
pub custom_thumbnail: Option<String>,
|
||||
/// Time when this post should be scheduled. Null means publish immediately.
|
||||
pub scheduled_publish_time: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
@ -471,7 +471,8 @@ pub async fn replace_image(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -78,7 +78,7 @@ pub struct Search {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub title_only: Option<bool>,
|
||||
pub post_title_only: Option<bool>,
|
||||
pub post_url_only: Option<bool>,
|
||||
pub saved_only: Option<bool>,
|
||||
pub liked_only: Option<bool>,
|
||||
|
@ -73,7 +73,13 @@ pub async fn is_mod_or_admin(
|
||||
community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
check_user_valid(person)?;
|
||||
CommunityView::check_is_mod_or_admin(pool, person.id, community_id).await
|
||||
|
||||
let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person.id, community_id).await?;
|
||||
if !is_mod_or_admin {
|
||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
@ -104,7 +110,13 @@ pub async fn check_community_mod_of_any_or_admin_action(
|
||||
let person = &local_user_view.person;
|
||||
|
||||
check_user_valid(person)?;
|
||||
CommunityView::check_is_mod_of_any_or_admin(pool, person.id).await
|
||||
|
||||
let is_mod_of_any_or_admin = CommunityView::is_mod_of_any_or_admin(pool, person.id).await?;
|
||||
if !is_mod_of_any_or_admin {
|
||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> {
|
||||
@ -230,7 +242,7 @@ pub async fn check_community_user_action(
|
||||
) -> LemmyResult<()> {
|
||||
check_user_valid(person)?;
|
||||
check_community_deleted_removed(community_id, pool).await?;
|
||||
CommunityPersonBanView::check(pool, person.id, community_id).await?;
|
||||
check_community_ban(person, community_id, pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -245,6 +257,19 @@ async fn check_community_deleted_removed(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_community_ban(
|
||||
person: &Person,
|
||||
community_id: CommunityId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
// check if user was banned from site or community
|
||||
let is_banned = CommunityPersonBanView::get(pool, person.id, community_id).await?;
|
||||
if is_banned {
|
||||
Err(LemmyErrorType::BannedFromCommunity)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that the given user can perform a mod action in the community.
|
||||
///
|
||||
/// In particular it checks that he is an admin or mod, wasn't banned and the community isn't
|
||||
@ -256,7 +281,7 @@ pub async fn check_community_mod_action(
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
is_mod_or_admin(pool, person, community_id).await?;
|
||||
CommunityPersonBanView::check(pool, person.id, community_id).await?;
|
||||
check_community_ban(person, community_id, pool).await?;
|
||||
|
||||
// it must be possible to restore deleted community
|
||||
if !allow_deleted {
|
||||
@ -282,6 +307,51 @@ pub fn check_comment_deleted_or_removed(comment: &Comment) -> LemmyResult<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Throws an error if a recipient has blocked a person.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_person_block(
|
||||
my_id: PersonId,
|
||||
potential_blocker_id: PersonId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
let is_blocked = PersonBlock::read(pool, potential_blocker_id, my_id).await?;
|
||||
if is_blocked {
|
||||
Err(LemmyErrorType::PersonIsBlocked)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Throws an error if a recipient has blocked a community.
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn check_community_block(
|
||||
community_id: CommunityId,
|
||||
person_id: PersonId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
let is_blocked = CommunityBlock::read(pool, person_id, community_id).await?;
|
||||
if is_blocked {
|
||||
Err(LemmyErrorType::CommunityIsBlocked)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Throws an error if a recipient has blocked an instance.
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn check_instance_block(
|
||||
instance_id: InstanceId,
|
||||
person_id: PersonId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
let is_blocked = InstanceBlock::read(pool, person_id, instance_id).await?;
|
||||
if is_blocked {
|
||||
Err(LemmyErrorType::InstanceIsBlocked)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_person_instance_community_block(
|
||||
my_id: PersonId,
|
||||
@ -290,9 +360,9 @@ pub async fn check_person_instance_community_block(
|
||||
community_id: CommunityId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
PersonBlock::read(pool, potential_blocker_id, my_id).await?;
|
||||
InstanceBlock::read(pool, potential_blocker_id, community_instance_id).await?;
|
||||
CommunityBlock::read(pool, potential_blocker_id, community_id).await?;
|
||||
check_person_block(my_id, potential_blocker_id, pool).await?;
|
||||
check_instance_block(community_instance_id, potential_blocker_id, pool).await?;
|
||||
check_community_block(community_id, potential_blocker_id, pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -776,13 +846,12 @@ pub async fn remove_or_restore_user_data_in_community(
|
||||
|
||||
// Comments
|
||||
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
||||
let site = Site::read_local(pool).await?;
|
||||
let comments = CommentQuery {
|
||||
creator_id: Some(banned_person_id),
|
||||
community_id: Some(community_id),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
for comment_view in &comments {
|
||||
@ -1067,7 +1136,8 @@ fn build_proxied_image_url(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -27,7 +27,6 @@ futures.workspace = true
|
||||
uuid = { workspace = true }
|
||||
moka.workspace = true
|
||||
anyhow.workspace = true
|
||||
chrono.workspace = true
|
||||
webmention = "0.6.0"
|
||||
accept-language = "3.1.0"
|
||||
serde_json = { workspace = true }
|
||||
|
@ -1,4 +1,3 @@
|
||||
use super::convert_published_time;
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
@ -96,12 +95,15 @@ pub async fn create_post(
|
||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||
if community.posting_restricted_to_mods {
|
||||
let community_id = data.community_id;
|
||||
CommunityModeratorView::check_is_community_moderator(
|
||||
let is_mod = CommunityModeratorView::is_community_moderator(
|
||||
&mut context.pool(),
|
||||
community_id,
|
||||
local_user_view.local_user.person_id,
|
||||
)
|
||||
.await?;
|
||||
if !is_mod {
|
||||
Err(LemmyErrorType::OnlyModsCanPostInCommunity)?
|
||||
}
|
||||
}
|
||||
|
||||
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||
@ -126,15 +128,12 @@ pub async fn create_post(
|
||||
}
|
||||
};
|
||||
|
||||
let scheduled_publish_time =
|
||||
convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?;
|
||||
let post_form = PostInsertForm {
|
||||
url: url.map(Into::into),
|
||||
body,
|
||||
alt_text: data.alt_text.clone(),
|
||||
nsfw: data.nsfw,
|
||||
language_id,
|
||||
scheduled_publish_time,
|
||||
..PostInsertForm::new(
|
||||
data.name.trim().to_string(),
|
||||
local_user_view.person.id,
|
||||
@ -146,16 +145,10 @@ pub async fn create_post(
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
||||
|
||||
let federate_post = if scheduled_publish_time.is_none() {
|
||||
send_webmention(inserted_post.clone(), community);
|
||||
|post| Some(SendActivityData::CreatePost(post))
|
||||
} else {
|
||||
|_| None
|
||||
};
|
||||
generate_post_link_metadata(
|
||||
inserted_post.clone(),
|
||||
custom_thumbnail.map(Into::into),
|
||||
federate_post,
|
||||
|post| Some(SendActivityData::CreatePost(post)),
|
||||
context.reset_request_count(),
|
||||
)
|
||||
.await?;
|
||||
@ -175,14 +168,11 @@ pub async fn create_post(
|
||||
|
||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||
|
||||
build_post_response(&context, community_id, local_user_view, post_id).await
|
||||
}
|
||||
|
||||
pub fn send_webmention(post: Post, community: Community) {
|
||||
if let Some(url) = post.url.clone() {
|
||||
if let Some(url) = inserted_post.url.clone() {
|
||||
if community.visibility == CommunityVisibility::Public {
|
||||
spawn_try_task(async move {
|
||||
let mut webmention = Webmention::new::<Url>(post.ap_id.clone().into(), url.clone().into())?;
|
||||
let mut webmention =
|
||||
Webmention::new::<Url>(inserted_post.ap_id.clone().into(), url.clone().into())?;
|
||||
webmention.set_checked(true);
|
||||
match webmention
|
||||
.send()
|
||||
@ -196,4 +186,6 @@ pub fn send_webmention(post: Post, community: Community) {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
build_post_response(&context, community_id, local_user_view, post_id).await
|
||||
}
|
||||
|
@ -1,38 +1,5 @@
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::source::post::Post;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
pub mod create;
|
||||
pub mod delete;
|
||||
pub mod read;
|
||||
pub mod remove;
|
||||
pub mod update;
|
||||
|
||||
async fn convert_published_time(
|
||||
scheduled_publish_time: Option<i64>,
|
||||
local_user_view: &LocalUserView,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<Option<DateTime<Utc>>> {
|
||||
const MAX_SCHEDULED_POSTS: i64 = 10;
|
||||
if let Some(scheduled_publish_time) = scheduled_publish_time {
|
||||
let converted = Utc
|
||||
.timestamp_opt(scheduled_publish_time, 0)
|
||||
.single()
|
||||
.ok_or(LemmyErrorType::InvalidUnixTime)?;
|
||||
if converted < Utc::now() {
|
||||
Err(LemmyErrorType::PostScheduleTimeMustBeInFuture)?;
|
||||
}
|
||||
if !local_user_view.local_user.admin {
|
||||
let count =
|
||||
Post::user_scheduled_post_count(local_user_view.person.id, &mut context.pool()).await?;
|
||||
if count >= MAX_SCHEDULED_POSTS {
|
||||
Err(LemmyErrorType::TooManyScheduledPosts)?;
|
||||
}
|
||||
}
|
||||
Ok(Some(converted))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use super::{convert_published_time, create::send_webmention};
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
@ -17,7 +16,6 @@ use lemmy_api_common::{
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
community::Community,
|
||||
local_site::LocalSite,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
@ -109,21 +107,6 @@ pub async fn update_post(
|
||||
)
|
||||
.await?;
|
||||
|
||||
// handle changes to scheduled_publish_time
|
||||
let scheduled_publish_time = match (
|
||||
orig_post.scheduled_publish_time,
|
||||
data.scheduled_publish_time,
|
||||
) {
|
||||
// schedule time can be changed if post is still scheduled (and not published yet)
|
||||
(Some(_), Some(_)) => {
|
||||
Some(convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?)
|
||||
}
|
||||
// post was scheduled, gets changed to publish immediately
|
||||
(Some(_), None) => Some(None),
|
||||
// unchanged
|
||||
(_, _) => None,
|
||||
};
|
||||
|
||||
let post_form = PostUpdateForm {
|
||||
name: data.name.clone(),
|
||||
url,
|
||||
@ -132,7 +115,6 @@ pub async fn update_post(
|
||||
nsfw: data.nsfw,
|
||||
language_id: data.language_id,
|
||||
updated: Some(Some(naive_now())),
|
||||
scheduled_publish_time,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -141,36 +123,13 @@ pub async fn update_post(
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
|
||||
|
||||
// send out federation/webmention if necessary
|
||||
match (
|
||||
orig_post.scheduled_publish_time,
|
||||
data.scheduled_publish_time,
|
||||
) {
|
||||
// schedule was removed, send create activity and webmention
|
||||
(Some(_), None) => {
|
||||
let community = Community::read(&mut context.pool(), orig_post.community_id).await?;
|
||||
send_webmention(updated_post.clone(), community);
|
||||
generate_post_link_metadata(
|
||||
updated_post.clone(),
|
||||
custom_thumbnail.flatten().map(Into::into),
|
||||
|post| Some(SendActivityData::CreatePost(post)),
|
||||
context.reset_request_count(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
// post was already public, send update
|
||||
(None, _) => {
|
||||
generate_post_link_metadata(
|
||||
updated_post.clone(),
|
||||
custom_thumbnail.flatten().map(Into::into),
|
||||
|post| Some(SendActivityData::UpdatePost(post)),
|
||||
context.reset_request_count(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
// schedule was changed, do nothing
|
||||
(Some(_), Some(_)) => {}
|
||||
};
|
||||
generate_post_link_metadata(
|
||||
updated_post.clone(),
|
||||
custom_thumbnail.flatten().map(Into::into),
|
||||
|post| Some(SendActivityData::UpdatePost(post)),
|
||||
context.reset_request_count(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
build_post_response(
|
||||
context.deref(),
|
||||
|
@ -5,6 +5,7 @@ use lemmy_api_common::{
|
||||
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::{
|
||||
check_person_block,
|
||||
get_interface_language,
|
||||
get_url_blocklist,
|
||||
local_site_to_slur_regex,
|
||||
@ -15,7 +16,6 @@ use lemmy_api_common::{
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
person_block::PersonBlock,
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
@ -39,10 +39,10 @@ pub async fn create_private_message(
|
||||
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
||||
is_valid_body_field(&content, false)?;
|
||||
|
||||
PersonBlock::read(
|
||||
&mut context.pool(),
|
||||
data.recipient_id,
|
||||
check_person_block(
|
||||
local_user_view.person.id,
|
||||
data.recipient_id,
|
||||
&mut context.pool(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -189,6 +189,8 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::site::create::validate_create_payload;
|
||||
|
@ -48,6 +48,8 @@ fn not_zero(val: Option<i32>) -> Option<i32> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::site::{application_question_check, not_zero, site_default_post_listing_type_check};
|
||||
|
@ -241,6 +241,8 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::site::update::validate_update_payload;
|
||||
|
@ -92,25 +92,36 @@ pub async fn register(
|
||||
}
|
||||
|
||||
if local_site.site_setup && local_site.captcha_enabled {
|
||||
let uuid = uuid::Uuid::parse_str(&data.captcha_uuid.clone().unwrap_or_default())?;
|
||||
CaptchaAnswer::check_captcha(
|
||||
&mut context.pool(),
|
||||
CheckCaptchaAnswer {
|
||||
uuid,
|
||||
answer: data.captcha_answer.clone().unwrap_or_default(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
if let Some(captcha_uuid) = &data.captcha_uuid {
|
||||
let uuid = uuid::Uuid::parse_str(captcha_uuid)?;
|
||||
let check = CaptchaAnswer::check_captcha(
|
||||
&mut context.pool(),
|
||||
CheckCaptchaAnswer {
|
||||
uuid,
|
||||
answer: data.captcha_answer.clone().unwrap_or_default(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
if !check {
|
||||
Err(LemmyErrorType::CaptchaIncorrect)?
|
||||
}
|
||||
} else {
|
||||
Err(LemmyErrorType::CaptchaIncorrect)?
|
||||
}
|
||||
}
|
||||
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs(&data.username, &slur_regex)?;
|
||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||
|
||||
Person::check_username_taken(&mut context.pool(), &data.username).await?;
|
||||
if Person::is_username_taken(&mut context.pool(), &data.username).await? {
|
||||
return Err(LemmyErrorType::UsernameAlreadyExists)?;
|
||||
}
|
||||
|
||||
if let Some(email) = &data.email {
|
||||
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
|
||||
Err(LemmyErrorType::EmailAlreadyExists)?
|
||||
}
|
||||
}
|
||||
|
||||
// We have to create both a person, and local_user
|
||||
@ -327,7 +338,9 @@ pub async fn authenticate_with_oauth(
|
||||
check_slurs(username, &slur_regex)?;
|
||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||
|
||||
Person::check_username_taken(&mut context.pool(), username).await?;
|
||||
if Person::is_username_taken(&mut context.pool(), username).await? {
|
||||
return Err(LemmyErrorType::UsernameAlreadyExists)?;
|
||||
}
|
||||
|
||||
// We have to create a person, a local_user, and an oauth_account
|
||||
person = create_person(
|
||||
|
@ -213,13 +213,15 @@ async fn can_accept_activity_in_community(
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
if let Some(community) = community {
|
||||
if !community.local
|
||||
&& !CommunityFollower::has_local_followers(&mut context.pool(), community.id).await?
|
||||
{
|
||||
Err(LemmyErrorType::CommunityHasNoFollowers)?
|
||||
}
|
||||
// Local only community can't federate
|
||||
if community.visibility != CommunityVisibility::Public {
|
||||
return Err(LemmyErrorType::NotFound.into());
|
||||
}
|
||||
if !community.local {
|
||||
CommunityFollower::check_has_local_followers(&mut context.pool(), community.id).await?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -87,7 +87,12 @@ pub(crate) async fn verify_person_in_community(
|
||||
}
|
||||
let person_id = person.id;
|
||||
let community_id = community.id;
|
||||
CommunityPersonBanView::check(&mut context.pool(), person_id, community_id).await
|
||||
let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id).await?;
|
||||
if is_banned {
|
||||
Err(LemmyErrorType::PersonIsBannedFromCommunity)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that mod action in community was performed by a moderator.
|
||||
@ -101,6 +106,14 @@ pub(crate) async fn verify_mod_action(
|
||||
community: &Community,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
let mod_ = mod_id.dereference(context).await?;
|
||||
|
||||
let is_mod_or_admin =
|
||||
CommunityView::is_mod_or_admin(&mut context.pool(), mod_.id, community.id).await?;
|
||||
if is_mod_or_admin {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// mod action comes from the same instance as the community, so it was presumably done
|
||||
// by an instance admin.
|
||||
// TODO: federate instance admin status and check it here
|
||||
@ -108,8 +121,7 @@ pub(crate) async fn verify_mod_action(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mod_ = mod_id.dereference(context).await?;
|
||||
CommunityView::check_is_mod_or_admin(&mut context.pool(), mod_.id, community.id).await
|
||||
Err(LemmyErrorType::NotAModerator)?
|
||||
}
|
||||
|
||||
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> {
|
||||
|
@ -123,6 +123,7 @@ impl InCommunity for AnnouncableActivities {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -12,13 +12,10 @@ use lemmy_api_common::{
|
||||
utils::check_private_instance,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{comment::Comment, community::Community},
|
||||
source::{comment::Comment, community::Community, local_site::LocalSite},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::{
|
||||
comment_view::CommentQuery,
|
||||
structs::{LocalUserView, SiteView},
|
||||
};
|
||||
use lemmy_db_views::{comment_view::CommentQuery, structs::LocalUserView};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
@ -27,8 +24,8 @@ pub async fn list_comments(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> LemmyResult<Json<GetCommentsResponse>> {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &site_view.local_site)?;
|
||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let community_id = if let Some(name) = &data.community_name {
|
||||
Some(
|
||||
@ -43,7 +40,7 @@ pub async fn list_comments(
|
||||
let sort = Some(comment_sort_type_with_default(
|
||||
data.sort,
|
||||
local_user_ref,
|
||||
&site_view.local_site,
|
||||
&local_site,
|
||||
));
|
||||
let max_depth = data.max_depth;
|
||||
let saved_only = data.saved_only;
|
||||
@ -61,7 +58,7 @@ pub async fn list_comments(
|
||||
let listing_type = Some(listing_type_with_default(
|
||||
data.type_,
|
||||
local_user_view.as_ref().map(|u| &u.local_user),
|
||||
&site_view.local_site,
|
||||
&local_site,
|
||||
community_id,
|
||||
));
|
||||
|
||||
@ -91,7 +88,7 @@ pub async fn list_comments(
|
||||
limit,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&site_view.site, &mut context.pool())
|
||||
.list(&mut context.pool())
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntGetComments)?;
|
||||
|
||||
|
@ -85,7 +85,7 @@ pub async fn read_person(
|
||||
creator_id,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.list(&mut context.pool())
|
||||
.await?;
|
||||
|
||||
let moderates = CommunityModeratorView::for_person(
|
||||
|
@ -47,7 +47,7 @@ pub async fn search(
|
||||
listing_type,
|
||||
page,
|
||||
limit,
|
||||
title_only,
|
||||
post_title_only,
|
||||
post_url_only,
|
||||
saved_only,
|
||||
liked_only,
|
||||
@ -78,7 +78,7 @@ pub async fn search(
|
||||
search_term: Some(q.clone()),
|
||||
page,
|
||||
limit,
|
||||
title_only,
|
||||
title_only: post_title_only,
|
||||
url_only: post_url_only,
|
||||
liked_only,
|
||||
disliked_only,
|
||||
@ -105,7 +105,6 @@ pub async fn search(
|
||||
sort,
|
||||
listing_type,
|
||||
search_term: Some(q.clone()),
|
||||
title_only,
|
||||
local_user,
|
||||
is_mod_or_admin: is_admin,
|
||||
page,
|
||||
@ -128,9 +127,7 @@ pub async fn search(
|
||||
.await?;
|
||||
}
|
||||
SearchType::Comments => {
|
||||
comments = comment_query
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
comments = comment_query.list(&mut context.pool()).await?;
|
||||
}
|
||||
SearchType::Communities => {
|
||||
communities = community_query
|
||||
@ -149,9 +146,7 @@ pub async fn search(
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
comments = comment_query
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
comments = comment_query.list(&mut context.pool()).await?;
|
||||
|
||||
communities = if community_or_creator_included {
|
||||
vec![]
|
||||
|
@ -127,6 +127,7 @@ pub async fn import_settings(
|
||||
show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts),
|
||||
open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab),
|
||||
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
||||
auto_expand: data.settings.as_ref().map(|s| s.auto_expand),
|
||||
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
||||
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
||||
..Default::default()
|
||||
@ -307,9 +308,8 @@ where
|
||||
});
|
||||
Ok(failed_items.into_iter().join(","))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::api::user_settings_backup::{export_settings, import_settings, UserSettingsBackup};
|
||||
|
@ -98,7 +98,7 @@ impl Collection for ApubCommunityModerators {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -120,7 +120,8 @@ pub(crate) async fn get_apub_community_featured(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub(crate) mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -39,7 +39,7 @@ use lemmy_db_schema::{
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||
use lemmy_utils::{
|
||||
error::{LemmyError, LemmyResult},
|
||||
error::{LemmyError, LemmyErrorType, LemmyResult},
|
||||
spawn_try_task,
|
||||
utils::{
|
||||
markdown::markdown_to_html,
|
||||
@ -180,12 +180,15 @@ impl Object for ApubPost {
|
||||
let creator = page.creator()?.dereference(context).await?;
|
||||
let community = page.community(context).await?;
|
||||
if community.posting_restricted_to_mods {
|
||||
CommunityModeratorView::check_is_community_moderator(
|
||||
let is_mod = CommunityModeratorView::is_community_moderator(
|
||||
&mut context.pool(),
|
||||
community.id,
|
||||
creator.id,
|
||||
)
|
||||
.await?;
|
||||
if !is_mod {
|
||||
Err(LemmyErrorType::OnlyModsCanPostInCommunity)?
|
||||
}
|
||||
}
|
||||
let mut name = page
|
||||
.name
|
||||
|
@ -15,13 +15,12 @@ use activitypub_federation::{
|
||||
use chrono::{DateTime, Utc};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{get_url_blocklist, local_site_opt_to_slur_regex, process_markdown},
|
||||
utils::{check_person_block, get_url_blocklist, local_site_opt_to_slur_regex, process_markdown},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
person::Person,
|
||||
person_block::PersonBlock,
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
@ -127,7 +126,7 @@ impl Object for ApubPrivateMessage {
|
||||
) -> LemmyResult<ApubPrivateMessage> {
|
||||
let creator = note.attributed_to.dereference(context).await?;
|
||||
let recipient = note.to[0].dereference(context).await?;
|
||||
PersonBlock::read(&mut context.pool(), recipient.id, creator.id).await?;
|
||||
check_person_block(creator.id, recipient.id, &mut context.pool()).await?;
|
||||
|
||||
let local_site = LocalSite::read(&mut context.pool()).await.ok();
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
||||
|
@ -75,7 +75,7 @@ impl<S: ValidGrouping<(), IsAggregate = is_aggregate::No>> ValidGrouping<()>
|
||||
type IsAggregate = is_aggregate::No;
|
||||
}
|
||||
|
||||
#[expect(non_camel_case_types)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(QueryId, Clone, Copy, Debug)]
|
||||
pub struct current_value;
|
||||
|
||||
|
@ -30,7 +30,8 @@ impl CommentAggregates {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -36,7 +36,8 @@ impl CommunityAggregates {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -20,7 +20,8 @@ impl PersonAggregates {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -49,8 +49,8 @@ impl PostAggregates {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -15,8 +15,8 @@ impl SiteAggregates {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -58,7 +58,8 @@ impl ReceivedActivity {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -392,8 +392,8 @@ async fn convert_read_languages(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -13,7 +13,6 @@ use diesel::{
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl CaptchaAnswer {
|
||||
pub async fn insert(pool: &mut DbPool<'_>, captcha: &CaptchaAnswerForm) -> Result<Self, Error> {
|
||||
@ -28,7 +27,7 @@ impl CaptchaAnswer {
|
||||
pub async fn check_captcha(
|
||||
pool: &mut DbPool<'_>,
|
||||
to_check: CheckCaptchaAnswer,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
// fetch requested captcha
|
||||
@ -44,13 +43,13 @@ impl CaptchaAnswer {
|
||||
.execute(conn)
|
||||
.await?;
|
||||
|
||||
captcha_exists
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CaptchaIncorrect.into())
|
||||
Ok(captcha_exists)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
@ -84,6 +83,7 @@ mod tests {
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@ -119,6 +119,7 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result_repeat.is_err());
|
||||
assert!(result_repeat.is_ok());
|
||||
assert!(!result_repeat.unwrap());
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +196,8 @@ impl Saveable for CommentSaved {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -35,7 +35,8 @@ use crate::{
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::{
|
||||
deserialize,
|
||||
dsl::{self, exists, insert_into},
|
||||
dsl,
|
||||
dsl::{exists, insert_into},
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
select,
|
||||
@ -319,18 +320,16 @@ impl CommunityFollower {
|
||||
|
||||
/// Check if a remote instance has any followers on local instance. For this it is enough to check
|
||||
/// if any follow relation is stored. Dont use this for local community.
|
||||
pub async fn check_has_local_followers(
|
||||
pub async fn has_local_followers(
|
||||
pool: &mut DbPool<'_>,
|
||||
remote_community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(community_follower::table.filter(
|
||||
community_follower::community_id.eq(remote_community_id),
|
||||
)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -431,6 +430,7 @@ impl ApubActor for Community {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
source::{
|
||||
|
@ -9,29 +9,26 @@ use crate::{
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into, not},
|
||||
dsl::{exists, insert_into},
|
||||
result::Error,
|
||||
select,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl CommunityBlock {
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_person_id: PersonId,
|
||||
for_community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(
|
||||
select(exists(
|
||||
community_block::table.find((for_person_id, for_community_id)),
|
||||
)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
|
||||
))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn for_person(
|
||||
|
@ -48,7 +48,8 @@ impl FederationAllowList {
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -9,29 +9,26 @@ use crate::{
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into, not},
|
||||
dsl::{exists, insert_into},
|
||||
result::Error,
|
||||
select,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl InstanceBlock {
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_person_id: PersonId,
|
||||
for_instance_id: InstanceId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(
|
||||
select(exists(
|
||||
instance_block::table.find((for_person_id, for_instance_id)),
|
||||
)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
|
||||
))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn for_person(
|
||||
|
@ -41,8 +41,8 @@ impl Language {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{source::language::Language, utils::build_db_pool_for_tests};
|
||||
|
@ -136,16 +136,14 @@ impl LocalUser {
|
||||
diesel::delete(persons).execute(conn).await
|
||||
}
|
||||
|
||||
pub async fn check_is_email_taken(pool: &mut DbPool<'_>, email: &str) -> LemmyResult<()> {
|
||||
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(not(exists(local_user::table.filter(
|
||||
select(exists(local_user::table.filter(
|
||||
lower(coalesce(local_user::email, "")).eq(email.to_lowercase()),
|
||||
))))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::EmailAlreadyExists.into())
|
||||
)))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO: maybe move this and pass in LocalUserView
|
||||
@ -369,6 +367,7 @@ pub struct UserBackupLists {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
source::{
|
||||
@ -420,32 +419,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_email_taken() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
|
||||
let darwin_email = "charles.darwin@gmail.com";
|
||||
|
||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||
|
||||
let darwin_person = PersonInsertForm::test_form(inserted_instance.id, "darwin");
|
||||
let inserted_darwin_person = Person::create(pool, &darwin_person).await?;
|
||||
|
||||
let mut darwin_local_user_form =
|
||||
LocalUserInsertForm::test_form_admin(inserted_darwin_person.id);
|
||||
darwin_local_user_form.email = Some(darwin_email.into());
|
||||
let _inserted_darwin_local_user =
|
||||
LocalUser::create(pool, &darwin_local_user_form, vec![]).await?;
|
||||
|
||||
let check = LocalUser::check_is_email_taken(pool, darwin_email).await;
|
||||
assert!(check.is_err());
|
||||
|
||||
let passed_check = LocalUser::check_is_email_taken(pool, "not_charles@gmail.com").await;
|
||||
assert!(passed_check.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use crate::{
|
||||
};
|
||||
use diesel::{delete, dsl::exists, insert_into, result::Error, select};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl LoginToken {
|
||||
pub async fn create(pool: &mut DbPool<'_>, form: LoginTokenCreateForm) -> Result<Self, Error> {
|
||||
@ -23,15 +22,13 @@ impl LoginToken {
|
||||
pool: &mut DbPool<'_>,
|
||||
user_id_: LocalUserId,
|
||||
token_: &str,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
login_token.find(token_).filter(user_id.eq(user_id_)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::NotLoggedIn.into())
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list(
|
||||
|
@ -465,7 +465,8 @@ impl Crud for AdminPurgeComment {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -1,13 +1,32 @@
|
||||
use crate::{
|
||||
newtypes::LocalUserId,
|
||||
newtypes::{LocalUserId, OAuthProviderId},
|
||||
schema::{oauth_account, oauth_account::dsl::local_user_id},
|
||||
source::oauth_account::{OAuthAccount, OAuthAccountInsertForm},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into},
|
||||
result::Error,
|
||||
select,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
impl OAuthAccount {
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_oauth_provider_id: OAuthProviderId,
|
||||
for_local_user_id: LocalUserId,
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
oauth_account::table.find((for_oauth_provider_id, for_local_user_id)),
|
||||
))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create(pool: &mut DbPool<'_>, form: &OAuthAccountInsertForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(oauth_account::table)
|
||||
@ -16,6 +35,17 @@ impl OAuthAccount {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_oauth_provider_id: OAuthProviderId,
|
||||
for_local_user_id: LocalUserId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(oauth_account::table.find((for_oauth_provider_id, for_local_user_id)))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_user_accounts(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_local_user_id: LocalUserId,
|
||||
|
@ -42,6 +42,8 @@ impl PasswordResetRequest {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -21,7 +21,6 @@ use diesel::{
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
#[async_trait]
|
||||
impl Crud for Person {
|
||||
@ -122,18 +121,16 @@ impl Person {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn check_username_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> {
|
||||
pub async fn is_username_taken(pool: &mut DbPool<'_>, username: &str) -> Result<bool, Error> {
|
||||
use diesel::dsl::{exists, select};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(
|
||||
select(exists(
|
||||
person::table
|
||||
.filter(lower(person::name).eq(username.to_lowercase()))
|
||||
.filter(person::local.eq(true)),
|
||||
)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::UsernameAlreadyExists.into())
|
||||
))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,6 +232,7 @@ impl PersonFollower {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into, not},
|
||||
dsl::{exists, insert_into},
|
||||
result::Error,
|
||||
select,
|
||||
ExpressionMethods,
|
||||
@ -17,22 +17,19 @@ use diesel::{
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl PersonBlock {
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
for_person_id: PersonId,
|
||||
for_recipient_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(
|
||||
select(exists(
|
||||
person_block::table.find((for_person_id, for_recipient_id)),
|
||||
)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::PersonIsBlocked.into())
|
||||
))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn for_person(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
diesel::{BoolExpressionMethods, OptionalExtension},
|
||||
diesel::OptionalExtension,
|
||||
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
||||
schema::{community, person, post, post_hide, post_like, post_read, post_saved},
|
||||
schema::{post, post_hide, post_like, post_read, post_saved},
|
||||
source::post::{
|
||||
Post,
|
||||
PostHide,
|
||||
@ -20,7 +20,6 @@ use crate::{
|
||||
functions::coalesce,
|
||||
get_conn,
|
||||
naive_now,
|
||||
now,
|
||||
DbPool,
|
||||
DELETED_REPLACEMENT_TEXT,
|
||||
FETCH_LIMIT_MAX,
|
||||
@ -31,7 +30,7 @@ use crate::{
|
||||
use ::url::Url;
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::{
|
||||
dsl::{count, insert_into, not},
|
||||
dsl::insert_into,
|
||||
result::Error,
|
||||
DecoratableTarget,
|
||||
ExpressionMethods,
|
||||
@ -173,7 +172,6 @@ impl Post {
|
||||
let object_id: DbUrl = object_id.into();
|
||||
post::table
|
||||
.filter(post::ap_id.eq(object_id))
|
||||
.filter(post::scheduled_publish_time.is_null())
|
||||
.first(conn)
|
||||
.await
|
||||
.optional()
|
||||
@ -247,28 +245,6 @@ impl Post {
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn user_scheduled_post_count(
|
||||
person_id: PersonId,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> Result<i64, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
post::table
|
||||
.inner_join(person::table)
|
||||
.inner_join(community::table)
|
||||
// find all posts which have scheduled_publish_time that is in the past
|
||||
.filter(post::scheduled_publish_time.is_not_null())
|
||||
.filter(coalesce(post::scheduled_publish_time, now()).lt(now()))
|
||||
// make sure the post and community are still around
|
||||
.filter(not(post::deleted.or(post::removed)))
|
||||
.filter(not(community::removed.or(community::deleted)))
|
||||
// only posts by specified user
|
||||
.filter(post::creator_id.eq(person_id))
|
||||
.select(count(post::id))
|
||||
.first::<i64>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -468,7 +444,6 @@ mod tests {
|
||||
featured_community: false,
|
||||
featured_local: false,
|
||||
url_content_type: None,
|
||||
scheduled_publish_time: None,
|
||||
};
|
||||
|
||||
// Post Like
|
||||
|
@ -80,7 +80,8 @@ impl Reportable for PostReport {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -85,7 +85,8 @@ impl PrivateMessage {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -27,6 +27,7 @@ pub mod newtypes;
|
||||
pub mod sensitive;
|
||||
#[cfg(feature = "full")]
|
||||
#[rustfmt::skip]
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
pub mod schema;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod aliases {
|
||||
|
@ -191,13 +191,13 @@ impl Display for DbUrl {
|
||||
}
|
||||
|
||||
// the project doesn't compile with From
|
||||
#[expect(clippy::from_over_into)]
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<DbUrl> for Url {
|
||||
fn into(self) -> DbUrl {
|
||||
DbUrl(Box::new(self))
|
||||
}
|
||||
}
|
||||
#[expect(clippy::from_over_into)]
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<Url> for DbUrl {
|
||||
fn into(self) -> Url {
|
||||
*self.0
|
||||
|
@ -459,6 +459,7 @@ diesel::table! {
|
||||
totp_2fa_secret -> Nullable<Text>,
|
||||
open_links_in_new_tab -> Bool,
|
||||
blur_nsfw -> Bool,
|
||||
auto_expand -> Bool,
|
||||
infinite_scroll_enabled -> Bool,
|
||||
admin -> Bool,
|
||||
post_listing_mode -> PostListingModeEnum,
|
||||
@ -769,7 +770,6 @@ diesel::table! {
|
||||
featured_local -> Bool,
|
||||
url_content_type -> Nullable<Text>,
|
||||
alt_text -> Nullable<Text>,
|
||||
scheduled_publish_time -> Nullable<Timestamptz>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ pub struct LocalUser {
|
||||
/// Open links in a new tab.
|
||||
pub open_links_in_new_tab: bool,
|
||||
pub blur_nsfw: bool,
|
||||
pub auto_expand: bool,
|
||||
/// Whether infinite scroll is enabled.
|
||||
pub infinite_scroll_enabled: bool,
|
||||
/// Whether the person is an admin.
|
||||
@ -103,6 +104,8 @@ pub struct LocalUserInsertForm {
|
||||
#[new(default)]
|
||||
pub blur_nsfw: Option<bool>,
|
||||
#[new(default)]
|
||||
pub auto_expand: Option<bool>,
|
||||
#[new(default)]
|
||||
pub infinite_scroll_enabled: Option<bool>,
|
||||
#[new(default)]
|
||||
pub admin: Option<bool>,
|
||||
@ -140,6 +143,7 @@ pub struct LocalUserUpdateForm {
|
||||
pub totp_2fa_secret: Option<Option<String>>,
|
||||
pub open_links_in_new_tab: Option<bool>,
|
||||
pub blur_nsfw: Option<bool>,
|
||||
pub auto_expand: Option<bool>,
|
||||
pub infinite_scroll_enabled: Option<bool>,
|
||||
pub admin: Option<bool>,
|
||||
pub post_listing_mode: Option<PostListingMode>,
|
||||
|
@ -87,30 +87,39 @@ impl Serialize for PublicOAuthProvider {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct OAuthProviderInsertForm {
|
||||
pub display_name: String,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub issuer: DbUrl,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub authorization_endpoint: DbUrl,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub token_endpoint: DbUrl,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub userinfo_endpoint: DbUrl,
|
||||
pub id_claim: String,
|
||||
pub client_id: String,
|
||||
pub client_secret: String,
|
||||
pub scopes: String,
|
||||
pub auto_verify_email: Option<bool>,
|
||||
pub account_linking_enabled: Option<bool>,
|
||||
pub enabled: Option<bool>,
|
||||
pub auto_verify_email: bool,
|
||||
pub account_linking_enabled: bool,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct OAuthProviderUpdateForm {
|
||||
pub display_name: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub authorization_endpoint: Option<DbUrl>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub token_endpoint: Option<DbUrl>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
pub userinfo_endpoint: Option<DbUrl>,
|
||||
pub id_claim: Option<String>,
|
||||
pub client_secret: Option<String>,
|
||||
|
@ -57,8 +57,6 @@ pub struct Post {
|
||||
pub url_content_type: Option<String>,
|
||||
/// An optional alt_text, usable for image posts.
|
||||
pub alt_text: Option<String>,
|
||||
/// Time at which the post will be published. None means publish immediately.
|
||||
pub scheduled_publish_time: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, derive_new::new)]
|
||||
@ -106,8 +104,6 @@ pub struct PostInsertForm {
|
||||
pub url_content_type: Option<String>,
|
||||
#[new(default)]
|
||||
pub alt_text: Option<String>,
|
||||
#[new(default)]
|
||||
pub scheduled_publish_time: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@ -134,7 +130,6 @@ pub struct PostUpdateForm {
|
||||
pub featured_local: Option<bool>,
|
||||
pub url_content_type: Option<Option<String>>,
|
||||
pub alt_text: Option<Option<String>>,
|
||||
pub scheduled_publish_time: Option<Option<DateTime<Utc>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
|
@ -595,6 +595,7 @@ impl<RF, LF> Queries<RF, LF> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -259,8 +259,8 @@ impl CommentReportQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -35,7 +35,7 @@ use lemmy_db_schema::{
|
||||
person_block,
|
||||
post,
|
||||
},
|
||||
source::{local_user::LocalUser, site::Site},
|
||||
source::local_user::LocalUser,
|
||||
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||
CommentSortType,
|
||||
ListingType,
|
||||
@ -43,7 +43,7 @@ use lemmy_db_schema::{
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
impl ReadFn<'a, CommentView, (CommentId, Option<&'a LocalUser>)>,
|
||||
impl ListFn<'a, CommentView, (CommentQuery<'a>, &'a Site)>,
|
||||
impl ListFn<'a, CommentView, CommentQuery<'a>>,
|
||||
> {
|
||||
let is_creator_banned_from_community = exists(
|
||||
community_person_ban::table.filter(
|
||||
@ -182,7 +182,7 @@ fn queries<'a>() -> Queries<
|
||||
query.first(&mut conn).await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, (options, site): (CommentQuery<'a>, &'a Site)| async move {
|
||||
let list = move |mut conn: DbConn<'a>, options: CommentQuery<'a>| async move {
|
||||
// The left join below will return None in this case
|
||||
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
||||
let local_user_id_join = options
|
||||
@ -295,12 +295,6 @@ fn queries<'a>() -> Queries<
|
||||
query = query.filter(not(is_creator_blocked(person_id_join)));
|
||||
};
|
||||
|
||||
if !options.local_user.show_nsfw(site) {
|
||||
query = query
|
||||
.filter(post::nsfw.eq(false))
|
||||
.filter(community::nsfw.eq(false));
|
||||
};
|
||||
|
||||
query = options.local_user.visible_communities_only(query);
|
||||
|
||||
// A Max depth given means its a tree fetch
|
||||
@ -404,10 +398,10 @@ pub struct CommentQuery<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CommentQuery<'a> {
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
|
||||
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
|
||||
Ok(
|
||||
queries()
|
||||
.list(pool, (self, site))
|
||||
.list(pool, self)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|mut c| {
|
||||
@ -422,8 +416,8 @@ impl<'a> CommentQuery<'a> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
@ -461,8 +455,7 @@ mod tests {
|
||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
||||
person::{Person, PersonInsertForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
post::{Post, PostInsertForm, PostUpdateForm},
|
||||
site::{Site, SiteInsertForm},
|
||||
post::{Post, PostInsertForm},
|
||||
},
|
||||
traits::{Bannable, Blockable, Crud, Joinable, Likeable, Saveable},
|
||||
utils::{build_db_pool_for_tests, RANK_DEFAULT},
|
||||
@ -482,7 +475,6 @@ mod tests {
|
||||
timmy_local_user_view: LocalUserView,
|
||||
inserted_sara_person: Person,
|
||||
inserted_community: Community,
|
||||
site: Site,
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||
@ -619,8 +611,6 @@ mod tests {
|
||||
person: inserted_timmy_person.clone(),
|
||||
counts: Default::default(),
|
||||
};
|
||||
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
||||
let site = Site::create(pool, &site_form).await?;
|
||||
Ok(Data {
|
||||
inserted_instance,
|
||||
inserted_comment_0,
|
||||
@ -630,7 +620,6 @@ mod tests {
|
||||
timmy_local_user_view,
|
||||
inserted_sara_person,
|
||||
inserted_community,
|
||||
site,
|
||||
})
|
||||
}
|
||||
|
||||
@ -651,7 +640,7 @@ mod tests {
|
||||
post_id: (Some(data.inserted_post.id)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
@ -665,7 +654,7 @@ mod tests {
|
||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
@ -717,7 +706,7 @@ mod tests {
|
||||
liked_only: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|c| c.comment.content)
|
||||
@ -733,7 +722,7 @@ mod tests {
|
||||
disliked_only: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
assert!(read_disliked_comment_views.is_empty());
|
||||
@ -754,7 +743,7 @@ mod tests {
|
||||
parent_path: (Some(top_path)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
let child_path = data.inserted_comment_1.path.clone();
|
||||
@ -763,7 +752,7 @@ mod tests {
|
||||
parent_path: (Some(child_path)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
// Make sure the comment parent-limited fetch is correct
|
||||
@ -783,7 +772,7 @@ mod tests {
|
||||
max_depth: (Some(1)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
// Make sure a depth limited one only has the top comment
|
||||
@ -801,7 +790,7 @@ mod tests {
|
||||
sort: (Some(CommentSortType::New)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
// Make sure a depth limited one, and given child comment 1, has 3
|
||||
@ -827,7 +816,7 @@ mod tests {
|
||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_length!(5, all_languages);
|
||||
|
||||
@ -845,7 +834,7 @@ mod tests {
|
||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_length!(2, finnish_comments);
|
||||
let finnish_comment = finnish_comments
|
||||
@ -868,7 +857,7 @@ mod tests {
|
||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_length!(1, undetermined_comment);
|
||||
|
||||
@ -892,7 +881,7 @@ mod tests {
|
||||
post_id: Some(data.inserted_comment_2.post_id),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_eq!(comments[0].comment.id, data.inserted_comment_2.id);
|
||||
assert!(comments[0].comment.distinguished);
|
||||
@ -921,7 +910,7 @@ mod tests {
|
||||
sort: (Some(CommentSortType::Old)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
assert_eq!(comments[1].creator.name, "sara");
|
||||
@ -942,7 +931,7 @@ mod tests {
|
||||
sort: (Some(CommentSortType::Old)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
// Timmy is an admin, and make sure that field is true
|
||||
@ -982,7 +971,7 @@ mod tests {
|
||||
saved_only: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
|
||||
// There should only be two comments
|
||||
@ -1012,7 +1001,6 @@ mod tests {
|
||||
LocalUser::delete(pool, data.timmy_local_user_view.local_user.id).await?;
|
||||
Person::delete(pool, data.inserted_sara_person.id).await?;
|
||||
Instance::delete(pool, data.inserted_instance.id).await?;
|
||||
Site::delete(pool, data.site.id).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1090,7 +1078,6 @@ mod tests {
|
||||
featured_community: false,
|
||||
featured_local: false,
|
||||
url_content_type: None,
|
||||
scheduled_publish_time: None,
|
||||
},
|
||||
community: Community {
|
||||
id: data.inserted_community.id,
|
||||
@ -1152,7 +1139,7 @@ mod tests {
|
||||
let unauthenticated_query = CommentQuery {
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_eq!(0, unauthenticated_query.len());
|
||||
|
||||
@ -1160,7 +1147,7 @@ mod tests {
|
||||
local_user: Some(&data.timmy_local_user_view.local_user),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&data.site, pool)
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_eq!(5, authenticated_query.len());
|
||||
|
||||
@ -1238,33 +1225,4 @@ mod tests {
|
||||
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn comment_listings_hide_nsfw() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await?;
|
||||
|
||||
// Mark a post as nsfw
|
||||
let update_form = PostUpdateForm {
|
||||
nsfw: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
Post::update(pool, data.inserted_post.id, &update_form).await?;
|
||||
|
||||
// Make sure comments of this post are not returned
|
||||
let comments = CommentQuery::default().list(&data.site, pool).await?;
|
||||
assert_eq!(0, comments.len());
|
||||
|
||||
// Mark site as nsfw
|
||||
let mut site = data.site.clone();
|
||||
site.content_warning = Some("nsfw".to_string());
|
||||
|
||||
// Now comments of nsfw post are returned
|
||||
let comments = CommentQuery::default().list(&site, pool).await?;
|
||||
assert_eq!(6, comments.len());
|
||||
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
}
|
||||
|
@ -284,8 +284,8 @@ impl PostReportQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -318,18 +318,11 @@ fn queries<'a>() -> Queries<
|
||||
// hide posts from deleted communities
|
||||
query = query.filter(community::deleted.eq(false));
|
||||
|
||||
// only creator can see deleted posts and unpublished scheduled posts
|
||||
// only show deleted posts to creator
|
||||
if let Some(person_id) = options.local_user.person_id() {
|
||||
query = query.filter(post::deleted.eq(false).or(post::creator_id.eq(person_id)));
|
||||
query = query.filter(
|
||||
post::scheduled_publish_time
|
||||
.is_null()
|
||||
.or(post::creator_id.eq(person_id)),
|
||||
);
|
||||
} else {
|
||||
query = query
|
||||
.filter(post::deleted.eq(false))
|
||||
.filter(post::scheduled_publish_time.is_null());
|
||||
query = query.filter(post::deleted.eq(false));
|
||||
}
|
||||
|
||||
// only show removed posts to admin when viewing user profile
|
||||
@ -394,12 +387,14 @@ fn queries<'a>() -> Queries<
|
||||
query = query.filter(post::url.eq(search_term));
|
||||
} else {
|
||||
let searcher = fuzzy_search(search_term);
|
||||
let name_filter = post::name.ilike(searcher.clone());
|
||||
let body_filter = post::body.ilike(searcher.clone());
|
||||
query = if options.title_only.unwrap_or_default() {
|
||||
query.filter(name_filter)
|
||||
query.filter(post::name.ilike(searcher))
|
||||
} else {
|
||||
query.filter(name_filter.or(body_filter))
|
||||
query.filter(
|
||||
post::name
|
||||
.ilike(searcher.clone())
|
||||
.or(post::body.ilike(searcher)),
|
||||
)
|
||||
}
|
||||
.filter(not(post::removed.or(post::deleted)));
|
||||
}
|
||||
@ -739,7 +734,7 @@ impl<'a> PostQuery<'a> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
post_view::{PaginationCursorData, PostQuery, PostView},
|
||||
@ -1776,7 +1771,6 @@ mod tests {
|
||||
featured_community: false,
|
||||
featured_local: false,
|
||||
url_content_type: None,
|
||||
scheduled_publish_time: None,
|
||||
},
|
||||
my_vote: None,
|
||||
unread_comments: 0,
|
||||
|
@ -111,8 +111,8 @@ impl PrivateMessageReportQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::private_message_report_view::PrivateMessageReportQuery;
|
||||
|
@ -173,8 +173,8 @@ impl PrivateMessageQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
||||
|
@ -135,7 +135,8 @@ impl RegistrationApplicationQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::registration_application_view::{
|
||||
@ -234,6 +235,7 @@ mod tests {
|
||||
person_id: inserted_sara_local_user.person_id,
|
||||
email: inserted_sara_local_user.email,
|
||||
show_nsfw: inserted_sara_local_user.show_nsfw,
|
||||
auto_expand: inserted_sara_local_user.auto_expand,
|
||||
blur_nsfw: inserted_sara_local_user.blur_nsfw,
|
||||
theme: inserted_sara_local_user.theme,
|
||||
default_post_sort_type: inserted_sara_local_user.default_post_sort_type,
|
||||
|
@ -83,7 +83,8 @@ impl VoteView {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::structs::VoteView;
|
||||
|
@ -15,13 +15,7 @@ doctest = false
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"lemmy_db_schema/full",
|
||||
"lemmy_utils/full",
|
||||
"diesel",
|
||||
"diesel-async",
|
||||
"ts-rs",
|
||||
]
|
||||
full = ["lemmy_db_schema/full", "diesel", "diesel-async", "ts-rs"]
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { workspace = true }
|
||||
@ -39,7 +33,6 @@ serde_with = { workspace = true }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
chrono.workspace = true
|
||||
strum = { workspace = true }
|
||||
lemmy_utils = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
@ -303,6 +303,7 @@ impl CommentReplyQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{comment_reply_view::CommentReplyQuery, structs::CommentReplyView};
|
||||
|
@ -8,14 +8,13 @@ use lemmy_db_schema::{
|
||||
source::local_user::LocalUser,
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl CommunityModeratorView {
|
||||
pub async fn check_is_community_moderator(
|
||||
pub async fn is_community_moderator(
|
||||
pool: &mut DbPool<'_>,
|
||||
find_community_id: CommunityId,
|
||||
find_person_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
use lemmy_db_schema::schema::community_moderator::dsl::{
|
||||
community_id,
|
||||
community_moderator,
|
||||
@ -28,24 +27,20 @@ impl CommunityModeratorView {
|
||||
.filter(person_id.eq(find_person_id)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::NotAModerator.into())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn is_community_moderator_of_any(
|
||||
pool: &mut DbPool<'_>,
|
||||
find_person_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
use lemmy_db_schema::schema::community_moderator::dsl::{community_moderator, person_id};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
community_moderator.filter(person_id.eq(find_person_id)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::NotAModerator.into())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn for_community(
|
||||
|
@ -1,33 +1,25 @@
|
||||
use crate::structs::CommunityPersonBanView;
|
||||
use diesel::{
|
||||
dsl::{exists, not},
|
||||
select,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel::{dsl::exists, result::Error, select, ExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommunityId, PersonId},
|
||||
schema::community_person_ban,
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
impl CommunityPersonBanView {
|
||||
pub async fn check(
|
||||
pub async fn get(
|
||||
pool: &mut DbPool<'_>,
|
||||
from_person_id: PersonId,
|
||||
from_community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(
|
||||
select(exists(
|
||||
community_person_ban::table
|
||||
.filter(community_person_ban::community_id.eq(from_community_id))
|
||||
.filter(community_person_ban::person_id.eq(from_person_id)),
|
||||
)))
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ use lemmy_db_schema::{
|
||||
ListingType,
|
||||
PostSortType,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
impl ReadFn<'a, CommunityView, (CommunityId, Option<&'a LocalUser>, bool)>,
|
||||
@ -112,14 +111,9 @@ fn queries<'a>() -> Queries<
|
||||
|
||||
if let Some(search_term) = options.search_term {
|
||||
let searcher = fuzzy_search(&search_term);
|
||||
let name_filter = community::name.ilike(searcher.clone());
|
||||
let title_filter = community::title.ilike(searcher.clone());
|
||||
let description_filter = community::description.ilike(searcher.clone());
|
||||
query = if options.title_only.unwrap_or_default() {
|
||||
query.filter(name_filter.or(title_filter))
|
||||
} else {
|
||||
query.filter(name_filter.or(title_filter.or(description_filter)))
|
||||
}
|
||||
query = query
|
||||
.filter(community::name.ilike(searcher.clone()))
|
||||
.or_filter(community::title.ilike(searcher))
|
||||
}
|
||||
|
||||
// Hide deleted and removed for non-admins or mods
|
||||
@ -191,39 +185,35 @@ impl CommunityView {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn check_is_mod_or_admin(
|
||||
pub async fn is_mod_or_admin(
|
||||
pool: &mut DbPool<'_>,
|
||||
person_id: PersonId,
|
||||
community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let is_mod =
|
||||
CommunityModeratorView::check_is_community_moderator(pool, community_id, person_id).await;
|
||||
if is_mod.is_ok()
|
||||
|| PersonView::read(pool, person_id)
|
||||
.await
|
||||
.is_ok_and(|t| t.is_admin)
|
||||
{
|
||||
Ok(())
|
||||
CommunityModeratorView::is_community_moderator(pool, community_id, person_id).await?;
|
||||
if is_mod {
|
||||
Ok(true)
|
||||
} else if let Ok(person_view) = PersonView::read(pool, person_id).await {
|
||||
Ok(person_view.is_admin)
|
||||
} else {
|
||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a person is an admin, or moderator of any community.
|
||||
pub async fn check_is_mod_of_any_or_admin(
|
||||
pub async fn is_mod_of_any_or_admin(
|
||||
pool: &mut DbPool<'_>,
|
||||
person_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
) -> Result<bool, Error> {
|
||||
let is_mod_of_any =
|
||||
CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await;
|
||||
if is_mod_of_any.is_ok()
|
||||
|| PersonView::read(pool, person_id)
|
||||
.await
|
||||
.is_ok_and(|t| t.is_admin)
|
||||
{
|
||||
Ok(())
|
||||
CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await?;
|
||||
if is_mod_of_any {
|
||||
Ok(true)
|
||||
} else if let Ok(person_view) = PersonView::read(pool, person_id).await {
|
||||
Ok(person_view.is_admin)
|
||||
} else {
|
||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,7 +224,6 @@ pub struct CommunityQuery<'a> {
|
||||
pub sort: Option<PostSortType>,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
pub search_term: Option<String>,
|
||||
pub title_only: Option<bool>,
|
||||
pub is_mod_or_admin: bool,
|
||||
pub show_nsfw: bool,
|
||||
pub page: Option<i64>,
|
||||
@ -248,7 +237,8 @@ impl<'a> CommunityQuery<'a> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{community_view::CommunityQuery, structs::CommunityView};
|
||||
|
@ -303,6 +303,7 @@ impl PersonMentionQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{person_mention_view::PersonMentionQuery, structs::PersonMentionView};
|
||||
|
@ -164,7 +164,7 @@ impl PersonQuery {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -222,8 +222,8 @@ impl<T: DataSource> CommunityInboxCollector<T> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use lemmy_db_schema::{
|
||||
|
@ -192,8 +192,8 @@ impl SendManager {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
@ -439,8 +439,8 @@ impl InstanceWorker {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
@ -172,8 +172,6 @@ pub enum LemmyErrorType {
|
||||
Unknown(String),
|
||||
CantDeleteSite,
|
||||
UrlLengthOverflow,
|
||||
PostScheduleTimeMustBeInFuture,
|
||||
TooManyScheduledPosts,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
|
@ -221,6 +221,8 @@ fn parse_ip(addr: &str) -> Option<IpAddr> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
|
@ -136,6 +136,7 @@ impl<K: Eq + Hash, C: MapLevel> MapLevel for Map<K, C> {
|
||||
.entry(addr_part)
|
||||
.or_insert(RateLimitedGroup::new(now, adjusted_configs));
|
||||
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
let total_passes = group.check_total(action_type, now, adjusted_configs[action_type]);
|
||||
|
||||
let children_pass = group.children.check(
|
||||
@ -160,6 +161,7 @@ impl<K: Eq + Hash, C: MapLevel> MapLevel for Map<K, C> {
|
||||
// Evaluated if `some_children_remaining` is false
|
||||
let total_has_refill_in_future = || {
|
||||
group.total.into_iter().any(|(action_type, bucket)| {
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
let config = configs[action_type];
|
||||
bucket.update(now, config).tokens != config.capacity
|
||||
})
|
||||
@ -212,6 +214,7 @@ impl<C: Default> RateLimitedGroup<C> {
|
||||
now: InstantSecs,
|
||||
config: BucketConfig,
|
||||
) -> bool {
|
||||
#[allow(clippy::indexing_slicing)] // `EnumMap` has no `get` function
|
||||
let bucket = &mut self.total[action_type];
|
||||
|
||||
let new_bucket = bucket.update(now, config);
|
||||
@ -308,7 +311,8 @@ fn split_ipv6(ip: Ipv6Addr) -> ([u8; 6], u8, u8) {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::{ActionType, BucketConfig, InstantSecs, RateLimitState, RateLimitedGroup};
|
||||
@ -357,6 +361,7 @@ mod tests {
|
||||
assert!(post_passed);
|
||||
}
|
||||
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
let expected_buckets = |factor: u32, tokens_consumed: u32| {
|
||||
let adjusted_configs = bucket_configs.map(|_, config| BucketConfig {
|
||||
capacity: config.capacity.saturating_mul(factor),
|
||||
|
@ -107,7 +107,8 @@ pub fn markdown_check_for_blocked_urls(text: &str, blocklist: &RegexSet) -> Lemm
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
@ -134,6 +134,8 @@ pub fn add(markdown_parser: &mut MarkdownIt) {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::utils::markdown::spoiler_rule::add;
|
||||
|
@ -34,7 +34,8 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod test {
|
||||
|
||||
use crate::utils::mention::scrape_text_for_mentions;
|
||||
|
@ -61,7 +61,8 @@ pub(crate) fn slurs_vec_to_str(slurs: &[&str]) -> String {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod test {
|
||||
|
||||
use crate::utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str};
|
||||
|
@ -351,6 +351,7 @@ pub fn build_url_str_without_scheme(url_str: &str) -> LemmyResult<String> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
|
||||
use crate::{
|
||||
|
@ -1,3 +0,0 @@
|
||||
ALTER TABLE post
|
||||
DROP COLUMN scheduled_publish_time;
|
||||
|
@ -1,5 +0,0 @@
|
||||
ALTER TABLE post
|
||||
ADD COLUMN scheduled_publish_time timestamptz;
|
||||
|
||||
CREATE INDEX idx_post_scheduled_publish_time ON post (scheduled_publish_time);
|
||||
|
@ -14,7 +14,7 @@ CREATE TABLE oauth_provider (
|
||||
scopes text NOT NULL,
|
||||
auto_verify_email boolean DEFAULT TRUE NOT NULL,
|
||||
account_linking_enabled boolean DEFAULT FALSE NOT NULL,
|
||||
enabled boolean DEFAULT TRUE NOT NULL,
|
||||
enabled boolean DEFAULT FALSE NOT NULL,
|
||||
published timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated timestamp with time zone
|
||||
);
|
||||
|
@ -1,3 +0,0 @@
|
||||
ALTER TABLE local_user
|
||||
ADD COLUMN auto_expand boolean NOT NULL DEFAULT FALSE;
|
||||
|
@ -1,3 +0,0 @@
|
||||
ALTER TABLE local_user
|
||||
DROP COLUMN auto_expand;
|
||||
|
14
src/lib.rs
14
src/lib.rs
@ -157,6 +157,11 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||
rate_limit_cell.clone(),
|
||||
);
|
||||
|
||||
let scheduled_tasks = (!args.disable_scheduled_tasks).then(|| {
|
||||
// Schedules various cleanup tasks for the DB
|
||||
tokio::task::spawn(scheduled_tasks::setup(context.clone()))
|
||||
});
|
||||
|
||||
if let Some(prometheus) = SETTINGS.prometheus.clone() {
|
||||
serve_prometheus(prometheus, context.clone())?;
|
||||
}
|
||||
@ -182,14 +187,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||
}))
|
||||
.expect("set function pointer");
|
||||
let request_data = federation_config.to_request_data();
|
||||
let outgoing_activities_task = tokio::task::spawn(handle_outgoing_activities(
|
||||
request_data.reset_request_count(),
|
||||
));
|
||||
|
||||
let scheduled_tasks = (!args.disable_scheduled_tasks).then(|| {
|
||||
// Schedules various cleanup tasks for the DB
|
||||
tokio::task::spawn(scheduled_tasks::setup(request_data.reset_request_count()))
|
||||
});
|
||||
let outgoing_activities_task = tokio::task::spawn(handle_outgoing_activities(request_data));
|
||||
|
||||
let server = if !args.disable_http_server {
|
||||
if let Some(startup_server_handle) = startup_server_handle {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user