2021-04-01 17:30:24 +00:00
use crate ::{ captcha_as_wav_base64 , Perform } ;
2020-08-18 13:43:50 +00:00
use actix_web ::web ::Data ;
2020-08-13 15:46:31 +00:00
use anyhow ::Context ;
2020-07-10 18:15:41 +00:00
use bcrypt ::verify ;
2020-07-29 13:02:46 +00:00
use captcha ::{ gen , Difficulty } ;
use chrono ::Duration ;
2021-03-25 19:19:40 +00:00
use lemmy_api_common ::{
blocking ,
collect_moderated_communities ,
community ::{ GetFollowedCommunities , GetFollowedCommunitiesResponse } ,
get_local_user_view_from_jwt ,
is_admin ,
password_length_check ,
person ::* ,
2021-02-04 16:34:58 +00:00
} ;
2020-12-21 23:27:42 +00:00
use lemmy_db_queries ::{
2020-08-05 16:03:46 +00:00
diesel_option_overwrite ,
2021-03-02 12:41:48 +00:00
diesel_option_overwrite_to_url ,
2020-12-13 17:04:42 +00:00
source ::{
2020-12-18 18:38:32 +00:00
comment ::Comment_ ,
2021-03-11 04:43:11 +00:00
local_user ::LocalUser_ ,
2020-12-21 13:38:34 +00:00
password_reset_request ::PasswordResetRequest_ ,
2021-03-11 04:43:11 +00:00
person ::Person_ ,
person_mention ::PersonMention_ ,
2020-12-18 18:38:32 +00:00
post ::Post_ ,
2020-12-21 13:38:34 +00:00
private_message ::PrivateMessage_ ,
2020-12-13 17:04:42 +00:00
} ,
2020-07-10 18:15:41 +00:00
Crud ,
SortType ,
} ;
2021-03-11 04:43:11 +00:00
use lemmy_db_schema ::{
naive_now ,
source ::{
comment ::Comment ,
local_user ::{ LocalUser , LocalUserForm } ,
moderator ::* ,
password_reset_request ::* ,
person ::* ,
person_mention ::* ,
post ::Post ,
2021-03-25 19:19:40 +00:00
private_message ::PrivateMessage ,
2021-03-11 04:43:11 +00:00
site ::* ,
} ,
} ;
2020-12-21 16:30:34 +00:00
use lemmy_db_views ::{
comment_report_view ::CommentReportView ,
comment_view ::CommentQueryBuilder ,
2021-03-11 04:43:11 +00:00
local_user_view ::LocalUserView ,
2020-12-21 16:30:34 +00:00
post_report_view ::PostReportView ,
2020-12-21 23:27:42 +00:00
} ;
use lemmy_db_views_actor ::{
community_follower_view ::CommunityFollowerView ,
2021-03-10 22:33:55 +00:00
person_mention_view ::{ PersonMentionQueryBuilder , PersonMentionView } ,
person_view ::PersonViewSafe ,
2020-12-21 16:30:34 +00:00
} ;
2020-07-10 18:15:41 +00:00
use lemmy_utils ::{
2021-02-09 18:26:06 +00:00
claims ::Claims ,
2020-09-14 15:29:50 +00:00
email ::send_email ,
2020-08-13 15:46:31 +00:00
location_info ,
2021-03-01 17:24:11 +00:00
settings ::structs ::Settings ,
2021-04-07 11:38:00 +00:00
utils ::{ generate_random_string , is_valid_display_name , is_valid_matrix_id , naive_from_unix } ,
2021-02-22 18:04:32 +00:00
ApiError ,
2020-09-01 14:25:34 +00:00
ConnectionId ,
LemmyError ,
2020-05-16 14:04:08 +00:00
} ;
2020-09-24 13:53:21 +00:00
use lemmy_websocket ::{
2021-03-25 19:19:40 +00:00
messages ::{ CaptchaItem , SendAllMessage , SendUserRoomMessage } ,
2020-09-24 13:53:21 +00:00
LemmyContext ,
UserOperation ,
} ;
2020-05-16 14:04:08 +00:00
use std ::str ::FromStr ;
2019-05-05 05:20:38 +00:00
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for Login {
2020-04-20 03:59:07 +00:00
type Response = LoginResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < LoginResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & Login = & self ;
2019-05-05 05:20:38 +00:00
// Fetch that username / email
2020-07-01 12:54:29 +00:00
let username_or_email = data . username_or_email . clone ( ) ;
2021-03-10 22:33:55 +00:00
let local_user_view = match blocking ( context . pool ( ) , move | conn | {
LocalUserView ::find_by_email_or_name ( conn , & username_or_email )
2020-07-01 12:54:29 +00:00
} )
. await ?
{
2021-03-10 22:33:55 +00:00
Ok ( uv ) = > uv ,
2021-02-22 18:04:32 +00:00
Err ( _e ) = > return Err ( ApiError ::err ( " couldnt_find_that_username_or_email " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
// Verify the password
2021-03-11 04:43:11 +00:00
let valid : bool = verify (
& data . password ,
& local_user_view . local_user . password_encrypted ,
)
. unwrap_or ( false ) ;
2019-05-05 05:20:38 +00:00
if ! valid {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " password_incorrect " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
// Return the jwt
2020-07-10 18:15:41 +00:00
Ok ( LoginResponse {
2021-03-19 04:31:49 +00:00
jwt : Claims ::jwt ( local_user_view . local_user . id . 0 ) ? ,
2020-07-10 18:15:41 +00:00
} )
2019-05-05 05:20:38 +00:00
}
}
2020-07-29 13:02:46 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for GetCaptcha {
2020-07-29 13:02:46 +00:00
type Response = GetCaptchaResponse ;
async fn perform (
& self ,
2020-08-24 11:58:24 +00:00
context : & Data < LemmyContext > ,
_websocket_id : Option < ConnectionId > ,
2020-07-29 13:02:46 +00:00
) -> Result < Self ::Response , LemmyError > {
2021-03-01 17:24:11 +00:00
let captcha_settings = Settings ::get ( ) . captcha ( ) ;
2020-07-29 13:02:46 +00:00
if ! captcha_settings . enabled {
return Ok ( GetCaptchaResponse { ok : None } ) ;
}
let captcha = match captcha_settings . difficulty . as_str ( ) {
" easy " = > gen ( Difficulty ::Easy ) ,
" medium " = > gen ( Difficulty ::Medium ) ,
" hard " = > gen ( Difficulty ::Hard ) ,
_ = > gen ( Difficulty ::Medium ) ,
} ;
let answer = captcha . chars_as_string ( ) ;
2021-04-01 17:30:24 +00:00
let png = captcha . as_base64 ( ) . expect ( " failed to generate captcha " ) ;
2020-07-29 13:02:46 +00:00
let uuid = uuid ::Uuid ::new_v4 ( ) . to_string ( ) ;
2021-04-01 17:30:24 +00:00
let wav = captcha_as_wav_base64 ( & captcha ) ;
2020-07-29 13:02:46 +00:00
let captcha_item = CaptchaItem {
answer ,
uuid : uuid . to_owned ( ) ,
expires : naive_now ( ) + Duration ::minutes ( 10 ) , // expires in 10 minutes
} ;
2020-08-24 11:58:24 +00:00
// Stores the captcha item on the queue
context . chat_server ( ) . do_send ( captcha_item ) ;
2020-07-29 13:02:46 +00:00
Ok ( GetCaptchaResponse {
2021-03-25 19:30:15 +00:00
ok : Some ( CaptchaResponse { png , wav , uuid } ) ,
2020-07-29 13:02:46 +00:00
} )
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for SaveUserSettings {
2020-04-20 03:59:07 +00:00
type Response = LoginResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < LoginResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & SaveUserSettings = & self ;
2021-03-11 04:43:11 +00:00
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-08-14 02:52:43 +00:00
2021-03-02 12:41:48 +00:00
let avatar = diesel_option_overwrite_to_url ( & data . avatar ) ? ;
let banner = diesel_option_overwrite_to_url ( & data . banner ) ? ;
2020-08-09 02:36:29 +00:00
let email = diesel_option_overwrite ( & data . email ) ;
2020-09-25 15:16:49 +00:00
let bio = diesel_option_overwrite ( & data . bio ) ;
2021-04-01 17:57:45 +00:00
let display_name = diesel_option_overwrite ( & data . display_name ) ;
2020-09-25 15:16:49 +00:00
let matrix_user_id = diesel_option_overwrite ( & data . matrix_user_id ) ;
2020-08-05 16:03:46 +00:00
2020-09-25 15:16:49 +00:00
if let Some ( Some ( bio ) ) = & bio {
if bio . chars ( ) . count ( ) > 300 {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " bio_length_overflow " ) . into ( ) ) ;
2020-08-12 11:13:44 +00:00
}
2020-09-25 15:16:49 +00:00
}
2021-04-01 17:57:45 +00:00
if let Some ( Some ( display_name ) ) = & display_name {
if ! is_valid_display_name ( display_name . trim ( ) ) {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " invalid_username " ) . into ( ) ) ;
2020-09-25 15:16:49 +00:00
}
}
2020-07-10 00:04:09 +00:00
2021-04-07 11:38:00 +00:00
if let Some ( Some ( matrix_user_id ) ) = & matrix_user_id {
if ! is_valid_matrix_id ( matrix_user_id ) {
return Err ( ApiError ::err ( " invalid_matrix_id " ) . into ( ) ) ;
}
}
2021-03-11 04:43:11 +00:00
let local_user_id = local_user_view . local_user . id ;
let person_id = local_user_view . person . id ;
2020-12-26 03:22:25 +00:00
let default_listing_type = data . default_listing_type ;
let default_sort_type = data . default_sort_type ;
2021-04-01 21:39:01 +00:00
let password_encrypted = local_user_view . local_user . password_encrypted ;
2020-12-20 01:10:47 +00:00
2021-03-11 04:43:11 +00:00
let person_form = PersonForm {
name : local_user_view . person . name ,
2020-07-10 00:04:09 +00:00
avatar ,
2020-08-05 16:03:46 +00:00
banner ,
2021-02-04 16:34:58 +00:00
inbox_url : None ,
2021-04-01 17:57:45 +00:00
display_name ,
2021-03-11 04:43:11 +00:00
published : None ,
2019-08-14 02:52:43 +00:00
updated : Some ( naive_now ( ) ) ,
2021-03-11 04:43:11 +00:00
banned : None ,
deleted : None ,
actor_id : None ,
bio ,
local : None ,
2021-03-22 14:28:00 +00:00
admin : None ,
2021-03-11 04:43:11 +00:00
private_key : None ,
public_key : None ,
last_refreshed_at : None ,
shared_inbox_url : None ,
2021-03-20 19:21:51 +00:00
matrix_user_id ,
2021-03-11 04:43:11 +00:00
} ;
let person_res = blocking ( context . pool ( ) , move | conn | {
Person ::update ( conn , person_id , & person_form )
} )
. await ? ;
2021-03-11 22:47:44 +00:00
let _updated_person : Person = match person_res {
2021-03-11 04:43:11 +00:00
Ok ( p ) = > p ,
Err ( _ ) = > {
return Err ( ApiError ::err ( " user_already_exists " ) . into ( ) ) ;
}
} ;
let local_user_form = LocalUserForm {
person_id ,
email ,
password_encrypted ,
2019-08-14 02:52:43 +00:00
show_nsfw : data . show_nsfw ,
2021-03-31 10:54:46 +00:00
show_scores : data . show_scores ,
2019-10-15 19:21:27 +00:00
theme : data . theme . to_owned ( ) ,
2020-12-20 01:10:47 +00:00
default_sort_type ,
default_listing_type ,
2019-12-09 08:24:53 +00:00
lang : data . lang . to_owned ( ) ,
2020-01-02 21:55:54 +00:00
show_avatars : data . show_avatars ,
send_notifications_to_email : data . send_notifications_to_email ,
2019-08-14 02:52:43 +00:00
} ;
2021-03-11 04:43:11 +00:00
let local_user_res = blocking ( context . pool ( ) , move | conn | {
LocalUser ::update ( conn , local_user_id , & local_user_form )
2020-08-18 13:43:50 +00:00
} )
. await ? ;
2021-03-11 22:47:44 +00:00
let updated_local_user = match local_user_res {
Ok ( u ) = > u ,
2020-01-18 01:34:16 +00:00
Err ( e ) = > {
let err_type = if e . to_string ( )
2021-03-11 04:43:11 +00:00
= = " duplicate key value violates unique constraint \" local_user_email_key \" "
2020-01-18 01:34:16 +00:00
{
" email_already_exists "
} else {
" user_already_exists "
} ;
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( err_type ) . into ( ) ) ;
2020-01-18 01:34:16 +00:00
}
2019-08-14 02:52:43 +00:00
} ;
// Return the jwt
2019-09-07 15:35:05 +00:00
Ok ( LoginResponse {
2021-03-19 04:31:49 +00:00
jwt : Claims ::jwt ( updated_local_user . id . 0 ) ? ,
2019-09-07 15:35:05 +00:00
} )
2019-08-14 02:52:43 +00:00
}
}
2019-05-05 05:20:38 +00:00
2021-04-01 21:39:01 +00:00
#[ async_trait::async_trait(?Send) ]
impl Perform for ChangePassword {
type Response = LoginResponse ;
async fn perform (
& self ,
context : & Data < LemmyContext > ,
_websocket_id : Option < ConnectionId > ,
) -> Result < LoginResponse , LemmyError > {
let data : & ChangePassword = & self ;
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
password_length_check ( & data . new_password ) ? ;
// Make sure passwords match
if data . new_password ! = data . new_password_verify {
return Err ( ApiError ::err ( " passwords_dont_match " ) . into ( ) ) ;
}
// Check the old password
let valid : bool = verify (
& data . old_password ,
& local_user_view . local_user . password_encrypted ,
)
. unwrap_or ( false ) ;
if ! valid {
return Err ( ApiError ::err ( " password_incorrect " ) . into ( ) ) ;
}
let local_user_id = local_user_view . local_user . id ;
let new_password = data . new_password . to_owned ( ) ;
let updated_local_user = blocking ( context . pool ( ) , move | conn | {
LocalUser ::update_password ( conn , local_user_id , & new_password )
} )
. await ? ? ;
// Return the jwt
Ok ( LoginResponse {
jwt : Claims ::jwt ( updated_local_user . id . 0 ) ? ,
} )
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for AddAdmin {
2020-04-20 03:59:07 +00:00
type Response = AddAdminResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < AddAdminResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & AddAdmin = & self ;
2021-03-11 04:43:11 +00:00
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
// Make sure user is an admin
2021-03-11 04:43:11 +00:00
is_admin ( & local_user_view ) ? ;
2019-05-05 05:20:38 +00:00
2020-07-01 12:54:29 +00:00
let added = data . added ;
2021-03-15 18:02:27 +00:00
let added_person_id = data . person_id ;
2021-03-11 04:43:11 +00:00
let added_admin = match blocking ( context . pool ( ) , move | conn | {
2021-03-22 14:28:00 +00:00
Person ::add_admin ( conn , added_person_id , added )
2021-03-11 04:43:11 +00:00
} )
. await ?
{
Ok ( a ) = > a ,
Err ( _ ) = > {
return Err ( ApiError ::err ( " couldnt_update_user " ) . into ( ) ) ;
}
} ;
2019-05-05 05:20:38 +00:00
// Mod tables
let form = ModAddForm {
2021-03-11 04:43:11 +00:00
mod_person_id : local_user_view . person . id ,
2021-03-22 14:28:00 +00:00
other_person_id : added_admin . id ,
2019-05-05 05:20:38 +00:00
removed : Some ( ! data . added ) ,
} ;
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn | ModAdd ::create ( conn , & form ) ) . await ? ? ;
2019-05-05 05:20:38 +00:00
2020-08-18 13:43:50 +00:00
let site_creator_id = blocking ( context . pool ( ) , move | conn | {
Site ::read ( conn , 1 ) . map ( | s | s . creator_id )
} )
. await ? ? ;
2020-07-01 12:54:29 +00:00
2021-03-11 04:43:11 +00:00
let mut admins = blocking ( context . pool ( ) , move | conn | PersonViewSafe ::admins ( conn ) ) . await ? ? ;
2020-08-13 15:46:31 +00:00
let creator_index = admins
. iter ( )
2021-03-11 04:43:11 +00:00
. position ( | r | r . person . id = = site_creator_id )
2020-08-13 15:46:31 +00:00
. context ( location_info! ( ) ) ? ;
2021-03-11 04:43:11 +00:00
let creator_person = admins . remove ( creator_index ) ;
admins . insert ( 0 , creator_person ) ;
2019-05-05 05:20:38 +00:00
2020-04-19 22:08:25 +00:00
let res = AddAdminResponse { admins } ;
2020-08-24 11:58:24 +00:00
context . chat_server ( ) . do_send ( SendAllMessage {
op : UserOperation ::AddAdmin ,
response : res . clone ( ) ,
websocket_id ,
} ) ;
2020-04-19 22:08:25 +00:00
Ok ( res )
2019-05-05 05:20:38 +00:00
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2021-03-11 04:43:11 +00:00
impl Perform for BanPerson {
type Response = BanPersonResponse ;
2020-04-20 03:59:07 +00:00
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
websocket_id : Option < ConnectionId > ,
2021-03-11 04:43:11 +00:00
) -> Result < BanPersonResponse , LemmyError > {
let data : & BanPerson = & self ;
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
// Make sure user is an admin
2021-03-11 04:43:11 +00:00
is_admin ( & local_user_view ) ? ;
2019-05-05 05:20:38 +00:00
2020-07-01 12:54:29 +00:00
let ban = data . ban ;
2021-03-11 04:43:11 +00:00
let banned_person_id = data . person_id ;
let ban_person = move | conn : & '_ _ | Person ::ban_person ( conn , banned_person_id , ban ) ;
if blocking ( context . pool ( ) , ban_person ) . await ? . is_err ( ) {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " couldnt_update_user " ) . into ( ) ) ;
2020-07-01 12:54:29 +00:00
}
2019-05-05 05:20:38 +00:00
2020-08-17 18:12:36 +00:00
// Remove their data if that's desired
2020-12-20 01:10:47 +00:00
if data . remove_data {
2020-08-17 18:12:36 +00:00
// Posts
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn : & '_ _ | {
2021-03-11 04:43:11 +00:00
Post ::update_removed_for_creator ( conn , banned_person_id , None , true )
2020-08-17 18:12:36 +00:00
} )
. await ? ? ;
// Communities
2021-04-08 11:29:08 +00:00
// Remove all communities where they're the top mod
// TODO couldn't get group by's working in diesel,
// for now, remove the communities manually
2020-08-17 18:12:36 +00:00
// Comments
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn : & '_ _ | {
2021-03-11 04:43:11 +00:00
Comment ::update_removed_for_creator ( conn , banned_person_id , true )
2020-08-17 18:12:36 +00:00
} )
. await ? ? ;
}
2019-05-05 05:20:38 +00:00
// Mod tables
2021-03-25 19:30:15 +00:00
let expires = data . expires . map ( naive_from_unix ) ;
2019-05-05 05:20:38 +00:00
let form = ModBanForm {
2021-03-11 04:43:11 +00:00
mod_person_id : local_user_view . person . id ,
other_person_id : data . person_id ,
2019-05-05 05:20:38 +00:00
reason : data . reason . to_owned ( ) ,
banned : Some ( data . ban ) ,
2019-12-09 19:08:19 +00:00
expires ,
2019-05-05 05:20:38 +00:00
} ;
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn | ModBan ::create ( conn , & form ) ) . await ? ? ;
2019-05-05 05:20:38 +00:00
2021-03-11 04:43:11 +00:00
let person_id = data . person_id ;
let person_view = blocking ( context . pool ( ) , move | conn | {
PersonViewSafe ::read ( conn , person_id )
2020-08-18 13:43:50 +00:00
} )
. await ? ? ;
2019-05-05 05:20:38 +00:00
2021-03-11 04:43:11 +00:00
let res = BanPersonResponse {
person_view ,
2019-09-07 15:35:05 +00:00
banned : data . ban ,
2020-04-19 22:08:25 +00:00
} ;
2020-08-24 11:58:24 +00:00
context . chat_server ( ) . do_send ( SendAllMessage {
2021-03-10 22:33:55 +00:00
op : UserOperation ::BanPerson ,
2020-08-24 11:58:24 +00:00
response : res . clone ( ) ,
websocket_id ,
} ) ;
2020-04-19 22:08:25 +00:00
Ok ( res )
2019-05-05 05:20:38 +00:00
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for GetReplies {
2020-04-20 03:59:07 +00:00
type Response = GetRepliesResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < GetRepliesResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & GetReplies = & self ;
2021-03-11 04:43:11 +00:00
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
let sort = SortType ::from_str ( & data . sort ) ? ;
2020-07-01 12:54:29 +00:00
let page = data . page ;
let limit = data . limit ;
let unread_only = data . unread_only ;
2021-03-11 04:43:11 +00:00
let person_id = local_user_view . person . id ;
2020-08-18 13:43:50 +00:00
let replies = blocking ( context . pool ( ) , move | conn | {
2020-12-16 18:59:43 +00:00
CommentQueryBuilder ::create ( conn )
2020-07-01 12:54:29 +00:00
. sort ( & sort )
. unread_only ( unread_only )
2021-03-11 04:43:11 +00:00
. recipient_id ( person_id )
. my_person_id ( person_id )
2020-07-01 12:54:29 +00:00
. page ( page )
. limit ( limit )
. list ( )
} )
. await ? ? ;
2019-05-05 05:20:38 +00:00
2020-01-16 14:39:08 +00:00
Ok ( GetRepliesResponse { replies } )
2019-05-05 05:20:38 +00:00
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2021-03-11 04:43:11 +00:00
impl Perform for GetPersonMentions {
type Response = GetPersonMentionsResponse ;
2020-04-20 03:59:07 +00:00
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2021-03-11 04:43:11 +00:00
) -> Result < GetPersonMentionsResponse , LemmyError > {
let data : & GetPersonMentions = & self ;
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-10-20 00:46:29 +00:00
let sort = SortType ::from_str ( & data . sort ) ? ;
2020-07-01 12:54:29 +00:00
let page = data . page ;
let limit = data . limit ;
let unread_only = data . unread_only ;
2021-03-11 04:43:11 +00:00
let person_id = local_user_view . person . id ;
2020-08-18 13:43:50 +00:00
let mentions = blocking ( context . pool ( ) , move | conn | {
2021-03-11 04:43:11 +00:00
PersonMentionQueryBuilder ::create ( conn )
. recipient_id ( person_id )
. my_person_id ( person_id )
2020-07-01 12:54:29 +00:00
. sort ( & sort )
. unread_only ( unread_only )
. page ( page )
. limit ( limit )
. list ( )
} )
. await ? ? ;
2019-10-20 00:46:29 +00:00
2021-03-11 04:43:11 +00:00
Ok ( GetPersonMentionsResponse { mentions } )
2019-10-20 00:46:29 +00:00
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2021-03-11 04:43:11 +00:00
impl Perform for MarkPersonMentionAsRead {
type Response = PersonMentionResponse ;
2020-04-20 03:59:07 +00:00
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2021-03-11 04:43:11 +00:00
) -> Result < PersonMentionResponse , LemmyError > {
let data : & MarkPersonMentionAsRead = & self ;
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-10-20 00:46:29 +00:00
2021-03-11 04:43:11 +00:00
let person_mention_id = data . person_mention_id ;
let read_person_mention = blocking ( context . pool ( ) , move | conn | {
PersonMention ::read ( conn , person_mention_id )
2020-08-18 13:43:50 +00:00
} )
. await ? ? ;
2019-10-20 00:46:29 +00:00
2021-03-11 04:43:11 +00:00
if local_user_view . person . id ! = read_person_mention . recipient_id {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
2020-07-14 16:12:04 +00:00
}
2021-03-11 04:43:11 +00:00
let person_mention_id = read_person_mention . id ;
2020-07-20 14:56:40 +00:00
let read = data . read ;
2021-03-11 04:43:11 +00:00
let update_mention =
move | conn : & '_ _ | PersonMention ::update_read ( conn , person_mention_id , read ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , update_mention ) . await ? . is_err ( ) {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
2020-07-01 12:54:29 +00:00
} ;
2019-10-20 00:46:29 +00:00
2021-03-11 04:43:11 +00:00
let person_mention_id = read_person_mention . id ;
let person_id = local_user_view . person . id ;
let person_mention_view = blocking ( context . pool ( ) , move | conn | {
PersonMentionView ::read ( conn , person_mention_id , Some ( person_id ) )
2020-07-01 12:54:29 +00:00
} )
. await ? ? ;
2019-10-20 00:46:29 +00:00
2021-03-11 04:43:11 +00:00
Ok ( PersonMentionResponse {
person_mention_view ,
} )
2019-10-20 00:46:29 +00:00
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for MarkAllAsRead {
2020-04-20 03:59:07 +00:00
type Response = GetRepliesResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < GetRepliesResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & MarkAllAsRead = & self ;
2021-03-11 04:43:11 +00:00
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
2021-03-11 04:43:11 +00:00
let person_id = local_user_view . person . id ;
2020-08-18 13:43:50 +00:00
let replies = blocking ( context . pool ( ) , move | conn | {
2020-12-16 18:59:43 +00:00
CommentQueryBuilder ::create ( conn )
2021-03-11 04:43:11 +00:00
. my_person_id ( person_id )
. recipient_id ( person_id )
2020-07-01 12:54:29 +00:00
. unread_only ( true )
. page ( 1 )
. limit ( 999 )
. list ( )
} )
. await ? ? ;
2019-05-05 05:20:38 +00:00
2020-07-01 12:54:29 +00:00
// TODO: this should probably be a bulk operation
2020-07-21 01:37:44 +00:00
// Not easy to do as a bulk operation,
// because recipient_id isn't in the comment table
2020-12-15 19:39:18 +00:00
for comment_view in & replies {
let reply_id = comment_view . comment . id ;
2020-07-21 01:37:44 +00:00
let mark_as_read = move | conn : & '_ _ | Comment ::update_read ( conn , reply_id , true ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , mark_as_read ) . await ? . is_err ( ) {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
2020-07-01 12:54:29 +00:00
}
2019-05-05 05:20:38 +00:00
}
2020-07-20 14:56:40 +00:00
// Mark all user mentions as read
2021-03-11 04:43:11 +00:00
let update_person_mentions =
move | conn : & '_ _ | PersonMention ::mark_all_as_read ( conn , person_id ) ;
if blocking ( context . pool ( ) , update_person_mentions )
2020-08-18 13:43:50 +00:00
. await ?
. is_err ( )
{
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
2019-10-20 00:46:29 +00:00
}
2019-05-05 05:20:38 +00:00
2020-07-20 04:29:44 +00:00
// Mark all private_messages as read
2021-03-11 04:43:11 +00:00
let update_pm = move | conn : & '_ _ | PrivateMessage ::mark_all_as_read ( conn , person_id ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , update_pm ) . await ? . is_err ( ) {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " couldnt_update_private_message " ) . into ( ) ) ;
2020-01-22 21:35:29 +00:00
}
2020-01-16 14:39:08 +00:00
Ok ( GetRepliesResponse { replies : vec ! [ ] } )
2019-05-05 05:20:38 +00:00
}
}
2019-10-15 22:09:01 +00:00
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for PasswordReset {
2020-04-20 03:59:07 +00:00
type Response = PasswordResetResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < PasswordResetResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & PasswordReset = & self ;
2019-10-30 03:35:39 +00:00
// Fetch that email
2020-07-01 12:54:29 +00:00
let email = data . email . clone ( ) ;
2021-03-11 04:43:11 +00:00
let local_user_view = match blocking ( context . pool ( ) , move | conn | {
LocalUserView ::find_by_email ( conn , & email )
2020-08-18 13:43:50 +00:00
} )
. await ?
{
2021-03-11 04:43:11 +00:00
Ok ( lu ) = > lu ,
2021-02-22 18:04:32 +00:00
Err ( _e ) = > return Err ( ApiError ::err ( " couldnt_find_that_username_or_email " ) . into ( ) ) ,
2019-10-30 03:35:39 +00:00
} ;
// Generate a random token
let token = generate_random_string ( ) ;
2019-11-02 06:43:21 +00:00
2019-10-30 03:35:39 +00:00
// Insert the row
2020-07-01 12:54:29 +00:00
let token2 = token . clone ( ) ;
2021-03-11 04:43:11 +00:00
let local_user_id = local_user_view . local_user . id ;
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn | {
2021-03-11 04:43:11 +00:00
PasswordResetRequest ::create_token ( conn , local_user_id , & token2 )
2020-07-01 12:54:29 +00:00
} )
. await ? ? ;
2019-10-30 03:35:39 +00:00
// Email the pure token to the user.
// TODO no i18n support here.
2021-03-11 04:43:11 +00:00
let email = & local_user_view . local_user . email . expect ( " email " ) ;
let subject = & format! ( " Password reset for {} " , local_user_view . person . name ) ;
2020-09-25 15:33:00 +00:00
let hostname = & Settings ::get ( ) . get_protocol_and_hostname ( ) ;
2021-03-11 04:43:11 +00:00
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 ) ;
match send_email ( subject , email , & local_user_view . person . name , html ) {
2019-10-30 03:35:39 +00:00
Ok ( _o ) = > _o ,
2021-02-22 18:04:32 +00:00
Err ( _e ) = > return Err ( ApiError ::err ( & _e ) . into ( ) ) ,
2019-10-30 03:35:39 +00:00
} ;
2020-01-16 14:39:08 +00:00
Ok ( PasswordResetResponse { } )
2019-10-30 03:35:39 +00:00
}
}
2020-07-01 12:54:29 +00:00
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for PasswordChange {
2020-04-20 03:59:07 +00:00
type Response = LoginResponse ;
2020-07-01 12:54:29 +00:00
async fn perform (
2020-04-19 22:08:25 +00:00
& self ,
2020-08-18 13:43:50 +00:00
context : & Data < LemmyContext > ,
2020-08-24 11:58:24 +00:00
_websocket_id : Option < ConnectionId > ,
2020-07-01 12:54:29 +00:00
) -> Result < LoginResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & PasswordChange = & self ;
2019-10-30 03:35:39 +00:00
// Fetch the user_id from the token
2020-07-01 12:54:29 +00:00
let token = data . token . clone ( ) ;
2021-03-11 04:43:11 +00:00
let local_user_id = blocking ( context . pool ( ) , move | conn | {
2021-02-26 13:49:58 +00:00
PasswordResetRequest ::read_from_token ( conn , & token ) . map ( | p | p . local_user_id )
2020-07-01 12:54:29 +00:00
} )
. await ? ? ;
2019-10-30 03:35:39 +00:00
2021-03-02 15:36:10 +00:00
password_length_check ( & data . password ) ? ;
2019-10-30 03:35:39 +00:00
// Make sure passwords match
2020-01-02 11:30:00 +00:00
if data . password ! = data . password_verify {
2021-02-22 18:04:32 +00:00
return Err ( ApiError ::err ( " passwords_dont_match " ) . into ( ) ) ;
2019-10-30 03:35:39 +00:00
}
// Update the user with the new password
2020-07-01 12:54:29 +00:00
let password = data . password . clone ( ) ;
2021-03-11 04:43:11 +00:00
let updated_local_user = match blocking ( context . pool ( ) , move | conn | {
LocalUser ::update_password ( conn , local_user_id , & password )
2020-07-01 12:54:29 +00:00
} )
. await ?
{
2021-03-11 04:43:11 +00:00
Ok ( u ) = > u ,
2021-02-22 18:04:32 +00:00
Err ( _e ) = > return Err ( ApiError ::err ( " couldnt_update_user " ) . into ( ) ) ,
2019-10-30 03:35:39 +00:00
} ;
// Return the jwt
Ok ( LoginResponse {
2021-03-19 04:31:49 +00:00
jwt : Claims ::jwt ( updated_local_user . id . 0 ) ? ,
2019-10-30 03:35:39 +00:00
} )
}
}
2020-01-22 21:35:29 +00:00
2020-10-25 02:59:13 +00:00
#[ async_trait::async_trait(?Send) ]
impl Perform for GetReportCount {
type Response = GetReportCountResponse ;
async fn perform (
& self ,
context : & Data < LemmyContext > ,
websocket_id : Option < ConnectionId > ,
) -> Result < GetReportCountResponse , LemmyError > {
let data : & GetReportCount = & self ;
2021-03-11 04:43:11 +00:00
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-10-25 02:59:13 +00:00
2021-03-11 04:43:11 +00:00
let person_id = local_user_view . person . id ;
2020-10-25 02:59:13 +00:00
let community_id = data . community ;
2020-11-04 02:15:11 +00:00
let community_ids =
2021-03-11 04:43:11 +00:00
collect_moderated_communities ( person_id , community_id , context . pool ( ) ) . await ? ;
2020-10-25 02:59:13 +00:00
let res = {
if community_ids . is_empty ( ) {
GetReportCountResponse {
community : None ,
comment_reports : 0 ,
post_reports : 0 ,
}
} else {
let ids = community_ids . clone ( ) ;
2020-11-04 02:15:11 +00:00
let comment_reports = blocking ( context . pool ( ) , move | conn | {
CommentReportView ::get_report_count ( conn , & ids )
} )
. await ? ? ;
2020-10-25 02:59:13 +00:00
let ids = community_ids . clone ( ) ;
2020-11-04 02:15:11 +00:00
let post_reports = blocking ( context . pool ( ) , move | conn | {
PostReportView ::get_report_count ( conn , & ids )
} )
. await ? ? ;
2020-10-25 02:59:13 +00:00
GetReportCountResponse {
community : data . community ,
comment_reports ,
post_reports ,
}
}
} ;
context . chat_server ( ) . do_send ( SendUserRoomMessage {
op : UserOperation ::GetReportCount ,
response : res . clone ( ) ,
2021-03-18 20:25:21 +00:00
local_recipient_id : local_user_view . local_user . id ,
2020-10-25 02:59:13 +00:00
websocket_id ,
} ) ;
Ok ( res )
}
}
2021-03-25 19:19:40 +00:00
#[ async_trait::async_trait(?Send) ]
impl Perform for GetFollowedCommunities {
type Response = GetFollowedCommunitiesResponse ;
async fn perform (
& self ,
context : & Data < LemmyContext > ,
_websocket_id : Option < ConnectionId > ,
) -> Result < GetFollowedCommunitiesResponse , LemmyError > {
let data : & GetFollowedCommunities = & self ;
let local_user_view = get_local_user_view_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
let person_id = local_user_view . person . id ;
let communities = match blocking ( context . pool ( ) , move | conn | {
CommunityFollowerView ::for_person ( conn , person_id )
} )
. await ?
{
Ok ( communities ) = > communities ,
_ = > return Err ( ApiError ::err ( " system_err_login " ) . into ( ) ) ,
} ;
// Return the jwt
Ok ( GetFollowedCommunitiesResponse { communities } )
}
}