mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-16 09:24:00 +00:00
Merge branch 'remove_settings_and_secret_singletons_squashed'
This commit is contained in:
commit
329a282aac
103 changed files with 1096 additions and 473 deletions
|
@ -16,8 +16,6 @@
|
||||||
hostname: "{{ domain }}"
|
hostname: "{{ domain }}"
|
||||||
# the port where lemmy should listen for incoming requests
|
# the port where lemmy should listen for incoming requests
|
||||||
port: 8536
|
port: 8536
|
||||||
# json web token for authorization between server and client
|
|
||||||
jwt_secret: "{{ jwt_password }}"
|
|
||||||
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
||||||
tls_enabled: true
|
tls_enabled: true
|
||||||
# address where pictrs is available
|
# address where pictrs is available
|
||||||
|
|
|
@ -33,8 +33,6 @@
|
||||||
port: 8536
|
port: 8536
|
||||||
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
||||||
tls_enabled: true
|
tls_enabled: true
|
||||||
# json web token for authorization between server and client
|
|
||||||
jwt_secret: "changeme"
|
|
||||||
# address where pictrs is available
|
# address where pictrs is available
|
||||||
pictrs_url: "http://pictrs:8080"
|
pictrs_url: "http://pictrs:8080"
|
||||||
# maximum length of local community and user names
|
# maximum length of local community and user names
|
||||||
|
|
|
@ -32,7 +32,8 @@ impl Perform for MarkCommentAsRead {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &MarkCommentAsRead = self;
|
let data: &MarkCommentAsRead = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let orig_comment = blocking(context.pool(), move |conn| {
|
let orig_comment = blocking(context.pool(), move |conn| {
|
||||||
|
@ -88,7 +89,8 @@ impl Perform for SaveComment {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &SaveComment = self;
|
let data: &SaveComment = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let comment_saved_form = CommentSavedForm {
|
let comment_saved_form = CommentSavedForm {
|
||||||
comment_id: data.comment_id,
|
comment_id: data.comment_id,
|
||||||
|
@ -132,7 +134,8 @@ impl Perform for CreateCommentLike {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &CreateCommentLike = self;
|
let data: &CreateCommentLike = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let mut recipient_ids = Vec::<LocalUserId>::new();
|
let mut recipient_ids = Vec::<LocalUserId>::new();
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@ impl Perform for CreateCommentReport {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CreateCommentReportResponse, LemmyError> {
|
) -> Result<CreateCommentReportResponse, LemmyError> {
|
||||||
let data: &CreateCommentReport = self;
|
let data: &CreateCommentReport = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// check size of report and check for whitespace
|
// check size of report and check for whitespace
|
||||||
let reason = data.reason.trim();
|
let reason = data.reason.trim();
|
||||||
|
@ -96,7 +97,8 @@ impl Perform for ResolveCommentReport {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<ResolveCommentReportResponse, LemmyError> {
|
) -> Result<ResolveCommentReportResponse, LemmyError> {
|
||||||
let data: &ResolveCommentReport = self;
|
let data: &ResolveCommentReport = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let report_id = data.report_id;
|
let report_id = data.report_id;
|
||||||
let report = blocking(context.pool(), move |conn| {
|
let report = blocking(context.pool(), move |conn| {
|
||||||
|
@ -149,7 +151,8 @@ impl Perform for ListCommentReports {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<ListCommentReportsResponse, LemmyError> {
|
) -> Result<ListCommentReportsResponse, LemmyError> {
|
||||||
let data: &ListCommentReports = self;
|
let data: &ListCommentReports = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let community_id = data.community;
|
let community_id = data.community;
|
||||||
|
|
|
@ -53,7 +53,8 @@ impl Perform for FollowCommunity {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &FollowCommunity = self;
|
let data: &FollowCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let community = blocking(context.pool(), move |conn| {
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
@ -121,7 +122,8 @@ impl Perform for BlockCommunity {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<BlockCommunityResponse, LemmyError> {
|
) -> Result<BlockCommunityResponse, LemmyError> {
|
||||||
let data: &BlockCommunity = self;
|
let data: &BlockCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
@ -181,7 +183,8 @@ impl Perform for BanFromCommunity {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<BanFromCommunityResponse, LemmyError> {
|
) -> Result<BanFromCommunityResponse, LemmyError> {
|
||||||
let data: &BanFromCommunity = self;
|
let data: &BanFromCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let banned_person_id = data.person_id;
|
let banned_person_id = data.person_id;
|
||||||
|
@ -314,7 +317,8 @@ impl Perform for AddModToCommunity {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<AddModToCommunityResponse, LemmyError> {
|
) -> Result<AddModToCommunityResponse, LemmyError> {
|
||||||
let data: &AddModToCommunity = self;
|
let data: &AddModToCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
|
||||||
|
@ -397,7 +401,8 @@ impl Perform for TransferCommunity {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetCommunityResponse, LemmyError> {
|
) -> Result<GetCommunityResponse, LemmyError> {
|
||||||
let data: &TransferCommunity = self;
|
let data: &TransferCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let site_creator_id = blocking(context.pool(), move |conn| {
|
let site_creator_id = blocking(context.pool(), move |conn| {
|
||||||
Site::read(conn, 1).map(|s| s.creator_id)
|
Site::read(conn, 1).map(|s| s.creator_id)
|
||||||
|
|
|
@ -188,16 +188,23 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lemmy_api_common::check_validator_time;
|
use lemmy_api_common::check_validator_time;
|
||||||
use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
|
use lemmy_db_queries::{
|
||||||
|
establish_unpooled_connection,
|
||||||
|
source::{local_user::LocalUser_, secret::Secret_},
|
||||||
|
Crud,
|
||||||
|
};
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
local_user::{LocalUser, LocalUserForm},
|
local_user::{LocalUser, LocalUserForm},
|
||||||
person::{Person, PersonForm},
|
person::{Person, PersonForm},
|
||||||
|
secret::Secret,
|
||||||
};
|
};
|
||||||
use lemmy_utils::claims::Claims;
|
use lemmy_utils::{claims::Claims, settings::structs::Settings};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_should_not_validate_user_token_after_password_change() {
|
fn test_should_not_validate_user_token_after_password_change() {
|
||||||
let conn = establish_unpooled_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
let secret = Secret::init(&conn).unwrap();
|
||||||
|
let settings = Settings::init().unwrap();
|
||||||
|
|
||||||
let new_person = PersonForm {
|
let new_person = PersonForm {
|
||||||
name: "Gerry9812".into(),
|
name: "Gerry9812".into(),
|
||||||
|
@ -214,8 +221,13 @@ mod tests {
|
||||||
|
|
||||||
let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
|
let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
|
||||||
|
|
||||||
let jwt = Claims::jwt(inserted_local_user.id.0).unwrap();
|
let jwt = Claims::jwt(
|
||||||
let claims = Claims::decode(&jwt).unwrap().claims;
|
inserted_local_user.id.0,
|
||||||
|
&secret.jwt_secret,
|
||||||
|
&settings.hostname,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let claims = Claims::decode(&jwt, &secret.jwt_secret).unwrap().claims;
|
||||||
let check = check_validator_time(&inserted_local_user.validator_time, &claims);
|
let check = check_validator_time(&inserted_local_user.validator_time, &claims);
|
||||||
assert!(check.is_ok());
|
assert!(check.is_ok());
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ use lemmy_utils::{
|
||||||
claims::Claims,
|
claims::Claims,
|
||||||
email::send_email,
|
email::send_email,
|
||||||
location_info,
|
location_info,
|
||||||
settings::structs::Settings,
|
|
||||||
utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
|
utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
|
||||||
ApiError,
|
ApiError,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
|
@ -104,7 +103,11 @@ impl Perform for Login {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(local_user_view.local_user.id.0)?,
|
jwt: Claims::jwt(
|
||||||
|
local_user_view.local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +121,7 @@ impl Perform for GetCaptcha {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<Self::Response, LemmyError> {
|
) -> Result<Self::Response, LemmyError> {
|
||||||
let captcha_settings = Settings::get().captcha;
|
let captcha_settings = context.settings().captcha;
|
||||||
|
|
||||||
if !captcha_settings.enabled {
|
if !captcha_settings.enabled {
|
||||||
return Ok(GetCaptchaResponse { ok: None });
|
return Ok(GetCaptchaResponse { ok: None });
|
||||||
|
@ -164,7 +167,8 @@ impl Perform for SaveUserSettings {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<LoginResponse, LemmyError> {
|
) -> Result<LoginResponse, LemmyError> {
|
||||||
let data: &SaveUserSettings = self;
|
let data: &SaveUserSettings = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
|
let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
|
||||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||||
|
@ -181,7 +185,10 @@ impl Perform for SaveUserSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Some(display_name)) = &display_name {
|
if let Some(Some(display_name)) = &display_name {
|
||||||
if !is_valid_display_name(display_name.trim()) {
|
if !is_valid_display_name(
|
||||||
|
display_name.trim(),
|
||||||
|
context.settings().actor_name_max_length,
|
||||||
|
) {
|
||||||
return Err(ApiError::err("invalid_username").into());
|
return Err(ApiError::err("invalid_username").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,7 +276,11 @@ impl Perform for SaveUserSettings {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(updated_local_user.id.0)?,
|
jwt: Claims::jwt(
|
||||||
|
updated_local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +295,8 @@ impl Perform for ChangePassword {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<LoginResponse, LemmyError> {
|
) -> Result<LoginResponse, LemmyError> {
|
||||||
let data: &ChangePassword = self;
|
let data: &ChangePassword = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
password_length_check(&data.new_password)?;
|
password_length_check(&data.new_password)?;
|
||||||
|
|
||||||
|
@ -312,7 +324,11 @@ impl Perform for ChangePassword {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(updated_local_user.id.0)?,
|
jwt: Claims::jwt(
|
||||||
|
updated_local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +343,8 @@ impl Perform for AddAdmin {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<AddAdminResponse, LemmyError> {
|
) -> Result<AddAdminResponse, LemmyError> {
|
||||||
let data: &AddAdmin = self;
|
let data: &AddAdmin = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
@ -389,7 +406,8 @@ impl Perform for BanPerson {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<BanPersonResponse, LemmyError> {
|
) -> Result<BanPersonResponse, LemmyError> {
|
||||||
let data: &BanPerson = self;
|
let data: &BanPerson = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
@ -481,7 +499,8 @@ impl Perform for BlockPerson {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<BlockPersonResponse, LemmyError> {
|
) -> Result<BlockPersonResponse, LemmyError> {
|
||||||
let data: &BlockPerson = self;
|
let data: &BlockPerson = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let target_id = data.person_id;
|
let target_id = data.person_id;
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
@ -534,7 +553,8 @@ impl Perform for GetReplies {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetRepliesResponse, LemmyError> {
|
) -> Result<GetRepliesResponse, LemmyError> {
|
||||||
let data: &GetReplies = self;
|
let data: &GetReplies = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
|
let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
|
||||||
|
|
||||||
|
@ -571,7 +591,8 @@ impl Perform for GetPersonMentions {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetPersonMentionsResponse, LemmyError> {
|
) -> Result<GetPersonMentionsResponse, LemmyError> {
|
||||||
let data: &GetPersonMentions = self;
|
let data: &GetPersonMentions = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
|
let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
|
||||||
|
|
||||||
|
@ -605,7 +626,8 @@ impl Perform for MarkPersonMentionAsRead {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PersonMentionResponse, LemmyError> {
|
) -> Result<PersonMentionResponse, LemmyError> {
|
||||||
let data: &MarkPersonMentionAsRead = self;
|
let data: &MarkPersonMentionAsRead = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_mention_id = data.person_mention_id;
|
let person_mention_id = data.person_mention_id;
|
||||||
let read_person_mention = blocking(context.pool(), move |conn| {
|
let read_person_mention = blocking(context.pool(), move |conn| {
|
||||||
|
@ -648,7 +670,8 @@ impl Perform for MarkAllAsRead {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetRepliesResponse, LemmyError> {
|
) -> Result<GetRepliesResponse, LemmyError> {
|
||||||
let data: &MarkAllAsRead = self;
|
let data: &MarkAllAsRead = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let replies = blocking(context.pool(), move |conn| {
|
let replies = blocking(context.pool(), move |conn| {
|
||||||
|
@ -727,9 +750,15 @@ impl Perform for PasswordReset {
|
||||||
// TODO no i18n support here.
|
// TODO no i18n support here.
|
||||||
let email = &local_user_view.local_user.email.expect("email");
|
let email = &local_user_view.local_user.email.expect("email");
|
||||||
let subject = &format!("Password reset for {}", local_user_view.person.name);
|
let subject = &format!("Password reset for {}", local_user_view.person.name);
|
||||||
let hostname = &Settings::get().get_protocol_and_hostname();
|
let protocol_and_hostname = &context.settings().get_protocol_and_hostname();
|
||||||
let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", local_user_view.person.name, hostname, &token);
|
let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", local_user_view.person.name, protocol_and_hostname, &token);
|
||||||
send_email(subject, email, &local_user_view.person.name, html)
|
send_email(
|
||||||
|
subject,
|
||||||
|
email,
|
||||||
|
&local_user_view.person.name,
|
||||||
|
html,
|
||||||
|
&context.settings(),
|
||||||
|
)
|
||||||
.map_err(|e| ApiError::err(&e))?;
|
.map_err(|e| ApiError::err(&e))?;
|
||||||
|
|
||||||
Ok(PasswordResetResponse {})
|
Ok(PasswordResetResponse {})
|
||||||
|
@ -771,7 +800,11 @@ impl Perform for PasswordChange {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(updated_local_user.id.0)?,
|
jwt: Claims::jwt(
|
||||||
|
updated_local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,7 +819,8 @@ impl Perform for GetReportCount {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetReportCountResponse, LemmyError> {
|
) -> Result<GetReportCountResponse, LemmyError> {
|
||||||
let data: &GetReportCount = self;
|
let data: &GetReportCount = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let community_id = data.community;
|
let community_id = data.community;
|
||||||
|
|
|
@ -38,7 +38,8 @@ impl Perform for CreatePostLike {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &CreatePostLike = self;
|
let data: &CreatePostLike = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Don't do a downvote if site has downvotes disabled
|
// Don't do a downvote if site has downvotes disabled
|
||||||
check_downvotes_enabled(data.score, context.pool()).await?;
|
check_downvotes_enabled(data.score, context.pool()).await?;
|
||||||
|
@ -120,7 +121,8 @@ impl Perform for LockPost {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &LockPost = self;
|
let data: &LockPost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
@ -186,7 +188,8 @@ impl Perform for StickyPost {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &StickyPost = self;
|
let data: &StickyPost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
@ -256,7 +259,8 @@ impl Perform for SavePost {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &SavePost = self;
|
let data: &SavePost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let post_saved_form = PostSavedForm {
|
let post_saved_form = PostSavedForm {
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
|
|
|
@ -39,7 +39,8 @@ impl Perform for CreatePostReport {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CreatePostReportResponse, LemmyError> {
|
) -> Result<CreatePostReportResponse, LemmyError> {
|
||||||
let data: &CreatePostReport = self;
|
let data: &CreatePostReport = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// check size of report and check for whitespace
|
// check size of report and check for whitespace
|
||||||
let reason = data.reason.trim();
|
let reason = data.reason.trim();
|
||||||
|
@ -105,7 +106,8 @@ impl Perform for ResolvePostReport {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<ResolvePostReportResponse, LemmyError> {
|
) -> Result<ResolvePostReportResponse, LemmyError> {
|
||||||
let data: &ResolvePostReport = self;
|
let data: &ResolvePostReport = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let report_id = data.report_id;
|
let report_id = data.report_id;
|
||||||
let report = blocking(context.pool(), move |conn| {
|
let report = blocking(context.pool(), move |conn| {
|
||||||
|
@ -157,7 +159,8 @@ impl Perform for ListPostReports {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<ListPostReportsResponse, LemmyError> {
|
) -> Result<ListPostReportsResponse, LemmyError> {
|
||||||
let data: &ListPostReports = self;
|
let data: &ListPostReports = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let community_id = data.community;
|
let community_id = data.community;
|
||||||
|
|
|
@ -20,7 +20,8 @@ impl Perform for MarkPrivateMessageAsRead {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &MarkPrivateMessageAsRead = self;
|
let data: &MarkPrivateMessageAsRead = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Checking permissions
|
// Checking permissions
|
||||||
let private_message_id = data.private_message_id;
|
let private_message_id = data.private_message_id;
|
||||||
|
|
|
@ -151,7 +151,8 @@ impl Perform for Search {
|
||||||
) -> Result<SearchResponse, LemmyError> {
|
) -> Result<SearchResponse, LemmyError> {
|
||||||
let data: &Search = self;
|
let data: &Search = self;
|
||||||
|
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
||||||
let show_bot_accounts = local_user_view
|
let show_bot_accounts = local_user_view
|
||||||
|
@ -180,7 +181,7 @@ impl Perform for Search {
|
||||||
let community_actor_id = data
|
let community_actor_id = data
|
||||||
.community_name
|
.community_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t).ok())
|
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok())
|
||||||
.unwrap_or(None);
|
.unwrap_or(None);
|
||||||
let creator_id = data.creator_id;
|
let creator_id = data.creator_id;
|
||||||
match search_type {
|
match search_type {
|
||||||
|
@ -384,7 +385,8 @@ impl Perform for ResolveObject {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<ResolveObjectResponse, LemmyError> {
|
) -> Result<ResolveObjectResponse, LemmyError> {
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&self.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&self.auth, context.pool(), context.secret()).await?;
|
||||||
let res = search_by_apub_id(&self.q, context)
|
let res = search_by_apub_id(&self.q, context)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ApiError::err("couldnt_find_object"))?;
|
.map_err(|_| ApiError::err("couldnt_find_object"))?;
|
||||||
|
@ -443,7 +445,8 @@ impl Perform for TransferSite {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetSiteResponse, LemmyError> {
|
) -> Result<GetSiteResponse, LemmyError> {
|
||||||
let data: &TransferSite = self;
|
let data: &TransferSite = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
|
@ -480,7 +483,12 @@ impl Perform for TransferSite {
|
||||||
admins.insert(0, creator_person);
|
admins.insert(0, creator_person);
|
||||||
|
|
||||||
let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
|
let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
|
||||||
let federated_instances = build_federated_instances(context.pool()).await?;
|
let federated_instances = build_federated_instances(
|
||||||
|
context.pool(),
|
||||||
|
&context.settings().federation,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site_view: Some(site_view),
|
site_view: Some(site_view),
|
||||||
|
@ -504,7 +512,8 @@ impl Perform for GetSiteConfig {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetSiteConfigResponse, LemmyError> {
|
) -> Result<GetSiteConfigResponse, LemmyError> {
|
||||||
let data: &GetSiteConfig = self;
|
let data: &GetSiteConfig = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Only let admins read this
|
// Only let admins read this
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
@ -525,7 +534,8 @@ impl Perform for SaveSiteConfig {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetSiteConfigResponse, LemmyError> {
|
) -> Result<GetSiteConfigResponse, LemmyError> {
|
||||||
let data: &SaveSiteConfig = self;
|
let data: &SaveSiteConfig = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Only let admins read this
|
// Only let admins read this
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
|
@ -17,7 +17,8 @@ impl Perform for UserJoin {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<UserJoinResponse, LemmyError> {
|
) -> Result<UserJoinResponse, LemmyError> {
|
||||||
let data: &UserJoin = self;
|
let data: &UserJoin = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
if let Some(ws_id) = websocket_id {
|
if let Some(ws_id) = websocket_id {
|
||||||
context.chat_server().do_send(JoinUserRoom {
|
context.chat_server().do_send(JoinUserRoom {
|
||||||
|
|
|
@ -25,6 +25,7 @@ use lemmy_db_schema::{
|
||||||
person_block::PersonBlock,
|
person_block::PersonBlock,
|
||||||
person_mention::{PersonMention, PersonMentionForm},
|
person_mention::{PersonMention, PersonMentionForm},
|
||||||
post::{Post, PostRead, PostReadForm},
|
post::{Post, PostRead, PostReadForm},
|
||||||
|
secret::Secret,
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
CommunityId,
|
CommunityId,
|
||||||
|
@ -40,7 +41,7 @@ use lemmy_db_views_actor::{
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
claims::Claims,
|
claims::Claims,
|
||||||
email::send_email,
|
email::send_email,
|
||||||
settings::structs::Settings,
|
settings::structs::{FederationConfig, Settings},
|
||||||
utils::MentionData,
|
utils::MentionData,
|
||||||
ApiError,
|
ApiError,
|
||||||
LemmyError,
|
LemmyError,
|
||||||
|
@ -71,9 +72,19 @@ pub async fn send_local_notifs(
|
||||||
post: Post,
|
post: Post,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
do_send_email: bool,
|
do_send_email: bool,
|
||||||
|
settings: &Settings,
|
||||||
) -> Result<Vec<LocalUserId>, LemmyError> {
|
) -> Result<Vec<LocalUserId>, LemmyError> {
|
||||||
|
let settings = settings.to_owned();
|
||||||
let ids = blocking(pool, move |conn| {
|
let ids = blocking(pool, move |conn| {
|
||||||
do_send_local_notifs(conn, &mentions, &comment, &person, &post, do_send_email)
|
do_send_local_notifs(
|
||||||
|
conn,
|
||||||
|
&mentions,
|
||||||
|
&comment,
|
||||||
|
&person,
|
||||||
|
&post,
|
||||||
|
do_send_email,
|
||||||
|
&settings,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -87,13 +98,14 @@ fn do_send_local_notifs(
|
||||||
person: &Person,
|
person: &Person,
|
||||||
post: &Post,
|
post: &Post,
|
||||||
do_send_email: bool,
|
do_send_email: bool,
|
||||||
|
settings: &Settings,
|
||||||
) -> Vec<LocalUserId> {
|
) -> Vec<LocalUserId> {
|
||||||
let mut recipient_ids = Vec::new();
|
let mut recipient_ids = Vec::new();
|
||||||
|
|
||||||
// Send the local mentions
|
// Send the local mentions
|
||||||
for mention in mentions
|
for mention in mentions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|m| m.is_local() && m.name.ne(&person.name))
|
.filter(|m| m.is_local(&settings.hostname) && m.name.ne(&person.name))
|
||||||
.collect::<Vec<&MentionData>>()
|
.collect::<Vec<&MentionData>>()
|
||||||
{
|
{
|
||||||
if let Ok(mention_user_view) = LocalUserView::read_from_name(conn, &mention.name) {
|
if let Ok(mention_user_view) = LocalUserView::read_from_name(conn, &mention.name) {
|
||||||
|
@ -119,6 +131,7 @@ fn do_send_local_notifs(
|
||||||
"Mentioned by",
|
"Mentioned by",
|
||||||
"Person Mention",
|
"Person Mention",
|
||||||
&comment.content,
|
&comment.content,
|
||||||
|
settings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +154,7 @@ fn do_send_local_notifs(
|
||||||
"Reply from",
|
"Reply from",
|
||||||
"Comment Reply",
|
"Comment Reply",
|
||||||
&comment.content,
|
&comment.content,
|
||||||
|
settings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,6 +173,7 @@ fn do_send_local_notifs(
|
||||||
"Reply from",
|
"Reply from",
|
||||||
"Post Reply",
|
"Post Reply",
|
||||||
&comment.content,
|
&comment.content,
|
||||||
|
settings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +188,7 @@ pub fn send_email_to_user(
|
||||||
subject_text: &str,
|
subject_text: &str,
|
||||||
body_text: &str,
|
body_text: &str,
|
||||||
comment_content: &str,
|
comment_content: &str,
|
||||||
|
settings: &Settings,
|
||||||
) {
|
) {
|
||||||
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
|
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
|
||||||
return;
|
return;
|
||||||
|
@ -181,18 +197,22 @@ pub fn send_email_to_user(
|
||||||
if let Some(user_email) = &local_user_view.local_user.email {
|
if let Some(user_email) = &local_user_view.local_user.email {
|
||||||
let subject = &format!(
|
let subject = &format!(
|
||||||
"{} - {} {}",
|
"{} - {} {}",
|
||||||
subject_text,
|
subject_text, settings.hostname, local_user_view.person.name,
|
||||||
Settings::get().hostname,
|
|
||||||
local_user_view.person.name,
|
|
||||||
);
|
);
|
||||||
let html = &format!(
|
let html = &format!(
|
||||||
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
||||||
body_text,
|
body_text,
|
||||||
local_user_view.person.name,
|
local_user_view.person.name,
|
||||||
comment_content,
|
comment_content,
|
||||||
Settings::get().get_protocol_and_hostname()
|
settings.get_protocol_and_hostname()
|
||||||
);
|
);
|
||||||
match send_email(subject, user_email, &local_user_view.person.name, html) {
|
match send_email(
|
||||||
|
subject,
|
||||||
|
user_email,
|
||||||
|
&local_user_view.person.name,
|
||||||
|
html,
|
||||||
|
settings,
|
||||||
|
) {
|
||||||
Ok(_o) => _o,
|
Ok(_o) => _o,
|
||||||
Err(e) => error!("{}", e),
|
Err(e) => error!("{}", e),
|
||||||
};
|
};
|
||||||
|
@ -244,8 +264,9 @@ pub async fn mark_post_as_read(
|
||||||
pub async fn get_local_user_view_from_jwt(
|
pub async fn get_local_user_view_from_jwt(
|
||||||
jwt: &str,
|
jwt: &str,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
|
secret: &Secret,
|
||||||
) -> Result<LocalUserView, LemmyError> {
|
) -> Result<LocalUserView, LemmyError> {
|
||||||
let claims = Claims::decode(jwt)
|
let claims = Claims::decode(jwt, &secret.jwt_secret)
|
||||||
.map_err(|_| ApiError::err("not_logged_in"))?
|
.map_err(|_| ApiError::err("not_logged_in"))?
|
||||||
.claims;
|
.claims;
|
||||||
let local_user_id = LocalUserId(claims.sub);
|
let local_user_id = LocalUserId(claims.sub);
|
||||||
|
@ -282,9 +303,10 @@ pub fn check_validator_time(
|
||||||
pub async fn get_local_user_view_from_jwt_opt(
|
pub async fn get_local_user_view_from_jwt_opt(
|
||||||
jwt: &Option<String>,
|
jwt: &Option<String>,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
|
secret: &Secret,
|
||||||
) -> Result<Option<LocalUserView>, LemmyError> {
|
) -> Result<Option<LocalUserView>, LemmyError> {
|
||||||
match jwt {
|
match jwt {
|
||||||
Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool).await?)),
|
Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool, secret).await?)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,8 +314,9 @@ pub async fn get_local_user_view_from_jwt_opt(
|
||||||
pub async fn get_local_user_settings_view_from_jwt(
|
pub async fn get_local_user_settings_view_from_jwt(
|
||||||
jwt: &str,
|
jwt: &str,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
|
secret: &Secret,
|
||||||
) -> Result<LocalUserSettingsView, LemmyError> {
|
) -> Result<LocalUserSettingsView, LemmyError> {
|
||||||
let claims = Claims::decode(jwt)
|
let claims = Claims::decode(jwt, &secret.jwt_secret)
|
||||||
.map_err(|_| ApiError::err("not_logged_in"))?
|
.map_err(|_| ApiError::err("not_logged_in"))?
|
||||||
.claims;
|
.claims;
|
||||||
let local_user_id = LocalUserId(claims.sub);
|
let local_user_id = LocalUserId(claims.sub);
|
||||||
|
@ -314,10 +337,11 @@ pub async fn get_local_user_settings_view_from_jwt(
|
||||||
pub async fn get_local_user_settings_view_from_jwt_opt(
|
pub async fn get_local_user_settings_view_from_jwt_opt(
|
||||||
jwt: &Option<String>,
|
jwt: &Option<String>,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
|
secret: &Secret,
|
||||||
) -> Result<Option<LocalUserSettingsView>, LemmyError> {
|
) -> Result<Option<LocalUserSettingsView>, LemmyError> {
|
||||||
match jwt {
|
match jwt {
|
||||||
Some(jwt) => Ok(Some(
|
Some(jwt) => Ok(Some(
|
||||||
get_local_user_settings_view_from_jwt(jwt, pool).await?,
|
get_local_user_settings_view_from_jwt(jwt, pool, secret).await?,
|
||||||
)),
|
)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
|
@ -387,15 +411,18 @@ pub async fn collect_moderated_communities(
|
||||||
|
|
||||||
pub async fn build_federated_instances(
|
pub async fn build_federated_instances(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
|
federation_config: &FederationConfig,
|
||||||
|
hostname: &str,
|
||||||
) -> Result<Option<FederatedInstances>, LemmyError> {
|
) -> Result<Option<FederatedInstances>, LemmyError> {
|
||||||
if Settings::get().federation.enabled {
|
let federation = federation_config.to_owned();
|
||||||
|
if federation.enabled {
|
||||||
let distinct_communities = blocking(pool, move |conn| {
|
let distinct_communities = blocking(pool, move |conn| {
|
||||||
Community::distinct_federated_communities(conn)
|
Community::distinct_federated_communities(conn)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let allowed = Settings::get().federation.allowed_instances;
|
let allowed = federation.allowed_instances;
|
||||||
let blocked = Settings::get().federation.blocked_instances;
|
let blocked = federation.blocked_instances;
|
||||||
|
|
||||||
let mut linked = distinct_communities
|
let mut linked = distinct_communities
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -407,7 +434,7 @@ pub async fn build_federated_instances(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(blocked) = blocked.as_ref() {
|
if let Some(blocked) = blocked.as_ref() {
|
||||||
linked.retain(|a| !blocked.contains(a) && !a.eq(&Settings::get().hostname));
|
linked.retain(|a| !blocked.contains(a) && !a.eq(hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort and remove dupes
|
// Sort and remove dupes
|
||||||
|
|
|
@ -40,9 +40,11 @@ impl PerformCrud for CreateComment {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &CreateComment = self;
|
let data: &CreateComment = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
let content_slurs_removed =
|
||||||
|
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex());
|
||||||
|
|
||||||
// Check for a community ban
|
// Check for a community ban
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
@ -91,10 +93,15 @@ impl PerformCrud for CreateComment {
|
||||||
|
|
||||||
// Necessary to update the ap_id
|
// Necessary to update the ap_id
|
||||||
let inserted_comment_id = inserted_comment.id;
|
let inserted_comment_id = inserted_comment.id;
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
|
|
||||||
let updated_comment: Comment =
|
let updated_comment: Comment =
|
||||||
blocking(context.pool(), move |conn| -> Result<Comment, LemmyError> {
|
blocking(context.pool(), move |conn| -> Result<Comment, LemmyError> {
|
||||||
let apub_id =
|
let apub_id = generate_apub_endpoint(
|
||||||
generate_apub_endpoint(EndpointType::Comment, &inserted_comment_id.to_string())?;
|
EndpointType::Comment,
|
||||||
|
&inserted_comment_id.to_string(),
|
||||||
|
&protocol_and_hostname,
|
||||||
|
)?;
|
||||||
Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?)
|
Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?)
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
|
@ -118,6 +125,7 @@ impl PerformCrud for CreateComment {
|
||||||
post,
|
post,
|
||||||
context.pool(),
|
context.pool(),
|
||||||
true,
|
true,
|
||||||
|
&context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ impl PerformCrud for DeleteComment {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &DeleteComment = self;
|
let data: &DeleteComment = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let orig_comment = blocking(context.pool(), move |conn| {
|
let orig_comment = blocking(context.pool(), move |conn| {
|
||||||
|
@ -76,6 +77,7 @@ impl PerformCrud for DeleteComment {
|
||||||
post,
|
post,
|
||||||
context.pool(),
|
context.pool(),
|
||||||
false,
|
false,
|
||||||
|
&context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -102,7 +104,8 @@ impl PerformCrud for RemoveComment {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &RemoveComment = self;
|
let data: &RemoveComment = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let orig_comment = blocking(context.pool(), move |conn| {
|
let orig_comment = blocking(context.pool(), move |conn| {
|
||||||
|
@ -169,6 +172,7 @@ impl PerformCrud for RemoveComment {
|
||||||
post,
|
post,
|
||||||
context.pool(),
|
context.pool(),
|
||||||
false,
|
false,
|
||||||
|
&context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ impl PerformCrud for GetComments {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetCommentsResponse, LemmyError> {
|
) -> Result<GetCommentsResponse, LemmyError> {
|
||||||
let data: &GetComments = self;
|
let data: &GetComments = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let show_bot_accounts = local_user_view
|
let show_bot_accounts = local_user_view
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -31,7 +32,7 @@ impl PerformCrud for GetComments {
|
||||||
let community_actor_id = data
|
let community_actor_id = data
|
||||||
.community_name
|
.community_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t).ok())
|
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok())
|
||||||
.unwrap_or(None);
|
.unwrap_or(None);
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
|
|
|
@ -32,7 +32,8 @@ impl PerformCrud for EditComment {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &EditComment = self;
|
let data: &EditComment = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let orig_comment = blocking(context.pool(), move |conn| {
|
let orig_comment = blocking(context.pool(), move |conn| {
|
||||||
|
@ -54,7 +55,8 @@ impl PerformCrud for EditComment {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the update
|
// Do the update
|
||||||
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
let content_slurs_removed =
|
||||||
|
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex());
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let updated_comment = blocking(context.pool(), move |conn| {
|
let updated_comment = blocking(context.pool(), move |conn| {
|
||||||
Comment::update_content(conn, comment_id, &content_slurs_removed)
|
Comment::update_content(conn, comment_id, &content_slurs_removed)
|
||||||
|
@ -81,6 +83,7 @@ impl PerformCrud for EditComment {
|
||||||
orig_comment.post,
|
orig_comment.post,
|
||||||
context.pool(),
|
context.pool(),
|
||||||
false,
|
false,
|
||||||
|
&context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -45,23 +45,28 @@ impl PerformCrud for CreateCommunity {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &CreateCommunity = self;
|
let data: &CreateCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let site = blocking(context.pool(), move |conn| Site::read(conn, 0)).await??;
|
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() {
|
if site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
|
||||||
return Err(ApiError::err("only_admins_can_create_communities").into());
|
return Err(ApiError::err("only_admins_can_create_communities").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
check_slurs(&data.name)?;
|
check_slurs(&data.name, &context.settings().slur_regex())?;
|
||||||
check_slurs(&data.title)?;
|
check_slurs(&data.title, &context.settings().slur_regex())?;
|
||||||
check_slurs_opt(&data.description)?;
|
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||||
|
|
||||||
if !is_valid_actor_name(&data.name) {
|
if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) {
|
||||||
return Err(ApiError::err("invalid_community_name").into());
|
return Err(ApiError::err("invalid_community_name").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double check for duplicate community actor_ids
|
// Double check for duplicate community actor_ids
|
||||||
let community_actor_id = generate_apub_endpoint(EndpointType::Community, &data.name)?;
|
let community_actor_id = generate_apub_endpoint(
|
||||||
|
EndpointType::Community,
|
||||||
|
&data.name,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let actor_id_cloned = community_actor_id.to_owned();
|
let actor_id_cloned = community_actor_id.to_owned();
|
||||||
let community_dupe = blocking(context.pool(), move |conn| {
|
let community_dupe = blocking(context.pool(), move |conn| {
|
||||||
Community::read_from_apub_id(conn, &actor_id_cloned)
|
Community::read_from_apub_id(conn, &actor_id_cloned)
|
||||||
|
|
|
@ -21,7 +21,8 @@ impl PerformCrud for DeleteCommunity {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &DeleteCommunity = self;
|
let data: &DeleteCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Fetch the community mods
|
// Fetch the community mods
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
@ -75,7 +76,8 @@ impl PerformCrud for RemoveCommunity {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &RemoveCommunity = self;
|
let data: &RemoveCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Verify its an admin (only an admin can remove a community)
|
// Verify its an admin (only an admin can remove a community)
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
|
@ -27,14 +27,16 @@ impl PerformCrud for GetCommunity {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetCommunityResponse, LemmyError> {
|
) -> Result<GetCommunityResponse, LemmyError> {
|
||||||
let data: &GetCommunity = self;
|
let data: &GetCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
let person_id = local_user_view.map(|u| u.person.id);
|
let person_id = local_user_view.map(|u| u.person.id);
|
||||||
|
|
||||||
let community_id = match data.id {
|
let community_id = match data.id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => {
|
None => {
|
||||||
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
|
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
|
||||||
let community_actor_id = build_actor_id_from_shortname(EndpointType::Community, &name)?;
|
let community_actor_id =
|
||||||
|
build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?;
|
||||||
|
|
||||||
blocking(context.pool(), move |conn| {
|
blocking(context.pool(), move |conn| {
|
||||||
Community::read_from_apub_id(conn, &community_actor_id)
|
Community::read_from_apub_id(conn, &community_actor_id)
|
||||||
|
@ -89,7 +91,8 @@ impl PerformCrud for ListCommunities {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<ListCommunitiesResponse, LemmyError> {
|
) -> Result<ListCommunitiesResponse, LemmyError> {
|
||||||
let data: &ListCommunities = self;
|
let data: &ListCommunities = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,11 @@ impl PerformCrud for EditCommunity {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &EditCommunity = self;
|
let data: &EditCommunity = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
check_slurs_opt(&data.title)?;
|
check_slurs_opt(&data.title, &context.settings().slur_regex())?;
|
||||||
check_slurs_opt(&data.description)?;
|
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||||
|
|
||||||
// Verify its a mod (only mods can edit it)
|
// Verify its a mod (only mods can edit it)
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
|
|
@ -38,10 +38,12 @@ impl PerformCrud for CreatePost {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &CreatePost = self;
|
let data: &CreatePost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
check_slurs(&data.name)?;
|
let slur_regex = &context.settings().slur_regex();
|
||||||
check_slurs_opt(&data.body)?;
|
check_slurs(&data.name, slur_regex)?;
|
||||||
|
check_slurs_opt(&data.body, slur_regex)?;
|
||||||
|
|
||||||
if !is_valid_post_title(&data.name) {
|
if !is_valid_post_title(&data.name) {
|
||||||
return Err(ApiError::err("invalid_post_title").into());
|
return Err(ApiError::err("invalid_post_title").into());
|
||||||
|
@ -51,7 +53,8 @@ impl PerformCrud for CreatePost {
|
||||||
|
|
||||||
// Fetch post links and pictrs cached image
|
// Fetch post links and pictrs cached image
|
||||||
let data_url = data.url.as_ref();
|
let data_url = data.url.as_ref();
|
||||||
let (metadata_res, pictrs_thumbnail) = fetch_site_data(context.client(), data_url).await;
|
let (metadata_res, pictrs_thumbnail) =
|
||||||
|
fetch_site_data(context.client(), &context.settings(), data_url).await;
|
||||||
let (embed_title, embed_description, embed_html) = metadata_res
|
let (embed_title, embed_description, embed_html) = metadata_res
|
||||||
.map(|u| (u.title, u.description, u.html))
|
.map(|u| (u.title, u.description, u.html))
|
||||||
.unwrap_or((None, None, None));
|
.unwrap_or((None, None, None));
|
||||||
|
@ -85,8 +88,13 @@ impl PerformCrud for CreatePost {
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post_id = inserted_post.id;
|
let inserted_post_id = inserted_post.id;
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let updated_post = blocking(context.pool(), move |conn| -> Result<Post, LemmyError> {
|
let updated_post = blocking(context.pool(), move |conn| -> Result<Post, LemmyError> {
|
||||||
let apub_id = generate_apub_endpoint(EndpointType::Post, &inserted_post_id.to_string())?;
|
let apub_id = generate_apub_endpoint(
|
||||||
|
EndpointType::Post,
|
||||||
|
&inserted_post_id.to_string(),
|
||||||
|
&protocol_and_hostname,
|
||||||
|
)?;
|
||||||
Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?)
|
Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?)
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
|
|
|
@ -23,7 +23,8 @@ impl PerformCrud for DeletePost {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &DeletePost = self;
|
let data: &DeletePost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
@ -83,7 +84,8 @@ impl PerformCrud for RemovePost {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &RemovePost = self;
|
let data: &RemovePost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
|
|
@ -24,7 +24,8 @@ impl PerformCrud for GetPost {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetPostResponse, LemmyError> {
|
) -> Result<GetPostResponse, LemmyError> {
|
||||||
let data: &GetPost = self;
|
let data: &GetPost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let show_bot_accounts = local_user_view
|
let show_bot_accounts = local_user_view
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -112,7 +113,8 @@ impl PerformCrud for GetPosts {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetPostsResponse, LemmyError> {
|
) -> Result<GetPostsResponse, LemmyError> {
|
||||||
let data: &GetPosts = self;
|
let data: &GetPosts = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ impl PerformCrud for GetPosts {
|
||||||
let community_actor_id = data
|
let community_actor_id = data
|
||||||
.community_name
|
.community_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t).ok())
|
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok())
|
||||||
.unwrap_or(None);
|
.unwrap_or(None);
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,12 @@ impl PerformCrud for EditPost {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &EditPost = self;
|
let data: &EditPost = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
check_slurs_opt(&data.name)?;
|
let slur_regex = &context.settings().slur_regex();
|
||||||
check_slurs_opt(&data.body)?;
|
check_slurs_opt(&data.name, slur_regex)?;
|
||||||
|
check_slurs_opt(&data.body, slur_regex)?;
|
||||||
|
|
||||||
if let Some(name) = &data.name {
|
if let Some(name) = &data.name {
|
||||||
if !is_valid_post_title(name) {
|
if !is_valid_post_title(name) {
|
||||||
|
@ -51,7 +53,8 @@ impl PerformCrud for EditPost {
|
||||||
|
|
||||||
// Fetch post links and Pictrs cached image
|
// Fetch post links and Pictrs cached image
|
||||||
let data_url = data.url.as_ref();
|
let data_url = data.url.as_ref();
|
||||||
let (metadata_res, pictrs_thumbnail) = fetch_site_data(context.client(), data_url).await;
|
let (metadata_res, pictrs_thumbnail) =
|
||||||
|
fetch_site_data(context.client(), &context.settings(), data_url).await;
|
||||||
let (embed_title, embed_description, embed_html) = metadata_res
|
let (embed_title, embed_description, embed_html) = metadata_res
|
||||||
.map(|u| (u.title, u.description, u.html))
|
.map(|u| (u.title, u.description, u.html))
|
||||||
.unwrap_or((None, None, None));
|
.unwrap_or((None, None, None));
|
||||||
|
|
|
@ -31,9 +31,11 @@ impl PerformCrud for CreatePrivateMessage {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &CreatePrivateMessage = self;
|
let data: &CreatePrivateMessage = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
let content_slurs_removed =
|
||||||
|
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex());
|
||||||
|
|
||||||
check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
|
check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
|
||||||
|
|
||||||
|
@ -56,12 +58,14 @@ impl PerformCrud for CreatePrivateMessage {
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_private_message_id = inserted_private_message.id;
|
let inserted_private_message_id = inserted_private_message.id;
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let updated_private_message = blocking(
|
let updated_private_message = blocking(
|
||||||
context.pool(),
|
context.pool(),
|
||||||
move |conn| -> Result<PrivateMessage, LemmyError> {
|
move |conn| -> Result<PrivateMessage, LemmyError> {
|
||||||
let apub_id = generate_apub_endpoint(
|
let apub_id = generate_apub_endpoint(
|
||||||
EndpointType::PrivateMessage,
|
EndpointType::PrivateMessage,
|
||||||
&inserted_private_message_id.to_string(),
|
&inserted_private_message_id.to_string(),
|
||||||
|
&protocol_and_hostname,
|
||||||
)?;
|
)?;
|
||||||
Ok(PrivateMessage::update_ap_id(
|
Ok(PrivateMessage::update_ap_id(
|
||||||
conn,
|
conn,
|
||||||
|
@ -101,6 +105,7 @@ impl PerformCrud for CreatePrivateMessage {
|
||||||
"Private Message from",
|
"Private Message from",
|
||||||
"Private Message",
|
"Private Message",
|
||||||
&content_slurs_removed,
|
&content_slurs_removed,
|
||||||
|
&context.settings(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ impl PerformCrud for DeletePrivateMessage {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &DeletePrivateMessage = self;
|
let data: &DeletePrivateMessage = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Checking permissions
|
// Checking permissions
|
||||||
let private_message_id = data.private_message_id;
|
let private_message_id = data.private_message_id;
|
||||||
|
|
|
@ -20,7 +20,8 @@ impl PerformCrud for GetPrivateMessages {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PrivateMessagesResponse, LemmyError> {
|
) -> Result<PrivateMessagesResponse, LemmyError> {
|
||||||
let data: &GetPrivateMessages = self;
|
let data: &GetPrivateMessages = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
|
|
|
@ -24,7 +24,8 @@ impl PerformCrud for EditPrivateMessage {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &EditPrivateMessage = self;
|
let data: &EditPrivateMessage = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Checking permissions
|
// Checking permissions
|
||||||
let private_message_id = data.private_message_id;
|
let private_message_id = data.private_message_id;
|
||||||
|
@ -37,7 +38,7 @@ impl PerformCrud for EditPrivateMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doing the update
|
// Doing the update
|
||||||
let content_slurs_removed = remove_slurs(&data.content);
|
let content_slurs_removed = remove_slurs(&data.content, &context.settings().slur_regex());
|
||||||
let private_message_id = data.private_message_id;
|
let private_message_id = data.private_message_id;
|
||||||
let updated_private_message = blocking(context.pool(), move |conn| {
|
let updated_private_message = blocking(context.pool(), move |conn| {
|
||||||
PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed)
|
PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed)
|
||||||
|
|
|
@ -39,10 +39,11 @@ impl PerformCrud for CreateSite {
|
||||||
return Err(ApiError::err("site_already_exists").into());
|
return Err(ApiError::err("site_already_exists").into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
check_slurs(&data.name)?;
|
check_slurs(&data.name, &context.settings().slur_regex())?;
|
||||||
check_slurs_opt(&data.description)?;
|
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
|
@ -15,7 +15,7 @@ use lemmy_db_views_actor::{
|
||||||
person_block_view::PersonBlockView,
|
person_block_view::PersonBlockView,
|
||||||
person_view::PersonViewSafe,
|
person_view::PersonViewSafe,
|
||||||
};
|
};
|
||||||
use lemmy_utils::{settings::structs::Settings, version, ApiError, ConnectionId, LemmyError};
|
use lemmy_utils::{version, ApiError, ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
|
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ impl PerformCrud for GetSite {
|
||||||
Ok(site_view) => Some(site_view),
|
Ok(site_view) => Some(site_view),
|
||||||
// If the site isn't created yet, check the setup
|
// If the site isn't created yet, check the setup
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
if let Some(setup) = Settings::get().setup.as_ref() {
|
if let Some(setup) = context.settings().setup.as_ref() {
|
||||||
let register = Register {
|
let register = Register {
|
||||||
username: setup.admin_username.to_owned(),
|
username: setup.admin_username.to_owned(),
|
||||||
email: setup.admin_email.to_owned(),
|
email: setup.admin_email.to_owned(),
|
||||||
|
@ -91,7 +91,8 @@ impl PerformCrud for GetSite {
|
||||||
|
|
||||||
// Build the local user
|
// Build the local user
|
||||||
let my_user = if let Some(local_user_view) =
|
let my_user = if let Some(local_user_view) =
|
||||||
get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool()).await?
|
get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool(), context.secret())
|
||||||
|
.await?
|
||||||
{
|
{
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let follows = blocking(context.pool(), move |conn| {
|
let follows = blocking(context.pool(), move |conn| {
|
||||||
|
@ -131,7 +132,12 @@ impl PerformCrud for GetSite {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let federated_instances = build_federated_instances(context.pool()).await?;
|
let federated_instances = build_federated_instances(
|
||||||
|
context.pool(),
|
||||||
|
&context.settings().federation,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site_view,
|
site_view,
|
||||||
|
|
|
@ -30,10 +30,11 @@ impl PerformCrud for EditSite {
|
||||||
websocket_id: Option<ConnectionId>,
|
websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<SiteResponse, LemmyError> {
|
) -> Result<SiteResponse, LemmyError> {
|
||||||
let data: &EditSite = self;
|
let data: &EditSite = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
check_slurs_opt(&data.name)?;
|
check_slurs_opt(&data.name, &context.settings().slur_regex())?;
|
||||||
check_slurs_opt(&data.description)?;
|
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
|
@ -29,7 +29,6 @@ use lemmy_db_views_actor::person_view::PersonViewSafe;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
apub::generate_actor_keypair,
|
apub::generate_actor_keypair,
|
||||||
claims::Claims,
|
claims::Claims,
|
||||||
settings::structs::Settings,
|
|
||||||
utils::{check_slurs, is_valid_actor_name},
|
utils::{check_slurs, is_valid_actor_name},
|
||||||
ApiError,
|
ApiError,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
|
@ -69,7 +68,7 @@ impl PerformCrud for Register {
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
// If its not the admin, check the captcha
|
// If its not the admin, check the captcha
|
||||||
if !no_admins && Settings::get().captcha.enabled {
|
if !no_admins && context.settings().captcha.enabled {
|
||||||
let check = context
|
let check = context
|
||||||
.chat_server()
|
.chat_server()
|
||||||
.send(CheckCaptcha {
|
.send(CheckCaptcha {
|
||||||
|
@ -88,13 +87,17 @@ impl PerformCrud for Register {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check_slurs(&data.username)?;
|
check_slurs(&data.username, &context.settings().slur_regex())?;
|
||||||
|
|
||||||
let actor_keypair = generate_actor_keypair()?;
|
let actor_keypair = generate_actor_keypair()?;
|
||||||
if !is_valid_actor_name(&data.username) {
|
if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
|
||||||
return Err(ApiError::err("invalid_username").into());
|
return Err(ApiError::err("invalid_username").into());
|
||||||
}
|
}
|
||||||
let actor_id = generate_apub_endpoint(EndpointType::Person, &data.username)?;
|
let actor_id = generate_apub_endpoint(
|
||||||
|
EndpointType::Person,
|
||||||
|
&data.username,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
|
|
||||||
// We have to create both a person, and local_user
|
// We have to create both a person, and local_user
|
||||||
|
|
||||||
|
@ -164,6 +167,7 @@ impl PerformCrud for Register {
|
||||||
let main_community_keypair = generate_actor_keypair()?;
|
let main_community_keypair = generate_actor_keypair()?;
|
||||||
|
|
||||||
// Create the main community if it doesn't exist
|
// Create the main community if it doesn't exist
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let main_community = match blocking(context.pool(), move |conn| {
|
let main_community = match blocking(context.pool(), move |conn| {
|
||||||
Community::read(conn, CommunityId(2))
|
Community::read(conn, CommunityId(2))
|
||||||
})
|
})
|
||||||
|
@ -172,7 +176,11 @@ impl PerformCrud for Register {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
let default_community_name = "main";
|
let default_community_name = "main";
|
||||||
let actor_id = generate_apub_endpoint(EndpointType::Community, default_community_name)?;
|
let actor_id = generate_apub_endpoint(
|
||||||
|
EndpointType::Community,
|
||||||
|
default_community_name,
|
||||||
|
&protocol_and_hostname,
|
||||||
|
)?;
|
||||||
let community_form = CommunityForm {
|
let community_form = CommunityForm {
|
||||||
name: default_community_name.to_string(),
|
name: default_community_name.to_string(),
|
||||||
title: "The Default Community".to_string(),
|
title: "The Default Community".to_string(),
|
||||||
|
@ -219,7 +227,11 @@ impl PerformCrud for Register {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(inserted_local_user.id.0)?,
|
jwt: Claims::jwt(
|
||||||
|
inserted_local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ impl PerformCrud for DeleteAccount {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<LoginResponse, LemmyError> {
|
) -> Result<LoginResponse, LemmyError> {
|
||||||
let data: &DeleteAccount = self;
|
let data: &DeleteAccount = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
// Verify the password
|
// Verify the password
|
||||||
let valid: bool = verify(
|
let valid: bool = verify(
|
||||||
|
|
|
@ -22,7 +22,8 @@ impl PerformCrud for GetPersonDetails {
|
||||||
_websocket_id: Option<ConnectionId>,
|
_websocket_id: Option<ConnectionId>,
|
||||||
) -> Result<GetPersonDetailsResponse, LemmyError> {
|
) -> Result<GetPersonDetailsResponse, LemmyError> {
|
||||||
let data: &GetPersonDetails = self;
|
let data: &GetPersonDetails = self;
|
||||||
let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
||||||
let show_bot_accounts = local_user_view
|
let show_bot_accounts = local_user_view
|
||||||
|
@ -41,7 +42,8 @@ impl PerformCrud for GetPersonDetails {
|
||||||
.username
|
.username
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap_or_else(|| "admin".to_string());
|
.unwrap_or_else(|| "admin".to_string());
|
||||||
let actor_id = build_actor_id_from_shortname(EndpointType::Person, &name)?;
|
let actor_id =
|
||||||
|
build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?;
|
||||||
|
|
||||||
let person = blocking(context.pool(), move |conn| {
|
let person = blocking(context.pool(), move |conn| {
|
||||||
Person::read_from_apub_id(conn, &actor_id)
|
Person::read_from_apub_id(conn, &actor_id)
|
||||||
|
|
|
@ -57,7 +57,10 @@ impl CreateOrUpdateComment {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let id = generate_activity_id(kind.clone())?;
|
let id = generate_activity_id(
|
||||||
|
kind.clone(),
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let maa = collect_non_local_mentions(comment, &community, context).await?;
|
let maa = collect_non_local_mentions(comment, &community, context).await?;
|
||||||
|
|
||||||
let create_or_update = CreateOrUpdateComment {
|
let create_or_update = CreateOrUpdateComment {
|
||||||
|
@ -87,7 +90,7 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
let community = extract_community(&self.cc, context, request_counter).await?;
|
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||||
let community_id = ObjectId::new(community.actor_id());
|
let community_id = ObjectId::new(community.actor_id());
|
||||||
|
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &community_id, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community_id, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
||||||
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
||||||
|
|
|
@ -14,13 +14,11 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
request::{retry, RecvError},
|
request::{retry, RecvError},
|
||||||
settings::structs::Settings,
|
|
||||||
utils::{scrape_text_for_mentions, MentionData},
|
utils::{scrape_text_for_mentions, MentionData},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use reqwest::Client;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub mod create_or_update;
|
pub mod create_or_update;
|
||||||
|
@ -41,7 +39,16 @@ async fn get_notif_recipients(
|
||||||
// anyway.
|
// anyway.
|
||||||
// TODO: for compatibility with other projects, it would be much better to read this from cc or tags
|
// TODO: for compatibility with other projects, it would be much better to read this from cc or tags
|
||||||
let mentions = scrape_text_for_mentions(&comment.content);
|
let mentions = scrape_text_for_mentions(&comment.content);
|
||||||
send_local_notifs(mentions, comment.clone(), actor, post, context.pool(), true).await
|
send_local_notifs(
|
||||||
|
mentions,
|
||||||
|
comment.clone(),
|
||||||
|
actor,
|
||||||
|
post,
|
||||||
|
context.pool(),
|
||||||
|
true,
|
||||||
|
&context.settings(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MentionsAndAddresses {
|
pub struct MentionsAndAddresses {
|
||||||
|
@ -70,12 +77,12 @@ pub async fn collect_non_local_mentions(
|
||||||
let mentions = scrape_text_for_mentions(&comment.content)
|
let mentions = scrape_text_for_mentions(&comment.content)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// Filter only the non-local ones
|
// Filter only the non-local ones
|
||||||
.filter(|m| !m.is_local())
|
.filter(|m| !m.is_local(&context.settings().hostname))
|
||||||
.collect::<Vec<MentionData>>();
|
.collect::<Vec<MentionData>>();
|
||||||
|
|
||||||
for mention in &mentions {
|
for mention in &mentions {
|
||||||
// TODO should it be fetching it every time?
|
// TODO should it be fetching it every time?
|
||||||
if let Ok(actor_id) = fetch_webfinger_url(mention, context.client()).await {
|
if let Ok(actor_id) = fetch_webfinger_url(mention, context).await {
|
||||||
let actor_id: ObjectId<Person> = ObjectId::new(actor_id);
|
let actor_id: ObjectId<Person> = ObjectId::new(actor_id);
|
||||||
debug!("mention actor_id: {}", actor_id);
|
debug!("mention actor_id: {}", actor_id);
|
||||||
addressed_ccs.push(actor_id.to_owned().to_string().parse()?);
|
addressed_ccs.push(actor_id.to_owned().to_string().parse()?);
|
||||||
|
@ -120,17 +127,20 @@ async fn get_comment_parent_creator(
|
||||||
|
|
||||||
/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
|
/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
|
||||||
/// using webfinger.
|
/// using webfinger.
|
||||||
async fn fetch_webfinger_url(mention: &MentionData, client: &Client) -> Result<Url, LemmyError> {
|
async fn fetch_webfinger_url(
|
||||||
|
mention: &MentionData,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<Url, LemmyError> {
|
||||||
let fetch_url = format!(
|
let fetch_url = format!(
|
||||||
"{}://{}/.well-known/webfinger?resource=acct:{}@{}",
|
"{}://{}/.well-known/webfinger?resource=acct:{}@{}",
|
||||||
Settings::get().get_protocol_string(),
|
context.settings().get_protocol_string(),
|
||||||
mention.domain,
|
mention.domain,
|
||||||
mention.name,
|
mention.name,
|
||||||
mention.domain
|
mention.domain
|
||||||
);
|
);
|
||||||
debug!("Fetching webfinger url: {}", &fetch_url);
|
debug!("Fetching webfinger url: {}", &fetch_url);
|
||||||
|
|
||||||
let response = retry(|| client.get(&fetch_url).send()).await?;
|
let response = retry(|| context.client().get(&fetch_url).send()).await?;
|
||||||
|
|
||||||
let res: WebfingerResponse = response
|
let res: WebfingerResponse = response
|
||||||
.json()
|
.json()
|
||||||
|
|
|
@ -55,7 +55,10 @@ impl AddMod {
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let id = generate_activity_id(AddType::Add)?;
|
let id = generate_activity_id(
|
||||||
|
AddType::Add,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let add = AddMod {
|
let add = AddMod {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
@ -81,7 +84,7 @@ impl ActivityHandler for AddMod {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
|
verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
|
||||||
|
|
|
@ -84,7 +84,10 @@ impl AnnounceActivity {
|
||||||
object,
|
object,
|
||||||
cc: vec![community.followers_url()],
|
cc: vec![community.followers_url()],
|
||||||
kind: AnnounceType::Announce,
|
kind: AnnounceType::Announce,
|
||||||
id: generate_activity_id(&AnnounceType::Announce)?,
|
id: generate_activity_id(
|
||||||
|
&AnnounceType::Announce,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -100,7 +103,7 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_community(&self.actor, context, request_counter).await?;
|
verify_community(&self.actor, context, request_counter).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -56,6 +56,7 @@ impl BlockUserFromCommunity {
|
||||||
community: &Community,
|
community: &Community,
|
||||||
target: &Person,
|
target: &Person,
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
|
context: &LemmyContext,
|
||||||
) -> Result<BlockUserFromCommunity, LemmyError> {
|
) -> Result<BlockUserFromCommunity, LemmyError> {
|
||||||
Ok(BlockUserFromCommunity {
|
Ok(BlockUserFromCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
|
@ -63,7 +64,10 @@ impl BlockUserFromCommunity {
|
||||||
object: ObjectId::new(target.actor_id()),
|
object: ObjectId::new(target.actor_id()),
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: [ObjectId::new(community.actor_id())],
|
||||||
kind: BlockType::Block,
|
kind: BlockType::Block,
|
||||||
id: generate_activity_id(BlockType::Block)?,
|
id: generate_activity_id(
|
||||||
|
BlockType::Block,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -75,7 +79,7 @@ impl BlockUserFromCommunity {
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let block = BlockUserFromCommunity::new(community, target, actor)?;
|
let block = BlockUserFromCommunity::new(community, target, actor, context)?;
|
||||||
let block_id = block.id.clone();
|
let block_id = block.id.clone();
|
||||||
|
|
||||||
let activity = AnnouncableActivities::BlockUserFromCommunity(block);
|
let activity = AnnouncableActivities::BlockUserFromCommunity(block);
|
||||||
|
@ -91,7 +95,7 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{check_is_apub_id_valid, CommunityType};
|
use crate::{check_is_apub_id_valid, CommunityType};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -19,14 +19,16 @@ async fn list_community_follower_inboxes(
|
||||||
) -> Result<Vec<Url>, LemmyError> {
|
) -> Result<Vec<Url>, LemmyError> {
|
||||||
Ok(
|
Ok(
|
||||||
vec![
|
vec![
|
||||||
community.get_follower_inboxes(context.pool()).await?,
|
community
|
||||||
|
.get_follower_inboxes(context.pool(), &context.settings())
|
||||||
|
.await?,
|
||||||
additional_inboxes,
|
additional_inboxes,
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.unique()
|
.unique()
|
||||||
.filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname))
|
.filter(|inbox| inbox.host_str() != Some(&context.settings().hostname))
|
||||||
.filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
|
.filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok())
|
||||||
.map(|inbox| inbox.to_owned())
|
.map(|inbox| inbox.to_owned())
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -57,7 +57,10 @@ impl RemoveMod {
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let id = generate_activity_id(RemoveType::Remove)?;
|
let id = generate_activity_id(
|
||||||
|
RemoveType::Remove,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let remove = RemoveMod {
|
let remove = RemoveMod {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
@ -83,7 +86,7 @@ impl ActivityHandler for RemoveMod {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
if let Some(target) = &self.target {
|
if let Some(target) = &self.target {
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
|
|
|
@ -52,9 +52,12 @@ impl UndoBlockUserFromCommunity {
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let block = BlockUserFromCommunity::new(community, target, actor)?;
|
let block = BlockUserFromCommunity::new(community, target, actor, context)?;
|
||||||
|
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let id = generate_activity_id(
|
||||||
|
UndoType::Undo,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let undo = UndoBlockUserFromCommunity {
|
let undo = UndoBlockUserFromCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
@ -79,7 +82,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
|
|
@ -55,7 +55,10 @@ impl UpdateCommunity {
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let id = generate_activity_id(UpdateType::Update)?;
|
let id = generate_activity_id(
|
||||||
|
UpdateType::Update,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let update = UpdateCommunity {
|
let update = UpdateCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
@ -79,7 +82,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -96,8 +99,12 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let updated_community =
|
let updated_community = Group::from_apub_to_form(
|
||||||
Group::from_apub_to_form(&self.object, &community.actor_id.clone().into()).await?;
|
&self.object,
|
||||||
|
&community.actor_id.clone().into(),
|
||||||
|
&context.settings(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let cf = CommunityForm {
|
let cf = CommunityForm {
|
||||||
name: updated_community.name,
|
name: updated_community.name,
|
||||||
title: updated_community.title,
|
title: updated_community.title,
|
||||||
|
|
|
@ -87,7 +87,7 @@ impl ActivityHandler for Delete {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object,
|
&self.object,
|
||||||
self,
|
self,
|
||||||
|
@ -138,6 +138,7 @@ impl Delete {
|
||||||
community: &Community,
|
community: &Community,
|
||||||
object_id: Url,
|
object_id: Url,
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
|
context: &LemmyContext,
|
||||||
) -> Result<Delete, LemmyError> {
|
) -> Result<Delete, LemmyError> {
|
||||||
Ok(Delete {
|
Ok(Delete {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
|
@ -146,7 +147,10 @@ impl Delete {
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: [ObjectId::new(community.actor_id())],
|
||||||
kind: DeleteType::Delete,
|
kind: DeleteType::Delete,
|
||||||
summary,
|
summary,
|
||||||
id: generate_activity_id(DeleteType::Delete)?,
|
id: generate_activity_id(
|
||||||
|
DeleteType::Delete,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -158,7 +162,7 @@ impl Delete {
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let delete = Delete::new(actor, community, object_id, summary)?;
|
let delete = Delete::new(actor, community, object_id, summary, context)?;
|
||||||
let delete_id = delete.id.clone();
|
let delete_id = delete.id.clone();
|
||||||
|
|
||||||
let activity = AnnouncableActivities::Delete(delete);
|
let activity = AnnouncableActivities::Delete(delete);
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl ActivityHandler for UndoDelete {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object.object,
|
&self.object.object,
|
||||||
|
@ -106,9 +106,12 @@ impl UndoDelete {
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let object = Delete::new(actor, community, object_id, summary)?;
|
let object = Delete::new(actor, community, object_id, summary, context)?;
|
||||||
|
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let id = generate_activity_id(
|
||||||
|
UndoType::Undo,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let undo = UndoDelete {
|
let undo = UndoDelete {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
|
|
@ -61,7 +61,10 @@ impl AcceptFollowCommunity {
|
||||||
to: ObjectId::new(person.actor_id()),
|
to: ObjectId::new(person.actor_id()),
|
||||||
object: follow,
|
object: follow,
|
||||||
kind: AcceptType::Accept,
|
kind: AcceptType::Accept,
|
||||||
id: generate_activity_id(AcceptType::Accept)?,
|
id: generate_activity_id(
|
||||||
|
AcceptType::Accept,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -77,7 +80,7 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_urls_match(self.to.inner(), self.object.actor())?;
|
verify_urls_match(self.to.inner(), self.object.actor())?;
|
||||||
verify_urls_match(self.actor(), self.object.to.inner())?;
|
verify_urls_match(self.actor(), self.object.to.inner())?;
|
||||||
verify_community(&self.actor, context, request_counter).await?;
|
verify_community(&self.actor, context, request_counter).await?;
|
||||||
|
|
|
@ -48,13 +48,17 @@ impl FollowCommunity {
|
||||||
pub(in crate::activities::following) fn new(
|
pub(in crate::activities::following) fn new(
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
community: &Community,
|
community: &Community,
|
||||||
|
context: &LemmyContext,
|
||||||
) -> Result<FollowCommunity, LemmyError> {
|
) -> Result<FollowCommunity, LemmyError> {
|
||||||
Ok(FollowCommunity {
|
Ok(FollowCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(community.actor_id()),
|
to: ObjectId::new(community.actor_id()),
|
||||||
object: ObjectId::new(community.actor_id()),
|
object: ObjectId::new(community.actor_id()),
|
||||||
kind: FollowType::Follow,
|
kind: FollowType::Follow,
|
||||||
id: generate_activity_id(FollowType::Follow)?,
|
id: generate_activity_id(
|
||||||
|
FollowType::Follow,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -74,7 +78,7 @@ impl FollowCommunity {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let follow = FollowCommunity::new(actor, community)?;
|
let follow = FollowCommunity::new(actor, community, context)?;
|
||||||
let inbox = vec![community.inbox_url.clone().into()];
|
let inbox = vec![community.inbox_url.clone().into()];
|
||||||
send_activity_new(context, &follow, &follow.id, actor, inbox, true).await
|
send_activity_new(context, &follow, &follow.id, actor, inbox, true).await
|
||||||
}
|
}
|
||||||
|
@ -87,7 +91,7 @@ impl ActivityHandler for FollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_urls_match(self.to.inner(), self.object.inner())?;
|
verify_urls_match(self.to.inner(), self.object.inner())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -49,13 +49,16 @@ impl UndoFollowCommunity {
|
||||||
community: &Community,
|
community: &Community,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let object = FollowCommunity::new(actor, community)?;
|
let object = FollowCommunity::new(actor, community, context)?;
|
||||||
let undo = UndoFollowCommunity {
|
let undo = UndoFollowCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(community.actor_id()),
|
to: ObjectId::new(community.actor_id()),
|
||||||
object,
|
object,
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
id: generate_activity_id(UndoType::Undo)?,
|
id: generate_activity_id(
|
||||||
|
UndoType::Undo,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -71,7 +74,7 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_urls_match(self.to.inner(), self.object.object.inner())?;
|
verify_urls_match(self.to.inner(), self.object.object.inner())?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor(), self.object.actor())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
|
|
|
@ -91,8 +91,8 @@ async fn verify_community(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> {
|
fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result<(), LemmyError> {
|
||||||
check_is_apub_id_valid(activity.actor(), false)?;
|
check_is_apub_id_valid(activity.actor(), false, settings)?;
|
||||||
verify_domains_match(activity.id_unchecked(), activity.actor())?;
|
verify_domains_match(activity.id_unchecked(), activity.actor())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -146,13 +146,13 @@ fn verify_add_remove_moderator_target(
|
||||||
|
|
||||||
/// Generate a unique ID for an activity, in the format:
|
/// Generate a unique ID for an activity, in the format:
|
||||||
/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
|
/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
|
||||||
fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
|
fn generate_activity_id<T>(kind: T, protocol_and_hostname: &str) -> Result<Url, ParseError>
|
||||||
where
|
where
|
||||||
T: ToString,
|
T: ToString,
|
||||||
{
|
{
|
||||||
let id = format!(
|
let id = format!(
|
||||||
"{}/activities/{}/{}",
|
"{}/activities/{}/{}",
|
||||||
Settings::get().get_protocol_and_hostname(),
|
protocol_and_hostname,
|
||||||
kind.to_string().to_lowercase(),
|
kind.to_string().to_lowercase(),
|
||||||
Uuid::new_v4()
|
Uuid::new_v4()
|
||||||
);
|
);
|
||||||
|
|
|
@ -59,7 +59,10 @@ impl CreateOrUpdatePost {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let id = generate_activity_id(kind.clone())?;
|
let id = generate_activity_id(
|
||||||
|
kind.clone(),
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let create_or_update = CreateOrUpdatePost {
|
let create_or_update = CreateOrUpdatePost {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
@ -83,7 +86,7 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
let community = self.cc[0].dereference(context, request_counter).await?;
|
let community = self.cc[0].dereference(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
match self.kind {
|
match self.kind {
|
||||||
|
|
|
@ -42,7 +42,10 @@ impl CreateOrUpdatePrivateMessage {
|
||||||
let recipient =
|
let recipient =
|
||||||
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
||||||
|
|
||||||
let id = generate_activity_id(kind.clone())?;
|
let id = generate_activity_id(
|
||||||
|
kind.clone(),
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let create_or_update = CreateOrUpdatePrivateMessage {
|
let create_or_update = CreateOrUpdatePrivateMessage {
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
@ -63,7 +66,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
|
|
@ -39,13 +39,17 @@ impl DeletePrivateMessage {
|
||||||
pub(in crate::activities::private_message) fn new(
|
pub(in crate::activities::private_message) fn new(
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
pm: &PrivateMessage,
|
pm: &PrivateMessage,
|
||||||
|
context: &LemmyContext,
|
||||||
) -> Result<DeletePrivateMessage, LemmyError> {
|
) -> Result<DeletePrivateMessage, LemmyError> {
|
||||||
Ok(DeletePrivateMessage {
|
Ok(DeletePrivateMessage {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(actor.actor_id()),
|
to: ObjectId::new(actor.actor_id()),
|
||||||
object: pm.ap_id.clone().into(),
|
object: pm.ap_id.clone().into(),
|
||||||
kind: DeleteType::Delete,
|
kind: DeleteType::Delete,
|
||||||
id: generate_activity_id(DeleteType::Delete)?,
|
id: generate_activity_id(
|
||||||
|
DeleteType::Delete,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -55,7 +59,7 @@ impl DeletePrivateMessage {
|
||||||
pm: &PrivateMessage,
|
pm: &PrivateMessage,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let delete = DeletePrivateMessage::new(actor, pm)?;
|
let delete = DeletePrivateMessage::new(actor, pm, context)?;
|
||||||
let delete_id = delete.id.clone();
|
let delete_id = delete.id.clone();
|
||||||
|
|
||||||
let recipient_id = pm.recipient_id;
|
let recipient_id = pm.recipient_id;
|
||||||
|
@ -73,7 +77,7 @@ impl ActivityHandler for DeletePrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), &self.object)?;
|
verify_domains_match(self.actor.inner(), &self.object)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -50,8 +50,11 @@ impl UndoDeletePrivateMessage {
|
||||||
let recipient =
|
let recipient =
|
||||||
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
||||||
|
|
||||||
let object = DeletePrivateMessage::new(actor, pm)?;
|
let object = DeletePrivateMessage::new(actor, pm, context)?;
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let id = generate_activity_id(
|
||||||
|
UndoType::Undo,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let undo = UndoDeletePrivateMessage {
|
let undo = UndoDeletePrivateMessage {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(recipient.actor_id()),
|
to: ObjectId::new(recipient.actor_id()),
|
||||||
|
@ -73,7 +76,7 @@ impl ActivityHandler for UndoDeletePrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor(), self.object.actor())?;
|
||||||
verify_domains_match(self.actor(), &self.object.object)?;
|
verify_domains_match(self.actor(), &self.object.object)?;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_api_common::blocking;
|
||||||
use lemmy_db_queries::DbPool;
|
use lemmy_db_queries::DbPool;
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
impl ActorType for Community {
|
impl ActorType for Community {
|
||||||
|
@ -40,7 +40,11 @@ impl CommunityType for Community {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For a given community, returns the inboxes of all followers.
|
/// For a given community, returns the inboxes of all followers.
|
||||||
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
|
async fn get_follower_inboxes(
|
||||||
|
&self,
|
||||||
|
pool: &DbPool,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> Result<Vec<Url>, LemmyError> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
let follows = blocking(pool, move |conn| {
|
let follows = blocking(pool, move |conn| {
|
||||||
|
@ -54,7 +58,7 @@ impl CommunityType for Community {
|
||||||
.map(|i| i.into_inner())
|
.map(|i| i.into_inner())
|
||||||
.unique()
|
.unique()
|
||||||
// Don't send to blocked instances
|
// Don't send to blocked instances
|
||||||
.filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
|
.filter(|inbox| check_is_apub_id_valid(inbox, false, settings).is_ok())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(inboxes)
|
Ok(inboxes)
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
|
|
|
@ -64,8 +64,11 @@ impl UndoVote {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let object = Vote::new(object, actor, &community, kind.clone())?;
|
let object = Vote::new(object, actor, &community, kind.clone(), context)?;
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let id = generate_activity_id(
|
||||||
|
UndoType::Undo,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
let undo_vote = UndoVote {
|
let undo_vote = UndoVote {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: [PublicUrl::Public],
|
||||||
|
@ -88,7 +91,7 @@ impl ActivityHandler for UndoVote {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor(), self.object.actor())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
|
|
@ -77,6 +77,7 @@ impl Vote {
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
community: &Community,
|
community: &Community,
|
||||||
kind: VoteType,
|
kind: VoteType,
|
||||||
|
context: &LemmyContext,
|
||||||
) -> Result<Vote, LemmyError> {
|
) -> Result<Vote, LemmyError> {
|
||||||
Ok(Vote {
|
Ok(Vote {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
|
@ -84,7 +85,7 @@ impl Vote {
|
||||||
object: ObjectId::new(object.ap_id()),
|
object: ObjectId::new(object.ap_id()),
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: [ObjectId::new(community.actor_id())],
|
||||||
kind: kind.clone(),
|
kind: kind.clone(),
|
||||||
id: generate_activity_id(kind)?,
|
id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -101,7 +102,7 @@ impl Vote {
|
||||||
Community::read(conn, community_id)
|
Community::read(conn, community_id)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let vote = Vote::new(object, actor, &community, kind)?;
|
let vote = Vote::new(object, actor, &community, kind, context)?;
|
||||||
let vote_id = vote.id.clone();
|
let vote_id = vote.id.clone();
|
||||||
|
|
||||||
let activity = AnnouncableActivities::Vote(vote);
|
let activity = AnnouncableActivities::Vote(vote);
|
||||||
|
@ -116,7 +117,7 @@ impl ActivityHandler for Vote {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self)?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use background_jobs::{
|
||||||
WorkerConfig,
|
WorkerConfig,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
@ -56,7 +56,7 @@ pub(crate) async fn send_activity_new<T>(
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
if !Settings::get().federation.enabled || inboxes.is_empty() {
|
if !context.settings().federation.enabled || inboxes.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ where
|
||||||
|
|
||||||
// Don't send anything to ourselves
|
// Don't send anything to ourselves
|
||||||
// TODO: this should be a debug assert
|
// TODO: this should be a debug assert
|
||||||
let hostname = Settings::get().get_hostname_without_port()?;
|
let hostname = context.settings().get_hostname_without_port()?;
|
||||||
let inboxes: Vec<&Url> = inboxes
|
let inboxes: Vec<&Url> = inboxes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|i| i.domain().expect("valid inbox url") != hostname)
|
.filter(|i| i.domain().expect("valid inbox url") != hostname)
|
||||||
|
|
|
@ -73,8 +73,13 @@ pub(crate) async fn fetch_community_outbox(
|
||||||
outbox: &Url,
|
outbox: &Url,
|
||||||
recursion_counter: &mut i32,
|
recursion_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let outbox =
|
let outbox = fetch_remote_object::<OrderedCollection>(
|
||||||
fetch_remote_object::<OrderedCollection>(context.client(), outbox, recursion_counter).await?;
|
context.client(),
|
||||||
|
&context.settings(),
|
||||||
|
outbox,
|
||||||
|
recursion_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let outbox_activities = outbox.items().context(location_info!())?.clone();
|
let outbox_activities = outbox.items().context(location_info!())?.clone();
|
||||||
let mut outbox_activities = outbox_activities.many().context(location_info!())?;
|
let mut outbox_activities = outbox_activities.many().context(location_info!())?;
|
||||||
if outbox_activities.len() > 20 {
|
if outbox_activities.len() > 20 {
|
||||||
|
@ -98,8 +103,12 @@ async fn fetch_community_mods(
|
||||||
recursion_counter: &mut i32,
|
recursion_counter: &mut i32,
|
||||||
) -> Result<Vec<Url>, LemmyError> {
|
) -> Result<Vec<Url>, LemmyError> {
|
||||||
if let Some(mods_url) = &group.moderators {
|
if let Some(mods_url) = &group.moderators {
|
||||||
let mods =
|
let mods = fetch_remote_object::<OrderedCollection>(
|
||||||
fetch_remote_object::<OrderedCollection>(context.client(), mods_url, recursion_counter)
|
context.client(),
|
||||||
|
&context.settings(),
|
||||||
|
mods_url,
|
||||||
|
recursion_counter,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let mods = mods
|
let mods = mods
|
||||||
.items()
|
.items()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{check_is_apub_id_valid, APUB_JSON_CONTENT_TYPE};
|
use crate::{check_is_apub_id_valid, APUB_JSON_CONTENT_TYPE};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_utils::{request::retry, LemmyError};
|
use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError};
|
||||||
use log::info;
|
use log::info;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -18,6 +18,7 @@ static MAX_REQUEST_NUMBER: i32 = 25;
|
||||||
/// timeouts etc.
|
/// timeouts etc.
|
||||||
pub(in crate::fetcher) async fn fetch_remote_object<Response>(
|
pub(in crate::fetcher) async fn fetch_remote_object<Response>(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
|
settings: &Settings,
|
||||||
url: &Url,
|
url: &Url,
|
||||||
recursion_counter: &mut i32,
|
recursion_counter: &mut i32,
|
||||||
) -> Result<Response, LemmyError>
|
) -> Result<Response, LemmyError>
|
||||||
|
@ -28,7 +29,7 @@ where
|
||||||
if *recursion_counter > MAX_REQUEST_NUMBER {
|
if *recursion_counter > MAX_REQUEST_NUMBER {
|
||||||
return Err(anyhow!("Maximum recursion depth reached").into());
|
return Err(anyhow!("Maximum recursion depth reached").into());
|
||||||
}
|
}
|
||||||
check_is_apub_id_valid(url, false)?;
|
check_is_apub_id_valid(url, false, settings)?;
|
||||||
|
|
||||||
let timeout = Duration::from_secs(60);
|
let timeout = Duration::from_secs(60);
|
||||||
|
|
||||||
|
|
107
crates/apub/src/fetcher/objects.rs
Normal file
107
crates/apub/src/fetcher/objects.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use crate::{
|
||||||
|
fetcher::fetch::fetch_remote_object,
|
||||||
|
objects::{comment::Note, post::Page, FromApub},
|
||||||
|
PostOrComment,
|
||||||
|
};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use diesel::result::Error::NotFound;
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_db_queries::{ApubObject, Crud};
|
||||||
|
use lemmy_db_schema::source::{comment::Comment, post::Post};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use log::debug;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
/// Gets a post by its apub ID. If it exists locally, it is returned directly. Otherwise it is
|
||||||
|
/// pulled from its apub ID, inserted and returned.
|
||||||
|
///
|
||||||
|
/// The parent community is also pulled if necessary. Comments are not pulled.
|
||||||
|
pub(crate) async fn get_or_fetch_and_insert_post(
|
||||||
|
post_ap_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
recursion_counter: &mut i32,
|
||||||
|
) -> Result<Post, LemmyError> {
|
||||||
|
let post_ap_id_owned = post_ap_id.to_owned();
|
||||||
|
let post = blocking(context.pool(), move |conn| {
|
||||||
|
Post::read_from_apub_id(conn, &post_ap_id_owned.into())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
match post {
|
||||||
|
Ok(p) => Ok(p),
|
||||||
|
Err(NotFound {}) => {
|
||||||
|
debug!("Fetching and creating remote post: {}", post_ap_id);
|
||||||
|
let page = fetch_remote_object::<Page>(
|
||||||
|
context.client(),
|
||||||
|
&context.settings(),
|
||||||
|
post_ap_id,
|
||||||
|
recursion_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let post = Post::from_apub(&page, context, post_ap_id, recursion_counter).await?;
|
||||||
|
|
||||||
|
Ok(post)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a comment by its apub ID. If it exists locally, it is returned directly. Otherwise it is
|
||||||
|
/// pulled from its apub ID, inserted and returned.
|
||||||
|
///
|
||||||
|
/// The parent community, post and comment are also pulled if necessary.
|
||||||
|
pub(crate) async fn get_or_fetch_and_insert_comment(
|
||||||
|
comment_ap_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
recursion_counter: &mut i32,
|
||||||
|
) -> Result<Comment, LemmyError> {
|
||||||
|
let comment_ap_id_owned = comment_ap_id.to_owned();
|
||||||
|
let comment = blocking(context.pool(), move |conn| {
|
||||||
|
Comment::read_from_apub_id(conn, &comment_ap_id_owned.into())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
match comment {
|
||||||
|
Ok(p) => Ok(p),
|
||||||
|
Err(NotFound {}) => {
|
||||||
|
debug!(
|
||||||
|
"Fetching and creating remote comment and its parents: {}",
|
||||||
|
comment_ap_id
|
||||||
|
);
|
||||||
|
let comment = fetch_remote_object::<Note>(
|
||||||
|
context.client(),
|
||||||
|
&context.settings(),
|
||||||
|
comment_ap_id,
|
||||||
|
recursion_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let comment = Comment::from_apub(&comment, context, comment_ap_id, recursion_counter).await?;
|
||||||
|
|
||||||
|
let post_id = comment.post_id;
|
||||||
|
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
if post.locked {
|
||||||
|
return Err(anyhow!("Post is locked").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(comment)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_or_fetch_and_insert_post_or_comment(
|
||||||
|
ap_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
recursion_counter: &mut i32,
|
||||||
|
) -> Result<PostOrComment, LemmyError> {
|
||||||
|
Ok(
|
||||||
|
match get_or_fetch_and_insert_post(ap_id, context, recursion_counter).await {
|
||||||
|
Ok(p) => PostOrComment::Post(Box::new(p)),
|
||||||
|
Err(_) => {
|
||||||
|
let c = get_or_fetch_and_insert_comment(ap_id, context, recursion_counter).await?;
|
||||||
|
PostOrComment::Comment(Box::new(c))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
80
crates/apub/src/fetcher/person.rs
Normal file
80
crates/apub/src/fetcher/person.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use crate::{
|
||||||
|
fetcher::{fetch::fetch_remote_object, is_deleted, should_refetch_actor},
|
||||||
|
objects::{person::Person as ApubPerson, FromApub},
|
||||||
|
};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use diesel::result::Error::NotFound;
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_db_queries::{source::person::Person_, ApubObject};
|
||||||
|
use lemmy_db_schema::source::person::Person;
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use log::debug;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
/// Get a person from its apub ID.
|
||||||
|
///
|
||||||
|
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
|
||||||
|
/// Otherwise it is fetched from the remote instance, stored and returned.
|
||||||
|
pub(crate) async fn get_or_fetch_and_upsert_person(
|
||||||
|
apub_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
recursion_counter: &mut i32,
|
||||||
|
) -> Result<Person, LemmyError> {
|
||||||
|
let apub_id_owned = apub_id.to_owned();
|
||||||
|
let person = blocking(context.pool(), move |conn| {
|
||||||
|
Person::read_from_apub_id(conn, &apub_id_owned.into())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
match person {
|
||||||
|
// If its older than a day, re-fetch it
|
||||||
|
Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => {
|
||||||
|
debug!("Fetching and updating from remote person: {}", apub_id);
|
||||||
|
let person = fetch_remote_object::<ApubPerson>(
|
||||||
|
context.client(),
|
||||||
|
&context.settings(),
|
||||||
|
apub_id,
|
||||||
|
recursion_counter,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if is_deleted(&person) {
|
||||||
|
// TODO: use Person::update_deleted() once implemented
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
Person::delete_account(conn, u.id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
return Err(anyhow!("Person was deleted by remote instance").into());
|
||||||
|
} else if person.is_err() {
|
||||||
|
return Ok(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
let person = Person::from_apub(&person?, context, apub_id, recursion_counter).await?;
|
||||||
|
|
||||||
|
let person_id = person.id;
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
Person::mark_as_updated(conn, person_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(person)
|
||||||
|
}
|
||||||
|
Ok(u) => Ok(u),
|
||||||
|
Err(NotFound {}) => {
|
||||||
|
debug!("Fetching and creating remote person: {}", apub_id);
|
||||||
|
let person = fetch_remote_object::<ApubPerson>(
|
||||||
|
context.client(),
|
||||||
|
&context.settings(),
|
||||||
|
apub_id,
|
||||||
|
recursion_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let person = Person::from_apub(&person, context, apub_id, recursion_counter).await?;
|
||||||
|
|
||||||
|
Ok(person)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,14 @@ pub async fn search_by_apub_id(
|
||||||
// remote actor, use webfinger to resolve url
|
// remote actor, use webfinger to resolve url
|
||||||
if name.contains('@') {
|
if name.contains('@') {
|
||||||
let (name, domain) = name.splitn(2, '@').collect_tuple().expect("invalid query");
|
let (name, domain) = name.splitn(2, '@').collect_tuple().expect("invalid query");
|
||||||
webfinger_resolve_actor(name, domain, kind, context.client()).await?
|
webfinger_resolve_actor(
|
||||||
|
name,
|
||||||
|
domain,
|
||||||
|
kind,
|
||||||
|
context.client(),
|
||||||
|
context.settings().get_protocol_string(),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
}
|
}
|
||||||
// local actor, read from database and return
|
// local actor, read from database and return
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -23,7 +23,7 @@ use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityFields, ActivityHandler};
|
use lemmy_apub_lib::{ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{source::activity::Activity_, DbPool};
|
use lemmy_db_queries::{source::activity::Activity_, DbPool};
|
||||||
use lemmy_db_schema::source::activity::Activity;
|
use lemmy_db_schema::source::activity::Activity;
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::{info, trace};
|
use log::{info, trace};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -98,10 +98,10 @@ where
|
||||||
if is_activity_already_known(context.pool(), activity.id_unchecked()).await? {
|
if is_activity_already_known(context.pool(), activity.id_unchecked()).await? {
|
||||||
return Ok(HttpResponse::Ok().finish());
|
return Ok(HttpResponse::Ok().finish());
|
||||||
}
|
}
|
||||||
check_is_apub_id_valid(activity.actor(), false)?;
|
check_is_apub_id_valid(activity.actor(), false, &context.settings())?;
|
||||||
info!("Verifying activity {}", activity.id_unchecked().to_string());
|
info!("Verifying activity {}", activity.id_unchecked().to_string());
|
||||||
activity.verify(context, request_counter).await?;
|
activity.verify(context, request_counter).await?;
|
||||||
assert_activity_not_local(&activity)?;
|
assert_activity_not_local(&activity, &context.settings().hostname)?;
|
||||||
|
|
||||||
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
|
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
|
||||||
// if we receive the same activity twice in very quick succession.
|
// if we receive the same activity twice in very quick succession.
|
||||||
|
@ -151,7 +151,7 @@ pub(crate) async fn get_activity(
|
||||||
info: web::Path<ActivityQuery>,
|
info: web::Path<ActivityQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
let settings = Settings::get();
|
let settings = context.settings();
|
||||||
let activity_id = Url::parse(&format!(
|
let activity_id = Url::parse(&format!(
|
||||||
"{}/activities/{}/{}",
|
"{}/activities/{}/{}",
|
||||||
settings.get_protocol_and_hostname(),
|
settings.get_protocol_and_hostname(),
|
||||||
|
@ -187,10 +187,13 @@ pub(crate) async fn is_activity_already_known(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_activity_not_local<T: Debug + ActivityFields>(activity: &T) -> Result<(), LemmyError> {
|
fn assert_activity_not_local<T: Debug + ActivityFields>(
|
||||||
|
activity: &T,
|
||||||
|
hostname: &str,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
|
let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
|
||||||
|
|
||||||
if activity_domain == Settings::get().hostname {
|
if activity_domain == hostname {
|
||||||
return Err(
|
return Err(
|
||||||
anyhow!(
|
anyhow!(
|
||||||
"Error: received activity which was sent by local instance: {:?}",
|
"Error: received activity which was sent by local instance: {:?}",
|
||||||
|
|
|
@ -24,9 +24,9 @@ use sha2::{Digest, Sha256};
|
||||||
static APUB_JSON_CONTENT_TYPE_LONG: &str =
|
static APUB_JSON_CONTENT_TYPE_LONG: &str =
|
||||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
|
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) {
|
||||||
if Settings::get().federation.enabled {
|
if settings.federation.enabled {
|
||||||
println!("federation enabled, host is {}", Settings::get().hostname);
|
println!("federation enabled, host is {}", settings.hostname);
|
||||||
let digest_verifier = VerifyDigest::new(Sha256::new());
|
let digest_verifier = VerifyDigest::new(Sha256::new());
|
||||||
|
|
||||||
let header_guard_accept = guard::Any(guard::Header("Accept", APUB_JSON_CONTENT_TYPE))
|
let header_guard_accept = guard::Any(guard::Header("Accept", APUB_JSON_CONTENT_TYPE))
|
||||||
|
|
|
@ -37,8 +37,8 @@ static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
|
||||||
pub(crate) fn check_is_apub_id_valid(
|
pub(crate) fn check_is_apub_id_valid(
|
||||||
apub_id: &Url,
|
apub_id: &Url,
|
||||||
use_strict_allowlist: bool,
|
use_strict_allowlist: bool,
|
||||||
|
settings: &Settings,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let settings = Settings::get();
|
|
||||||
let domain = apub_id.domain().context(location_info!())?.to_string();
|
let domain = apub_id.domain().context(location_info!())?.to_string();
|
||||||
let local_instance = settings.get_hostname_without_port()?;
|
let local_instance = settings.get_hostname_without_port()?;
|
||||||
|
|
||||||
|
@ -62,22 +62,22 @@ pub(crate) fn check_is_apub_id_valid(
|
||||||
return Err(anyhow!("invalid hostname {}: {}", host, apub_id).into());
|
return Err(anyhow!("invalid hostname {}: {}", host, apub_id).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if apub_id.scheme() != Settings::get().get_protocol_string() {
|
if apub_id.scheme() != settings.get_protocol_string() {
|
||||||
return Err(anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id).into());
|
return Err(anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: might be good to put the part above in one method, and below in another
|
// TODO: might be good to put the part above in one method, and below in another
|
||||||
// (which only gets called in apub::objects)
|
// (which only gets called in apub::objects)
|
||||||
// -> no that doesnt make sense, we still need the code below for blocklist and strict allowlist
|
// -> no that doesnt make sense, we still need the code below for blocklist and strict allowlist
|
||||||
if let Some(blocked) = Settings::get().federation.blocked_instances {
|
if let Some(blocked) = settings.to_owned().federation.blocked_instances {
|
||||||
if blocked.contains(&domain) {
|
if blocked.contains(&domain) {
|
||||||
return Err(anyhow!("{} is in federation blocklist", domain).into());
|
return Err(anyhow!("{} is in federation blocklist", domain).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut allowed) = Settings::get().federation.allowed_instances {
|
if let Some(mut allowed) = settings.to_owned().federation.allowed_instances {
|
||||||
// Only check allowlist if this is a community, or strict allowlist is enabled.
|
// Only check allowlist if this is a community, or strict allowlist is enabled.
|
||||||
let strict_allowlist = Settings::get().federation.strict_allowlist;
|
let strict_allowlist = settings.to_owned().federation.strict_allowlist;
|
||||||
if use_strict_allowlist || strict_allowlist {
|
if use_strict_allowlist || strict_allowlist {
|
||||||
// need to allow this explicitly because apub receive might contain objects from our local
|
// need to allow this explicitly because apub receive might contain objects from our local
|
||||||
// instance.
|
// instance.
|
||||||
|
@ -128,7 +128,11 @@ trait ActorType {
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
pub trait CommunityType {
|
pub trait CommunityType {
|
||||||
fn followers_url(&self) -> Url;
|
fn followers_url(&self) -> Url;
|
||||||
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
|
async fn get_follower_inboxes(
|
||||||
|
&self,
|
||||||
|
pool: &DbPool,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> Result<Vec<Url>, LemmyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum EndpointType {
|
pub enum EndpointType {
|
||||||
|
@ -160,12 +164,9 @@ fn generate_apub_endpoint_for_domain(
|
||||||
pub fn generate_apub_endpoint(
|
pub fn generate_apub_endpoint(
|
||||||
endpoint_type: EndpointType,
|
endpoint_type: EndpointType,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
) -> Result<DbUrl, ParseError> {
|
) -> Result<DbUrl, ParseError> {
|
||||||
generate_apub_endpoint_for_domain(
|
generate_apub_endpoint_for_domain(endpoint_type, name, protocol_and_hostname)
|
||||||
endpoint_type,
|
|
||||||
name,
|
|
||||||
&Settings::get().get_protocol_and_hostname(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||||
|
@ -200,6 +201,7 @@ fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
|
||||||
pub fn build_actor_id_from_shortname(
|
pub fn build_actor_id_from_shortname(
|
||||||
endpoint_type: EndpointType,
|
endpoint_type: EndpointType,
|
||||||
short_name: &str,
|
short_name: &str,
|
||||||
|
settings: &Settings,
|
||||||
) -> Result<DbUrl, ParseError> {
|
) -> Result<DbUrl, ParseError> {
|
||||||
let split = short_name.split('@').collect::<Vec<&str>>();
|
let split = short_name.split('@').collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
@ -207,9 +209,9 @@ pub fn build_actor_id_from_shortname(
|
||||||
|
|
||||||
// If there's no @, its local
|
// If there's no @, its local
|
||||||
let domain = if split.len() == 1 {
|
let domain = if split.len() == 1 {
|
||||||
Settings::get().get_protocol_and_hostname()
|
settings.get_protocol_and_hostname()
|
||||||
} else {
|
} else {
|
||||||
format!("{}://{}", Settings::get().get_protocol_string(), split[1])
|
format!("{}://{}", settings.get_protocol_string(), split[1])
|
||||||
};
|
};
|
||||||
|
|
||||||
generate_apub_endpoint_for_domain(endpoint_type, name, &domain)
|
generate_apub_endpoint_for_domain(endpoint_type, name, &domain)
|
||||||
|
|
|
@ -222,7 +222,7 @@ impl FromApub for Comment {
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = ¬e.source.content;
|
let content = ¬e.source.content;
|
||||||
let content_slurs_removed = remove_slurs(content);
|
let content_slurs_removed = remove_slurs(content, &context.settings().slur_regex());
|
||||||
|
|
||||||
let form = CommentForm {
|
let form = CommentForm {
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
|
|
|
@ -24,6 +24,7 @@ use lemmy_db_schema::{
|
||||||
source::community::{Community, CommunityForm},
|
source::community::{Community, CommunityForm},
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
settings::structs::Settings,
|
||||||
utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
|
utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
|
@ -74,6 +75,7 @@ impl Group {
|
||||||
pub(crate) async fn from_apub_to_form(
|
pub(crate) async fn from_apub_to_form(
|
||||||
group: &Group,
|
group: &Group,
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
settings: &Settings,
|
||||||
) -> Result<CommunityForm, LemmyError> {
|
) -> Result<CommunityForm, LemmyError> {
|
||||||
let actor_id = Some(group.id(expected_domain)?.clone().into());
|
let actor_id = Some(group.id(expected_domain)?.clone().into());
|
||||||
let name = group.preferred_username.clone();
|
let name = group.preferred_username.clone();
|
||||||
|
@ -81,9 +83,10 @@ impl Group {
|
||||||
let description = group.source.clone().map(|s| s.content);
|
let description = group.source.clone().map(|s| s.content);
|
||||||
let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into());
|
let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into());
|
||||||
|
|
||||||
check_slurs(&name)?;
|
let slur_regex = &settings.slur_regex();
|
||||||
check_slurs(&title)?;
|
check_slurs(&name, slur_regex)?;
|
||||||
check_slurs_opt(&description)?;
|
check_slurs(&title, slur_regex)?;
|
||||||
|
check_slurs_opt(&description, slur_regex)?;
|
||||||
|
|
||||||
Ok(CommunityForm {
|
Ok(CommunityForm {
|
||||||
name,
|
name,
|
||||||
|
@ -175,7 +178,7 @@ impl FromApub for Community {
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<Community, LemmyError> {
|
) -> Result<Community, LemmyError> {
|
||||||
let form = Group::from_apub_to_form(group, expected_domain).await?;
|
let form = Group::from_apub_to_form(group, expected_domain, &context.settings()).await?;
|
||||||
|
|
||||||
let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??;
|
let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??;
|
||||||
update_community_mods(group, &community, context, request_counter).await?;
|
update_community_mods(group, &community, context, request_counter).await?;
|
||||||
|
|
|
@ -150,10 +150,12 @@ impl FromApub for DbPerson {
|
||||||
UserTypes::Service => true,
|
UserTypes::Service => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
check_slurs(&name)?;
|
let slur_regex = &context.settings().slur_regex();
|
||||||
check_slurs_opt(&display_name)?;
|
check_slurs(&name, slur_regex)?;
|
||||||
check_slurs_opt(&bio)?;
|
check_slurs_opt(&display_name, slur_regex)?;
|
||||||
check_is_apub_id_valid(&person.id, false)?;
|
check_slurs_opt(&bio, slur_regex)?;
|
||||||
|
|
||||||
|
check_is_apub_id_valid(&person.id, false, &context.settings())?;
|
||||||
|
|
||||||
let person_form = PersonForm {
|
let person_form = PersonForm {
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl Page {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = extract_community(&self.to, context, request_counter).await?;
|
let community = extract_community(&self.to, context, request_counter).await?;
|
||||||
|
|
||||||
check_slurs(&self.name)?;
|
check_slurs(&self.name, &context.settings().slur_regex())?;
|
||||||
verify_domains_match(self.attributed_to.inner(), &self.id)?;
|
verify_domains_match(self.attributed_to.inner(), &self.id)?;
|
||||||
verify_person_in_community(
|
verify_person_in_community(
|
||||||
&self.attributed_to,
|
&self.attributed_to,
|
||||||
|
@ -191,7 +191,7 @@ impl FromApub for Post {
|
||||||
|
|
||||||
let thumbnail_url: Option<Url> = page.image.clone().map(|i| i.url);
|
let thumbnail_url: Option<Url> = page.image.clone().map(|i| i.url);
|
||||||
let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url {
|
let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url {
|
||||||
fetch_site_data(context.client(), Some(url)).await
|
fetch_site_data(context.client(), &context.settings(), Some(url)).await
|
||||||
} else {
|
} else {
|
||||||
(None, thumbnail_url)
|
(None, thumbnail_url)
|
||||||
};
|
};
|
||||||
|
@ -199,7 +199,10 @@ impl FromApub for Post {
|
||||||
.map(|u| (u.title, u.description, u.html))
|
.map(|u| (u.title, u.description, u.html))
|
||||||
.unwrap_or((None, None, None));
|
.unwrap_or((None, None, None));
|
||||||
|
|
||||||
let body_slurs_removed = page.source.as_ref().map(|s| remove_slurs(&s.content));
|
let body_slurs_removed = page
|
||||||
|
.source
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| remove_slurs(&s.content, &context.settings().slur_regex()));
|
||||||
let form = PostForm {
|
let form = PostForm {
|
||||||
name: page.name.clone(),
|
name: page.name.clone(),
|
||||||
url: page.url.clone().map(|u| u.into()),
|
url: page.url.clone().map(|u| u.into()),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
request::{retry, RecvError},
|
request::{retry, RecvError},
|
||||||
settings::structs::Settings,
|
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
@ -38,6 +37,7 @@ pub async fn webfinger_resolve_actor(
|
||||||
domain: &str,
|
domain: &str,
|
||||||
webfinger_type: WebfingerType,
|
webfinger_type: WebfingerType,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
|
protocol_string: &str,
|
||||||
) -> Result<Url, LemmyError> {
|
) -> Result<Url, LemmyError> {
|
||||||
let webfinger_type = match webfinger_type {
|
let webfinger_type = match webfinger_type {
|
||||||
WebfingerType::Person => "acct",
|
WebfingerType::Person => "acct",
|
||||||
|
@ -45,11 +45,7 @@ pub async fn webfinger_resolve_actor(
|
||||||
};
|
};
|
||||||
let fetch_url = format!(
|
let fetch_url = format!(
|
||||||
"{}://{}/.well-known/webfinger?resource={}:{}@{}",
|
"{}://{}/.well-known/webfinger?resource={}:{}@{}",
|
||||||
Settings::get().get_protocol_string(),
|
protocol_string, domain, webfinger_type, name, domain
|
||||||
domain,
|
|
||||||
webfinger_type,
|
|
||||||
name,
|
|
||||||
domain
|
|
||||||
);
|
);
|
||||||
debug!("Fetching webfinger url: {}", &fetch_url);
|
debug!("Fetching webfinger url: {}", &fetch_url);
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,5 @@ pub mod person_mention;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod post_report;
|
pub mod post_report;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
|
pub mod secret;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
|
|
19
crates/db_queries/src/source/secret.rs
Normal file
19
crates/db_queries/src/source/secret.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use diesel::{result::Error, *};
|
||||||
|
use lemmy_db_schema::source::secret::Secret;
|
||||||
|
|
||||||
|
pub trait Secret_ {
|
||||||
|
fn init(conn: &PgConnection) -> Result<Secret, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Secret_ for Secret {
|
||||||
|
/// Initialize the Secrets from the DB.
|
||||||
|
/// Warning: You should only call this once.
|
||||||
|
fn init(conn: &PgConnection) -> Result<Secret, Error> {
|
||||||
|
read_secrets(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_secrets(conn: &PgConnection) -> Result<Secret, Error> {
|
||||||
|
use lemmy_db_schema::schema::secret::dsl::*;
|
||||||
|
secret.first::<Secret>(conn)
|
||||||
|
}
|
|
@ -551,6 +551,13 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
secret(id) {
|
||||||
|
id -> Int4,
|
||||||
|
jwt_secret -> Varchar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
joinable!(comment_alias_1 -> person_alias_1 (creator_id));
|
joinable!(comment_alias_1 -> person_alias_1 (creator_id));
|
||||||
joinable!(comment -> comment_alias_1 (parent_id));
|
joinable!(comment -> comment_alias_1 (parent_id));
|
||||||
joinable!(person_mention -> person_alias_1 (recipient_id));
|
joinable!(person_mention -> person_alias_1 (recipient_id));
|
||||||
|
|
|
@ -12,4 +12,5 @@ pub mod person_mention;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod post_report;
|
pub mod post_report;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
|
pub mod secret;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
|
|
8
crates/db_schema/src/source/secret.rs
Normal file
8
crates/db_schema/src/source/secret.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::schema::secret;
|
||||||
|
|
||||||
|
#[derive(Queryable, Identifiable, Clone)]
|
||||||
|
#[table_name = "secret"]
|
||||||
|
pub struct Secret {
|
||||||
|
pub id: i32,
|
||||||
|
pub jwt_secret: String,
|
||||||
|
}
|
|
@ -19,12 +19,7 @@ use lemmy_db_views::{
|
||||||
site_view::SiteView,
|
site_view::SiteView,
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView};
|
use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{claims::Claims, utils::markdown_to_html, LemmyError};
|
||||||
claims::Claims,
|
|
||||||
settings::structs::Settings,
|
|
||||||
utils::markdown_to_html,
|
|
||||||
LemmyError,
|
|
||||||
};
|
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use rss::{
|
use rss::{
|
||||||
extension::dublincore::DublinCoreExtensionBuilder,
|
extension::dublincore::DublinCoreExtensionBuilder,
|
||||||
|
@ -98,7 +93,7 @@ async fn get_feed_data(
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let items = create_post_items(posts)?;
|
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
|
||||||
|
|
||||||
let mut channel_builder = ChannelBuilder::default();
|
let mut channel_builder = ChannelBuilder::default();
|
||||||
channel_builder
|
channel_builder
|
||||||
|
@ -108,7 +103,7 @@ async fn get_feed_data(
|
||||||
site_view.site.name,
|
site_view.site.name,
|
||||||
listing_type.to_string()
|
listing_type.to_string()
|
||||||
))
|
))
|
||||||
.link(Settings::get().get_protocol_and_hostname())
|
.link(context.settings().get_protocol_and_hostname())
|
||||||
.items(items);
|
.items(items);
|
||||||
|
|
||||||
if let Some(site_desc) = site_view.site.description {
|
if let Some(site_desc) = site_view.site.description {
|
||||||
|
@ -141,11 +136,20 @@ async fn get_feed(
|
||||||
_ => return Err(ErrorBadRequest(LemmyError::from(anyhow!("wrong_type")))),
|
_ => return Err(ErrorBadRequest(LemmyError::from(anyhow!("wrong_type")))),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let jwt_secret = context.secret().jwt_secret.to_owned();
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
|
|
||||||
let builder = blocking(context.pool(), move |conn| match request_type {
|
let builder = blocking(context.pool(), move |conn| match request_type {
|
||||||
RequestType::User => get_feed_user(conn, &sort_type, param),
|
RequestType::User => get_feed_user(conn, &sort_type, ¶m, &protocol_and_hostname),
|
||||||
RequestType::Community => get_feed_community(conn, &sort_type, param),
|
RequestType::Community => get_feed_community(conn, &sort_type, ¶m, &protocol_and_hostname),
|
||||||
RequestType::Front => get_feed_front(conn, &sort_type, param),
|
RequestType::Front => get_feed_front(
|
||||||
RequestType::Inbox => get_feed_inbox(conn, param),
|
conn,
|
||||||
|
&jwt_secret,
|
||||||
|
&sort_type,
|
||||||
|
¶m,
|
||||||
|
&protocol_and_hostname,
|
||||||
|
),
|
||||||
|
RequestType::Inbox => get_feed_inbox(conn, &jwt_secret, ¶m, &protocol_and_hostname),
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
.map_err(ErrorBadRequest)?;
|
.map_err(ErrorBadRequest)?;
|
||||||
|
@ -170,10 +174,11 @@ fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
|
||||||
fn get_feed_user(
|
fn get_feed_user(
|
||||||
conn: &PgConnection,
|
conn: &PgConnection,
|
||||||
sort_type: &SortType,
|
sort_type: &SortType,
|
||||||
user_name: String,
|
user_name: &str,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
) -> Result<ChannelBuilder, LemmyError> {
|
) -> Result<ChannelBuilder, LemmyError> {
|
||||||
let site_view = SiteView::read(conn)?;
|
let site_view = SiteView::read(conn)?;
|
||||||
let person = Person::find_by_name(conn, &user_name)?;
|
let person = Person::find_by_name(conn, user_name)?;
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(conn)
|
let posts = PostQueryBuilder::create(conn)
|
||||||
.listing_type(ListingType::All)
|
.listing_type(ListingType::All)
|
||||||
|
@ -181,7 +186,7 @@ fn get_feed_user(
|
||||||
.creator_id(person.id)
|
.creator_id(person.id)
|
||||||
.list()?;
|
.list()?;
|
||||||
|
|
||||||
let items = create_post_items(posts)?;
|
let items = create_post_items(posts, protocol_and_hostname)?;
|
||||||
|
|
||||||
let mut channel_builder = ChannelBuilder::default();
|
let mut channel_builder = ChannelBuilder::default();
|
||||||
channel_builder
|
channel_builder
|
||||||
|
@ -196,10 +201,11 @@ fn get_feed_user(
|
||||||
fn get_feed_community(
|
fn get_feed_community(
|
||||||
conn: &PgConnection,
|
conn: &PgConnection,
|
||||||
sort_type: &SortType,
|
sort_type: &SortType,
|
||||||
community_name: String,
|
community_name: &str,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
) -> Result<ChannelBuilder, LemmyError> {
|
) -> Result<ChannelBuilder, LemmyError> {
|
||||||
let site_view = SiteView::read(conn)?;
|
let site_view = SiteView::read(conn)?;
|
||||||
let community = Community::read_from_name(conn, &community_name)?;
|
let community = Community::read_from_name(conn, community_name)?;
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(conn)
|
let posts = PostQueryBuilder::create(conn)
|
||||||
.listing_type(ListingType::All)
|
.listing_type(ListingType::All)
|
||||||
|
@ -207,7 +213,7 @@ fn get_feed_community(
|
||||||
.community_id(community.id)
|
.community_id(community.id)
|
||||||
.list()?;
|
.list()?;
|
||||||
|
|
||||||
let items = create_post_items(posts)?;
|
let items = create_post_items(posts, protocol_and_hostname)?;
|
||||||
|
|
||||||
let mut channel_builder = ChannelBuilder::default();
|
let mut channel_builder = ChannelBuilder::default();
|
||||||
channel_builder
|
channel_builder
|
||||||
|
@ -225,31 +231,30 @@ fn get_feed_community(
|
||||||
|
|
||||||
fn get_feed_front(
|
fn get_feed_front(
|
||||||
conn: &PgConnection,
|
conn: &PgConnection,
|
||||||
|
jwt_secret: &str,
|
||||||
sort_type: &SortType,
|
sort_type: &SortType,
|
||||||
jwt: String,
|
jwt: &str,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
) -> Result<ChannelBuilder, LemmyError> {
|
) -> Result<ChannelBuilder, LemmyError> {
|
||||||
let site_view = SiteView::read(conn)?;
|
let site_view = SiteView::read(conn)?;
|
||||||
let local_user_id = LocalUserId(Claims::decode(&jwt)?.claims.sub);
|
let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub);
|
||||||
let local_user = LocalUser::read(conn, local_user_id)?;
|
let local_user = LocalUser::read(conn, local_user_id)?;
|
||||||
let person_id = local_user.person_id;
|
|
||||||
let show_bot_accounts = local_user.show_bot_accounts;
|
|
||||||
let show_read_posts = local_user.show_read_posts;
|
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(conn)
|
let posts = PostQueryBuilder::create(conn)
|
||||||
.listing_type(ListingType::Subscribed)
|
.listing_type(ListingType::Subscribed)
|
||||||
.my_person_id(person_id)
|
.my_person_id(local_user.person_id)
|
||||||
.show_bot_accounts(show_bot_accounts)
|
.show_bot_accounts(local_user.show_bot_accounts)
|
||||||
.show_read_posts(show_read_posts)
|
.show_read_posts(local_user.show_read_posts)
|
||||||
.sort(*sort_type)
|
.sort(*sort_type)
|
||||||
.list()?;
|
.list()?;
|
||||||
|
|
||||||
let items = create_post_items(posts)?;
|
let items = create_post_items(posts, protocol_and_hostname)?;
|
||||||
|
|
||||||
let mut channel_builder = ChannelBuilder::default();
|
let mut channel_builder = ChannelBuilder::default();
|
||||||
channel_builder
|
channel_builder
|
||||||
.namespaces(RSS_NAMESPACE.to_owned())
|
.namespaces(RSS_NAMESPACE.to_owned())
|
||||||
.title(&format!("{} - Subscribed", site_view.site.name))
|
.title(&format!("{} - Subscribed", site_view.site.name))
|
||||||
.link(Settings::get().get_protocol_and_hostname())
|
.link(protocol_and_hostname)
|
||||||
.items(items);
|
.items(items);
|
||||||
|
|
||||||
if let Some(site_desc) = site_view.site.description {
|
if let Some(site_desc) = site_view.site.description {
|
||||||
|
@ -259,9 +264,14 @@ fn get_feed_front(
|
||||||
Ok(channel_builder)
|
Ok(channel_builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
|
fn get_feed_inbox(
|
||||||
|
conn: &PgConnection,
|
||||||
|
jwt_secret: &str,
|
||||||
|
jwt: &str,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
|
) -> Result<ChannelBuilder, LemmyError> {
|
||||||
let site_view = SiteView::read(conn)?;
|
let site_view = SiteView::read(conn)?;
|
||||||
let local_user_id = LocalUserId(Claims::decode(&jwt)?.claims.sub);
|
let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub);
|
||||||
let local_user = LocalUser::read(conn, local_user_id)?;
|
let local_user = LocalUser::read(conn, local_user_id)?;
|
||||||
let person_id = local_user.person_id;
|
let person_id = local_user.person_id;
|
||||||
let show_bot_accounts = local_user.show_bot_accounts;
|
let show_bot_accounts = local_user.show_bot_accounts;
|
||||||
|
@ -281,16 +291,13 @@ fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, Le
|
||||||
.sort(sort)
|
.sort(sort)
|
||||||
.list()?;
|
.list()?;
|
||||||
|
|
||||||
let items = create_reply_and_mention_items(replies, mentions)?;
|
let items = create_reply_and_mention_items(replies, mentions, protocol_and_hostname)?;
|
||||||
|
|
||||||
let mut channel_builder = ChannelBuilder::default();
|
let mut channel_builder = ChannelBuilder::default();
|
||||||
channel_builder
|
channel_builder
|
||||||
.namespaces(RSS_NAMESPACE.to_owned())
|
.namespaces(RSS_NAMESPACE.to_owned())
|
||||||
.title(&format!("{} - Inbox", site_view.site.name))
|
.title(&format!("{} - Inbox", site_view.site.name))
|
||||||
.link(format!(
|
.link(format!("{}/inbox", protocol_and_hostname,))
|
||||||
"{}/inbox",
|
|
||||||
Settings::get().get_protocol_and_hostname()
|
|
||||||
))
|
|
||||||
.items(items);
|
.items(items);
|
||||||
|
|
||||||
if let Some(site_desc) = site_view.site.description {
|
if let Some(site_desc) = site_view.site.description {
|
||||||
|
@ -303,21 +310,21 @@ fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, Le
|
||||||
fn create_reply_and_mention_items(
|
fn create_reply_and_mention_items(
|
||||||
replies: Vec<CommentView>,
|
replies: Vec<CommentView>,
|
||||||
mentions: Vec<PersonMentionView>,
|
mentions: Vec<PersonMentionView>,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
) -> Result<Vec<Item>, LemmyError> {
|
) -> Result<Vec<Item>, LemmyError> {
|
||||||
let mut reply_items: Vec<Item> = replies
|
let mut reply_items: Vec<Item> = replies
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
let reply_url = format!(
|
let reply_url = format!(
|
||||||
"{}/post/{}/comment/{}",
|
"{}/post/{}/comment/{}",
|
||||||
Settings::get().get_protocol_and_hostname(),
|
protocol_and_hostname, r.post.id, r.comment.id
|
||||||
r.post.id,
|
|
||||||
r.comment.id
|
|
||||||
);
|
);
|
||||||
build_item(
|
build_item(
|
||||||
&r.creator.name,
|
&r.creator.name,
|
||||||
&r.comment.published,
|
&r.comment.published,
|
||||||
&reply_url,
|
&reply_url,
|
||||||
&r.comment.content,
|
&r.comment.content,
|
||||||
|
protocol_and_hostname,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Item>, LemmyError>>()?;
|
.collect::<Result<Vec<Item>, LemmyError>>()?;
|
||||||
|
@ -327,15 +334,14 @@ fn create_reply_and_mention_items(
|
||||||
.map(|m| {
|
.map(|m| {
|
||||||
let mention_url = format!(
|
let mention_url = format!(
|
||||||
"{}/post/{}/comment/{}",
|
"{}/post/{}/comment/{}",
|
||||||
Settings::get().get_protocol_and_hostname(),
|
protocol_and_hostname, m.post.id, m.comment.id
|
||||||
m.post.id,
|
|
||||||
m.comment.id
|
|
||||||
);
|
);
|
||||||
build_item(
|
build_item(
|
||||||
&m.creator.name,
|
&m.creator.name,
|
||||||
&m.comment.published,
|
&m.comment.published,
|
||||||
&mention_url,
|
&mention_url,
|
||||||
&m.comment.content,
|
&m.comment.content,
|
||||||
|
protocol_and_hostname,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Item>, LemmyError>>()?;
|
.collect::<Result<Vec<Item>, LemmyError>>()?;
|
||||||
|
@ -349,14 +355,11 @@ fn build_item(
|
||||||
published: &NaiveDateTime,
|
published: &NaiveDateTime,
|
||||||
url: &str,
|
url: &str,
|
||||||
content: &str,
|
content: &str,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
) -> Result<Item, LemmyError> {
|
) -> Result<Item, LemmyError> {
|
||||||
let mut i = ItemBuilder::default();
|
let mut i = ItemBuilder::default();
|
||||||
i.title(format!("Reply from {}", creator_name));
|
i.title(format!("Reply from {}", creator_name));
|
||||||
let author_url = format!(
|
let author_url = format!("{}/u/{}", protocol_and_hostname, creator_name);
|
||||||
"{}/u/{}",
|
|
||||||
Settings::get().get_protocol_and_hostname(),
|
|
||||||
creator_name
|
|
||||||
);
|
|
||||||
i.author(format!(
|
i.author(format!(
|
||||||
"/u/{} <a href=\"{}\">(link)</a>",
|
"/u/{} <a href=\"{}\">(link)</a>",
|
||||||
creator_name, author_url
|
creator_name, author_url
|
||||||
|
@ -377,7 +380,10 @@ fn build_item(
|
||||||
Ok(i.build().map_err(|e| anyhow!(e))?)
|
Ok(i.build().map_err(|e| anyhow!(e))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_post_items(posts: Vec<PostView>) -> Result<Vec<Item>, LemmyError> {
|
fn create_post_items(
|
||||||
|
posts: Vec<PostView>,
|
||||||
|
protocol_and_hostname: &str,
|
||||||
|
) -> Result<Vec<Item>, LemmyError> {
|
||||||
let mut items: Vec<Item> = Vec::new();
|
let mut items: Vec<Item> = Vec::new();
|
||||||
|
|
||||||
for p in posts {
|
for p in posts {
|
||||||
|
@ -391,11 +397,7 @@ fn create_post_items(posts: Vec<PostView>) -> Result<Vec<Item>, LemmyError> {
|
||||||
let dt = DateTime::<Utc>::from_utc(p.post.published, Utc);
|
let dt = DateTime::<Utc>::from_utc(p.post.published, Utc);
|
||||||
i.pub_date(dt.to_rfc2822());
|
i.pub_date(dt.to_rfc2822());
|
||||||
|
|
||||||
let post_url = format!(
|
let post_url = format!("{}/post/{}", protocol_and_hostname, p.post.id);
|
||||||
"{}/post/{}",
|
|
||||||
Settings::get().get_protocol_and_hostname(),
|
|
||||||
p.post.id
|
|
||||||
);
|
|
||||||
i.link(post_url.to_owned());
|
i.link(post_url.to_owned());
|
||||||
i.comments(post_url.to_owned());
|
i.comments(post_url.to_owned());
|
||||||
let guid = GuidBuilder::default()
|
let guid = GuidBuilder::default()
|
||||||
|
@ -405,11 +407,7 @@ fn create_post_items(posts: Vec<PostView>) -> Result<Vec<Item>, LemmyError> {
|
||||||
.map_err(|e| anyhow!(e))?;
|
.map_err(|e| anyhow!(e))?;
|
||||||
i.guid(guid);
|
i.guid(guid);
|
||||||
|
|
||||||
let community_url = format!(
|
let community_url = format!("{}/c/{}", protocol_and_hostname, p.community.name);
|
||||||
"{}/c/{}",
|
|
||||||
Settings::get().get_protocol_and_hostname(),
|
|
||||||
p.community.name
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO add images
|
// TODO add images
|
||||||
let mut description = format!("submitted by <a href=\"{}\">{}</a> to <a href=\"{}\">{}</a><br>{} points | <a href=\"{}\">{} comments</a>",
|
let mut description = format!("submitted by <a href=\"{}\">{}</a> to <a href=\"{}\">{}</a><br>{} points | <a href=\"{}\">{} comments</a>",
|
||||||
|
|
|
@ -2,7 +2,8 @@ use actix_http::http::header::ACCEPT_ENCODING;
|
||||||
use actix_web::{body::BodyStream, http::StatusCode, web::Data, *};
|
use actix_web::{body::BodyStream, http::StatusCode, web::Data, *};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use awc::Client;
|
use awc::Client;
|
||||||
use lemmy_utils::{claims::Claims, rate_limit::RateLimit, settings::structs::Settings, LemmyError};
|
use lemmy_utils::{claims::Claims, rate_limit::RateLimit, LemmyError};
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -46,17 +47,21 @@ async fn upload(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
body: web::Payload,
|
body: web::Payload,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
// TODO: check rate limit here
|
// TODO: check rate limit here
|
||||||
let jwt = req
|
let jwt = req
|
||||||
.cookie("jwt")
|
.cookie("jwt")
|
||||||
.expect("No auth header for picture upload");
|
.expect("No auth header for picture upload");
|
||||||
|
|
||||||
if Claims::decode(jwt.value()).is_err() {
|
if Claims::decode(jwt.value(), &context.secret().jwt_secret).is_err() {
|
||||||
return Ok(HttpResponse::Unauthorized().finish());
|
return Ok(HttpResponse::Unauthorized().finish());
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut client_req = client.request_from(format!("{}/image", pictrs_url()?), req.head());
|
let mut client_req = client.request_from(
|
||||||
|
format!("{}/image", pictrs_url(context.settings().pictrs_url)?),
|
||||||
|
req.head(),
|
||||||
|
);
|
||||||
// remove content-encoding header so that pictrs doesnt send gzipped response
|
// remove content-encoding header so that pictrs doesnt send gzipped response
|
||||||
client_req.headers_mut().remove(ACCEPT_ENCODING);
|
client_req.headers_mut().remove(ACCEPT_ENCODING);
|
||||||
|
|
||||||
|
@ -79,17 +84,28 @@ async fn full_res(
|
||||||
web::Query(params): web::Query<PictrsParams>,
|
web::Query(params): web::Query<PictrsParams>,
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let name = &filename.into_inner();
|
let name = &filename.into_inner();
|
||||||
|
|
||||||
// If there are no query params, the URL is original
|
// If there are no query params, the URL is original
|
||||||
|
let pictrs_url_settings = context.settings().pictrs_url;
|
||||||
let url = if params.format.is_none() && params.thumbnail.is_none() {
|
let url = if params.format.is_none() && params.thumbnail.is_none() {
|
||||||
format!("{}/image/original/{}", pictrs_url()?, name,)
|
format!(
|
||||||
|
"{}/image/original/{}",
|
||||||
|
pictrs_url(pictrs_url_settings)?,
|
||||||
|
name,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Use jpg as a default when none is given
|
// Use jpg as a default when none is given
|
||||||
let format = params.format.unwrap_or_else(|| "jpg".to_string());
|
let format = params.format.unwrap_or_else(|| "jpg".to_string());
|
||||||
|
|
||||||
let mut url = format!("{}/image/process.{}?src={}", pictrs_url()?, format, name,);
|
let mut url = format!(
|
||||||
|
"{}/image/process.{}?src={}",
|
||||||
|
pictrs_url(pictrs_url_settings)?,
|
||||||
|
format,
|
||||||
|
name,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(size) = params.thumbnail {
|
if let Some(size) = params.thumbnail {
|
||||||
url = format!("{}&thumbnail={}", url, size,);
|
url = format!("{}&thumbnail={}", url, size,);
|
||||||
|
@ -135,10 +151,16 @@ async fn delete(
|
||||||
components: web::Path<(String, String)>,
|
components: web::Path<(String, String)>,
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let (token, file) = components.into_inner();
|
let (token, file) = components.into_inner();
|
||||||
|
|
||||||
let url = format!("{}/image/delete/{}/{}", pictrs_url()?, &token, &file);
|
let url = format!(
|
||||||
|
"{}/image/delete/{}/{}",
|
||||||
|
pictrs_url(context.settings().pictrs_url)?,
|
||||||
|
&token,
|
||||||
|
&file
|
||||||
|
);
|
||||||
|
|
||||||
let mut client_req = client.request_from(url, req.head());
|
let mut client_req = client.request_from(url, req.head());
|
||||||
client_req.headers_mut().remove(ACCEPT_ENCODING);
|
client_req.headers_mut().remove(ACCEPT_ENCODING);
|
||||||
|
@ -156,8 +178,6 @@ async fn delete(
|
||||||
Ok(HttpResponse::build(res.status()).body(BodyStream::new(res)))
|
Ok(HttpResponse::build(res.status()).body(BodyStream::new(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pictrs_url() -> Result<String, LemmyError> {
|
fn pictrs_url(pictrs_url: Option<String>) -> Result<String, LemmyError> {
|
||||||
Settings::get()
|
pictrs_url.ok_or_else(|| anyhow!("images_disabled").into())
|
||||||
.pictrs_url
|
|
||||||
.ok_or_else(|| anyhow!("images_disabled").into())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use actix_web::{body::Body, error::ErrorBadRequest, *};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_db_views::site_view::SiteView;
|
use lemmy_db_views::site_view::SiteView;
|
||||||
use lemmy_utils::{settings::structs::Settings, version, LemmyError};
|
use lemmy_utils::{version, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -13,13 +13,15 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
|
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn node_info_well_known() -> Result<HttpResponse<Body>, LemmyError> {
|
async fn node_info_well_known(
|
||||||
|
context: web::Data<LemmyContext>,
|
||||||
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
let node_info = NodeInfoWellKnown {
|
let node_info = NodeInfoWellKnown {
|
||||||
links: NodeInfoWellKnownLinks {
|
links: NodeInfoWellKnownLinks {
|
||||||
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
|
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
|
||||||
href: Url::parse(&format!(
|
href: Url::parse(&format!(
|
||||||
"{}/nodeinfo/2.0.json",
|
"{}/nodeinfo/2.0.json",
|
||||||
Settings::get().get_protocol_and_hostname()
|
&context.settings().get_protocol_and_hostname(),
|
||||||
))?,
|
))?,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -31,7 +33,7 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
|
||||||
.await?
|
.await?
|
||||||
.map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
|
.map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
|
||||||
|
|
||||||
let protocols = if Settings::get().federation.enabled {
|
let protocols = if context.settings().federation.enabled {
|
||||||
vec!["activitypub".to_string()]
|
vec!["activitypub".to_string()]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
|
@ -4,12 +4,7 @@ use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::webfinger::{WebfingerLink, WebfingerResponse};
|
use lemmy_apub_lib::webfinger::{WebfingerLink, WebfingerResponse};
|
||||||
use lemmy_db_queries::source::{community::Community_, person::Person_};
|
use lemmy_db_queries::source::{community::Community_, person::Person_};
|
||||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
use lemmy_db_schema::source::{community::Community, person::Person};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
||||||
settings::structs::Settings,
|
|
||||||
LemmyError,
|
|
||||||
WEBFINGER_COMMUNITY_REGEX,
|
|
||||||
WEBFINGER_USERNAME_REGEX,
|
|
||||||
};
|
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
@ -18,8 +13,8 @@ struct Params {
|
||||||
resource: String,
|
resource: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) {
|
||||||
if Settings::get().federation.enabled {
|
if settings.federation.enabled {
|
||||||
cfg.route(
|
cfg.route(
|
||||||
".well-known/webfinger",
|
".well-known/webfinger",
|
||||||
web::get().to(get_webfinger_response),
|
web::get().to(get_webfinger_response),
|
||||||
|
@ -37,12 +32,16 @@ async fn get_webfinger_response(
|
||||||
info: Query<Params>,
|
info: Query<Params>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX
|
let community_regex_parsed = context
|
||||||
|
.settings()
|
||||||
|
.webfinger_community_regex()
|
||||||
.captures(&info.resource)
|
.captures(&info.resource)
|
||||||
.map(|c| c.get(1))
|
.map(|c| c.get(1))
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let username_regex_parsed = WEBFINGER_USERNAME_REGEX
|
let username_regex_parsed = context
|
||||||
|
.settings()
|
||||||
|
.webfinger_username_regex()
|
||||||
.captures(&info.resource)
|
.captures(&info.resource)
|
||||||
.map(|c| c.get(1))
|
.map(|c| c.get(1))
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
|
@ -35,7 +35,7 @@ strum_macros = "0.21.1"
|
||||||
futures = "0.3.16"
|
futures = "0.3.16"
|
||||||
diesel = "1.4.7"
|
diesel = "1.4.7"
|
||||||
http = "0.2.4"
|
http = "0.2.4"
|
||||||
jsonwebtoken = "7.2.0"
|
|
||||||
deser-hjson = "1.0.2"
|
deser-hjson = "1.0.2"
|
||||||
smart-default = "0.6.0"
|
smart-default = "0.6.0"
|
||||||
webpage = { version = "1.3.0", default-features = false, features = ["serde"] }
|
webpage = { version = "1.3.0", default-features = false, features = ["serde"] }
|
||||||
|
jsonwebtoken = "7.2.0"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::settings::structs::Settings;
|
use crate::LemmyError;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
|
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -15,28 +15,23 @@ pub struct Claims {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Claims {
|
impl Claims {
|
||||||
pub fn decode(jwt: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> {
|
pub fn decode(jwt: &str, jwt_secret: &str) -> Result<TokenData<Claims>, LemmyError> {
|
||||||
let v = Validation {
|
let v = Validation {
|
||||||
validate_exp: false,
|
validate_exp: false,
|
||||||
..Validation::default()
|
..Validation::default()
|
||||||
};
|
};
|
||||||
decode::<Claims>(
|
let key = DecodingKey::from_secret(jwt_secret.as_ref());
|
||||||
jwt,
|
Ok(decode::<Claims>(jwt, &key, &v)?)
|
||||||
&DecodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
|
|
||||||
&v,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jwt(local_user_id: i32) -> Result<Jwt, jsonwebtoken::errors::Error> {
|
pub fn jwt(local_user_id: i32, jwt_secret: &str, hostname: &str) -> Result<Jwt, LemmyError> {
|
||||||
let my_claims = Claims {
|
let my_claims = Claims {
|
||||||
sub: local_user_id,
|
sub: local_user_id,
|
||||||
iss: Settings::get().hostname,
|
iss: hostname.to_string(),
|
||||||
iat: Utc::now().timestamp(),
|
iat: Utc::now().timestamp(),
|
||||||
};
|
};
|
||||||
encode(
|
|
||||||
&Header::default(),
|
let key = EncodingKey::from_secret(jwt_secret.as_ref());
|
||||||
&my_claims,
|
Ok(encode(&Header::default(), &my_claims, &key)?)
|
||||||
&EncodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,10 @@ pub fn send_email(
|
||||||
to_email: &str,
|
to_email: &str,
|
||||||
to_username: &str,
|
to_username: &str,
|
||||||
html: &str,
|
html: &str,
|
||||||
|
settings: &Settings,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let email_config = Settings::get().email.ok_or("no_email_setup")?;
|
let email_config = settings.email.to_owned().ok_or("no_email_setup")?;
|
||||||
let domain = Settings::get().hostname;
|
let domain = settings.hostname.to_owned();
|
||||||
|
|
||||||
let (smtp_server, smtp_port) = {
|
let (smtp_server, smtp_port) = {
|
||||||
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
|
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
|
||||||
|
|
|
@ -6,20 +6,19 @@ extern crate strum_macros;
|
||||||
extern crate smart_default;
|
extern crate smart_default;
|
||||||
|
|
||||||
pub mod apub;
|
pub mod apub;
|
||||||
pub mod claims;
|
|
||||||
pub mod email;
|
pub mod email;
|
||||||
pub mod rate_limit;
|
pub mod rate_limit;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
|
pub mod claims;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
|
||||||
use crate::settings::structs::Settings;
|
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use regex::Regex;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -88,16 +87,3 @@ impl actix_web::error::ResponseError for LemmyError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
|
|
||||||
"^group:([a-z0-9_]{{3,}})@{}$",
|
|
||||||
Settings::get().hostname
|
|
||||||
))
|
|
||||||
.expect("compile webfinger regex");
|
|
||||||
pub static ref WEBFINGER_USERNAME_REGEX: Regex = Regex::new(&format!(
|
|
||||||
"^acct:([a-z0-9_]{{3,}})@{}$",
|
|
||||||
Settings::get().hostname
|
|
||||||
))
|
|
||||||
.expect("compile webfinger regex");
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
use crate::{
|
use crate::{settings::structs::RateLimitConfig, utils::get_ip, IpAddr, LemmyError};
|
||||||
settings::structs::{RateLimitConfig, Settings},
|
|
||||||
utils::get_ip,
|
|
||||||
IpAddr,
|
|
||||||
LemmyError,
|
|
||||||
};
|
|
||||||
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||||
use futures::future::{ok, Ready};
|
use futures::future::{ok, Ready};
|
||||||
use rate_limiter::{RateLimitType, RateLimiter};
|
use rate_limiter::{RateLimitType, RateLimiter};
|
||||||
|
@ -22,11 +17,13 @@ pub struct RateLimit {
|
||||||
// it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this
|
// it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this
|
||||||
// across await points
|
// across await points
|
||||||
pub rate_limiter: Arc<Mutex<RateLimiter>>,
|
pub rate_limiter: Arc<Mutex<RateLimiter>>,
|
||||||
|
pub rate_limit_config: RateLimitConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RateLimited {
|
pub struct RateLimited {
|
||||||
rate_limiter: Arc<Mutex<RateLimiter>>,
|
rate_limiter: Arc<Mutex<RateLimiter>>,
|
||||||
|
rate_limit_config: RateLimitConfig,
|
||||||
type_: RateLimitType,
|
type_: RateLimitType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +52,7 @@ impl RateLimit {
|
||||||
fn kind(&self, type_: RateLimitType) -> RateLimited {
|
fn kind(&self, type_: RateLimitType) -> RateLimited {
|
||||||
RateLimited {
|
RateLimited {
|
||||||
rate_limiter: self.rate_limiter.clone(),
|
rate_limiter: self.rate_limiter.clone(),
|
||||||
|
rate_limit_config: self.rate_limit_config.clone(),
|
||||||
type_,
|
type_,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +69,7 @@ impl RateLimited {
|
||||||
{
|
{
|
||||||
// Does not need to be blocking because the RwLock in settings never held across await points,
|
// Does not need to be blocking because the RwLock in settings never held across await points,
|
||||||
// and the operation here locks only long enough to clone
|
// and the operation here locks only long enough to clone
|
||||||
let rate_limit: RateLimitConfig = Settings::get().rate_limit.unwrap_or_default();
|
let rate_limit = self.rate_limit_config;
|
||||||
|
|
||||||
// before
|
// before
|
||||||
{
|
{
|
||||||
|
|
|
@ -120,9 +120,10 @@ pub(crate) struct PictrsFile {
|
||||||
|
|
||||||
pub(crate) async fn fetch_pictrs(
|
pub(crate) async fn fetch_pictrs(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
|
settings: &Settings,
|
||||||
image_url: &Url,
|
image_url: &Url,
|
||||||
) -> Result<PictrsResponse, LemmyError> {
|
) -> Result<PictrsResponse, LemmyError> {
|
||||||
if let Some(pictrs_url) = Settings::get().pictrs_url {
|
if let Some(pictrs_url) = settings.pictrs_url.to_owned() {
|
||||||
is_image_content_type(client, image_url).await?;
|
is_image_content_type(client, image_url).await?;
|
||||||
|
|
||||||
let fetch_url = format!(
|
let fetch_url = format!(
|
||||||
|
@ -152,6 +153,7 @@ pub(crate) async fn fetch_pictrs(
|
||||||
/// Returns the SiteMetadata, and a Pictrs URL, if there is a picture associated
|
/// Returns the SiteMetadata, and a Pictrs URL, if there is a picture associated
|
||||||
pub async fn fetch_site_data(
|
pub async fn fetch_site_data(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
|
settings: &Settings,
|
||||||
url: Option<&Url>,
|
url: Option<&Url>,
|
||||||
) -> (Option<SiteMetadata>, Option<Url>) {
|
) -> (Option<SiteMetadata>, Option<Url>) {
|
||||||
match &url {
|
match &url {
|
||||||
|
@ -166,16 +168,16 @@ pub async fn fetch_site_data(
|
||||||
Some(metadata_res) => match &metadata_res.image {
|
Some(metadata_res) => match &metadata_res.image {
|
||||||
// Metadata, with image
|
// Metadata, with image
|
||||||
// Try to generate a small thumbnail if there's a full sized one from post-links
|
// Try to generate a small thumbnail if there's a full sized one from post-links
|
||||||
Some(metadata_image) => fetch_pictrs(client, metadata_image)
|
Some(metadata_image) => fetch_pictrs(client, settings, metadata_image)
|
||||||
.await
|
.await
|
||||||
.map(|r| r.files[0].file.to_owned()),
|
.map(|r| r.files[0].file.to_owned()),
|
||||||
// Metadata, but no image
|
// Metadata, but no image
|
||||||
None => fetch_pictrs(client, url)
|
None => fetch_pictrs(client, settings, url)
|
||||||
.await
|
.await
|
||||||
.map(|r| r.files[0].file.to_owned()),
|
.map(|r| r.files[0].file.to_owned()),
|
||||||
},
|
},
|
||||||
// No metadata, try to fetch the URL as an image
|
// No metadata, try to fetch the URL as an image
|
||||||
None => fetch_pictrs(client, url)
|
None => fetch_pictrs(client, settings, url)
|
||||||
.await
|
.await
|
||||||
.map(|r| r.files[0].file.to_owned()),
|
.map(|r| r.files[0].file.to_owned()),
|
||||||
};
|
};
|
||||||
|
@ -185,7 +187,7 @@ pub async fn fetch_site_data(
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
Url::parse(&format!(
|
Url::parse(&format!(
|
||||||
"{}/pictrs/image/{}",
|
"{}/pictrs/image/{}",
|
||||||
Settings::get().get_protocol_and_hostname(),
|
settings.get_protocol_and_hostname(),
|
||||||
p
|
p
|
||||||
))
|
))
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{location_info, settings::structs::Settings, LemmyError};
|
use crate::{location_info, settings::structs::Settings, LemmyError};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use deser_hjson::from_str;
|
use deser_hjson::from_str;
|
||||||
|
use regex::{Regex, RegexBuilder};
|
||||||
use std::{env, fs, io::Error, sync::RwLock};
|
use std::{env, fs, io::Error, sync::RwLock};
|
||||||
|
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
|
@ -17,14 +18,25 @@ impl Settings {
|
||||||
///
|
///
|
||||||
/// Note: The env var `LEMMY_DATABASE_URL` is parsed in
|
/// Note: The env var `LEMMY_DATABASE_URL` is parsed in
|
||||||
/// `lemmy_db_queries/src/lib.rs::get_database_url_from_env()`
|
/// `lemmy_db_queries/src/lib.rs::get_database_url_from_env()`
|
||||||
fn init() -> Result<Self, LemmyError> {
|
/// Warning: Only call this once.
|
||||||
|
pub fn init() -> Result<Self, LemmyError> {
|
||||||
// Read the config file
|
// Read the config file
|
||||||
let config = from_str::<Settings>(&Self::read_config_file()?)?;
|
let mut config = from_str::<Settings>(&Self::read_config_file()?)?;
|
||||||
|
|
||||||
if config.hostname == "unset" {
|
if config.hostname == "unset" {
|
||||||
return Err(anyhow!("Hostname variable is not set!").into());
|
return Err(anyhow!("Hostname variable is not set!").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the regexes
|
||||||
|
config.webfinger_community_regex = Some(
|
||||||
|
Regex::new(&format!("^group:([a-z0-9_]{{3,}})@{}$", config.hostname))
|
||||||
|
.expect("compile webfinger regex"),
|
||||||
|
);
|
||||||
|
config.webfinger_username_regex = Some(
|
||||||
|
Regex::new(&format!("^acct:([a-z0-9_]{{3,}})@{}$", config.hostname))
|
||||||
|
.expect("compile webfinger regex"),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,4 +104,30 @@ impl Settings {
|
||||||
|
|
||||||
Ok(Self::read_config_file()?)
|
Ok(Self::read_config_file()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn webfinger_community_regex(&self) -> Regex {
|
||||||
|
self
|
||||||
|
.webfinger_community_regex
|
||||||
|
.to_owned()
|
||||||
|
.expect("compile webfinger regex")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn webfinger_username_regex(&self) -> Regex {
|
||||||
|
self
|
||||||
|
.webfinger_username_regex
|
||||||
|
.to_owned()
|
||||||
|
.expect("compile webfinger regex")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slur_regex(&self) -> Regex {
|
||||||
|
let mut slurs = r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)".to_string();
|
||||||
|
if let Some(additional_slurs) = &self.additional_slurs {
|
||||||
|
slurs.push('|');
|
||||||
|
slurs.push_str(additional_slurs);
|
||||||
|
};
|
||||||
|
RegexBuilder::new(&slurs)
|
||||||
|
.case_insensitive(true)
|
||||||
|
.build()
|
||||||
|
.expect("compile regex")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
@ -24,14 +25,18 @@ pub struct Settings {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
#[default(true)]
|
#[default(true)]
|
||||||
pub tls_enabled: bool,
|
pub tls_enabled: bool,
|
||||||
#[default("changeme")]
|
|
||||||
pub jwt_secret: String,
|
|
||||||
#[default(None)]
|
#[default(None)]
|
||||||
pub pictrs_url: Option<String>,
|
pub pictrs_url: Option<String>,
|
||||||
#[default(None)]
|
#[default(None)]
|
||||||
pub additional_slurs: Option<String>,
|
pub additional_slurs: Option<String>,
|
||||||
#[default(20)]
|
#[default(20)]
|
||||||
pub actor_name_max_length: usize,
|
pub actor_name_max_length: usize,
|
||||||
|
#[default(None)]
|
||||||
|
#[serde(skip)]
|
||||||
|
pub webfinger_community_regex: Option<Regex>,
|
||||||
|
#[default(None)]
|
||||||
|
#[serde(skip)]
|
||||||
|
pub webfinger_username_regex: Option<Regex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, SmartDefault)]
|
#[derive(Debug, Deserialize, Clone, SmartDefault)]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::utils::{
|
use crate::{
|
||||||
|
settings::structs::Settings,
|
||||||
|
utils::{
|
||||||
is_valid_actor_name,
|
is_valid_actor_name,
|
||||||
is_valid_display_name,
|
is_valid_display_name,
|
||||||
is_valid_matrix_id,
|
is_valid_matrix_id,
|
||||||
|
@ -7,6 +9,7 @@ use crate::utils::{
|
||||||
scrape_text_for_mentions,
|
scrape_text_for_mentions,
|
||||||
slur_check,
|
slur_check,
|
||||||
slurs_vec_to_str,
|
slurs_vec_to_str,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -21,23 +24,28 @@ fn test_mentions_regex() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_actor_name() {
|
fn test_valid_actor_name() {
|
||||||
assert!(is_valid_actor_name("Hello_98"));
|
let actor_name_max_length = Settings::init().unwrap().actor_name_max_length;
|
||||||
assert!(is_valid_actor_name("ten"));
|
assert!(is_valid_actor_name("Hello_98", actor_name_max_length));
|
||||||
assert!(!is_valid_actor_name("Hello-98"));
|
assert!(is_valid_actor_name("ten", actor_name_max_length));
|
||||||
assert!(!is_valid_actor_name("a"));
|
assert!(!is_valid_actor_name("Hello-98", actor_name_max_length));
|
||||||
assert!(!is_valid_actor_name(""));
|
assert!(!is_valid_actor_name("a", actor_name_max_length));
|
||||||
|
assert!(!is_valid_actor_name("", actor_name_max_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_display_name() {
|
fn test_valid_display_name() {
|
||||||
assert!(is_valid_display_name("hello @there"));
|
let actor_name_max_length = Settings::init().unwrap().actor_name_max_length;
|
||||||
assert!(!is_valid_display_name("@hello there"));
|
assert!(is_valid_display_name("hello @there", actor_name_max_length));
|
||||||
|
assert!(!is_valid_display_name(
|
||||||
|
"@hello there",
|
||||||
|
actor_name_max_length
|
||||||
|
));
|
||||||
|
|
||||||
// Make sure zero-space with an @ doesn't work
|
// Make sure zero-space with an @ doesn't work
|
||||||
assert!(!is_valid_display_name(&format!(
|
assert!(!is_valid_display_name(
|
||||||
"{}@my name is",
|
&format!("{}@my name is", '\u{200b}'),
|
||||||
'\u{200b}'
|
actor_name_max_length
|
||||||
)));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -57,11 +65,12 @@ fn test_valid_matrix_id() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slur_filter() {
|
fn test_slur_filter() {
|
||||||
|
let slur_regex = Settings::init().unwrap().slur_regex();
|
||||||
let test =
|
let test =
|
||||||
"faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
|
"faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
|
||||||
let slur_free = "No slurs here";
|
let slur_free = "No slurs here";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
remove_slurs(test),
|
remove_slurs(test, &slur_regex),
|
||||||
"*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text."
|
"*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text."
|
||||||
.to_string()
|
.to_string()
|
||||||
);
|
);
|
||||||
|
@ -76,9 +85,9 @@ fn test_slur_filter() {
|
||||||
];
|
];
|
||||||
let has_slurs_err_str = "No slurs - Niggerz, cocksucker, faggot, kike, retardeds, tranny";
|
let has_slurs_err_str = "No slurs - Niggerz, cocksucker, faggot, kike, retardeds, tranny";
|
||||||
|
|
||||||
assert_eq!(slur_check(test), Err(has_slurs_vec));
|
assert_eq!(slur_check(test, &slur_regex), Err(has_slurs_vec));
|
||||||
assert_eq!(slur_check(slur_free), Ok(()));
|
assert_eq!(slur_check(slur_free, &slur_regex), Ok(()));
|
||||||
if let Err(slur_vec) = slur_check(test) {
|
if let Err(slur_vec) = slur_check(test, &slur_regex) {
|
||||||
assert_eq!(&slurs_vec_to_str(slur_vec), has_slurs_err_str);
|
assert_eq!(&slurs_vec_to_str(slur_vec), has_slurs_err_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
use crate::{settings::structs::Settings, ApiError, IpAddr};
|
use crate::{ApiError, IpAddr};
|
||||||
use actix_web::dev::ConnectionInfo;
|
use actix_web::dev::ConnectionInfo;
|
||||||
use chrono::{DateTime, FixedOffset, NaiveDateTime};
|
use chrono::{DateTime, FixedOffset, NaiveDateTime};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
use regex::{Regex, RegexBuilder};
|
use regex::Regex;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").expect("compile regex");
|
static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").expect("compile regex");
|
||||||
static ref SLUR_REGEX: Regex = {
|
|
||||||
let mut slurs = r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)".to_string();
|
|
||||||
if let Some(additional_slurs) = Settings::get().additional_slurs {
|
|
||||||
slurs.push('|');
|
|
||||||
slurs.push_str(&additional_slurs);
|
|
||||||
};
|
|
||||||
RegexBuilder::new(&slurs).case_insensitive(true).build().expect("compile regex")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").expect("compile regex");
|
static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").expect("compile regex");
|
||||||
// TODO keep this old one, it didn't work with port well tho
|
// TODO keep this old one, it didn't work with port well tho
|
||||||
|
@ -37,12 +28,12 @@ pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
|
||||||
DateTime::<FixedOffset>::from_utc(datetime, FixedOffset::east(0))
|
DateTime::<FixedOffset>::from_utc(datetime, FixedOffset::east(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_slurs(test: &str) -> String {
|
pub fn remove_slurs(test: &str, slur_regex: &Regex) -> String {
|
||||||
SLUR_REGEX.replace_all(test, "*removed*").to_string()
|
slur_regex.replace_all(test, "*removed*").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn slur_check(test: &str) -> Result<(), Vec<&str>> {
|
pub(crate) fn slur_check<'a>(test: &'a str, slur_regex: &'a Regex) -> Result<(), Vec<&'a str>> {
|
||||||
let mut matches: Vec<&str> = SLUR_REGEX.find_iter(test).map(|mat| mat.as_str()).collect();
|
let mut matches: Vec<&str> = slur_regex.find_iter(test).map(|mat| mat.as_str()).collect();
|
||||||
|
|
||||||
// Unique
|
// Unique
|
||||||
matches.sort_unstable();
|
matches.sort_unstable();
|
||||||
|
@ -55,17 +46,17 @@ pub(crate) fn slur_check(test: &str) -> Result<(), Vec<&str>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_slurs(text: &str) -> Result<(), ApiError> {
|
pub fn check_slurs(text: &str, slur_regex: &Regex) -> Result<(), ApiError> {
|
||||||
if let Err(slurs) = slur_check(text) {
|
if let Err(slurs) = slur_check(text, slur_regex) {
|
||||||
Err(ApiError::err(&slurs_vec_to_str(slurs)))
|
Err(ApiError::err(&slurs_vec_to_str(slurs)))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_slurs_opt(text: &Option<String>) -> Result<(), ApiError> {
|
pub fn check_slurs_opt(text: &Option<String>, slur_regex: &Regex) -> Result<(), ApiError> {
|
||||||
match text {
|
match text {
|
||||||
Some(t) => check_slurs(t),
|
Some(t) => check_slurs(t, slur_regex),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,8 +87,8 @@ pub struct MentionData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MentionData {
|
impl MentionData {
|
||||||
pub fn is_local(&self) -> bool {
|
pub fn is_local(&self, hostname: &str) -> bool {
|
||||||
Settings::get().hostname.eq(&self.domain)
|
hostname.eq(&self.domain)
|
||||||
}
|
}
|
||||||
pub fn full_name(&self) -> String {
|
pub fn full_name(&self) -> String {
|
||||||
format!("@{}@{}", &self.name, &self.domain)
|
format!("@{}@{}", &self.name, &self.domain)
|
||||||
|
@ -115,17 +106,16 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
|
||||||
out.into_iter().unique().collect()
|
out.into_iter().unique().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_actor_name(name: &str) -> bool {
|
pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> bool {
|
||||||
name.chars().count() <= Settings::get().actor_name_max_length
|
name.chars().count() <= actor_name_max_length && VALID_ACTOR_NAME_REGEX.is_match(name)
|
||||||
&& VALID_ACTOR_NAME_REGEX.is_match(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't do a regex here, reverse lookarounds not supported
|
// Can't do a regex here, reverse lookarounds not supported
|
||||||
pub fn is_valid_display_name(name: &str) -> bool {
|
pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> bool {
|
||||||
!name.starts_with('@')
|
!name.starts_with('@')
|
||||||
&& !name.starts_with('\u{200b}')
|
&& !name.starts_with('\u{200b}')
|
||||||
&& name.chars().count() >= 3
|
&& name.chars().count() >= 3
|
||||||
&& name.chars().count() <= Settings::get().actor_name_max_length
|
&& name.chars().count() <= actor_name_max_length
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
|
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
|
||||||
|
|
|
@ -14,10 +14,11 @@ use diesel::{
|
||||||
PgConnection,
|
PgConnection,
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{comment::*, post::*};
|
use lemmy_api_common::{comment::*, post::*};
|
||||||
use lemmy_db_schema::{CommunityId, LocalUserId, PostId};
|
use lemmy_db_schema::{source::secret::Secret, CommunityId, LocalUserId, PostId};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
location_info,
|
location_info,
|
||||||
rate_limit::RateLimit,
|
rate_limit::RateLimit,
|
||||||
|
settings::structs::Settings,
|
||||||
ApiError,
|
ApiError,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
IpAddr,
|
IpAddr,
|
||||||
|
@ -71,6 +72,12 @@ pub struct ChatServer {
|
||||||
/// The DB Pool
|
/// The DB Pool
|
||||||
pub(super) pool: Pool<ConnectionManager<PgConnection>>,
|
pub(super) pool: Pool<ConnectionManager<PgConnection>>,
|
||||||
|
|
||||||
|
/// The Settings
|
||||||
|
pub(super) settings: Settings,
|
||||||
|
|
||||||
|
/// The Secrets
|
||||||
|
pub(super) secret: Secret,
|
||||||
|
|
||||||
/// Rate limiting based on rate type and IP addr
|
/// Rate limiting based on rate type and IP addr
|
||||||
pub(super) rate_limiter: RateLimit,
|
pub(super) rate_limiter: RateLimit,
|
||||||
|
|
||||||
|
@ -95,6 +102,7 @@ pub struct SessionInfo {
|
||||||
/// And manages available rooms. Peers send messages to other peers in same
|
/// And manages available rooms. Peers send messages to other peers in same
|
||||||
/// room through `ChatServer`.
|
/// room through `ChatServer`.
|
||||||
impl ChatServer {
|
impl ChatServer {
|
||||||
|
#![allow(clippy::too_many_arguments)]
|
||||||
pub fn startup(
|
pub fn startup(
|
||||||
pool: Pool<ConnectionManager<PgConnection>>,
|
pool: Pool<ConnectionManager<PgConnection>>,
|
||||||
rate_limiter: RateLimit,
|
rate_limiter: RateLimit,
|
||||||
|
@ -102,6 +110,8 @@ impl ChatServer {
|
||||||
message_handler_crud: MessageHandlerCrudType,
|
message_handler_crud: MessageHandlerCrudType,
|
||||||
client: Client,
|
client: Client,
|
||||||
activity_queue: QueueHandle,
|
activity_queue: QueueHandle,
|
||||||
|
settings: Settings,
|
||||||
|
secret: Secret,
|
||||||
) -> ChatServer {
|
) -> ChatServer {
|
||||||
ChatServer {
|
ChatServer {
|
||||||
sessions: HashMap::new(),
|
sessions: HashMap::new(),
|
||||||
|
@ -117,6 +127,8 @@ impl ChatServer {
|
||||||
message_handler_crud,
|
message_handler_crud,
|
||||||
client,
|
client,
|
||||||
activity_queue,
|
activity_queue,
|
||||||
|
settings,
|
||||||
|
secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,6 +464,8 @@ impl ChatServer {
|
||||||
chat_server: ctx.address(),
|
chat_server: ctx.address(),
|
||||||
client: self.client.to_owned(),
|
client: self.client.to_owned(),
|
||||||
activity_queue: self.activity_queue.to_owned(),
|
activity_queue: self.activity_queue.to_owned(),
|
||||||
|
settings: self.settings.to_owned(),
|
||||||
|
secret: self.secret.to_owned(),
|
||||||
};
|
};
|
||||||
let message_handler_crud = self.message_handler_crud;
|
let message_handler_crud = self.message_handler_crud;
|
||||||
let message_handler = self.message_handler;
|
let message_handler = self.message_handler;
|
||||||
|
|
|
@ -5,7 +5,8 @@ use crate::chat_server::ChatServer;
|
||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use background_jobs::QueueHandle;
|
use background_jobs::QueueHandle;
|
||||||
use lemmy_db_queries::DbPool;
|
use lemmy_db_queries::DbPool;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_db_schema::source::secret::Secret;
|
||||||
|
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
@ -20,6 +21,8 @@ pub struct LemmyContext {
|
||||||
pub chat_server: Addr<ChatServer>,
|
pub chat_server: Addr<ChatServer>,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub activity_queue: QueueHandle,
|
pub activity_queue: QueueHandle,
|
||||||
|
pub settings: Settings,
|
||||||
|
pub secret: Secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LemmyContext {
|
impl LemmyContext {
|
||||||
|
@ -28,12 +31,16 @@ impl LemmyContext {
|
||||||
chat_server: Addr<ChatServer>,
|
chat_server: Addr<ChatServer>,
|
||||||
client: Client,
|
client: Client,
|
||||||
activity_queue: QueueHandle,
|
activity_queue: QueueHandle,
|
||||||
|
settings: Settings,
|
||||||
|
secret: Secret,
|
||||||
) -> LemmyContext {
|
) -> LemmyContext {
|
||||||
LemmyContext {
|
LemmyContext {
|
||||||
pool,
|
pool,
|
||||||
chat_server,
|
chat_server,
|
||||||
client,
|
client,
|
||||||
activity_queue,
|
activity_queue,
|
||||||
|
settings,
|
||||||
|
secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn pool(&self) -> &DbPool {
|
pub fn pool(&self) -> &DbPool {
|
||||||
|
@ -48,6 +55,13 @@ impl LemmyContext {
|
||||||
pub fn activity_queue(&self) -> &QueueHandle {
|
pub fn activity_queue(&self) -> &QueueHandle {
|
||||||
&self.activity_queue
|
&self.activity_queue
|
||||||
}
|
}
|
||||||
|
pub fn settings(&self) -> Settings {
|
||||||
|
// TODO hacky solution to be able to hotload the settings.
|
||||||
|
Settings::get()
|
||||||
|
}
|
||||||
|
pub fn secret(&self) -> &Secret {
|
||||||
|
&self.secret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for LemmyContext {
|
impl Clone for LemmyContext {
|
||||||
|
@ -57,6 +71,8 @@ impl Clone for LemmyContext {
|
||||||
chat_server: self.chat_server.clone(),
|
chat_server: self.chat_server.clone(),
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
activity_queue: self.activity_queue.clone(),
|
activity_queue: self.activity_queue.clone(),
|
||||||
|
settings: self.settings.clone(),
|
||||||
|
secret: self.secret.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
hostname: lemmy-alpha:8541
|
hostname: lemmy-alpha:8541
|
||||||
port: 8541
|
port: 8541
|
||||||
tls_enabled: false
|
tls_enabled: false
|
||||||
jwt_secret: changeme
|
|
||||||
setup: {
|
setup: {
|
||||||
admin_username: lemmy_alpha
|
admin_username: lemmy_alpha
|
||||||
admin_password: lemmylemmy
|
admin_password: lemmylemmy
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
hostname: lemmy-beta:8551
|
hostname: lemmy-beta:8551
|
||||||
port: 8551
|
port: 8551
|
||||||
tls_enabled: false
|
tls_enabled: false
|
||||||
jwt_secret: changeme
|
|
||||||
setup: {
|
setup: {
|
||||||
admin_username: lemmy_beta
|
admin_username: lemmy_beta
|
||||||
admin_password: lemmylemmy
|
admin_password: lemmylemmy
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
hostname: lemmy-delta:8571
|
hostname: lemmy-delta:8571
|
||||||
port: 8571
|
port: 8571
|
||||||
tls_enabled: false
|
tls_enabled: false
|
||||||
jwt_secret: changeme
|
|
||||||
setup: {
|
setup: {
|
||||||
admin_username: lemmy_delta
|
admin_username: lemmy_delta
|
||||||
admin_password: lemmylemmy
|
admin_password: lemmylemmy
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
hostname: lemmy-epsilon:8581
|
hostname: lemmy-epsilon:8581
|
||||||
port: 8581
|
port: 8581
|
||||||
tls_enabled: false
|
tls_enabled: false
|
||||||
jwt_secret: changeme
|
|
||||||
setup: {
|
setup: {
|
||||||
admin_username: lemmy_epsilon
|
admin_username: lemmy_epsilon
|
||||||
admin_password: lemmylemmy
|
admin_password: lemmylemmy
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
hostname: lemmy-gamma:8561
|
hostname: lemmy-gamma:8561
|
||||||
port: 8561
|
port: 8561
|
||||||
tls_enabled: false
|
tls_enabled: false
|
||||||
jwt_secret: changeme
|
|
||||||
setup: {
|
setup: {
|
||||||
admin_username: lemmy_gamma
|
admin_username: lemmy_gamma
|
||||||
admin_password: lemmylemmy
|
admin_password: lemmylemmy
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
bind: "0.0.0.0"
|
bind: "0.0.0.0"
|
||||||
# port where lemmy should listen for incoming requests
|
# port where lemmy should listen for incoming requests
|
||||||
port: 8536
|
port: 8536
|
||||||
# json web token for authorization between server and client
|
|
||||||
jwt_secret: "changeme"
|
|
||||||
# settings related to the postgresql database
|
# settings related to the postgresql database
|
||||||
# address where pictrs is available
|
# address where pictrs is available
|
||||||
pictrs_url: "http://pictrs:8080"
|
pictrs_url: "http://pictrs:8080"
|
||||||
|
|
1
migrations/2021-09-20-112945_jwt-secret/down.sql
Normal file
1
migrations/2021-09-20-112945_jwt-secret/down.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
drop table secret;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue