[[package]]
name = "actix-http"
-version = "3.0.0-beta.12"
+version = "3.0.0-beta.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afaeb3d3fcb06b775ac62f05d580aae4afe5a149513333a73f688fdf26c06639"
+checksum = "1bc3f9d97e32d75fae3ad7d955ac005eea3fd3ea60a89132768700911a60fd94"
dependencies = [
"actix-codec",
"actix-rt",
[[package]]
name = "actix-rt"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a0c218d0a17c120f10ee0c69c9f0c45d87319e8f66b1f065e8412b612fc3e24"
+checksum = "05c2f80ce8d0c990941c7a7a931f69fd0701b76d521f8d36298edf59cd3fbf1f"
dependencies = [
"actix-macros",
"futures-core",
[[package]]
name = "actix-tls"
-version = "3.0.0-beta.8"
+version = "3.0.0-beta.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a563b0245222230c860c1b077ca7309179fff0f575b1914967c1ee385aa5da64"
+checksum = "53d4739910b49c77ea88308a9fbfae544524b34884161527f9978c0102052da0"
dependencies = [
"actix-codec",
"actix-rt",
"futures-core",
"http",
"log",
+ "pin-project-lite",
"tokio-rustls",
"tokio-util",
"webpki-roots",
[[package]]
name = "actix-web"
-version = "4.0.0-beta.11"
+version = "4.0.0-beta.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e85aa9bb018d83a0db70f557ba0cde9c6170a5d1de4fede02e377f68c1ac5aa9"
+checksum = "e87cfc4efaad42f8a054e269d1b85046397ff4e8707e49128dea3f99a512a9d6"
dependencies = [
"actix-codec",
"actix-http",
[[package]]
name = "awc"
-version = "3.0.0-beta.10"
+version = "3.0.0-beta.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f122bed94dc044b13a991b292ff6a3cde4c3fba890a4e3dbbec0f2eedc607f0a"
+checksum = "f9f7d0c472987e454f41c3f4c7fa336ca139707ab255644b0480144c2060c800"
dependencies = [
"actix-codec",
"actix-http",
"strum_macros",
"thiserror",
"tokio",
+ "tracing",
"url",
"uuid",
]
"lemmy_utils",
"serde",
"serde_json",
+ "tracing",
"url",
]
"serde",
"sha2",
"strum",
+ "tracing",
"url",
]
[[package]]
name = "tracing-actix-web"
-version = "0.5.0-beta.2"
+version = "0.5.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cac34827e06f78b69523b2fbe5b2dd4dfc75940b2ea5ba37e4fa2a25d4a0edf"
+checksum = "994e4a59135823bdca121a8d086e3fcc71741c8677b47fa95a6afdd15e8f646f"
dependencies = [
"actix-web",
"pin-project",
actix = "0.12.0"
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
tracing = "0.1.29"
-tracing-actix-web = "0.5.0-beta.2"
+tracing-actix-web = { version = "0.5.0-beta.3", default-features = false }
tracing-error = "0.2.0"
tracing-log = "0.1.2"
tracing-subscriber = { version = "0.3.2", features = ["env-filter"] }
captcha = "0.0.8"
anyhow = "1.0.44"
thiserror = "1.0.29"
+tracing = "0.1.29"
background-jobs = "0.11.0"
reqwest = { version = "0.11.4", features = ["json"] }
traits::{Likeable, Saveable},
};
use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
use crate::Perform;
impl Perform for MarkCommentAsRead {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Verify that only the recipient can mark as read
if local_user_view.person.id != orig_comment.get_recipient_id() {
- return Err(ApiError::err_plain("no_comment_edit_allowed").into());
+ return Err(LemmyError::from_message("no_comment_edit_allowed"));
}
// Do the mark as read
Comment::update_read(conn, comment_id, read)
})
.await?
- .map_err(|_| ApiError::err_plain("couldnt_update_comment"))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
// Refetch it
let comment_id = data.comment_id;
impl Perform for SaveComment {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
blocking(context.pool(), save_comment)
.await?
- .map_err(|e| ApiError::err("couldnt_save_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_save_comment"))?;
} else {
let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
blocking(context.pool(), unsave_comment)
.await?
- .map_err(|e| ApiError::err("couldnt_save_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_save_comment"))?;
}
let comment_id = data.comment_id;
impl Perform for CreateCommentLike {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let like = move |conn: &'_ _| CommentLike::like(conn, &like_form2);
blocking(context.pool(), like)
.await?
- .map_err(|e| ApiError::err("couldnt_like_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_like_comment"))?;
Vote::send(
&object,
comment_report_view::{CommentReportQueryBuilder, CommentReportView},
comment_view::CommentView,
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
/// Creates a comment report and notifies the moderators of the community
impl Perform for CreateCommentReport {
type Response = CommentReportResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// check size of report and check for whitespace
let reason = data.reason.trim();
if reason.is_empty() {
- return Err(ApiError::err_plain("report_reason_required").into());
+ return Err(LemmyError::from_message("report_reason_required"));
}
if reason.chars().count() > 1000 {
- return Err(ApiError::err_plain("report_too_long").into());
+ return Err(LemmyError::from_message("report_too_long"));
}
let person_id = local_user_view.person.id;
CommentReport::report(conn, &report_form)
})
.await?
- .map_err(|e| ApiError::err("couldnt_create_report", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_report"))?;
let comment_report_view = blocking(context.pool(), move |conn| {
CommentReportView::read(conn, report.id, person_id)
impl Perform for ResolveCommentReport {
type Response = CommentReportResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
blocking(context.pool(), resolve_fun)
.await?
- .map_err(|e| ApiError::err("couldnt_resolve_report", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_resolve_report"))?;
let report_id = data.report_id;
let comment_report_view = blocking(context.pool(), move |conn| {
impl Perform for ListCommentReports {
type Response = ListCommentReportsResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
community_view::CommunityView,
person_view::PersonViewSafe,
};
-use lemmy_utils::{location_info, utils::naive_from_unix, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{location_info, utils::naive_from_unix, ConnectionId, LemmyError};
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
impl Perform for FollowCommunity {
type Response = CommunityResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
blocking(context.pool(), follow)
.await?
- .map_err(|e| ApiError::err("community_follower_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_follower_already_exists"))?;
} else {
let unfollow =
move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
blocking(context.pool(), unfollow)
.await?
- .map_err(|e| ApiError::err("community_follower_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_follower_already_exists"))?;
}
} else if data.follow {
// Dont actually add to the community followers here, because you need
let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
blocking(context.pool(), unfollow)
.await?
- .map_err(|e| ApiError::err("community_follower_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_follower_already_exists"))?;
}
let community_id = data.community_id;
impl Perform for BlockCommunity {
type Response = BlockCommunityResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let block = move |conn: &'_ _| CommunityBlock::block(conn, &community_block_form);
blocking(context.pool(), block)
.await?
- .map_err(|e| ApiError::err("community_block_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_block_already_exists"))?;
// Also, unfollow the community, and send a federated unfollow
let community_follower_form = CommunityFollowerForm {
let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form);
blocking(context.pool(), unblock)
.await?
- .map_err(|e| ApiError::err("community_block_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_block_already_exists"))?;
}
let community_view = blocking(context.pool(), move |conn| {
impl Perform for BanFromCommunity {
type Response = BanFromCommunityResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
blocking(context.pool(), ban)
.await?
- .map_err(|e| ApiError::err("community_user_already_banned", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_user_already_banned"))?;
// Also unsubscribe them from the community, if they are subscribed
let community_follower_form = CommunityFollowerForm {
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
blocking(context.pool(), unban)
.await?
- .map_err(|e| ApiError::err("community_user_already_banned", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_user_already_banned"))?;
UndoBlockUserFromCommunity::send(
&community,
&banned_person,
impl Perform for AddModToCommunity {
type Response = AddModToCommunityResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
blocking(context.pool(), join)
.await?
- .map_err(|e| ApiError::err("community_moderator_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_moderator_already_exists"))?;
} else {
let leave = move |conn: &'_ _| CommunityModerator::leave(conn, &community_moderator_form);
blocking(context.pool(), leave)
.await?
- .map_err(|e| ApiError::err("community_moderator_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_moderator_already_exists"))?;
}
// Mod tables
impl Perform for TransferCommunity {
type Response = GetCommunityResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
.map(|a| a.person.id)
.any(|x| x == local_user_view.person.id)
{
- return Err(ApiError::err_plain("not_an_admin").into());
+ return Err(LemmyError::from_message("not_an_admin"));
}
// You have to re-do the community_moderator table, reordering it.
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
blocking(context.pool(), join)
.await?
- .map_err(|e| ApiError::err("community_moderator_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_moderator_already_exists"))?;
}
// Mod tables
CommunityView::read(conn, community_id, Some(person_id))
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?;
let community_id = data.community_id;
let moderators = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?;
// Return the jwt
Ok(GetCommunityResponse {
email::send_email,
location_info,
utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
- ApiError,
ConnectionId,
LemmyError,
+ Sensitive,
};
use lemmy_websocket::{
messages::{CaptchaItem, SendAllMessage},
impl Perform for Login {
type Response = LoginResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
LocalUserView::find_by_email_or_name(conn, &username_or_email)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_that_username_or_email", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?;
// Verify the password
let valid: bool = verify(
)
.unwrap_or(false);
if !valid {
- return Err(ApiError::err_plain("password_incorrect").into());
+ return Err(LemmyError::from_message("password_incorrect"));
}
// Return the jwt
local_user_view.local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
- )?,
+ )?
+ .into(),
})
}
}
impl Perform for GetCaptcha {
type Response = GetCaptchaResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for SaveUserSettings {
type Response = LoginResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?;
- let email = diesel_option_overwrite(&data.email);
+ let email = diesel_option_overwrite(&data.email.clone().map(Sensitive::into_inner));
let bio = diesel_option_overwrite(&data.bio);
let display_name = diesel_option_overwrite(&data.display_name);
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
if let Some(Some(bio)) = &bio {
if bio.chars().count() > 300 {
- return Err(ApiError::err_plain("bio_length_overflow").into());
+ return Err(LemmyError::from_message("bio_length_overflow"));
}
}
display_name.trim(),
context.settings().actor_name_max_length,
) {
- return Err(ApiError::err_plain("invalid_username").into());
+ return Err(LemmyError::from_message("invalid_username"));
}
}
if let Some(Some(matrix_user_id)) = &matrix_user_id {
if !is_valid_matrix_id(matrix_user_id) {
- return Err(ApiError::err_plain("invalid_matrix_id").into());
+ return Err(LemmyError::from_message("invalid_matrix_id"));
}
}
Person::update(conn, person_id, &person_form)
})
.await?
- .map_err(|e| ApiError::err("user_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("user_already_exists"))?;
let local_user_form = LocalUserForm {
person_id,
"user_already_exists"
};
- return Err(ApiError::err(err_type, e).into());
+ return Err(LemmyError::from(e).with_message(err_type));
}
};
updated_local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
- )?,
+ )?
+ .into(),
})
}
}
impl Perform for ChangePassword {
type Response = LoginResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<LoginResponse, LemmyError> {
let data: &ChangePassword = self;
let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt(data.auth.as_ref(), context.pool(), context.secret()).await?;
password_length_check(&data.new_password)?;
// Make sure passwords match
if data.new_password != data.new_password_verify {
- return Err(ApiError::err_plain("passwords_dont_match").into());
+ return Err(LemmyError::from_message("passwords_dont_match"));
}
// Check the old password
)
.unwrap_or(false);
if !valid {
- return Err(ApiError::err_plain("password_incorrect").into());
+ return Err(LemmyError::from_message("password_incorrect"));
}
let local_user_id = local_user_view.local_user.id;
updated_local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
- )?,
+ )?
+ .into(),
})
}
}
impl Perform for AddAdmin {
type Response = AddAdminResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
Person::add_admin(conn, added_person_id, added)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_user", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_user"))?;
// Mod tables
let form = ModAddForm {
impl Perform for BanPerson {
type Response = BanPersonResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let ban_person = move |conn: &'_ _| Person::ban_person(conn, banned_person_id, ban);
blocking(context.pool(), ban_person)
.await?
- .map_err(|e| ApiError::err("couldnt_update_user", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_user"))?;
// Remove their data if that's desired
if data.remove_data.unwrap_or(false) {
impl Perform for BlockPerson {
type Response = BlockPersonResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Don't let a person block themselves
if target_id == person_id {
- return Err(ApiError::err_plain("cant_block_yourself").into());
+ return Err(LemmyError::from_message("cant_block_yourself"));
}
let person_block_form = PersonBlockForm {
let block = move |conn: &'_ _| PersonBlock::block(conn, &person_block_form);
blocking(context.pool(), block)
.await?
- .map_err(|e| ApiError::err("person_block_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("person_block_already_exists"))?;
} else {
let unblock = move |conn: &'_ _| PersonBlock::unblock(conn, &person_block_form);
blocking(context.pool(), unblock)
.await?
- .map_err(|e| ApiError::err("person_block_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("person_block_already_exists"))?;
}
// TODO does any federated stuff need to be done here?
impl Perform for GetReplies {
type Response = GetRepliesResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for GetPersonMentions {
type Response = GetPersonMentionsResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for MarkPersonMentionAsRead {
type Response = PersonMentionResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
.await??;
if local_user_view.person.id != read_person_mention.recipient_id {
- return Err(ApiError::err_plain("couldnt_update_comment").into());
+ return Err(LemmyError::from_message("couldnt_update_comment"));
}
let person_mention_id = read_person_mention.id;
move |conn: &'_ _| PersonMention::update_read(conn, person_mention_id, read);
blocking(context.pool(), update_mention)
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
let person_mention_id = read_person_mention.id;
let person_id = local_user_view.person.id;
impl Perform for MarkAllAsRead {
type Response = GetRepliesResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
blocking(context.pool(), mark_as_read)
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
}
// Mark all user mentions as read
move |conn: &'_ _| PersonMention::mark_all_as_read(conn, person_id);
blocking(context.pool(), update_person_mentions)
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
// Mark all private_messages as read
let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, person_id);
blocking(context.pool(), update_pm)
.await?
- .map_err(|e| ApiError::err("couldnt_update_private_message", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_private_message"))?;
Ok(GetRepliesResponse { replies: vec![] })
}
impl Perform for PasswordReset {
type Response = PasswordResetResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
LocalUserView::find_by_email(conn, &email)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_that_username_or_email", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?;
// Generate a random token
let token = generate_random_string();
html,
&context.settings(),
)
- .map_err(|e| ApiError::err("email_send_failed", e))?;
+ .map_err(|e| anyhow::anyhow!("{}", e))
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("email_send_failed"))?;
Ok(PasswordResetResponse {})
}
impl Perform for PasswordChange {
type Response = LoginResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Make sure passwords match
if data.password != data.password_verify {
- return Err(ApiError::err_plain("passwords_dont_match").into());
+ return Err(LemmyError::from_message("passwords_dont_match"));
}
// Update the user with the new password
LocalUser::update_password(conn, local_user_id, &password)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_user", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_user"))?;
// Return the jwt
Ok(LoginResponse {
updated_local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
- )?,
+ )?
+ .into(),
})
}
}
impl Perform for GetReportCount {
type Response = GetReportCountResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for GetUnreadCount {
type Response = GetUnreadCountResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
traits::{Crud, Likeable, Saveable},
};
use lemmy_db_views::post_view::PostView;
-use lemmy_utils::{request::fetch_site_metadata, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{request::fetch_site_metadata, ConnectionId, LemmyError};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
use std::convert::TryInto;
impl Perform for CreatePostLike {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
blocking(context.pool(), like)
.await?
- .map_err(|e| ApiError::err("couldnt_like_post", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_like_post"))?;
Vote::send(
&object,
impl Perform for MarkPostAsRead {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for LockPost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for StickyPost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for SavePost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
blocking(context.pool(), save)
.await?
- .map_err(|e| ApiError::err("couldnt_save_post", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_save_post"))?;
} else {
let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
blocking(context.pool(), unsave)
.await?
- .map_err(|e| ApiError::err("couldnt_save_post", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_save_post"))?;
}
let post_id = data.post_id;
impl Perform for GetSiteMetadata {
type Response = GetSiteMetadataResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
post_report_view::{PostReportQueryBuilder, PostReportView},
post_view::PostView,
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
/// Creates a post report and notifies the moderators of the community
impl Perform for CreatePostReport {
type Response = PostReportResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// check size of report and check for whitespace
let reason = data.reason.trim();
if reason.is_empty() {
- return Err(ApiError::err_plain("report_reason_required").into());
+ return Err(LemmyError::from_message("report_reason_required"));
}
if reason.chars().count() > 1000 {
- return Err(ApiError::err_plain("report_too_long").into());
+ return Err(LemmyError::from_message("report_too_long"));
}
let person_id = local_user_view.person.id;
PostReport::report(conn, &report_form)
})
.await?
- .map_err(|e| ApiError::err("couldnt_create_report", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_report"))?;
let post_report_view = blocking(context.pool(), move |conn| {
PostReportView::read(conn, report.id, person_id)
impl Perform for ResolvePostReport {
type Response = PostReportResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
blocking(context.pool(), resolve_fun)
.await?
- .map_err(|e| ApiError::err("couldnt_resolve_report", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_resolve_report"))?;
let post_report_view = blocking(context.pool(), move |conn| {
PostReportView::read(conn, report_id, person_id)
impl Perform for ListPostReports {
type Response = ListPostReportsResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
person::{MarkPrivateMessageAsRead, PrivateMessageResponse},
};
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
impl Perform for MarkPrivateMessageAsRead {
type Response = PrivateMessageResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
})
.await??;
if local_user_view.person.id != orig_private_message.recipient_id {
- return Err(ApiError::err_plain("couldnt_update_private_message").into());
+ return Err(LemmyError::from_message("couldnt_update_private_message"));
}
// Doing the update
PrivateMessage::update_read(conn, private_message_id, read)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_private_message", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_private_message"))?;
// No need to send an apub update
let op = UserOperation::MarkPrivateMessageAsRead;
mod_sticky_post_view::ModStickyPostView,
mod_transfer_community_view::ModTransferCommunityView,
};
-use lemmy_utils::{
- location_info,
- settings::structs::Settings,
- version,
- ApiError,
- ConnectionId,
- LemmyError,
-};
+use lemmy_utils::{location_info, settings::structs::Settings, version, ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
impl Perform for GetModlog {
type Response = GetModlogResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for Search {
type Response = SearchResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let data: &Search = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
let show_bot_accounts = local_user_view
impl Perform for ResolveObject {
type Response = ResolveObjectResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<ResolveObjectResponse, LemmyError> {
let local_user_view =
- get_local_user_view_from_jwt_opt(&self.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let res = search_by_apub_id(&self.q, context)
.await
- .map_err(|e| ApiError::err("couldnt_find_object", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_object"))?;
convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
.await
- .map_err(|e| ApiError::err("couldnt_find_object", e).into())
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_object"))
}
}
impl Perform for TransferSite {
type Response = GetSiteResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Make sure user is the creator
if read_site.creator_id != local_user_view.person.id {
- return Err(ApiError::err_plain("not_an_admin").into());
+ return Err(LemmyError::from_message("not_an_admin"));
}
let new_creator_id = data.person_id;
let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
blocking(context.pool(), transfer_site)
.await?
- .map_err(|e| ApiError::err("couldnt_update_site", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_site"))?;
// Mod tables
let form = ModAddForm {
impl Perform for GetSiteConfig {
type Response = GetSiteConfigResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for SaveSiteConfig {
type Response = GetSiteConfigResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
let config_hjson = Settings::save_config_file(&data.config_hjson)
- .map_err(|e| ApiError::err("couldnt_update_site", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_site"))?;
Ok(GetSiteConfigResponse { config_hjson })
}
impl Perform for UserJoin {
type Response = UserJoinResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for CommunityJoin {
type Response = CommunityJoinResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for ModJoin {
type Response = ModJoinResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
impl Perform for PostJoin {
type Response = PostJoinResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["cookies"] }
chrono = { version = "0.4.19", features = ["serde"] }
serde_json = { version = "1.0.68", features = ["preserve_order"] }
+tracing = "0.1.29"
url = "2.2.2"
use lemmy_db_schema::newtypes::{CommentId, CommentReportId, CommunityId, LocalUserId, PostId};
use lemmy_db_views::{comment_report_view::CommentReportView, comment_view::CommentView};
+use lemmy_utils::Sensitive;
use serde::{Deserialize, Serialize};
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreateComment {
pub content: String,
pub post_id: PostId,
pub parent_id: Option<CommentId>,
pub form_id: Option<String>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetComment {
pub id: CommentId,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct EditComment {
pub content: String,
pub comment_id: CommentId,
pub form_id: Option<String>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteComment {
pub comment_id: CommentId,
pub deleted: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct RemoveComment {
pub comment_id: CommentId,
pub removed: bool,
pub reason: Option<String>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct MarkCommentAsRead {
pub comment_id: CommentId,
pub read: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct SaveComment {
pub comment_id: CommentId,
pub save: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CommentResponse {
pub comment_view: CommentView,
pub recipient_ids: Vec<LocalUserId>,
pub form_id: Option<String>, // An optional front end ID, to tell which is coming back
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreateCommentLike {
pub comment_id: CommentId,
pub score: i16,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetComments {
pub type_: Option<String>,
pub sort: Option<String>,
pub community_id: Option<CommunityId>,
pub community_name: Option<String>,
pub saved_only: Option<bool>,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetCommentsResponse {
pub comments: Vec<CommentView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreateCommentReport {
pub comment_id: CommentId,
pub reason: String,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CommentReportResponse {
pub comment_report_view: CommentReportView,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ResolveCommentReport {
pub report_id: CommentReportId,
pub resolved: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ListCommentReports {
pub page: Option<i64>,
pub limit: Option<i64>,
pub unresolved_only: Option<bool>,
/// if no community is given, it returns reports for all communities moderated by the auth user
pub community_id: Option<CommunityId>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ListCommentReportsResponse {
pub comment_reports: Vec<CommentReportView>,
}
community_view::CommunityView,
person_view::PersonViewSafe,
};
+use lemmy_utils::Sensitive;
use serde::{Deserialize, Serialize};
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetCommunity {
pub id: Option<CommunityId>,
/// Example: star_trek , or star_trek@xyz.tld
pub name: Option<String>,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetCommunityResponse {
pub community_view: CommunityView,
pub moderators: Vec<CommunityModeratorView>,
pub online: usize,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreateCommunity {
pub name: String,
pub title: String,
pub icon: Option<String>,
pub banner: Option<String>,
pub nsfw: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CommunityResponse {
pub community_view: CommunityView,
}
pub sort: Option<String>,
pub page: Option<i64>,
pub limit: Option<i64>,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub communities: Vec<CommunityView>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BanFromCommunity {
pub community_id: CommunityId,
pub person_id: PersonId,
pub remove_data: Option<bool>,
pub reason: Option<String>,
pub expires: Option<i64>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BanFromCommunityResponse {
pub person_view: PersonViewSafe,
pub banned: bool,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct AddModToCommunity {
pub community_id: CommunityId,
pub person_id: PersonId,
pub added: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AddModToCommunityResponse {
pub moderators: Vec<CommunityModeratorView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct EditCommunity {
pub community_id: CommunityId,
pub title: Option<String>,
pub icon: Option<String>,
pub banner: Option<String>,
pub nsfw: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCommunity {
pub community_id: CommunityId,
pub deleted: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct RemoveCommunity {
pub community_id: CommunityId,
pub removed: bool,
pub reason: Option<String>,
pub expires: Option<i64>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct FollowCommunity {
pub community_id: CommunityId,
pub follow: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct BlockCommunity {
pub community_id: CommunityId,
pub block: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BlockCommunityResponse {
pub community_view: CommunityView,
pub blocked: bool,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct TransferCommunity {
pub community_id: CommunityId,
pub person_id: PersonId,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
community_person_ban_view::CommunityPersonBanView,
community_view::CommunityView,
};
-use lemmy_utils::{claims::Claims, settings::structs::FederationConfig, ApiError, LemmyError};
+use lemmy_utils::{claims::Claims, settings::structs::FederationConfig, LemmyError, Sensitive};
use url::Url;
pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
T: Send + 'static,
{
let pool = pool.clone();
+ let blocking_span = tracing::info_span!("blocking operation");
let res = actix_web::web::block(move || {
+ let entered = blocking_span.enter();
let conn = pool.get()?;
let res = (f)(&conn);
+ drop(entered);
Ok(res) as Result<T, LemmyError>
})
.await?;
})
.await?;
if !is_mod_or_admin {
- return Err(ApiError::err_plain("not_a_mod_or_admin").into());
+ return Err(LemmyError::from_message("not_a_mod_or_admin"));
}
Ok(())
}
pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
if !local_user_view.person.admin {
- return Err(ApiError::err_plain("not_an_admin").into());
+ return Err(LemmyError::from_message("not_an_admin"));
}
Ok(())
}
pub async fn get_post(post_id: PostId, pool: &DbPool) -> Result<Post, LemmyError> {
blocking(pool, move |conn| Post::read(conn, post_id))
.await?
- .map_err(|_| ApiError::err_plain("couldnt_find_post").into())
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_post"))
}
pub async fn mark_post_as_read(
PostRead::mark_as_read(conn, &post_read_form)
})
.await?
- .map_err(|e| ApiError::err("couldnt_mark_post_as_read", e).into())
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_mark_post_as_read"))
}
pub async fn mark_post_as_unread(
PostRead::mark_as_unread(conn, &post_read_form)
})
.await?
- .map_err(|e| ApiError::err("couldnt_mark_post_as_read", e).into())
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_mark_post_as_read"))
}
pub async fn get_local_user_view_from_jwt(
secret: &Secret,
) -> Result<LocalUserView, LemmyError> {
let claims = Claims::decode(jwt, &secret.jwt_secret)
- .map_err(|e| ApiError::err("not_logged_in", e))?
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("not_logged_in"))?
.claims;
let local_user_id = LocalUserId(claims.sub);
let local_user_view =
blocking(pool, move |conn| LocalUserView::read(conn, local_user_id)).await??;
// Check for a site ban
if local_user_view.person.banned {
- return Err(ApiError::err_plain("site_ban").into());
+ return Err(LemmyError::from_message("site_ban"));
}
// Check for user deletion
if local_user_view.person.deleted {
- return Err(ApiError::err_plain("deleted").into());
+ return Err(LemmyError::from_message("deleted"));
}
check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
) -> Result<(), LemmyError> {
let user_validation_time = validator_time.timestamp();
if user_validation_time > claims.iat {
- Err(ApiError::err_plain("not_logged_in").into())
+ Err(LemmyError::from_message("not_logged_in"))
} else {
Ok(())
}
}
pub async fn get_local_user_view_from_jwt_opt(
- jwt: &Option<String>,
+ jwt: Option<&Sensitive<String>>,
pool: &DbPool,
secret: &Secret,
) -> Result<Option<LocalUserView>, LemmyError> {
}
pub async fn get_local_user_settings_view_from_jwt(
- jwt: &str,
+ jwt: &Sensitive<String>,
pool: &DbPool,
secret: &Secret,
) -> Result<LocalUserSettingsView, LemmyError> {
- let claims = Claims::decode(jwt, &secret.jwt_secret)
- .map_err(|e| ApiError::err("not_logged_in", e))?
+ let claims = Claims::decode(jwt.as_ref(), &secret.jwt_secret)
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("not_logged_in"))?
.claims;
let local_user_id = LocalUserId(claims.sub);
let local_user_view = blocking(pool, move |conn| {
.await??;
// Check for a site ban
if local_user_view.person.banned {
- return Err(ApiError::err_plain("site_ban").into());
+ return Err(LemmyError::from_message("site_ban"));
}
check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
}
pub async fn get_local_user_settings_view_from_jwt_opt(
- jwt: &Option<String>,
+ jwt: Option<&Sensitive<String>>,
pool: &DbPool,
secret: &Secret,
) -> Result<Option<LocalUserSettingsView>, LemmyError> {
let is_banned =
move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
- Err(ApiError::err_plain("community_ban").into())
+ Err(LemmyError::from_message("community_ban"))
} else {
Ok(())
}
) -> Result<(), LemmyError> {
let community = blocking(pool, move |conn| Community::read(conn, community_id))
.await?
- .map_err(|e| ApiError::err("couldnt_find_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?;
if community.deleted || community.removed {
- Err(ApiError::err_plain("deleted").into())
+ Err(LemmyError::from_message("deleted"))
} else {
Ok(())
}
pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
if post.deleted || post.removed {
- Err(ApiError::err_plain("deleted").into())
+ Err(LemmyError::from_message("deleted"))
} else {
Ok(())
}
) -> Result<(), LemmyError> {
let is_blocked = move |conn: &'_ _| PersonBlock::read(conn, potential_blocker_id, my_id).is_ok();
if blocking(pool, is_blocked).await? {
- Err(ApiError::err_plain("person_block").into())
+ Err(LemmyError::from_message("person_block"))
} else {
Ok(())
}
if score == -1 {
let site = blocking(pool, Site::read_simple).await??;
if !site.enable_downvotes {
- return Err(ApiError::err_plain("downvotes_disabled").into());
+ return Err(LemmyError::from_message("downvotes_disabled"));
}
}
Ok(())
/// Checks the password length
pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
if !(10..=60).contains(&pass.len()) {
- Err(ApiError::err_plain("invalid_password").into())
+ Err(LemmyError::from_message("invalid_password"))
} else {
Ok(())
}
/// Checks the site description length
pub fn site_description_length_check(description: &str) -> Result<(), LemmyError> {
if description.len() > 150 {
- Err(ApiError::err_plain("site_description_length_overflow").into())
+ Err(LemmyError::from_message("site_description_length_overflow"))
} else {
Ok(())
}
/// Checks for a honeypot. If this field is filled, fail the rest of the function
pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> {
if honeypot.is_some() {
- Err(ApiError::err_plain("honeypot_fail").into())
+ Err(LemmyError::from_message("honeypot_fail"))
} else {
Ok(())
}
person_mention_view::PersonMentionView,
person_view::PersonViewSafe,
};
+use lemmy_utils::Sensitive;
use serde::{Deserialize, Serialize};
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct Login {
- pub username_or_email: String,
- pub password: String,
+ pub username_or_email: Sensitive<String>,
+ pub password: Sensitive<String>,
}
use lemmy_db_schema::newtypes::{CommunityId, PersonId, PersonMentionId, PrivateMessageId};
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct Register {
pub username: String,
- pub password: String,
- pub password_verify: String,
+ pub password: Sensitive<String>,
+ pub password_verify: Sensitive<String>,
pub show_nsfw: bool,
- pub email: Option<String>,
+ pub email: Option<Sensitive<String>>,
pub captcha_uuid: Option<String>,
pub captcha_answer: Option<String>,
pub honeypot: Option<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetCaptcha {}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetCaptchaResponse {
pub ok: Option<CaptchaResponse>, // Will be None if captchas are disabled
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CaptchaResponse {
pub png: String, // A Base64 encoded png
pub wav: String, // A Base64 encoded wav audio
pub uuid: String,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct SaveUserSettings {
pub show_nsfw: Option<bool>,
pub show_scores: Option<bool>,
pub avatar: Option<String>,
pub banner: Option<String>,
pub display_name: Option<String>,
- pub email: Option<String>,
+ pub email: Option<Sensitive<String>>,
pub bio: Option<String>,
pub matrix_user_id: Option<String>,
pub show_avatars: Option<bool>,
pub show_bot_accounts: Option<bool>,
pub show_read_posts: Option<bool>,
pub show_new_post_notifs: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ChangePassword {
- pub new_password: String,
- pub new_password_verify: String,
- pub old_password: String,
- pub auth: String,
+ pub new_password: Sensitive<String>,
+ pub new_password_verify: Sensitive<String>,
+ pub old_password: Sensitive<String>,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct LoginResponse {
- pub jwt: String,
+ pub jwt: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPersonDetails {
pub person_id: Option<PersonId>, // One of these two are required
/// Example: dessalines , or dessalines@xyz.tld
pub limit: Option<i64>,
pub community_id: Option<CommunityId>,
pub saved_only: Option<bool>,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPersonDetailsResponse {
pub person_view: PersonViewSafe,
pub comments: Vec<CommentView>,
pub moderates: Vec<CommunityModeratorView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetRepliesResponse {
pub replies: Vec<CommentView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPersonMentionsResponse {
pub mentions: Vec<PersonMentionView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct MarkAllAsRead {
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct AddAdmin {
pub person_id: PersonId,
pub added: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AddAdminResponse {
pub admins: Vec<PersonViewSafe>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct BanPerson {
pub person_id: PersonId,
pub ban: bool,
pub remove_data: Option<bool>,
pub reason: Option<String>,
pub expires: Option<i64>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BanPersonResponse {
pub person_view: PersonViewSafe,
pub banned: bool,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct BlockPerson {
pub person_id: PersonId,
pub block: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BlockPersonResponse {
pub person_view: PersonViewSafe,
pub blocked: bool,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetReplies {
pub sort: Option<String>,
pub page: Option<i64>,
pub limit: Option<i64>,
pub unread_only: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPersonMentions {
pub sort: Option<String>,
pub page: Option<i64>,
pub limit: Option<i64>,
pub unread_only: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct MarkPersonMentionAsRead {
pub person_mention_id: PersonMentionId,
pub read: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PersonMentionResponse {
pub person_mention_view: PersonMentionView,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteAccount {
- pub password: String,
- pub auth: String,
+ pub password: Sensitive<String>,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct PasswordReset {
- pub email: String,
+ pub email: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PasswordResetResponse {}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct PasswordChange {
- pub token: String,
- pub password: String,
- pub password_verify: String,
+ pub token: Sensitive<String>,
+ pub password: Sensitive<String>,
+ pub password_verify: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreatePrivateMessage {
pub content: String,
pub recipient_id: PersonId,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct EditPrivateMessage {
pub private_message_id: PrivateMessageId,
pub content: String,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct DeletePrivateMessage {
pub private_message_id: PrivateMessageId,
pub deleted: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct MarkPrivateMessageAsRead {
pub private_message_id: PrivateMessageId,
pub read: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPrivateMessages {
pub unread_only: Option<bool>,
pub page: Option<i64>,
pub limit: Option<i64>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PrivateMessagesResponse {
pub private_messages: Vec<PrivateMessageView>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PrivateMessageResponse {
pub private_message_view: PrivateMessageView,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetReportCount {
pub community_id: Option<CommunityId>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GetReportCountResponse {
pub community_id: Option<CommunityId>,
pub comment_reports: i64,
pub post_reports: i64,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetUnreadCount {
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GetUnreadCountResponse {
pub replies: i64,
pub mentions: i64,
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
};
-use lemmy_utils::request::SiteMetadata;
+use lemmy_utils::{request::SiteMetadata, Sensitive};
use serde::{Deserialize, Serialize};
use url::Url;
pub body: Option<String>,
pub honeypot: Option<String>,
pub nsfw: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PostResponse {
pub post_view: PostView,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPost {
pub id: PostId,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetPostResponse {
pub post_view: PostView,
pub community_view: CommunityView,
pub community_id: Option<CommunityId>,
pub community_name: Option<String>,
pub saved_only: Option<bool>,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub posts: Vec<PostView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreatePostLike {
pub post_id: PostId,
pub score: i16,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct EditPost {
pub post_id: PostId,
pub name: Option<String>,
pub url: Option<Url>,
pub body: Option<String>,
pub nsfw: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct DeletePost {
pub post_id: PostId,
pub deleted: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct RemovePost {
pub post_id: PostId,
pub removed: bool,
pub reason: Option<String>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct MarkPostAsRead {
pub post_id: PostId,
pub read: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct LockPost {
pub post_id: PostId,
pub locked: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct StickyPost {
pub post_id: PostId,
pub stickied: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct SavePost {
pub post_id: PostId,
pub save: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreatePostReport {
pub post_id: PostId,
pub reason: String,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PostReportResponse {
pub post_report_view: PostReportView,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ResolvePostReport {
pub report_id: PostReportId,
pub resolved: bool,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ListPostReports {
pub page: Option<i64>,
pub limit: Option<i64>,
pub unresolved_only: Option<bool>,
/// if no community is given, it returns reports for all communities moderated by the auth user
pub community_id: Option<CommunityId>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct ListPostReportsResponse {
pub post_reports: Vec<PostReportView>,
}
mod_sticky_post_view::ModStickyPostView,
mod_transfer_community_view::ModTransferCommunityView,
};
+use lemmy_utils::Sensitive;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub listing_type: Option<String>,
pub page: Option<i64>,
pub limit: Option<i64>,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ResolveObject {
pub q: String,
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize, Default)]
+#[derive(Debug, Serialize, Deserialize, Default)]
pub struct ResolveObjectResponse {
pub comment: Option<CommentView>,
pub post: Option<PostView>,
pub person: Option<PersonViewSafe>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetModlog {
pub mod_person_id: Option<PersonId>,
pub community_id: Option<CommunityId>,
pub limit: Option<i64>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetModlogResponse {
pub removed_posts: Vec<ModRemovePostView>,
pub locked_posts: Vec<ModLockPostView>,
pub added: Vec<ModAddView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct CreateSite {
pub name: String,
pub sidebar: Option<String>,
pub open_registration: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct EditSite {
pub name: Option<String>,
pub sidebar: Option<String>,
pub open_registration: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetSite {
- pub auth: Option<String>,
+ pub auth: Option<Sensitive<String>>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SiteResponse {
pub site_view: SiteView,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetSiteResponse {
pub site_view: Option<SiteView>, // Because the site might not be set up yet
pub admins: Vec<PersonViewSafe>,
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct MyUserInfo {
pub local_user_view: LocalUserSettingsView,
pub follows: Vec<CommunityFollowerView>,
pub person_blocks: Vec<PersonBlockView>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct TransferSite {
pub person_id: PersonId,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetSiteConfig {
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct GetSiteConfigResponse {
pub config_hjson: String,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct SaveSiteConfig {
pub config_hjson: String,
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
pub struct FederatedInstances {
pub linked: Vec<String>,
pub allowed: Option<Vec<String>>,
use lemmy_db_schema::newtypes::{CommunityId, PostId};
+use lemmy_utils::Sensitive;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct UserJoin {
- pub auth: String,
+ pub auth: Sensitive<String>,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserJoinResponse {
pub joined: bool,
}
pub community_id: CommunityId,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CommunityJoinResponse {
pub joined: bool,
}
pub community_id: CommunityId,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ModJoinResponse {
pub joined: bool,
}
pub post_id: PostId,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PostJoinResponse {
pub joined: bool,
}
use lemmy_db_views::comment_view::CommentView;
use lemmy_utils::{
utils::{remove_slurs, scrape_text_for_mentions},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for CreateComment {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Check if post is locked, no new comments
if post.locked {
- return Err(ApiError::err_plain("locked").into());
+ return Err(LemmyError::from_message("locked"));
}
// If there's a parent_id, check to make sure that comment is in that post
// Make sure the parent comment exists
let parent = blocking(context.pool(), move |conn| Comment::read(conn, parent_id))
.await?
- .map_err(|e| ApiError::err("couldnt_create_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_comment"))?;
// Strange issue where sometimes the post ID is incorrect
if parent.post_id != post_id {
- return Err(ApiError::err_plain("couldnt_create_comment").into());
+ return Err(LemmyError::from_message("couldnt_create_comment"));
}
}
Comment::create(conn, &comment_form2)
})
.await?
- .map_err(|e| ApiError::err("couldnt_create_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_comment"))?;
// Necessary to update the ap_id
let inserted_comment_id = inserted_comment.id;
Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?)
})
.await?
- .map_err(|e| ApiError::err("couldnt_create_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_comment"))?;
// Scan the comment for user mentions, add those rows
let post_id = post.id;
let like = move |conn: &'_ _| CommentLike::like(conn, &like_form);
blocking(context.pool(), like)
.await?
- .map_err(|e| ApiError::err("couldnt_like_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_like_comment"))?;
let apub_comment: ApubComment = updated_comment.into();
CreateOrUpdateComment::send(
Comment::update_read(conn, comment_id, true)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
}
// If its a reply, mark the parent as read
if let Some(parent_id) = data.parent_id {
Comment::update_read(conn, parent_id, true)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_parent_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_parent_comment"))?;
}
// If the parent has PersonMentions mark them as read too
let person_id = local_user_view.person.id;
PersonMention::update_read(conn, mention.id, true)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_person_mentions", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_person_mentions"))?;
}
}
traits::Crud,
};
use lemmy_db_views::comment_view::CommentView;
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
LemmyContext,
impl PerformCrud for DeleteComment {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Dont delete it if its already been deleted.
if orig_comment.comment.deleted == data.deleted {
- return Err(ApiError::err_plain("couldnt_update_comment").into());
+ return Err(LemmyError::from_message("couldnt_update_comment"));
}
check_community_ban(
// Verify that only the creator can delete
if local_user_view.person.id != orig_comment.creator.id {
- return Err(ApiError::err_plain("no_comment_edit_allowed").into());
+ return Err(LemmyError::from_message("no_comment_edit_allowed"));
}
// Do the delete
Comment::update_deleted(conn, comment_id, deleted)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
let post_id = updated_comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
impl PerformCrud for RemoveComment {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
Comment::update_removed(conn, comment_id, removed)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
// Mod tables
let form = ModRemoveCommentForm {
SortType,
};
use lemmy_db_views::comment_view::{CommentQueryBuilder, CommentView};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
impl PerformCrud for GetComment {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<Self::Response, LemmyError> {
let data = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let person_id = local_user_view.map(|u| u.person.id);
let id = data.id;
CommentView::read(conn, id, person_id)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_comment"))?;
Ok(Self::Response {
comment_view,
impl PerformCrud for GetComments {
type Response = GetCommentsResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<GetCommentsResponse, LemmyError> {
let data: &GetComments = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let show_bot_accounts = local_user_view
.as_ref()
.list()
})
.await?
- .map_err(|e| ApiError::err("couldnt_get_comments", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_get_comments"))?;
// Blank out deleted or removed info
for cv in comments
use lemmy_db_views::comment_view::CommentView;
use lemmy_utils::{
utils::{remove_slurs, scrape_text_for_mentions},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for EditComment {
type Response = CommentResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Verify that only the creator can edit
if local_user_view.person.id != orig_comment.creator.id {
- return Err(ApiError::err_plain("no_comment_edit_allowed").into());
+ return Err(LemmyError::from_message("no_comment_edit_allowed"));
}
// Do the update
Comment::update_content(conn, comment_id, &content_slurs_removed)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
// Do the mentions / recipients
let updated_comment_content = updated_comment.content.to_owned();
use lemmy_utils::{
apub::generate_actor_keypair,
utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for CreateCommunity {
type Response = CommunityResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let site = blocking(context.pool(), move |conn| Site::read(conn, 0)).await??;
if site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
- return Err(ApiError::err_plain("only_admins_can_create_communities").into());
+ return Err(LemmyError::from_message(
+ "only_admins_can_create_communities",
+ ));
}
check_slurs(&data.name, &context.settings().slur_regex())?;
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) {
- return Err(ApiError::err_plain("invalid_community_name").into());
+ return Err(LemmyError::from_message("invalid_community_name"));
}
// Double check for duplicate community actor_ids
let community_actor_id_wrapped = ObjectId::<ApubCommunity>::new(community_actor_id.clone());
let community_dupe = community_actor_id_wrapped.dereference_local(context).await;
if community_dupe.is_ok() {
- return Err(ApiError::err_plain("community_already_exists").into());
+ return Err(LemmyError::from_message("community_already_exists"));
}
// Check to make sure the icon and banners are urls
Community::create(conn, &community_form)
})
.await?
- .map_err(|e| ApiError::err("community_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_already_exists"))?;
// The community creator becomes a moderator
let community_moderator_form = CommunityModeratorForm {
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
if blocking(context.pool(), join).await?.is_err() {
- return Err(ApiError::err_plain("community_moderator_already_exists").into());
+ return Err(LemmyError::from_message(
+ "community_moderator_already_exists",
+ ));
}
// Follow your own community
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
if blocking(context.pool(), follow).await?.is_err() {
- return Err(ApiError::err_plain("community_follower_already_exists").into());
+ return Err(LemmyError::from_message(
+ "community_follower_already_exists",
+ ));
}
let person_id = local_user_view.person.id;
traits::Crud,
};
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
-use lemmy_utils::{utils::naive_from_unix, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteCommunity {
type Response = CommunityResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Make sure deleter is the top mod
if local_user_view.person.id != community_mods[0].moderator.id {
- return Err(ApiError::err_plain("no_community_edit_allowed").into());
+ return Err(LemmyError::from_message("no_community_edit_allowed"));
}
// Do the delete
Community::update_deleted(conn, community_id, deleted)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_community"))?;
// Send apub messages
send_apub_delete(
impl PerformCrud for RemoveCommunity {
type Response = CommunityResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
Community::update_removed(conn, community_id, removed)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_community"))?;
// Mod tables
let expires = data.expires.map(naive_from_unix);
community_moderator_view::CommunityModeratorView,
community_view::{CommunityQueryBuilder, CommunityView},
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{messages::GetCommunityUsersOnline, LemmyContext};
#[async_trait::async_trait(?Send)]
impl PerformCrud for GetCommunity {
type Response = GetCommunityResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<GetCommunityResponse, LemmyError> {
let data: &GetCommunity = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let person_id = local_user_view.map(|u| u.person.id);
let community_id = match data.id {
ObjectId::<ApubCommunity>::new(community_actor_id)
.dereference(context, &mut 0)
.await
- .map_err(|e| ApiError::err("couldnt_find_community", e))?
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?
.id
}
};
CommunityView::read(conn, community_id, person_id)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?;
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() && (community_view.community.deleted || community_view.community.removed)
CommunityModeratorView::for_community(conn, community_id)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?;
let online = context
.chat_server()
impl PerformCrud for ListCommunities {
type Response = ListCommunitiesResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<ListCommunitiesResponse, LemmyError> {
let data: &ListCommunities = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let person_id = local_user_view.to_owned().map(|l| l.person.id);
traits::Crud,
};
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
-use lemmy_utils::{utils::check_slurs_opt, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{utils::check_slurs_opt, ConnectionId, LemmyError};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditCommunity {
type Response = CommunityResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
})
.await??;
if !mods.contains(&local_user_view.person.id) {
- return Err(ApiError::err_plain("not_a_moderator").into());
+ return Err(LemmyError::from_message("not_a_moderator"));
}
let community_id = data.community_id;
Community::update(conn, community_id, &community_form)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_community"))?;
UpdateCommunity::send(
updated_community.into(),
use lemmy_utils::{
request::fetch_site_data,
utils::{check_slurs, check_slurs_opt, clean_url_params, is_valid_post_title},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for CreatePost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
honeypot_check(&data.honeypot)?;
if !is_valid_post_title(&data.name) {
- return Err(ApiError::err_plain("invalid_post_title").into());
+ return Err(LemmyError::from_message("invalid_post_title"));
}
check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
"couldnt_create_post"
};
- return Err(ApiError::err(err_type, e).into());
+ return Err(LemmyError::from(e).with_message(err_type));
}
};
Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?)
})
.await?
- .map_err(|e| ApiError::err("couldnt_create_post", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_post"))?;
// They like their own post by default
let person_id = local_user_view.person.id;
let like = move |conn: &'_ _| PostLike::like(conn, &like_form);
if blocking(context.pool(), like).await?.is_err() {
- return Err(ApiError::err_plain("couldnt_like_post").into());
+ return Err(LemmyError::from_message("couldnt_like_post"));
}
// Mark the post as read
},
traits::Crud,
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for DeletePost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Dont delete it if its already been deleted.
if orig_post.deleted == data.deleted {
- return Err(ApiError::err_plain("couldnt_update_post").into());
+ return Err(LemmyError::from_message("couldnt_update_post"));
}
check_community_ban(
// Verify that only the creator can delete
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
- return Err(ApiError::err_plain("no_post_edit_allowed").into());
+ return Err(LemmyError::from_message("no_post_edit_allowed"));
}
// Update the post
impl PerformCrud for RemovePost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{messages::GetPostUsersOnline, LemmyContext};
#[async_trait::async_trait(?Send)]
impl PerformCrud for GetPost {
type Response = GetPostResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<GetPostResponse, LemmyError> {
let data: &GetPost = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let show_bot_accounts = local_user_view
.as_ref()
PostView::read(conn, id, person_id)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_post", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_post"))?;
// Mark the post as read
if let Some(person_id) = person_id {
CommunityView::read(conn, community_id, person_id)
})
.await?
- .map_err(|e| ApiError::err("couldnt_find_community", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_community"))?;
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() {
impl PerformCrud for GetPosts {
type Response = GetPostsResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<GetPostsResponse, LemmyError> {
let data: &GetPosts = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let person_id = local_user_view.to_owned().map(|l| l.person.id);
.list()
})
.await?
- .map_err(|e| ApiError::err("couldnt_get_posts", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_get_posts"))?;
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() {
use lemmy_utils::{
request::fetch_site_data,
utils::{check_slurs_opt, clean_url_params, is_valid_post_title},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for EditPost {
type Response = PostResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
if let Some(name) = &data.name {
if !is_valid_post_title(name) {
- return Err(ApiError::err_plain("invalid_post_title").into());
+ return Err(LemmyError::from_message("invalid_post_title"));
}
}
// Verify that only the creator can edit
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
- return Err(ApiError::err_plain("no_post_edit_allowed").into());
+ return Err(LemmyError::from_message("no_post_edit_allowed"));
}
// Fetch post links and Pictrs cached image
"couldnt_update_post"
};
- return Err(ApiError::err(err_type, e).into());
+ return Err(LemmyError::from(e).with_message(err_type));
}
};
traits::Crud,
};
use lemmy_db_views::local_user_view::LocalUserView;
-use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
use lemmy_websocket::{
send::{send_email_to_user, send_pm_ws_message},
LemmyContext,
impl PerformCrud for CreatePrivateMessage {
type Response = PrivateMessageResponse;
+ #[tracing::instrument(skip(self, context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
{
Ok(private_message) => private_message,
Err(e) => {
- return Err(ApiError::err("couldnt_create_private_message", e).into());
+ return Err(LemmyError::from(e).with_message("couldnt_create_private_message"));
}
};
},
)
.await?
- .map_err(|e| ApiError::err("couldnt_create_private_message", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_create_private_message"))?;
CreateOrUpdatePrivateMessage::send(
updated_private_message.into(),
source::private_message::PrivateMessage,
traits::{Crud, DeleteableOrRemoveable},
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for DeletePrivateMessage {
type Response = PrivateMessageResponse;
+ #[tracing::instrument(skip(self, context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
})
.await??;
if local_user_view.person.id != orig_private_message.creator_id {
- return Err(ApiError::err_plain("no_private_message_edit_allowed").into());
+ return Err(LemmyError::from_message("no_private_message_edit_allowed"));
}
// Doing the update
PrivateMessage::update_deleted(conn, private_message_id, deleted)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_private_message", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_private_message"))?;
// Send the apub update
if data.deleted {
impl PerformCrud for GetPrivateMessages {
type Response = PrivateMessagesResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<PrivateMessagesResponse, LemmyError> {
let data: &GetPrivateMessages = self;
let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt(data.auth.as_ref(), context.pool(), context.secret()).await?;
let person_id = local_user_view.person.id;
let page = data.page;
CreateOrUpdateType,
};
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
-use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditPrivateMessage {
type Response = PrivateMessageResponse;
+ #[tracing::instrument(skip(self, context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
})
.await??;
if local_user_view.person.id != orig_private_message.creator_id {
- return Err(ApiError::err_plain("no_private_message_edit_allowed").into());
+ return Err(LemmyError::from_message("no_private_message_edit_allowed"));
}
// Doing the update
PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed)
})
.await?
- .map_err(|e| ApiError::err("couldnt_update_private_message", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_private_message"))?;
// Send the apub update
CreateOrUpdatePrivateMessage::send(
use lemmy_db_views::site_view::SiteView;
use lemmy_utils::{
utils::{check_slurs, check_slurs_opt},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for CreateSite {
type Response = SiteResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let read_site = Site::read_simple;
if blocking(context.pool(), read_site).await?.is_ok() {
- return Err(ApiError::err_plain("site_already_exists").into());
+ return Err(LemmyError::from_message("site_already_exists"));
};
let local_user_view =
let create_site = move |conn: &'_ _| Site::create(conn, &site_form);
if blocking(context.pool(), create_site).await?.is_err() {
- return Err(ApiError::err_plain("site_already_exists").into());
+ return Err(LemmyError::from_message("site_already_exists"));
}
let site_view = blocking(context.pool(), SiteView::read).await??;
person_block_view::PersonBlockView,
person_view::PersonViewSafe,
};
-use lemmy_utils::{version, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{version, ConnectionId, LemmyError};
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
use tracing::info;
impl PerformCrud for GetSite {
type Response = GetSiteResponse;
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
if let Some(setup) = context.settings().setup.as_ref() {
let register = Register {
username: setup.admin_username.to_owned(),
- email: setup.admin_email.to_owned(),
- password: setup.admin_password.to_owned(),
- password_verify: setup.admin_password.to_owned(),
+ email: setup.admin_email.clone().map(|s| s.into()),
+ password: setup.admin_password.clone().into(),
+ password_verify: setup.admin_password.clone().into(),
show_nsfw: true,
captcha_uuid: None,
captcha_answer: None,
.unwrap_or(1);
// Build the local user
- let my_user = if let Some(local_user_view) =
- get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool(), context.secret())
- .await?
+ let my_user = if let Some(local_user_view) = get_local_user_settings_view_from_jwt_opt(
+ data.auth.as_ref(),
+ context.pool(),
+ context.secret(),
+ )
+ .await?
{
let person_id = local_user_view.person.id;
let follows = blocking(context.pool(), move |conn| {
CommunityFollowerView::for_person(conn, person_id)
})
.await?
- .map_err(|e| ApiError::err("system_err_login", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("system_err_login"))?;
let person_id = local_user_view.person.id;
let community_blocks = blocking(context.pool(), move |conn| {
CommunityBlockView::for_person(conn, person_id)
})
.await?
- .map_err(|e| ApiError::err("system_err_login", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("system_err_login"))?;
let person_id = local_user_view.person.id;
let person_blocks = blocking(context.pool(), move |conn| {
PersonBlockView::for_person(conn, person_id)
})
.await?
- .map_err(|e| ApiError::err("system_err_login", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("system_err_login"))?;
let moderates = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_person(conn, person_id)
})
.await?
- .map_err(|e| ApiError::err("system_err_login", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("system_err_login"))?;
Some(MyUserInfo {
local_user_view,
traits::Crud,
};
use lemmy_db_views::site_view::SiteView;
-use lemmy_utils::{utils::check_slurs_opt, ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{utils::check_slurs_opt, ConnectionId, LemmyError};
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditSite {
type Response = SiteResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
blocking(context.pool(), update_site)
.await?
- .map_err(|e| ApiError::err("couldnt_update_site", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_site"))?;
let site_view = blocking(context.pool(), SiteView::read).await??;
apub::generate_actor_keypair,
claims::Claims,
utils::{check_slurs, is_valid_actor_name},
- ApiError,
ConnectionId,
LemmyError,
};
impl PerformCrud for Register {
type Response = LoginResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
// Make sure site has open registration
if let Ok(site) = blocking(context.pool(), Site::read_simple).await? {
if !site.open_registration {
- return Err(ApiError::err_plain("registration_closed").into());
+ return Err(LemmyError::from_message("registration_closed"));
}
}
// Make sure passwords match
if data.password != data.password_verify {
- return Err(ApiError::err_plain("passwords_dont_match").into());
+ return Err(LemmyError::from_message("passwords_dont_match"));
}
// Check if there are admins. False if admins exist
})
.await?;
if !check {
- return Err(ApiError::err_plain("captcha_incorrect").into());
+ return Err(LemmyError::from_message("captcha_incorrect"));
}
}
let actor_keypair = generate_actor_keypair()?;
if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
- return Err(ApiError::err_plain("invalid_username").into());
+ return Err(LemmyError::from_message("invalid_username"));
}
let actor_id = generate_local_apub_endpoint(
EndpointType::Person,
Person::create(conn, &person_form)
})
.await?
- .map_err(|e| ApiError::err("user_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("user_already_exists"))?;
// Create the local user
// TODO some of these could probably use the DB defaults
let local_user_form = LocalUserForm {
person_id: inserted_person.id,
- email: Some(data.email.to_owned()),
- password_encrypted: data.password.to_owned(),
+ email: Some(data.email.as_deref().map(|s| s.to_owned())),
+ password_encrypted: data.password.to_string(),
show_nsfw: Some(data.show_nsfw),
show_bot_accounts: Some(true),
theme: Some("browser".into()),
})
.await??;
- return Err(ApiError::err(err_type, e).into());
+ return Err(LemmyError::from(e).with_message(err_type));
}
};
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
blocking(context.pool(), follow)
.await?
- .map_err(|e| ApiError::err("community_follower_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_follower_already_exists"))?;
// If its an admin, add them as a mod and follower to main
if no_admins {
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
blocking(context.pool(), join)
.await?
- .map_err(|e| ApiError::err("community_moderator_already_exists", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("community_moderator_already_exists"))?;
}
// Return the jwt
inserted_local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
- )?,
+ )?
+ .into(),
})
}
}
use bcrypt::verify;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt, person::*};
use lemmy_db_schema::source::{comment::Comment, person::Person, post::Post};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteAccount {
type Response = LoginResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<LoginResponse, LemmyError> {
let data: &DeleteAccount = self;
let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt(data.auth.as_ref(), context.pool(), context.secret()).await?;
// Verify the password
let valid: bool = verify(
)
.unwrap_or(false);
if !valid {
- return Err(ApiError::err_plain("password_incorrect").into());
+ return Err(LemmyError::from_message("password_incorrect"));
}
// Comments
let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, person_id);
blocking(context.pool(), permadelete)
.await?
- .map_err(|e| ApiError::err("couldnt_update_comment", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_comment"))?;
// Posts
let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, person_id);
blocking(context.pool(), permadelete)
.await?
- .map_err(|e| ApiError::err("couldnt_update_post", e))?;
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_post"))?;
blocking(context.pool(), move |conn| {
Person::delete_account(conn, person_id)
.await??;
Ok(LoginResponse {
- jwt: data.auth.to_owned(),
+ jwt: data.auth.clone(),
})
}
}
community_moderator_view::CommunityModeratorView,
person_view::PersonViewSafe,
};
-use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
impl PerformCrud for GetPersonDetails {
type Response = GetPersonDetailsResponse;
+ #[tracing::instrument(skip(self, context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<GetPersonDetailsResponse, LemmyError> {
let data: &GetPersonDetails = self;
let local_user_view =
- get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
let show_bot_accounts = local_user_view
.dereference(context, &mut 0)
.await;
person
- .map_err(|e| ApiError::err("couldnt_find_that_username_or_email", e))?
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?
.id
}
};
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
impl CreateOrUpdateComment {
+ #[tracing::instrument(skip(comment, actor, kind, context))]
pub async fn send(
comment: ApubComment,
actor: &ApubPerson,
impl ActivityHandler for CreateOrUpdateComment {
type DataType = LemmyContext;
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for CreateOrUpdateComment {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
pub mod create_or_update;
+#[tracing::instrument(skip_all)]
async fn get_notif_recipients(
actor: &ObjectId<ApubPerson>,
comment: &Comment,
use lemmy_websocket::LemmyContext;
impl AddMod {
+ #[tracing::instrument(skip_all)]
pub async fn send(
community: &ApubCommunity,
added_mod: &ApubPerson,
impl ActivityHandler for AddMod {
type DataType = LemmyContext;
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for AddMod {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
})
}
+ #[tracing::instrument(skip_all)]
pub async fn send(
object: AnnouncableActivities,
community: &ApubCommunity,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for AnnounceActivity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
})
}
+ #[tracing::instrument(skip_all)]
pub async fn send(
community: &ApubCommunity,
target: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for BlockUserFromCommunity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for BlockUserFromCommunity {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
pub mod undo_block_user;
pub mod update;
+#[tracing::instrument(skip_all)]
pub(crate) async fn send_activity_in_community<T: ActorType>(
activity: AnnouncableActivities,
activity_id: &Url,
Ok(())
}
+#[tracing::instrument(skip_all)]
async fn get_community_from_moderators_url(
moderators: &Url,
context: &LemmyContext,
use lemmy_websocket::LemmyContext;
impl RemoveMod {
+ #[tracing::instrument(skip_all)]
pub async fn send(
community: &ApubCommunity,
removed_mod: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for RemoveMod {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for RemoveMod {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
impl Report {
+ #[tracing::instrument(skip_all)]
pub async fn send(
object_id: ObjectId<PostOrComment>,
actor: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Report {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
use lemmy_websocket::LemmyContext;
impl UndoBlockUserFromCommunity {
+ #[tracing::instrument(skip_all)]
pub async fn send(
community: &ApubCommunity,
target: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoBlockUserFromCommunity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for UndoBlockUserFromCommunity {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
impl UpdateCommunity {
+ #[tracing::instrument(skip_all)]
pub async fn send(
community: ApubCommunity,
actor: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UpdateCommunity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for UpdateCommunity {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
protocol::activities::deletion::delete::Delete,
};
use activitystreams_kinds::{activity::DeleteType, public};
-use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Delete {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
unparsed: Default::default(),
})
}
+
+ #[tracing::instrument(skip_all)]
pub(in crate::activities::deletion) async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
}
}
+#[tracing::instrument(skip_all)]
pub(in crate::activities) async fn receive_remove_action(
actor: &ObjectId<ApubPerson>,
object: &Url,
match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => {
if community.local {
- return Err(anyhow!("Only local admin can remove community").into());
+ return Err(LemmyError::from_message(
+ "Only local admin can remove community",
+ ));
}
let form = ModRemoveCommunityForm {
mod_person_id: actor.id,
#[async_trait::async_trait(?Send)]
impl GetCommunity for Delete {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
pub mod delete;
pub mod undo_delete;
+#[tracing::instrument(skip_all)]
pub async fn send_apub_delete(
actor: &ApubPerson,
community: &ApubCommunity,
// TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its
// ugly
+#[tracing::instrument(skip_all)]
pub async fn send_apub_remove(
actor: &ApubPerson,
community: &ApubCommunity,
}
impl DeletableObjects {
+ #[tracing::instrument(skip_all)]
pub(crate) async fn read_from_db(
ap_id: &Url,
context: &LemmyContext,
}
}
+#[tracing::instrument(skip_all)]
pub(in crate::activities) async fn verify_delete_activity(
object: &Url,
actor: &ObjectId<ApubPerson>,
Ok(())
}
+#[tracing::instrument(skip_all)]
async fn verify_delete_activity_post_or_comment(
actor: &ObjectId<ApubPerson>,
object_id: &Url,
/// Write deletion or restoring of an object to the database, and send websocket message.
/// TODO: we should do something similar for receive_remove_action(), but its much more complicated
/// because of the mod log
+#[tracing::instrument(skip_all)]
async fn receive_delete_action(
object: &Url,
actor: &ObjectId<ApubPerson>,
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
};
use activitystreams_kinds::{activity::UndoType, public};
-use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoDelete {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
}
impl UndoDelete {
+ #[tracing::instrument(skip_all)]
pub(in crate::activities::deletion) async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
send_activity_in_community(activity, &id, actor, community, vec![], context).await
}
+ #[tracing::instrument(skip_all)]
pub(in crate::activities) async fn receive_undo_remove_action(
object: &Url,
context: &LemmyContext,
match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => {
if community.local {
- return Err(anyhow!("Only local admin can restore community").into());
+ return Err(LemmyError::from_message(
+ "Only local admin can restore community",
+ ));
}
let deleted_community = blocking(context.pool(), move |conn| {
Community::update_removed(conn, community.id, false)
#[async_trait::async_trait(?Send)]
impl GetCommunity for UndoDelete {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
use lemmy_websocket::LemmyContext;
impl AcceptFollowCommunity {
+ #[tracing::instrument(skip_all)]
pub async fn send(
follow: FollowCommunity,
context: &LemmyContext,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for AcceptFollowCommunity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
unparsed: Default::default(),
})
}
+
+ #[tracing::instrument(skip_all)]
pub async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for FollowCommunity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
use lemmy_websocket::LemmyContext;
impl UndoFollowCommunity {
+ #[tracing::instrument(skip_all)]
pub async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoFollowCommunity {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams_kinds::public;
-use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
activity_queue::send_activity,
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
/// doesn't have a site ban.
+#[tracing::instrument(skip_all)]
async fn verify_person(
person_id: &ObjectId<ApubPerson>,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let person = person_id.dereference(context, request_counter).await?;
if person.banned {
- return Err(anyhow!("Person {} is banned", person_id).into());
+ let error = LemmyError::from(anyhow::anyhow!("Person {} is banned", person_id));
+ return Err(error.with_message("banned"));
}
Ok(())
}
/// Fetches the person and community to verify their type, then checks if person is banned from site
/// or community.
+#[tracing::instrument(skip_all)]
pub(crate) async fn verify_person_in_community(
person_id: &ObjectId<ApubPerson>,
community: &ApubCommunity,
) -> Result<(), LemmyError> {
let person = person_id.dereference(context, request_counter).await?;
if person.banned {
- return Err(anyhow!("Person is banned from site").into());
+ return Err(LemmyError::from_message("Person is banned from site"));
}
let person_id = person.id;
let community_id = community.id;
let is_banned =
move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
if blocking(context.pool(), is_banned).await? {
- return Err(anyhow!("Person is banned from community").into());
+ return Err(LemmyError::from_message("Person is banned from community"));
}
Ok(())
/// Verify that the actor is a community mod. This check is only run if the community is local,
/// because in case of remote communities, admins can also perform mod actions. As admin status
/// is not federated, we cant verify their actions remotely.
+#[tracing::instrument(skip_all)]
pub(crate) async fn verify_mod_action(
actor_id: &ObjectId<ApubPerson>,
community: &ApubCommunity,
})
.await?;
if !is_mod_or_admin {
- return Err(anyhow!("Not a mod").into());
+ return Err(LemmyError::from_message("Not a mod"));
}
}
Ok(())
community: &ApubCommunity,
) -> Result<(), LemmyError> {
if target != &generate_moderators_url(&community.actor_id)?.into() {
- return Err(anyhow!("Unkown target url").into());
+ return Err(LemmyError::from_message("Unkown target url"));
}
Ok(())
}
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> {
if ![to, cc].iter().any(|set| set.contains(&public())) {
- return Err(anyhow!("Object is not public").into());
+ return Err(LemmyError::from_message("Object is not public"));
}
Ok(())
}
pub(crate) fn check_community_deleted_or_removed(community: &Community) -> Result<(), LemmyError> {
if community.deleted || community.removed {
- Err(anyhow!("New post or comment cannot be created in deleted or removed community").into())
+ Err(LemmyError::from_message(
+ "New post or comment cannot be created in deleted or removed community",
+ ))
} else {
Ok(())
}
Url::parse(&id)
}
+#[tracing::instrument(skip_all)]
async fn send_lemmy_activity<T: Serialize>(
context: &LemmyContext,
activity: &T,
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
};
use activitystreams_kinds::public;
-use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
unparsed: Default::default(),
})
}
+
+ #[tracing::instrument(skip_all)]
pub async fn send(
post: ApubPost,
actor: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for CreateOrUpdatePost {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
let is_stickied_or_locked =
self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
if community.local && is_stickied_or_locked {
- return Err(anyhow!("New post cannot be stickied or locked").into());
+ return Err(LemmyError::from_message(
+ "New post cannot be stickied or locked",
+ ));
}
}
CreateOrUpdateType::Update => {
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for CreateOrUpdatePost {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
impl CreateOrUpdatePrivateMessage {
+ #[tracing::instrument(skip_all)]
pub async fn send(
private_message: ApubPrivateMessage,
actor: &ApubPerson,
send_lemmy_activity(context, &create_or_update, &id, actor, inbox, true).await
}
}
+
#[async_trait::async_trait(?Send)]
impl ActivityHandler for CreateOrUpdatePrivateMessage {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
unparsed: Default::default(),
})
}
+
+ #[tracing::instrument(skip_all)]
pub async fn send(
actor: &ApubPerson,
pm: &ApubPrivateMessage,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for DeletePrivateMessage {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
impl UndoDeletePrivateMessage {
+ #[tracing::instrument(skip_all)]
pub async fn send(
actor: &ApubPerson,
pm: &ApubPrivateMessage,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoDeletePrivateMessage {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
pub mod undo_vote;
pub mod vote;
+#[tracing::instrument(skip_all)]
async fn vote_comment(
vote_type: &VoteType,
actor: ApubPerson,
Ok(())
}
+#[tracing::instrument(skip_all)]
async fn vote_post(
vote_type: &VoteType,
actor: ApubPerson,
Ok(())
}
+#[tracing::instrument(skip_all)]
async fn undo_vote_comment(
actor: ApubPerson,
comment: &ApubComment,
Ok(())
}
+#[tracing::instrument(skip_all)]
async fn undo_vote_post(
actor: ApubPerson,
post: &ApubPost,
use lemmy_websocket::LemmyContext;
impl UndoVote {
+ #[tracing::instrument(skip_all)]
pub async fn send(
object: &PostOrComment,
actor: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoVote {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for UndoVote {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
})
}
+ #[tracing::instrument(skip_all)]
pub async fn send(
object: &PostOrComment,
actor: &ApubPerson,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Vote {
type DataType = LemmyContext;
+
+ #[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
#[async_trait::async_trait(?Send)]
impl GetCommunity for Vote {
+ #[tracing::instrument(skip_all)]
async fn get_community(
&self,
context: &LemmyContext,
#[async_trait::async_trait(?Send)]
impl GetCommunity for AnnouncableActivities {
+ #[tracing::instrument(skip(self, context))]
async fn get_community(
&self,
context: &LemmyContext,
None
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
_object_id: Url,
data: &Self::DataType,
}
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
let ordered_items = self
.0
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
group_moderators: &GroupModerators,
expected_domain: &Url,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
apub: Self::ApubType,
data: &Self::DataType,
None
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
_object_id: Url,
data: &Self::DataType,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
let mut ordered_items = vec![];
for post in self.0 {
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
group_outbox: &GroupOutbox,
expected_domain: &Url,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
apub: Self::ApubType,
data: &Self::DataType,
}
// TODO: this can probably be implemented using a single sql query
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
data: &Self::DataType,
})
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self {
PostOrComment::Post(p) => p.delete(data).await,
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
}
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
apub: PageOrNote,
context: &LemmyContext,
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
EndpointType,
};
-use anyhow::anyhow;
use chrono::NaiveDateTime;
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
use lemmy_utils::LemmyError;
/// http://lemmy_beta:8551/u/lemmy_alpha, or @lemmy_beta@lemmy_beta:8551
/// http://lemmy_gamma:8561/post/3
/// http://lemmy_delta:8571/comment/2
+#[tracing::instrument(skip_all)]
pub async fn search_by_apub_id(
query: &str,
context: &LemmyContext,
.await?,
))
}
- _ => Err(anyhow!("invalid query").into()),
+ _ => Err(LemmyError::from_message("invalid query")),
}
}
}
// a single query.
// we could skip this and always return an error, but then it would always fetch objects
// over http, and not be able to mark objects as deleted that were deleted by remote server.
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
Ok(None)
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self {
SearchableObjects::Person(p) => p.delete(data).await,
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
}
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
apub: Self::ApubType,
context: &LemmyContext,
})
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
data: &Self::DataType,
})
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self {
UserOrCommunity::User(p) => p.delete(data).await,
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
}
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
apub: Self::ApubType,
data: &Self::DataType,
use crate::{generate_local_apub_endpoint, EndpointType};
-use anyhow::anyhow;
use itertools::Itertools;
use lemmy_apub_lib::{
object_id::ObjectId,
///
/// TODO: later provide a method in ApubObject to generate the endpoint, so that we dont have to
/// pass in EndpointType
+#[tracing::instrument(skip_all)]
pub async fn webfinger_resolve<Kind>(
identifier: &str,
endpoint_type: EndpointType,
/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
/// using webfinger.
+#[tracing::instrument(skip_all)]
pub(crate) async fn webfinger_resolve_actor<Kind>(
identifier: &str,
context: &LemmyContext,
return object.map(|o| o.actor_id().into());
}
}
- Err(anyhow!("Failed to resolve actor for {}", identifier).into())
+ let error = LemmyError::from(anyhow::anyhow!(
+ "Failed to resolve actor for {}",
+ identifier
+ ));
+ Err(error.with_message("failed_to_resolve"))
}
http::{create_apub_response, create_apub_tombstone_response},
objects::comment::ApubComment,
};
-use actix_web::{body::Body, web, web::Path, HttpResponse};
+use actix_web::{body::AnyBody, web, web::Path, HttpResponse};
use diesel::result::Error::NotFound;
use lemmy_api_common::blocking;
use lemmy_apub_lib::traits::ApubObject;
}
/// Return the ActivityPub json representation of a local comment over HTTP.
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_comment(
info: Path<CommentQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let id = CommentId(info.comment_id.parse::<i32>()?);
let comment: ApubComment = blocking(context.pool(), move |conn| Comment::read(conn, id))
.await??
collections::group_followers::GroupFollowers,
},
};
-use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
+use actix_web::{body::AnyBody, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking;
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
use lemmy_db_schema::source::community::Community;
}
/// Return the ActivityPub json representation of a local community over HTTP.
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_community_http(
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name)
})
}
/// Handler for all incoming receive to community inboxes.
+#[tracing::instrument(skip_all)]
pub async fn community_inbox(
request: HttpRequest,
payload: Payload,
pub(crate) async fn get_apub_community_followers(
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let community = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name)
})
pub(crate) async fn get_apub_community_outbox(
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let community = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name)
})
Ok(create_apub_response(&outbox.into_apub(&outbox_data).await?))
}
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_community_moderators(
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name)
})
insert_activity,
};
use actix_web::{
- body::Body,
+ body::AnyBody,
web,
web::{Bytes, BytesMut, Payload},
HttpRequest,
HttpResponse,
};
-use anyhow::{anyhow, Context};
+use anyhow::Context;
use futures::StreamExt;
use http::StatusCode;
use lemmy_api_common::blocking;
mod post;
pub mod routes;
+#[tracing::instrument(skip_all)]
pub async fn shared_inbox(
request: HttpRequest,
payload: Payload,
}
// TODO: move most of this code to library
+#[tracing::instrument(skip_all)]
async fn receive_activity<'a, T>(
request: HttpRequest,
activity: T,
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
/// headers.
-fn create_apub_response<T>(data: &T) -> HttpResponse<Body>
+fn create_apub_response<T>(data: &T) -> HttpResponse<AnyBody>
where
T: Serialize,
{
.json(WithContext::new(data))
}
-fn create_json_apub_response(data: serde_json::Value) -> HttpResponse<Body> {
+fn create_json_apub_response(data: serde_json::Value) -> HttpResponse<AnyBody> {
HttpResponse::Ok()
.content_type(APUB_JSON_CONTENT_TYPE)
.json(data)
}
-fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<Body>
+fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<AnyBody>
where
T: Serialize,
{
}
/// Return the ActivityPub json representation of a local activity over HTTP.
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_activity(
info: web::Path<ActivityQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let settings = context.settings();
let activity_id = Url::parse(&format!(
"{}/activities/{}/{}",
let activity_domain = id.domain().context(location_info!())?;
if activity_domain == hostname {
- return Err(
- anyhow!(
- "Error: received activity which was sent by local instance: {:?}",
- id
- )
- .into(),
- );
+ let error = LemmyError::from(anyhow::anyhow!(
+ "Error: received activity which was sent by local instance: {:?}",
+ id
+ ));
+ return Err(error.with_message("received_local_activity"));
}
Ok(())
}
objects::person::ApubPerson,
protocol::collections::person_outbox::PersonOutbox,
};
-use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
+use actix_web::{body::AnyBody, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking;
use lemmy_apub_lib::traits::ApubObject;
use lemmy_db_schema::source::person::Person;
}
/// Return the ActivityPub json representation of a local person over HTTP.
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_person_http(
info: web::Path<PersonQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let user_name = info.into_inner().user_name;
// TODO: this needs to be able to read deleted persons, so that it can send tombstones
let person: ApubPerson = blocking(context.pool(), move |conn| {
}
}
+#[tracing::instrument(skip_all)]
pub async fn person_inbox(
request: HttpRequest,
payload: Payload,
receive_activity(request, activity, activity_data, context).await
}
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_person_outbox(
info: web::Path<PersonQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let person = blocking(context.pool(), move |conn| {
Person::find_by_name(conn, &info.user_name)
})
http::{create_apub_response, create_apub_tombstone_response},
objects::post::ApubPost,
};
-use actix_web::{body::Body, web, HttpResponse};
+use actix_web::{body::AnyBody, web, HttpResponse};
use diesel::result::Error::NotFound;
use lemmy_api_common::blocking;
use lemmy_apub_lib::traits::ApubObject;
}
/// Return the ActivityPub json representation of a local post over HTTP.
+#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_post(
info: web::Path<PostQuery>,
context: web::Data<LemmyContext>,
-) -> Result<HttpResponse<Body>, LemmyError> {
+) -> Result<HttpResponse<AnyBody>, LemmyError> {
let id = PostId(info.post_id.parse::<i32>()?);
let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, id))
.await??
use crate::fetcher::post_or_comment::PostOrComment;
-use anyhow::{anyhow, Context};
+use anyhow::Context;
use lemmy_api_common::blocking;
use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool};
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
///
/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
/// post/comment in a local community.
+#[tracing::instrument(skip(settings))]
pub(crate) fn check_is_apub_id_valid(
apub_id: &Url,
use_strict_allowlist: bool,
return if domain == local_instance {
Ok(())
} else {
- Err(
- anyhow!(
- "Trying to connect with {}, but federation is disabled",
- domain
- )
- .into(),
- )
+ let error = LemmyError::from(anyhow::anyhow!(
+ "Trying to connect with {}, but federation is disabled",
+ domain
+ ));
+ Err(error.with_message("federation_disabled"))
};
}
let host = apub_id.host_str().context(location_info!())?;
let host_as_ip = host.parse::<IpAddr>();
if host == "localhost" || host_as_ip.is_ok() {
- return Err(anyhow!("invalid hostname {}: {}", host, apub_id).into());
+ let error = LemmyError::from(anyhow::anyhow!("invalid hostname {}: {}", host, apub_id));
+ return Err(error.with_message("invalid_hostname"));
}
if apub_id.scheme() != settings.get_protocol_string() {
- return Err(anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id).into());
+ let error = LemmyError::from(anyhow::anyhow!(
+ "invalid apub id scheme {}: {}",
+ apub_id.scheme(),
+ apub_id
+ ));
+ return Err(error.with_message("invalid_scheme"));
}
// TODO: might be good to put the part above in one method, and below in another
// -> no that doesnt make sense, we still need the code below for blocklist and strict allowlist
if let Some(blocked) = settings.to_owned().federation.blocked_instances {
if blocked.contains(&domain) {
- return Err(anyhow!("{} is in federation blocklist", domain).into());
+ let error = LemmyError::from(anyhow::anyhow!("{} is in federation blocklist", domain));
+ return Err(error.with_message("federation_blocked"));
}
}
allowed.push(local_instance);
if !allowed.contains(&domain) {
- return Err(anyhow!("{} not in federation allowlist", domain).into());
+ let error = LemmyError::from(anyhow::anyhow!("{} not in federation allowlist", domain));
+ return Err(error.with_message("federation_not_allowed"));
}
}
}
/// Store a sent or received activity in the database, for logging purposes. These records are not
/// persistent.
+#[tracing::instrument(skip(pool))]
async fn insert_activity(
ap_id: &Url,
activity: serde_json::Value,
/// This takes a comment, and builds a list of to_addresses, inboxes,
/// and mention tags, so they know where to be sent to.
/// Addresses are the persons / addresses that go in the cc field.
+#[tracing::instrument(skip(comment, community_id, context))]
pub async fn collect_non_local_mentions(
comment: &ApubComment,
community_id: ObjectId<ApubCommunity>,
/// Returns the apub ID of the person this comment is responding to. Meaning, in case this is a
/// top-level comment, the creator of the post, otherwise the creator of the parent comment.
+#[tracing::instrument(skip(pool, comment))]
async fn get_comment_parent_creator(
pool: &DbPool,
comment: &Comment,
PostOrComment,
};
use activitystreams_kinds::{object::NoteType, public};
-use anyhow::anyhow;
use chrono::NaiveDateTime;
use html2md::parse_html;
use lemmy_api_common::blocking;
None
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
)
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
if !self.deleted {
blocking(context.pool(), move |conn| {
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, context: &LemmyContext) -> Result<Note, LemmyError> {
let creator_id = self.creator_id;
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
Ok(Tombstone::new(self.ap_id.clone().into()))
}
+ #[tracing::instrument(skip_all)]
async fn verify(
note: &Note,
expected_domain: &Url,
)
.await?;
if post.locked {
- return Err(anyhow!("Post is locked").into());
+ return Err(LemmyError::from_message("Post is locked"));
}
Ok(())
}
/// Converts a `Note` to `Comment`.
///
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
+ #[tracing::instrument(skip_all)]
async fn from_apub(
note: Note,
context: &LemmyContext,
Some(self.last_refreshed_at)
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
)
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| {
Community::update_deleted(conn, self.id, true)
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, _context: &LemmyContext) -> Result<Group, LemmyError> {
let source = self.description.clone().map(|bio| Source {
content: bio,
Ok(Tombstone::new(self.actor_id()))
}
+ #[tracing::instrument(skip_all)]
async fn verify(
group: &Group,
expected_domain: &Url,
}
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
+ #[tracing::instrument(skip_all)]
async fn from_apub(
group: Group,
context: &LemmyContext,
impl ApubCommunity {
/// For a given community, returns the inboxes of all followers.
+ #[tracing::instrument(skip_all)]
pub(crate) async fn get_follower_inboxes(
&self,
context: &LemmyContext,
Some(self.last_refreshed_at)
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
)
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| {
DbPerson::update_deleted(conn, self.id, true)
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, _pool: &LemmyContext) -> Result<Person, LemmyError> {
let kind = if self.bot_account {
UserTypes::Service
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
person: &Person,
expected_domain: &Url,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
person: Person,
context: &LemmyContext,
None
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
)
}
+ #[tracing::instrument(skip_all)]
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
if !self.deleted {
blocking(context.pool(), move |conn| {
}
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, context: &LemmyContext) -> Result<Page, LemmyError> {
let creator_id = self.creator_id;
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
Ok(Tombstone::new(self.ap_id.clone().into()))
}
+ #[tracing::instrument(skip_all)]
async fn verify(
page: &Page,
expected_domain: &Url,
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
page: Page,
context: &LemmyContext,
objects::chat_message::{ChatMessage, ChatMessageType},
Source,
};
-use anyhow::anyhow;
use chrono::NaiveDateTime;
use html2md::parse_html;
use lemmy_api_common::blocking;
None
}
+ #[tracing::instrument(skip_all)]
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn into_apub(self, context: &LemmyContext) -> Result<ChatMessage, LemmyError> {
let creator_id = self.creator_id;
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
unimplemented!()
}
+ #[tracing::instrument(skip_all)]
async fn verify(
note: &ChatMessage,
expected_domain: &Url,
.dereference(context, request_counter)
.await?;
if person.banned {
- return Err(anyhow!("Person is banned from site").into());
+ return Err(LemmyError::from_message("Person is banned from site"));
}
Ok(())
}
+ #[tracing::instrument(skip_all)]
async fn from_apub(
note: ChatMessage,
context: &LemmyContext,
objects::person::ApubPerson,
protocol::Unparsed,
};
-use anyhow::anyhow;
use lemmy_apub_lib::object_id::ObjectId;
use lemmy_utils::LemmyError;
use serde::{Deserialize, Serialize};
match value {
1 => Ok(VoteType::Like),
-1 => Ok(VoteType::Dislike),
- _ => Err(anyhow!("invalid vote value").into()),
+ _ => Err(LemmyError::from_message("invalid vote value")),
}
}
}
protocol::{ImageObject, Source, Unparsed},
};
use activitystreams_kinds::object::PageType;
-use anyhow::anyhow;
use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{
data::Data,
break Ok(c);
}
} else {
- return Err(anyhow!("No community found in cc").into());
+ return Err(LemmyError::from_message("No community found in cc"));
}
}
}
use crate::newtypes::DbUrl;
use chrono::NaiveDateTime;
use diesel::{Connection, PgConnection};
-use lemmy_utils::ApiError;
+use lemmy_utils::LemmyError;
use once_cell::sync::Lazy;
use regex::Regex;
use serde::{Deserialize, Serialize};
pub fn diesel_option_overwrite_to_url(
opt: &Option<String>,
-) -> Result<Option<Option<DbUrl>>, ApiError> {
+) -> Result<Option<Option<DbUrl>>, LemmyError> {
match opt.as_ref().map(|s| s.as_str()) {
// An empty string is an erase
Some("") => Ok(Some(None)),
Some(str_url) => match Url::parse(str_url) {
Ok(url) => Ok(Some(Some(url.into()))),
- Err(e) => Err(ApiError::err("invalid_url", e)),
+ Err(e) => Err(LemmyError::from(e).with_message("invalid_url")),
},
None => Ok(None),
}
url = { version = "2.2.2", features = ["serde"] }
strum = "0.21.0"
once_cell = "1.8.0"
+tracing = "0.1.29"
h
});
+#[tracing::instrument(skip_all)]
async fn get_all_feed(
info: web::Query<Params>,
context: web::Data<LemmyContext>,
Ok(get_feed_data(&context, ListingType::All, sort_type).await?)
}
+#[tracing::instrument(skip_all)]
async fn get_local_feed(
info: web::Query<Params>,
context: web::Data<LemmyContext>,
Ok(get_feed_data(&context, ListingType::Local, sort_type).await?)
}
+#[tracing::instrument(skip_all)]
async fn get_feed_data(
context: &LemmyContext,
listing_type: ListingType,
)
}
+#[tracing::instrument(skip_all)]
async fn get_feed(
req: HttpRequest,
info: web::Query<Params>,
SortType::from_str(&sort_query)
}
+#[tracing::instrument(skip_all)]
fn get_feed_user(
conn: &PgConnection,
sort_type: &SortType,
Ok(channel_builder)
}
+#[tracing::instrument(skip_all)]
fn get_feed_community(
conn: &PgConnection,
sort_type: &SortType,
Ok(channel_builder)
}
+#[tracing::instrument(skip_all)]
fn get_feed_front(
conn: &PgConnection,
jwt_secret: &str,
Ok(channel_builder)
}
+#[tracing::instrument(skip_all)]
fn get_feed_inbox(
conn: &PgConnection,
jwt_secret: &str,
Ok(channel_builder)
}
+#[tracing::instrument(skip_all)]
fn create_reply_and_mention_items(
replies: Vec<CommentView>,
mentions: Vec<PersonMentionView>,
Ok(reply_items)
}
+#[tracing::instrument(skip_all)]
fn build_item(
creator_name: &str,
published: &NaiveDateTime,
Ok(i.build().map_err(|e| anyhow!(e))?)
}
+#[tracing::instrument(skip_all)]
fn create_post_items(
posts: Vec<PostView>,
protocol_and_hostname: &str,
pub mod utils;
pub mod version;
+mod sensitive;
+
+pub use sensitive::Sensitive;
+
+use actix_web::HttpResponse;
use http::StatusCode;
use std::{fmt, fmt::Display};
-use thiserror::Error;
-use tracing::warn;
use tracing_error::SpanTrace;
pub type ConnectionId = usize;
};
}
-#[derive(Debug, Error)]
-#[error("{{\"error\":\"{message}\"}}")]
-pub struct ApiError {
- message: String,
+#[derive(serde::Serialize)]
+struct ApiError {
+ error: &'static str,
+}
+
+pub struct LemmyError {
+ pub message: Option<&'static str>,
+ pub inner: anyhow::Error,
+ pub context: SpanTrace,
}
-impl ApiError {
- pub fn err_plain(msg: &str) -> Self {
- ApiError {
- message: msg.to_string(),
+impl LemmyError {
+ pub fn from_message(message: &'static str) -> Self {
+ let inner = anyhow::anyhow!("{}", message);
+ LemmyError {
+ message: Some(message),
+ inner,
+ context: SpanTrace::capture(),
}
}
- pub fn err<E: Display>(msg: &str, original_error: E) -> Self {
- warn!("{}", original_error);
- ApiError {
- message: msg.to_string(),
+ pub fn with_message(self, message: &'static str) -> Self {
+ LemmyError {
+ message: Some(message),
+ ..self
}
}
-}
+ pub fn to_json(&self) -> Result<String, Self> {
+ let api_error = match self.message {
+ Some(error) => ApiError { error },
+ None => ApiError { error: "Unknown" },
+ };
-#[derive(Debug)]
-pub struct LemmyError {
- pub inner: anyhow::Error,
- pub context: SpanTrace,
+ Ok(serde_json::to_string(&api_error)?)
+ }
}
impl<T> From<T> for LemmyError
{
fn from(t: T) -> Self {
LemmyError {
+ message: None,
inner: t.into(),
context: SpanTrace::capture(),
}
}
}
+impl std::fmt::Debug for LemmyError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("LemmyError")
+ .field("message", &self.message)
+ .field("inner", &self.inner)
+ .field("context", &"SpanTrace")
+ .finish()
+ }
+}
+
impl Display for LemmyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- self.inner.fmt(f)?;
+ if let Some(message) = self.message {
+ write!(f, "{}: ", message)?;
+ }
+ writeln!(f, "{}", self.inner)?;
self.context.fmt(f)
}
}
fn status_code(&self) -> StatusCode {
match self.inner.downcast_ref::<diesel::result::Error>() {
Some(diesel::result::Error::NotFound) => StatusCode::NOT_FOUND,
- _ => StatusCode::INTERNAL_SERVER_ERROR,
+ _ => StatusCode::BAD_REQUEST,
+ }
+ }
+
+ fn error_response(&self) -> HttpResponse {
+ if let Some(message) = &self.message {
+ HttpResponse::build(self.status_code()).json(ApiError { error: message })
+ } else {
+ HttpResponse::build(self.status_code())
+ .content_type("text/plain")
+ .body(self.inner.to_string())
}
}
}
-use crate::{ApiError, IpAddr, LemmyError};
+use crate::{IpAddr, LemmyError};
use std::{collections::HashMap, time::SystemTime};
use strum::IntoEnumIterator;
use tracing::debug;
time_passed,
rate_limit.allowance
);
- Err(
- ApiError {
- message: format!(
- "Too many requests. type: {}, IP: {}, {} per {} seconds",
- type_.as_ref(),
- ip,
- rate,
- per
- ),
- }
- .into(),
- )
+ let error = LemmyError::from(anyhow::anyhow!(
+ "Too many requests. type: {}, IP: {}, {} per {} seconds",
+ type_.as_ref(),
+ ip,
+ rate,
+ per
+ ));
+ Err(error.with_message("too_many_requests"))
} else {
if !check_only {
rate_limit.allowance -= 1.0;
--- /dev/null
+use std::{
+ borrow::Borrow,
+ ops::{Deref, DerefMut},
+};
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize)]
+#[serde(transparent)]
+pub struct Sensitive<T>(T);
+
+impl<T> Sensitive<T> {
+ pub fn new(item: T) -> Self {
+ Sensitive(item)
+ }
+
+ pub fn into_inner(this: Self) -> T {
+ this.0
+ }
+}
+
+impl<T> std::fmt::Debug for Sensitive<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Sensitive").finish()
+ }
+}
+
+impl<T> AsRef<T> for Sensitive<T> {
+ fn as_ref(&self) -> &T {
+ &self.0
+ }
+}
+
+impl AsRef<str> for Sensitive<String> {
+ fn as_ref(&self) -> &str {
+ &self.0
+ }
+}
+
+impl AsRef<[u8]> for Sensitive<String> {
+ fn as_ref(&self) -> &[u8] {
+ self.0.as_ref()
+ }
+}
+
+impl AsRef<[u8]> for Sensitive<Vec<u8>> {
+ fn as_ref(&self) -> &[u8] {
+ self.0.as_ref()
+ }
+}
+
+impl<T> AsMut<T> for Sensitive<T> {
+ fn as_mut(&mut self) -> &mut T {
+ &mut self.0
+ }
+}
+
+impl AsMut<str> for Sensitive<String> {
+ fn as_mut(&mut self) -> &mut str {
+ &mut self.0
+ }
+}
+
+impl Deref for Sensitive<String> {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for Sensitive<String> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl<T> From<T> for Sensitive<T> {
+ fn from(t: T) -> Self {
+ Sensitive(t)
+ }
+}
+
+impl From<&str> for Sensitive<String> {
+ fn from(s: &str) -> Self {
+ Sensitive(s.into())
+ }
+}
+
+impl<T> Borrow<T> for Sensitive<T> {
+ fn borrow(&self) -> &T {
+ &self.0
+ }
+}
+
+impl Borrow<str> for Sensitive<String> {
+ fn borrow(&self) -> &str {
+ &self.0
+ }
+}
-use crate::{ApiError, IpAddr};
+use crate::{IpAddr, LemmyError};
use actix_web::dev::ConnectionInfo;
use chrono::{DateTime, FixedOffset, NaiveDateTime};
use itertools::Itertools;
}
}
-pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> Result<(), ApiError> {
+pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> Result<(), LemmyError> {
if let Err(slurs) = slur_check(text, slur_regex) {
- Err(ApiError::err_plain(&slurs_vec_to_str(slurs)))
+ let error = LemmyError::from(anyhow::anyhow!("{}", slurs_vec_to_str(slurs)));
+ Err(error.with_message("slurs"))
} else {
Ok(())
}
}
-pub fn check_slurs_opt(text: &Option<String>, slur_regex: &Option<Regex>) -> Result<(), ApiError> {
+pub fn check_slurs_opt(
+ text: &Option<String>,
+ slur_regex: &Option<Regex>,
+) -> Result<(), LemmyError> {
match text {
Some(t) => check_slurs(t, slur_regex),
None => Ok(()),
location_info,
rate_limit::RateLimit,
settings::structs::Settings,
- ApiError,
ConnectionId,
IpAddr,
LemmyError,
let data = &json["data"].to_string();
let op = &json["op"]
.as_str()
- .ok_or_else(|| ApiError::err_plain("missing op"))?;
+ .ok_or_else(|| LemmyError::from_message("missing op"))?;
if let Ok(user_operation_crud) = UserOperationCrud::from_str(op) {
let fut = (message_handler_crud)(context, msg.id, user_operation_crud.clone(), data);
}
Err(e) => {
error!("Error during message handling {}", e);
- Ok(e.to_string())
+ Ok(
+ e.to_json()
+ .unwrap_or_else(|_| String::from(r#"{"error":"failed to serialize json"}"#)),
+ )
}
}
})
}
ws::Message::Text(text) => {
let m = text.trim().to_owned();
- info!("Message received: {:?} from id: {}", &m, self.id);
self
.cs_addr
-use actix_web::{error::ErrorBadRequest, *};
+use actix_web::*;
use lemmy_api::Perform;
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
use lemmy_api_crud::PerformCrud;
let res = data
.perform(&context, None)
.await
- .map(|json| HttpResponse::Ok().json(json))
- .map_err(ErrorBadRequest)?;
+ .map(|json| HttpResponse::Ok().json(json))?;
Ok(res)
}
let res = data
.perform(&context, None)
.await
- .map(|json| HttpResponse::Ok().json(json))
- .map_err(ErrorBadRequest)?;
+ .map(|json| HttpResponse::Ok().json(json))?;
Ok(res)
}
#![recursion_limit = "512"]
pub mod api_routes;
pub mod code_migrations;
+pub mod root_span_builder;
pub mod scheduled_tasks;
use lemmy_utils::LemmyError;
use tracing::subscriber::set_global_default;
use tracing_error::ErrorLayer;
use tracing_log::LogTracer;
-use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, EnvFilter, Registry};
+use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
pub fn init_tracing() -> Result<(), LemmyError> {
LogTracer::init()?;
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
- let format_layer = tracing_subscriber::fmt::layer()
- .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
- .pretty();
+ let format_layer = tracing_subscriber::fmt::layer();
let subscriber = Registry::default()
.with(env_filter)
api_routes,
code_migrations::run_advanced_migrations,
init_tracing,
+ root_span_builder::QuieterRootSpanBuilder,
scheduled_tasks,
};
use lemmy_utils::{
);
let rate_limiter = rate_limiter.clone();
App::new()
- .wrap(TracingLogger::default())
+ .wrap(actix_web::middleware::Logger::default())
+ .wrap(TracingLogger::<QuieterRootSpanBuilder>::new())
.app_data(Data::new(context))
// The routes
.configure(|cfg| api_routes::config(cfg, &rate_limiter))
--- /dev/null
+use actix_web::{http::StatusCode, ResponseError};
+use tracing::Span;
+use tracing_actix_web::RootSpanBuilder;
+
+// Code in this module adapted from DefaultRootSpanBuilder
+// https://github.com/LukeMathWalker/tracing-actix-web/blob/main/src/root_span_builder.rs
+// and root_span!
+// https://github.com/LukeMathWalker/tracing-actix-web/blob/main/src/root_span_macro.rs
+
+pub struct QuieterRootSpanBuilder;
+
+impl RootSpanBuilder for QuieterRootSpanBuilder {
+ fn on_request_start(request: &actix_web::dev::ServiceRequest) -> Span {
+ let request_id = tracing_actix_web::root_span_macro::private::get_request_id(request);
+
+ tracing::info_span!(
+ "HTTP request",
+ http.method = %request.method(),
+ http.scheme = request.connection_info().scheme(),
+ http.host = %request.connection_info().host(),
+ http.target = %request.uri().path(),
+ http.status_code = tracing::field::Empty,
+ otel.kind = "server",
+ otel.status_code = tracing::field::Empty,
+ trace_id = tracing::field::Empty,
+ request_id = %request_id,
+ exception.message = tracing::field::Empty,
+ // Not proper OpenTelemetry, but their terminology is fairly exception-centric
+ exception.details = tracing::field::Empty,
+ )
+ }
+
+ fn on_request_end<B>(
+ span: tracing::Span,
+ outcome: &Result<actix_web::dev::ServiceResponse<B>, actix_web::Error>,
+ ) {
+ match &outcome {
+ Ok(response) => {
+ if let Some(error) = response.response().error() {
+ // use the status code already constructed for the outgoing HTTP response
+ handle_error(span, response.status(), error.as_response_error());
+ } else {
+ let code: i32 = response.response().status().as_u16().into();
+ span.record("http.status_code", &code);
+ span.record("otel.status_code", &"OK");
+ }
+ }
+ Err(error) => {
+ let response_error = error.as_response_error();
+ handle_error(span, response_error.status_code(), response_error);
+ }
+ };
+ }
+}
+
+fn handle_error(span: Span, status_code: StatusCode, response_error: &dyn ResponseError) {
+ let code: i32 = status_code.as_u16().into();
+
+ span.record("http.status_code", &code);
+
+ if status_code.is_client_error() {
+ span.record("otel.status_code", &"OK");
+ } else {
+ span.record("otel.status_code", &"ERROR");
+ }
+
+ // pre-formatting errors is a workaround for https://github.com/tokio-rs/tracing/issues/1565
+ let display_error = format!("{}", response_error);
+ let debug_error = format!("{:?}", response_error);
+
+ tracing::info_span!(
+ parent: None,
+ "Error encountered while processing the incoming HTTP request"
+ )
+ .in_scope(|| {
+ if status_code.is_client_error() {
+ tracing::warn!("{}\n{}", display_error, debug_error);
+ } else {
+ tracing::error!("{}\n{}", display_error, debug_error);
+ }
+ });
+
+ span.record("exception.message", &tracing::field::display(display_error));
+ span.record("exception.details", &tracing::field::display(debug_error));
+}