Move federation errors into separate enum (fixes #4926)

This commit is contained in:
Felix Ableitner 2024-09-16 11:24:16 +02:00
parent c422b88cf8
commit e7376eeaef
15 changed files with 97 additions and 70 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?;
@ -216,7 +216,7 @@ async fn can_accept_activity_in_community(
if !community.local if !community.local
&& !CommunityFollower::has_local_followers(&mut context.pool(), community.id).await? && !CommunityFollower::has_local_followers(&mut context.pool(), community.id).await?
{ {
Err(LemmyErrorType::CommunityHasNoFollowers)? Err(FederationError::CommunityHasNoFollowers)?
} }
// Local only community can't federate // Local only community can't federate
if community.visibility != CommunityVisibility::Public { if community.visibility != CommunityVisibility::Public {

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(),
))? ))?
} }
@ -89,7 +89,7 @@ pub(crate) async fn verify_person_in_community(
let community_id = community.id; let community_id = community.id;
let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id).await?; let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id).await?;
if is_banned { if is_banned {
Err(LemmyErrorType::PersonIsBannedFromCommunity)? Err(FederationError::PersonIsBannedFromCommunity)?
} else { } else {
Ok(()) Ok(())
} }
@ -126,7 +126,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(())
} }
@ -138,7 +138,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(())
} }
@ -146,7 +146,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

@ -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
@ -108,7 +108,7 @@ pub(crate) async fn get_activity(
.into(); .into();
let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id) let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id)
.await? .await?
.ok_or(LemmyErrorType::CouldntFindActivity)?; .ok_or(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,11 @@ 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,
LemmyErrorType,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use url::Url; use url::Url;
@ -57,7 +61,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

@ -32,7 +32,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;
@ -157,7 +157,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

@ -41,12 +41,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;
@ -88,7 +87,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)]
@ -143,7 +142,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();
@ -218,7 +217,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

@ -27,7 +27,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;
@ -115,7 +115,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

@ -1,6 +1,6 @@
use cfg_if::cfg_if; use cfg_if::cfg_if;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::{backtrace::Backtrace, fmt::Debug};
use strum::{Display, EnumIter}; use strum::{Display, EnumIter};
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)] #[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
@ -50,7 +50,6 @@ pub enum LemmyErrorType {
CouldntFindRegistrationApplication, CouldntFindRegistrationApplication,
CouldntFindCommentReply, CouldntFindCommentReply,
CouldntFindPrivateMessage, CouldntFindPrivateMessage,
CouldntFindActivity,
PersonIsBlocked, PersonIsBlocked,
CommunityIsBlocked, CommunityIsBlocked,
InstanceIsBlocked, InstanceIsBlocked,
@ -80,20 +79,9 @@ pub enum LemmyErrorType {
RegistrationClosed, RegistrationClosed,
RegistrationApplicationAnswerRequired, RegistrationApplicationAnswerRequired,
EmailAlreadyExists, EmailAlreadyExists,
PersonIsBannedFromCommunity,
ObjectIsNotPublic,
InvalidCommunity,
CannotCreatePostOrCommentInDeletedOrRemovedCommunity,
CannotReceivePage,
OnlyLocalAdminCanRemoveCommunity,
OnlyLocalAdminCanRestoreCommunity,
NoIdGiven, NoIdGiven,
IncorrectLogin, IncorrectLogin,
ObjectNotLocal, ObjectNotLocal,
PostIsLocked,
PersonIsBannedFromSite(String),
InvalidVoteValue,
PageDoesNotSpecifyCreator,
NoEmailSetup, NoEmailSetup,
LocalSiteNotSetup, LocalSiteNotSetup,
EmailSmtpServerNeedsAPort, EmailSmtpServerNeedsAPort,
@ -146,10 +134,6 @@ pub enum LemmyErrorType {
Slurs, Slurs,
CouldntFindObject, CouldntFindObject,
RegistrationDenied(Option<String>), RegistrationDenied(Option<String>),
FederationDisabled,
DomainBlocked(String),
DomainNotInAllowList(String),
FederationDisabledByStrictAllowList,
SiteNameRequired, SiteNameRequired,
SiteNameLengthOverflow, SiteNameLengthOverflow,
PermissiveRegex, PermissiveRegex,
@ -163,16 +147,46 @@ 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,
Unknown(String),
UrlLengthOverflow,
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))]
#[serde(tag = "error", content = "message", rename_all = "snake_case")]
#[non_exhaustive]
pub enum FederationError {
// TODO: merge into a single NotFound error
CouldntFindActivity,
PersonIsBannedFromCommunity,
InvalidCommunity,
CannotCreatePostOrCommentInDeletedOrRemovedCommunity,
CannotReceivePage,
OnlyLocalAdminCanRemoveCommunity,
OnlyLocalAdminCanRestoreCommunity,
PostIsLocked,
PersonIsBannedFromSite(String),
InvalidVoteValue,
PageDoesNotSpecifyCreator,
CouldntGetComments,
CouldntGetPosts,
FederationDisabled,
DomainBlocked(String),
DomainNotInAllowList(String),
FederationDisabledByStrictAllowList,
ContradictingFilters,
CommunityHasNoFollowers,
UrlWithoutDomain, UrlWithoutDomain,
InboxTimeout, InboxTimeout,
Unknown(String),
CantDeleteSite, CantDeleteSite,
UrlLengthOverflow, ObjectIsNotPublic,
} }
cfg_if! { cfg_if! {
@ -249,6 +263,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>;
} }
@ -299,12 +324,12 @@ cfg_if! {
#[test] #[test]
fn deserializes_with_message() { fn deserializes_with_message() {
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().to_vec()).unwrap(); let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
assert_eq!( assert_eq!(
&json, &json,
"{\"error\":\"person_is_banned_from_site\",\"message\":\"reason\"}" "{\"error\":\"pictrs_response_error\",\"message\":\"reason\"}"
) )
} }

View file

@ -24,6 +24,7 @@ fn test_errors_used() {
let grep_apub = grep_apub let grep_apub = grep_apub
.current_dir(current_dir.clone()) .current_dir(current_dir.clone())
.arg("-R") .arg("-R")
.arg("--exclude-dir=api")
.arg(error.to_string()) .arg(error.to_string())
.arg("crates/apub/"); .arg("crates/apub/");
let output = grep_apub.output().unwrap(); let output = grep_apub.output().unwrap();