Merge branch 'main' into add_community_description

This commit is contained in:
Dessalines 2024-10-28 11:17:38 -04:00 committed by GitHub
commit 0c63e72cc1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 145 additions and 84 deletions

View file

@ -39,10 +39,7 @@ use lemmy_db_schema::{
}, },
traits::{Bannable, Crud, Followable}, traits::{Bannable, Crud, Followable},
}; };
use lemmy_utils::{ use lemmy_utils::error::{FederationError, LemmyError, LemmyResult};
error::{LemmyError, LemmyResult},
LemmyErrorType,
};
use url::Url; use url::Url;
impl BlockUser { impl BlockUser {
@ -135,7 +132,7 @@ impl ActivityHandler for BlockUser {
.object .object
.inner() .inner()
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)?; .ok_or(FederationError::UrlWithoutDomain)?;
if context.settings().hostname == domain { if context.settings().hostname == domain {
return Err( return Err(
anyhow!("Site bans from remote instance can't affect user's home instance").into(), anyhow!("Site bans from remote instance can't affect user's home instance").into(),

View file

@ -26,7 +26,7 @@ use lemmy_db_schema::{
source::{activity::ActivitySendTargets, community::CommunityFollower}, source::{activity::ActivitySendTargets, community::CommunityFollower},
CommunityVisibility, CommunityVisibility,
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult};
use serde_json::Value; use serde_json::Value;
use url::Url; use url::Url;
@ -54,7 +54,7 @@ impl ActivityHandler for RawAnnouncableActivities {
// This is only for sending, not receiving so we reject it. // This is only for sending, not receiving so we reject it.
if let AnnouncableActivities::Page(_) = activity { if let AnnouncableActivities::Page(_) = activity {
Err(LemmyErrorType::CannotReceivePage)? Err(FederationError::CannotReceivePage)?
} }
// Need to treat community as optional here because `Delete/PrivateMessage` gets routed through // Need to treat community as optional here because `Delete/PrivateMessage` gets routed through
@ -165,7 +165,7 @@ impl ActivityHandler for AnnounceActivity {
// This is only for sending, not receiving so we reject it. // This is only for sending, not receiving so we reject it.
if let AnnouncableActivities::Page(_) = object { if let AnnouncableActivities::Page(_) = object {
Err(LemmyErrorType::CannotReceivePage)? Err(FederationError::CannotReceivePage)?
} }
let community = object.community(context).await?; let community = object.community(context).await?;

View file

@ -27,7 +27,7 @@ use lemmy_db_schema::{
}, },
traits::{Crud, Reportable}, traits::{Crud, Reportable},
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult};
use url::Url; use url::Url;
#[async_trait::async_trait] #[async_trait::async_trait]
@ -118,7 +118,7 @@ pub(in crate::activities) async fn receive_remove_action(
match DeletableObjects::read_from_db(object, context).await? { match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => { DeletableObjects::Community(community) => {
if community.local { if community.local {
Err(LemmyErrorType::OnlyLocalAdminCanRemoveCommunity)? Err(FederationError::OnlyLocalAdminCanRemoveCommunity)?
} }
let form = ModRemoveCommunityForm { let form = ModRemoveCommunityForm {
mod_person_id: actor.id, mod_person_id: actor.id,

View file

@ -25,7 +25,7 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult};
use url::Url; use url::Url;
#[async_trait::async_trait] #[async_trait::async_trait]
@ -100,7 +100,7 @@ impl UndoDelete {
match DeletableObjects::read_from_db(object, context).await? { match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => { DeletableObjects::Community(community) => {
if community.local { if community.local {
Err(LemmyErrorType::OnlyLocalAdminCanRestoreCommunity)? Err(FederationError::OnlyLocalAdminCanRestoreCommunity)?
} }
let form = ModRemoveCommunityForm { let form = ModRemoveCommunityForm {
mod_person_id: actor.id, mod_person_id: actor.id,

View file

@ -42,7 +42,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
use serde::Serialize; use serde::Serialize;
use tracing::info; use tracing::info;
use url::{ParseError, Url}; use url::{ParseError, Url};
@ -81,7 +81,7 @@ pub(crate) async fn verify_person_in_community(
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let person = person_id.dereference(context).await?; let person = person_id.dereference(context).await?;
if person.banned { if person.banned {
Err(LemmyErrorType::PersonIsBannedFromSite( Err(FederationError::PersonIsBannedFromSite(
person.actor_id.to_string(), person.actor_id.to_string(),
))? ))?
} }
@ -114,7 +114,7 @@ pub(crate) async fn verify_mod_action(
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> { pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> {
if ![to, cc].iter().any(|set| set.contains(&public())) { if ![to, cc].iter().any(|set| set.contains(&public())) {
Err(LemmyErrorType::ObjectIsNotPublic)? Err(FederationError::ObjectIsNotPublic)?
} else { } else {
Ok(()) Ok(())
} }
@ -126,7 +126,7 @@ where
{ {
let b: ObjectId<ApubCommunity> = b.into(); let b: ObjectId<ApubCommunity> = b.into();
if a != &b { if a != &b {
Err(LemmyErrorType::InvalidCommunity)? Err(FederationError::InvalidCommunity)?
} else { } else {
Ok(()) Ok(())
} }
@ -134,7 +134,7 @@ where
pub(crate) fn check_community_deleted_or_removed(community: &Community) -> LemmyResult<()> { pub(crate) fn check_community_deleted_or_removed(community: &Community) -> LemmyResult<()> {
if community.deleted || community.removed { if community.deleted || community.removed {
Err(LemmyErrorType::CannotCreatePostOrCommentInDeletedOrRemovedCommunity)? Err(FederationError::CannotCreatePostOrCommentInDeletedOrRemovedCommunity)?
} else { } else {
Ok(()) Ok(())
} }

View file

@ -26,7 +26,7 @@ pub enum PostOrComment {
#[serde(untagged)] #[serde(untagged)]
pub enum PageOrNote { pub enum PageOrNote {
Page(Box<Page>), Page(Box<Page>),
Note(Note), Note(Box<Note>),
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -61,7 +61,7 @@ impl Object for PostOrComment {
async fn into_json(self, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> { async fn into_json(self, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> {
Ok(match self { Ok(match self {
PostOrComment::Post(p) => PageOrNote::Page(Box::new(p.into_json(data).await?)), PostOrComment::Post(p) => PageOrNote::Page(Box::new(p.into_json(data).await?)),
PostOrComment::Comment(c) => PageOrNote::Note(c.into_json(data).await?), PostOrComment::Comment(c) => PageOrNote::Note(Box::new(c.into_json(data).await?)),
}) })
} }
@ -81,7 +81,7 @@ impl Object for PostOrComment {
async fn from_json(apub: PageOrNote, context: &Data<LemmyContext>) -> LemmyResult<Self> { async fn from_json(apub: PageOrNote, context: &Data<LemmyContext>) -> LemmyResult<Self> {
Ok(match apub { Ok(match apub {
PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?), PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?),
PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(n, context).await?), PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(*n, context).await?),
}) })
} }
} }

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
source::{activity::SentActivity, community::Community}, source::{activity::SentActivity, community::Community},
CommunityVisibility, CommunityVisibility,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyErrorType, LemmyResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ops::Deref, time::Duration}; use std::{ops::Deref, time::Duration};
use tokio::time::timeout; use tokio::time::timeout;
@ -45,7 +45,7 @@ pub async fn shared_inbox(
// consider the activity broken and move on. // consider the activity broken and move on.
timeout(INCOMING_ACTIVITY_TIMEOUT, receive_fut) timeout(INCOMING_ACTIVITY_TIMEOUT, receive_fut)
.await .await
.map_err(|_| LemmyErrorType::InboxTimeout)? .map_err(|_| FederationError::InboxTimeout)?
} }
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
@ -106,7 +106,9 @@ pub(crate) async fn get_activity(
info.id info.id
))? ))?
.into(); .into();
let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id).await?; let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id)
.await
.map_err(|_| FederationError::CouldntFindActivity)?;
let sensitive = activity.sensitive; let sensitive = activity.sensitive;
if sensitive { if sensitive {

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
utils::{ActualDbPool, DbPool}, utils::{ActualDbPool, DbPool},
}; };
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyErrorType, LemmyResult}, error::{FederationError, LemmyError, LemmyErrorType, LemmyResult},
CACHE_DURATION_FEDERATION, CACHE_DURATION_FEDERATION,
}; };
use moka::future::Cache; use moka::future::Cache;
@ -51,17 +51,18 @@ impl UrlVerifier for VerifyUrlData {
let local_site_data = local_site_data_cached(&mut (&self.0).into()) let local_site_data = local_site_data_cached(&mut (&self.0).into())
.await .await
.expect("read local site data"); .expect("read local site data");
use FederationError::*;
check_apub_id_valid(url, &local_site_data).map_err(|err| match err { check_apub_id_valid(url, &local_site_data).map_err(|err| match err {
LemmyError { LemmyError {
error_type: LemmyErrorType::FederationDisabled, error_type: LemmyErrorType::FederationError(Some(FederationDisabled)),
.. ..
} => ActivityPubError::Other("Federation disabled".into()), } => ActivityPubError::Other("Federation disabled".into()),
LemmyError { LemmyError {
error_type: LemmyErrorType::DomainBlocked(domain), error_type: LemmyErrorType::FederationError(Some(DomainBlocked(domain))),
.. ..
} => ActivityPubError::Other(format!("Domain {domain:?} is blocked")), } => ActivityPubError::Other(format!("Domain {domain:?} is blocked")),
LemmyError { LemmyError {
error_type: LemmyErrorType::DomainNotInAllowList(domain), error_type: LemmyErrorType::FederationError(Some(DomainNotInAllowList(domain))),
.. ..
} => ActivityPubError::Other(format!("Domain {domain:?} is not in allowlist")), } => ActivityPubError::Other(format!("Domain {domain:?} is not in allowlist")),
_ => ActivityPubError::Other("Failed validating apub id".into()), _ => ActivityPubError::Other("Failed validating apub id".into()),
@ -81,7 +82,7 @@ impl UrlVerifier for VerifyUrlData {
fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyResult<()> { fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyResult<()> {
let domain = apub_id let domain = apub_id
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)? .ok_or(FederationError::UrlWithoutDomain)?
.to_string(); .to_string();
if !local_site_data if !local_site_data
@ -90,7 +91,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyR
.map(|l| l.federation_enabled) .map(|l| l.federation_enabled)
.unwrap_or(true) .unwrap_or(true)
{ {
Err(LemmyErrorType::FederationDisabled)? Err(FederationError::FederationDisabled)?
} }
if local_site_data if local_site_data
@ -98,7 +99,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyR
.iter() .iter()
.any(|i| domain.to_lowercase().eq(&i.domain.to_lowercase())) .any(|i| domain.to_lowercase().eq(&i.domain.to_lowercase()))
{ {
Err(LemmyErrorType::DomainBlocked(domain.clone()))? Err(FederationError::DomainBlocked(domain.clone()))?
} }
// Only check this if there are instances in the allowlist // Only check this if there are instances in the allowlist
@ -108,7 +109,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> LemmyR
.iter() .iter()
.any(|i| domain.to_lowercase().eq(&i.domain.to_lowercase())) .any(|i| domain.to_lowercase().eq(&i.domain.to_lowercase()))
{ {
Err(LemmyErrorType::DomainNotInAllowList(domain))? Err(FederationError::DomainNotInAllowList(domain))?
} }
Ok(()) Ok(())
@ -164,7 +165,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let domain = apub_id let domain = apub_id
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)? .ok_or(FederationError::UrlWithoutDomain)?
.to_string(); .to_string();
let local_instance = context let local_instance = context
.settings() .settings()
@ -194,10 +195,10 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
let domain = apub_id let domain = apub_id
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)? .ok_or(FederationError::UrlWithoutDomain)?
.to_string(); .to_string();
if !allowed_and_local.contains(&domain) { if !allowed_and_local.contains(&domain) {
Err(LemmyErrorType::FederationDisabledByStrictAllowList)? Err(FederationError::FederationDisabledByStrictAllowList)?
} }
} }
Ok(()) Ok(())

View file

@ -11,7 +11,10 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
utils::DbPool, utils::DbPool,
}; };
use lemmy_utils::{error::LemmyResult, utils::mention::scrape_text_for_mentions, LemmyErrorType}; use lemmy_utils::{
error::{FederationError, LemmyResult},
utils::mention::scrape_text_for_mentions,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use url::Url; use url::Url;
@ -57,7 +60,7 @@ pub async fn collect_non_local_mentions(
&parent_creator &parent_creator
.id() .id()
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)? .ok_or(FederationError::UrlWithoutDomain)?
)), )),
kind: MentionType::Mention, kind: MentionType::Mention,
}; };

View file

@ -33,7 +33,7 @@ use lemmy_db_schema::{
utils::naive_now, utils::naive_now,
}; };
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyErrorType, LemmyResult}, error::{FederationError, LemmyError, LemmyResult},
utils::markdown::markdown_to_html, utils::markdown::markdown_to_html,
}; };
use std::ops::Deref; use std::ops::Deref;
@ -162,7 +162,7 @@ impl Object for ApubComment {
.await .await
.is_ok(); .is_ok();
if post.locked && !is_mod_or_admin { if post.locked && !is_mod_or_admin {
Err(LemmyErrorType::PostIsLocked)? Err(FederationError::PostIsLocked)?
} else { } else {
Ok(()) Ok(())
} }

View file

@ -42,12 +42,11 @@ use lemmy_db_schema::{
utils::naive_now, utils::naive_now,
}; };
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyResult}, error::{FederationError, LemmyError, LemmyResult},
utils::{ utils::{
markdown::markdown_to_html, markdown::markdown_to_html,
slurs::{check_slurs, check_slurs_opt}, slurs::{check_slurs, check_slurs_opt},
}, },
LemmyErrorType,
}; };
use std::ops::Deref; use std::ops::Deref;
use tracing::debug; use tracing::debug;
@ -89,7 +88,7 @@ impl Object for ApubSite {
} }
async fn delete(self, _data: &Data<Self::DataType>) -> LemmyResult<()> { async fn delete(self, _data: &Data<Self::DataType>) -> LemmyResult<()> {
Err(LemmyErrorType::CantDeleteSite.into()) Err(FederationError::CantDeleteSite.into())
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -144,7 +143,7 @@ impl Object for ApubSite {
.id .id
.inner() .inner()
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)?; .ok_or(FederationError::UrlWithoutDomain)?;
let instance = DbInstance::read_or_create(&mut context.pool(), domain.to_string()).await?; let instance = DbInstance::read_or_create(&mut context.pool(), domain.to_string()).await?;
let local_site = LocalSite::read(&mut context.pool()).await.ok(); let local_site = LocalSite::read(&mut context.pool()).await.ok();
@ -220,7 +219,7 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + C
debug!("Failed to dereference site for {}: {}", &instance_id, e); debug!("Failed to dereference site for {}: {}", &instance_id, e);
let domain = instance_id let domain = instance_id
.domain() .domain()
.ok_or(LemmyErrorType::UrlWithoutDomain)?; .ok_or(FederationError::UrlWithoutDomain)?;
Ok( Ok(
DbInstance::read_or_create(&mut context.pool(), domain.to_string()) DbInstance::read_or_create(&mut context.pool(), domain.to_string())
.await? .await?

View file

@ -29,7 +29,7 @@ use lemmy_db_schema::{
utils::naive_now, utils::naive_now,
}; };
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyErrorType, LemmyResult}, error::{FederationError, LemmyError, LemmyErrorType, LemmyResult},
utils::markdown::markdown_to_html, utils::markdown::markdown_to_html,
}; };
use std::ops::Deref; use std::ops::Deref;
@ -113,7 +113,7 @@ impl Object for ApubPrivateMessage {
check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?; check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?;
let person = note.attributed_to.dereference(context).await?; let person = note.attributed_to.dereference(context).await?;
if person.banned { if person.banned {
Err(LemmyErrorType::PersonIsBannedFromSite( Err(FederationError::PersonIsBannedFromSite(
person.actor_id.to_string(), person.actor_id.to_string(),
))? ))?
} else { } else {

View file

@ -6,7 +6,7 @@ use crate::{
}; };
use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyError, LemmyResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::Display; use strum::Display;
use url::Url; use url::Url;
@ -35,7 +35,7 @@ impl TryFrom<i16> for VoteType {
match value { match value {
1 => Ok(VoteType::Like), 1 => Ok(VoteType::Like),
-1 => Ok(VoteType::Dislike), -1 => Ok(VoteType::Dislike),
_ => Err(LemmyErrorType::InvalidVoteValue.into()), _ => Err(FederationError::InvalidVoteValue.into()),
} }
} }
} }

View file

@ -20,7 +20,7 @@ use activitypub_federation::{
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult};
use serde::{de::Error, Deserialize, Deserializer, Serialize}; use serde::{de::Error, Deserialize, Deserializer, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use url::Url; use url::Url;
@ -162,7 +162,7 @@ impl Page {
.iter() .iter()
.find(|a| a.kind == PersonOrGroupType::Person) .find(|a| a.kind == PersonOrGroupType::Person)
.map(|a| ObjectId::<ApubPerson>::from(a.id.clone().into_inner())) .map(|a| ObjectId::<ApubPerson>::from(a.id.clone().into_inner()))
.ok_or_else(|| LemmyErrorType::PageDoesNotSpecifyCreator.into()), .ok_or_else(|| FederationError::PageDoesNotSpecifyCreator.into()),
} }
} }
} }

View file

@ -23,30 +23,24 @@ pub enum LemmyErrorType {
CouldntUpdateComment, CouldntUpdateComment,
CouldntUpdatePrivateMessage, CouldntUpdatePrivateMessage,
CannotLeaveAdmin, CannotLeaveAdmin,
NoLinesInHtml, // TODO: also remove the translations of unused errors
SiteMetadataPageIsNotDoctypeHtml,
PictrsResponseError(String), PictrsResponseError(String),
PictrsPurgeResponseError(String), PictrsPurgeResponseError(String),
PictrsCachingDisabled,
ImageUrlMissingPathSegments, ImageUrlMissingPathSegments,
ImageUrlMissingLastPathSegment, ImageUrlMissingLastPathSegment,
PictrsApiKeyNotProvided, PictrsApiKeyNotProvided,
NoContentTypeHeader, NoContentTypeHeader,
NotAnImageType, NotAnImageType,
NotAModOrAdmin, NotAModOrAdmin,
NoAdmins,
NotTopAdmin,
NotTopMod, NotTopMod,
NotLoggedIn, NotLoggedIn,
NotHigherMod, NotHigherMod,
NotHigherAdmin, NotHigherAdmin,
SiteBan, SiteBan,
Deleted, Deleted,
BannedFromCommunity,
PersonIsBlocked, PersonIsBlocked,
CommunityIsBlocked, CommunityIsBlocked,
InstanceIsBlocked, InstanceIsBlocked,
VoteNotAllowed,
InstanceIsPrivate, InstanceIsPrivate,
/// Password must be between 10 and 60 characters /// Password must be between 10 and 60 characters
InvalidPassword, InvalidPassword,
@ -61,7 +55,6 @@ pub enum LemmyErrorType {
OnlyAdminsCanCreateCommunities, OnlyAdminsCanCreateCommunities,
CommunityAlreadyExists, CommunityAlreadyExists,
LanguageNotAllowed, LanguageNotAllowed,
OnlyModsCanPostInCommunity,
CouldntUpdatePost, CouldntUpdatePost,
NoPostEditAllowed, NoPostEditAllowed,
EditPrivateMessageNotAllowed, EditPrivateMessageNotAllowed,
@ -73,23 +66,10 @@ pub enum LemmyErrorType {
RegistrationUsernameRequired, RegistrationUsernameRequired,
EmailAlreadyExists, EmailAlreadyExists,
UsernameAlreadyExists, UsernameAlreadyExists,
FederationForbiddenByStrictAllowList,
PersonIsBannedFromCommunity, PersonIsBannedFromCommunity,
ObjectIsNotPublic,
InvalidCommunity,
CannotCreatePostOrCommentInDeletedOrRemovedCommunity,
CannotReceivePage,
NewPostCannotBeLocked,
OnlyLocalAdminCanRemoveCommunity,
OnlyLocalAdminCanRestoreCommunity,
NoIdGiven, NoIdGiven,
IncorrectLogin, IncorrectLogin,
InvalidQuery,
ObjectNotLocal, ObjectNotLocal,
PostIsLocked,
PersonIsBannedFromSite(String),
InvalidVoteValue,
PageDoesNotSpecifyCreator,
NoEmailSetup, NoEmailSetup,
LocalSiteNotSetup, LocalSiteNotSetup,
EmailSmtpServerNeedsAPort, EmailSmtpServerNeedsAPort,
@ -126,7 +106,6 @@ pub enum LemmyErrorType {
CouldntUpdateCommunity, CouldntUpdateCommunity,
CouldntUpdateReplies, CouldntUpdateReplies,
CouldntUpdatePersonMentions, CouldntUpdatePersonMentions,
PostTitleTooLong,
CouldntCreatePost, CouldntCreatePost,
CouldntCreatePrivateMessage, CouldntCreatePrivateMessage,
CouldntUpdatePrivate, CouldntUpdatePrivate,
@ -141,10 +120,6 @@ pub enum LemmyErrorType {
EmailSendFailed, EmailSendFailed,
Slurs, Slurs,
RegistrationDenied(Option<String>), RegistrationDenied(Option<String>),
FederationDisabled,
DomainBlocked(String),
DomainNotInAllowList(String),
FederationDisabledByStrictAllowList,
SiteNameRequired, SiteNameRequired,
SiteNameLengthOverflow, SiteNameLengthOverflow,
PermissiveRegex, PermissiveRegex,
@ -158,23 +133,51 @@ pub enum LemmyErrorType {
/// Thrown when an API call is submitted with more than 1000 array elements, see /// Thrown when an API call is submitted with more than 1000 array elements, see
/// [[MAX_API_PARAM_ELEMENTS]] /// [[MAX_API_PARAM_ELEMENTS]]
TooManyItems, TooManyItems,
CommunityHasNoFollowers,
BanExpirationInPast, BanExpirationInPast,
InvalidUnixTime, InvalidUnixTime,
InvalidBotAction, InvalidBotAction,
CantBlockLocalInstance, CantBlockLocalInstance,
UrlWithoutDomain, Unknown(String),
InboxTimeout, UrlLengthOverflow,
OauthAuthorizationInvalid, OauthAuthorizationInvalid,
OauthLoginFailed, OauthLoginFailed,
OauthRegistrationClosed, OauthRegistrationClosed,
CouldntDeleteOauthProvider, CouldntDeleteOauthProvider,
Unknown(String), NotFound,
CantDeleteSite, CommunityHasNoFollowers,
UrlLengthOverflow,
PostScheduleTimeMustBeInFuture, PostScheduleTimeMustBeInFuture,
TooManyScheduledPosts, TooManyScheduledPosts,
NotFound, FederationError(Option<FederationError>),
}
/// Federation related errors, these dont need to be translated.
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
#[cfg_attr(feature = "full", derive(ts_rs::TS))]
#[cfg_attr(feature = "full", ts(export))]
#[non_exhaustive]
pub enum FederationError {
// TODO: merge into a single NotFound error
CouldntFindActivity,
InvalidCommunity,
CannotCreatePostOrCommentInDeletedOrRemovedCommunity,
CannotReceivePage,
OnlyLocalAdminCanRemoveCommunity,
OnlyLocalAdminCanRestoreCommunity,
PostIsLocked,
PersonIsBannedFromSite(String),
InvalidVoteValue,
PageDoesNotSpecifyCreator,
CouldntGetComments,
CouldntGetPosts,
FederationDisabled,
DomainBlocked(String),
DomainNotInAllowList(String),
FederationDisabledByStrictAllowList,
ContradictingFilters,
UrlWithoutDomain,
InboxTimeout,
CantDeleteSite,
ObjectIsNotPublic,
} }
cfg_if! { cfg_if! {
@ -255,6 +258,17 @@ cfg_if! {
} }
} }
impl From<FederationError> for LemmyError {
fn from(error_type: FederationError) -> Self {
let inner = anyhow::anyhow!("{}", error_type);
LemmyError {
error_type: LemmyErrorType::FederationError(Some(error_type)),
inner,
context: Backtrace::capture(),
}
}
}
pub trait LemmyErrorExt<T, E: Into<anyhow::Error>> { pub trait LemmyErrorExt<T, E: Into<anyhow::Error>> {
fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult<T>; fn with_lemmy_type(self, error_type: LemmyErrorType) -> LemmyResult<T>;
} }
@ -306,12 +320,12 @@ cfg_if! {
#[test] #[test]
fn deserializes_with_message() -> LemmyResult<()> { fn deserializes_with_message() -> LemmyResult<()> {
let reg_banned = LemmyErrorType::PersonIsBannedFromSite(String::from("reason")); let reg_banned = LemmyErrorType::PictrsResponseError(String::from("reason"));
let err = LemmyError::from(reg_banned).error_response(); let err = LemmyError::from(reg_banned).error_response();
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap_or_default().to_vec())?; let json = String::from_utf8(err.into_body().try_into_bytes().unwrap_or_default().to_vec())?;
assert_eq!( assert_eq!(
&json, &json,
"{\"error\":\"person_is_banned_from_site\",\"message\":\"reason\"}" "{\"error\":\"pictrs_response_error\",\"message\":\"reason\"}"
); );
Ok(()) Ok(())

View file

@ -0,0 +1,45 @@
use lemmy_utils::LemmyErrorType;
use std::{env::current_dir, process::Command};
use strum::IntoEnumIterator;
#[test]
#[allow(clippy::unwrap_used)]
fn test_errors_used() {
let mut unused_error_found = false;
let mut current_dir = current_dir().unwrap();
current_dir.pop();
current_dir.pop();
for error in LemmyErrorType::iter() {
let search = format!("LemmyErrorType::{error}");
let mut grep_all = Command::new("grep");
let grep_all = grep_all
.current_dir(current_dir.clone())
.arg("-R")
.arg("--exclude=error.rs")
.arg(&search)
.arg("crates/")
.arg("src/");
let output = grep_all.output().unwrap();
let grep_all_out = std::str::from_utf8(&output.stdout).unwrap();
let mut grep_apub = Command::new("grep");
let grep_apub = grep_apub
.current_dir(current_dir.clone())
.arg("-R")
.arg("--exclude-dir=api")
.arg(&search)
.arg("crates/apub/");
let output = grep_apub.output().unwrap();
let grep_apub_out = std::str::from_utf8(&output.stdout).unwrap();
if grep_all_out.is_empty() {
println!("LemmyErrorType::{} is unused", error);
unused_error_found = true;
}
if search != "LemmyErrorType::FederationError" && grep_all_out == grep_apub_out {
println!("LemmyErrorType::{} is only used for federation", error);
unused_error_found = true;
}
}
assert!(!unused_error_found);
}