2020-05-16 14:04:08 +00:00
use crate ::{
2020-07-29 13:02:46 +00:00
captcha_espeak_wav_base64 ,
2020-10-15 18:23:56 +00:00
check_optional_url ,
2020-09-24 13:53:21 +00:00
claims ::Claims ,
2020-11-04 02:15:11 +00:00
collect_moderated_communities ,
2020-09-24 13:53:21 +00:00
get_user_from_jwt ,
get_user_from_jwt_opt ,
is_admin ,
Perform ,
2020-07-10 18:15:41 +00:00
} ;
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 ;
2020-09-24 13:53:21 +00:00
use lemmy_apub ::ApubObjectType ;
2020-12-21 23:27:42 +00:00
use lemmy_db_queries ::{
2020-08-05 16:03:46 +00:00
diesel_option_overwrite ,
2020-12-13 17:04:42 +00:00
source ::{
2020-12-18 18:38:32 +00:00
comment ::Comment_ ,
2020-12-21 12:28:12 +00:00
community ::Community_ ,
2020-12-21 13:38:34 +00:00
password_reset_request ::PasswordResetRequest_ ,
2020-12-18 18:38:32 +00:00
post ::Post_ ,
2020-12-21 13:38:34 +00:00
private_message ::PrivateMessage_ ,
2020-12-21 14:28:20 +00:00
site ::Site_ ,
2020-12-18 18:38:32 +00:00
user ::User ,
2020-12-21 13:38:34 +00:00
user_mention ::UserMention_ ,
2020-12-13 17:04:42 +00:00
} ,
2020-07-10 18:15:41 +00:00
Crud ,
Followable ,
Joinable ,
ListingType ,
SortType ,
} ;
2020-12-18 17:27:25 +00:00
use lemmy_db_schema ::{
naive_now ,
2020-12-21 13:38:34 +00:00
source ::{
comment ::Comment ,
community ::* ,
moderator ::* ,
password_reset_request ::* ,
post ::Post ,
private_message ::* ,
site ::* ,
user ::* ,
user_mention ::* ,
} ,
2020-12-18 17:27:25 +00:00
} ;
2020-12-21 16:30:34 +00:00
use lemmy_db_views ::{
comment_report_view ::CommentReportView ,
comment_view ::CommentQueryBuilder ,
post_report_view ::PostReportView ,
post_view ::PostQueryBuilder ,
private_message_view ::{ PrivateMessageQueryBuilder , PrivateMessageView } ,
2020-12-21 23:27:42 +00:00
} ;
use lemmy_db_views_actor ::{
community_follower_view ::CommunityFollowerView ,
community_moderator_view ::CommunityModeratorView ,
2020-12-21 16:30:34 +00:00
user_mention_view ::{ UserMentionQueryBuilder , UserMentionView } ,
user_view ::{ UserViewDangerous , UserViewSafe } ,
} ;
2020-11-05 15:13:01 +00:00
use lemmy_structs ::{ blocking , send_email_to_user , user ::* } ;
2020-07-10 18:15:41 +00:00
use lemmy_utils ::{
2020-09-14 15:29:50 +00:00
apub ::{ generate_actor_keypair , make_apub_endpoint , EndpointType } ,
email ::send_email ,
2020-08-13 15:46:31 +00:00
location_info ,
2020-05-16 14:04:08 +00:00
settings ::Settings ,
2020-09-14 15:29:50 +00:00
utils ::{
check_slurs ,
generate_random_string ,
is_valid_preferred_username ,
is_valid_username ,
naive_from_unix ,
remove_slurs ,
} ,
2020-09-03 19:45:12 +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 ::{
messages ::{ CaptchaItem , CheckCaptcha , JoinUserRoom , SendAllMessage , SendUserRoomMessage } ,
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 ( ) ;
2020-08-18 13:43:50 +00:00
let user = match blocking ( context . pool ( ) , move | conn | {
2020-07-20 19:32:15 +00:00
User_ ::find_by_email_or_username ( conn , & username_or_email )
2020-07-01 12:54:29 +00:00
} )
. await ?
{
2019-05-05 05:20:38 +00:00
Ok ( user ) = > user ,
2020-01-16 14:39:08 +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
let valid : bool = verify ( & data . password , & user . password_encrypted ) . unwrap_or ( false ) ;
if ! valid {
2020-01-16 14:39:08 +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 {
2020-08-13 15:46:31 +00:00
jwt : Claims ::jwt ( user , Settings ::get ( ) . hostname ) ? ,
2020-07-10 18:15:41 +00:00
} )
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 Register {
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 : & Register = & self ;
2019-05-05 05:20:38 +00:00
2019-12-11 20:21:47 +00:00
// Make sure site has open registration
2020-12-20 01:10:47 +00:00
if let Ok ( site ) = blocking ( context . pool ( ) , move | conn | Site ::read_simple ( conn ) ) . await ? {
if ! site . open_registration {
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( " registration_closed " ) . into ( ) ) ;
2019-12-11 20:21:47 +00:00
}
}
2020-10-05 00:10:15 +00:00
// Password length check
if data . password . len ( ) > 60 {
return Err ( APIError ::err ( " invalid_password " ) . into ( ) ) ;
}
2019-05-05 05:20:38 +00:00
// Make sure passwords match
2020-01-02 11:30:00 +00:00
if data . password ! = data . password_verify {
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( " passwords_dont_match " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
2020-07-29 13:02:46 +00:00
// If its not the admin, check the captcha
if ! data . admin & & Settings ::get ( ) . captcha . enabled {
2020-08-24 11:58:24 +00:00
let check = context
. chat_server ( )
. send ( CheckCaptcha {
uuid : data
. captcha_uuid
. to_owned ( )
. unwrap_or_else ( | | " " . to_string ( ) ) ,
answer : data
. captcha_answer
. to_owned ( )
. unwrap_or_else ( | | " " . to_string ( ) ) ,
} )
. await ? ;
if ! check {
return Err ( APIError ::err ( " captcha_incorrect " ) . into ( ) ) ;
}
2020-07-29 13:02:46 +00:00
}
2020-08-04 14:39:55 +00:00
check_slurs ( & data . username ) ? ;
2019-05-05 05:20:38 +00:00
// Make sure there are no admins
2020-08-18 13:43:50 +00:00
let any_admins = blocking ( context . pool ( ) , move | conn | {
2020-12-04 00:47:58 +00:00
UserViewSafe ::admins ( conn ) . map ( | a | a . is_empty ( ) )
2020-07-01 12:54:29 +00:00
} )
. await ? ? ;
if data . admin & & ! any_admins {
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( " admin_already_created " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
2020-04-24 14:04:36 +00:00
let user_keypair = generate_actor_keypair ( ) ? ;
2020-05-28 18:07:36 +00:00
if ! is_valid_username ( & data . username ) {
return Err ( APIError ::err ( " invalid_username " ) . into ( ) ) ;
}
2020-04-03 04:12:05 +00:00
2019-05-05 05:20:38 +00:00
// Register the new user
let user_form = UserForm {
name : data . username . to_owned ( ) ,
2020-08-09 02:36:29 +00:00
email : Some ( data . email . to_owned ( ) ) ,
2020-01-22 21:35:29 +00:00
matrix_user_id : None ,
2019-12-29 20:39:48 +00:00
avatar : None ,
2020-08-05 16:03:46 +00:00
banner : None ,
2019-05-05 05:20:38 +00:00
password_encrypted : data . password . to_owned ( ) ,
preferred_username : None ,
2020-09-18 11:04:12 +00:00
published : None ,
2019-05-05 05:20:38 +00:00
updated : None ,
admin : data . admin ,
2020-11-09 14:29:36 +00:00
banned : Some ( false ) ,
2019-08-14 02:52:43 +00:00
show_nsfw : data . show_nsfw ,
2020-10-03 16:44:25 +00:00
theme : " browser " . into ( ) ,
2020-08-05 16:03:46 +00:00
default_sort_type : SortType ::Active as i16 ,
2019-10-21 04:21:54 +00:00
default_listing_type : ListingType ::Subscribed as i16 ,
2019-12-09 08:24:53 +00:00
lang : " browser " . into ( ) ,
2020-01-02 21:55:54 +00:00
show_avatars : true ,
send_notifications_to_email : false ,
2020-08-31 13:48:02 +00:00
actor_id : Some ( make_apub_endpoint ( EndpointType ::User , & data . username ) . to_string ( ) ) ,
2020-04-03 04:12:05 +00:00
bio : None ,
local : true ,
2020-04-24 14:04:36 +00:00
private_key : Some ( user_keypair . private_key ) ,
public_key : Some ( user_keypair . public_key ) ,
2020-04-03 04:12:05 +00:00
last_refreshed_at : None ,
2019-05-05 05:20:38 +00:00
} ;
// Create the user
2020-08-18 13:43:50 +00:00
let inserted_user = match blocking ( context . pool ( ) , move | conn | {
User_ ::register ( conn , & user_form )
} )
. await ?
{
2019-05-05 05:20:38 +00:00
Ok ( user ) = > user ,
2020-01-18 01:34:16 +00:00
Err ( e ) = > {
let err_type = if e . to_string ( )
= = " duplicate key value violates unique constraint \" user__email_key \" "
{
" email_already_exists "
} else {
" user_already_exists "
} ;
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( err_type ) . into ( ) ) ;
2020-01-18 01:34:16 +00:00
}
2019-05-05 05:20:38 +00:00
} ;
2020-04-24 14:04:36 +00:00
let main_community_keypair = generate_actor_keypair ( ) ? ;
2020-04-03 04:12:05 +00:00
2019-06-02 16:34:45 +00:00
// Create the main community if it doesn't exist
2020-08-31 13:48:02 +00:00
let main_community =
match blocking ( context . pool ( ) , move | conn | Community ::read ( conn , 2 ) ) . await ? {
Ok ( c ) = > c ,
Err ( _e ) = > {
let default_community_name = " main " ;
let community_form = CommunityForm {
name : default_community_name . to_string ( ) ,
title : " The Default Community " . to_string ( ) ,
description : Some ( " The Default Community " . to_string ( ) ) ,
category_id : 1 ,
nsfw : false ,
creator_id : inserted_user . id ,
removed : None ,
deleted : None ,
updated : None ,
actor_id : Some (
make_apub_endpoint ( EndpointType ::Community , default_community_name ) . to_string ( ) ,
) ,
local : true ,
private_key : Some ( main_community_keypair . private_key ) ,
public_key : Some ( main_community_keypair . public_key ) ,
last_refreshed_at : None ,
published : None ,
icon : None ,
banner : None ,
} ;
blocking ( context . pool ( ) , move | conn | {
Community ::create ( conn , & community_form )
} )
. await ? ?
}
} ;
2019-06-02 16:34:45 +00:00
// Sign them up for main community no matter what
let community_follower_form = CommunityFollowerForm {
community_id : main_community . id ,
user_id : inserted_user . id ,
2020-11-10 15:45:10 +00:00
pending : false ,
2019-06-02 16:34:45 +00:00
} ;
2020-07-01 12:54:29 +00:00
let follow = move | conn : & '_ _ | CommunityFollower ::follow ( conn , & community_follower_form ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , follow ) . await ? . is_err ( ) {
2020-07-01 12:54:29 +00:00
return Err ( APIError ::err ( " community_follower_already_exists " ) . into ( ) ) ;
} ;
2019-06-02 16:34:45 +00:00
// If its an admin, add them as a mod and follower to main
if data . admin {
let community_moderator_form = CommunityModeratorForm {
community_id : main_community . id ,
user_id : inserted_user . id ,
} ;
2020-07-01 12:54:29 +00:00
let join = move | conn : & '_ _ | CommunityModerator ::join ( conn , & community_moderator_form ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , join ) . await ? . is_err ( ) {
2020-07-01 12:54:29 +00:00
return Err ( APIError ::err ( " community_moderator_already_exists " ) . into ( ) ) ;
}
2019-06-02 16:34:45 +00:00
}
2019-05-05 05:20:38 +00:00
// Return the jwt
2019-09-07 15:35:05 +00:00
Ok ( LoginResponse {
2020-08-13 15:46:31 +00:00
jwt : Claims ::jwt ( inserted_user , Settings ::get ( ) . hostname ) ? ,
2019-09-07 15:35:05 +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 > {
let captcha_settings = Settings ::get ( ) . captcha ;
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 ( ) ;
let png_byte_array = captcha . as_png ( ) . expect ( " failed to generate captcha " ) ;
let png = base64 ::encode ( png_byte_array ) ;
let uuid = uuid ::Uuid ::new_v4 ( ) . to_string ( ) ;
let wav = captcha_espeak_wav_base64 ( & answer ) . ok ( ) ;
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 {
ok : Some ( CaptchaResponse { png , uuid , wav } ) ,
} )
}
}
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 ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-08-14 02:52:43 +00:00
2020-08-05 16:03:46 +00:00
let avatar = diesel_option_overwrite ( & data . avatar ) ;
let banner = diesel_option_overwrite ( & 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 ) ;
let preferred_username = diesel_option_overwrite ( & data . preferred_username ) ;
let matrix_user_id = diesel_option_overwrite ( & data . matrix_user_id ) ;
2020-08-05 16:03:46 +00:00
2020-10-15 18:23:56 +00:00
// Check to make sure the avatar and banners are urls
2020-10-22 17:23:25 +00:00
check_optional_url ( & avatar ) ? ;
check_optional_url ( & banner ) ? ;
2020-10-15 18:23:56 +00:00
2020-09-25 15:16:49 +00:00
if let Some ( Some ( bio ) ) = & bio {
if bio . chars ( ) . count ( ) > 300 {
return Err ( APIError ::err ( " bio_length_overflow " ) . into ( ) ) ;
2020-08-12 11:13:44 +00:00
}
2020-09-25 15:16:49 +00:00
}
if let Some ( Some ( preferred_username ) ) = & preferred_username {
if ! is_valid_preferred_username ( preferred_username . trim ( ) ) {
return Err ( APIError ::err ( " invalid_username " ) . into ( ) ) ;
}
}
2020-07-10 00:04:09 +00:00
2020-12-20 01:10:47 +00:00
let user_id = user . id ;
2020-01-01 20:46:14 +00:00
let password_encrypted = match & data . new_password {
Some ( new_password ) = > {
match & data . new_password_verify {
Some ( new_password_verify ) = > {
// Make sure passwords match
if new_password ! = new_password_verify {
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( " passwords_dont_match " ) . into ( ) ) ;
2020-01-01 20:46:14 +00:00
}
// Check the old password
match & data . old_password {
Some ( old_password ) = > {
2020-12-20 01:10:47 +00:00
let valid : bool = verify ( old_password , & user . password_encrypted ) . unwrap_or ( false ) ;
2020-01-01 20:46:14 +00:00
if ! valid {
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( " password_incorrect " ) . into ( ) ) ;
2020-01-01 20:46:14 +00:00
}
2020-07-01 12:54:29 +00:00
let new_password = new_password . to_owned ( ) ;
2020-08-18 13:43:50 +00:00
let user = blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
User_ ::update_password ( conn , user_id , & new_password )
} )
. await ? ? ;
user . password_encrypted
2020-01-01 20:46:14 +00:00
}
2020-01-16 14:39:08 +00:00
None = > return Err ( APIError ::err ( " password_incorrect " ) . into ( ) ) ,
2020-01-01 20:46:14 +00:00
}
}
2020-01-16 14:39:08 +00:00
None = > return Err ( APIError ::err ( " passwords_dont_match " ) . into ( ) ) ,
2020-01-01 20:46:14 +00:00
}
}
2020-12-20 01:10:47 +00:00
None = > user . password_encrypted ,
2020-01-01 20:46:14 +00:00
} ;
2020-12-26 03:22:25 +00:00
let default_listing_type = data . default_listing_type ;
let default_sort_type = data . default_sort_type ;
2020-12-20 01:10:47 +00:00
2019-08-14 02:52:43 +00:00
let user_form = UserForm {
2020-12-20 01:10:47 +00:00
name : user . name ,
2020-01-01 20:46:14 +00:00
email ,
2020-09-25 15:16:49 +00:00
matrix_user_id ,
2020-07-10 00:04:09 +00:00
avatar ,
2020-08-05 16:03:46 +00:00
banner ,
2020-01-01 20:46:14 +00:00
password_encrypted ,
2020-08-05 16:03:46 +00:00
preferred_username ,
2020-12-20 01:10:47 +00:00
published : Some ( user . published ) ,
2019-08-14 02:52:43 +00:00
updated : Some ( naive_now ( ) ) ,
2020-12-20 01:10:47 +00:00
admin : user . admin ,
banned : Some ( user . banned ) ,
2019-08-14 02:52:43 +00:00
show_nsfw : data . show_nsfw ,
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 ,
2020-12-20 01:10:47 +00:00
actor_id : Some ( user . actor_id ) ,
2020-07-31 01:08:13 +00:00
bio ,
2020-12-20 01:10:47 +00:00
local : user . local ,
private_key : user . private_key ,
public_key : user . public_key ,
2020-04-03 04:12:05 +00:00
last_refreshed_at : None ,
2019-08-14 02:52:43 +00:00
} ;
2020-08-18 13:43:50 +00:00
let res = blocking ( context . pool ( ) , move | conn | {
User_ ::update ( conn , user_id , & user_form )
} )
. await ? ;
2020-07-01 12:54:29 +00:00
let updated_user : User_ = match res {
2019-08-14 02:52:43 +00:00
Ok ( user ) = > user ,
2020-01-18 01:34:16 +00:00
Err ( e ) = > {
let err_type = if e . to_string ( )
= = " duplicate key value violates unique constraint \" user__email_key \" "
{
" email_already_exists "
} else {
" user_already_exists "
} ;
2020-01-16 14:39:08 +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 {
2020-08-13 15:46:31 +00:00
jwt : Claims ::jwt ( updated_user , Settings ::get ( ) . hostname ) ? ,
2019-09-07 15:35:05 +00:00
} )
2019-08-14 02:52:43 +00:00
}
}
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 GetUserDetails {
2020-04-20 03:59:07 +00:00
type Response = GetUserDetailsResponse ;
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 < GetUserDetailsResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & GetUserDetails = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt_opt ( & data . auth , context . pool ( ) ) . await ? ;
2019-08-14 02:52:43 +00:00
2020-07-27 13:23:08 +00:00
let show_nsfw = match & user {
Some ( user ) = > user . show_nsfw ,
2019-09-07 15:35:05 +00:00
None = > false ,
2019-08-14 02:52:43 +00:00
} ;
2019-05-05 05:20:38 +00:00
let sort = SortType ::from_str ( & data . sort ) ? ;
2020-07-01 12:54:29 +00:00
let username = data
. username
. to_owned ( )
. unwrap_or_else ( | | " admin " . to_string ( ) ) ;
2019-05-05 05:20:38 +00:00
let user_details_id = match data . user_id {
Some ( id ) = > id ,
2019-09-07 15:35:05 +00:00
None = > {
2020-08-18 13:43:50 +00:00
let user = blocking ( context . pool ( ) , move | conn | {
User_ ::read_from_name ( conn , & username )
} )
. await ? ;
2020-07-01 12:54:29 +00:00
match user {
2019-12-29 01:58:01 +00:00
Ok ( user ) = > user . id ,
2020-01-16 14:39:08 +00:00
Err ( _e ) = > return Err ( APIError ::err ( " couldnt_find_that_username_or_email " ) . into ( ) ) ,
2019-12-29 01:58:01 +00:00
}
2019-09-07 15:35:05 +00:00
}
2019-05-05 05:20:38 +00:00
} ;
2020-10-30 22:19:47 +00:00
let user_id = user . map ( | u | u . id ) ;
2020-12-04 00:47:58 +00:00
2020-12-26 03:22:25 +00:00
let ( user_view , user_view_dangerous ) = if let Some ( auth_user_id ) = user_id {
2020-12-04 00:47:58 +00:00
if user_details_id = = auth_user_id {
(
None ,
Some (
blocking ( context . pool ( ) , move | conn | {
UserViewDangerous ::read ( conn , auth_user_id )
} )
. await ? ? ,
) ,
)
} else {
(
Some (
blocking ( context . pool ( ) , move | conn | {
UserViewSafe ::read ( conn , user_details_id )
} )
. await ? ? ,
) ,
None ,
)
2020-10-30 22:19:47 +00:00
}
2020-12-04 00:47:58 +00:00
} else {
(
Some (
blocking ( context . pool ( ) , move | conn | {
UserViewSafe ::read ( conn , user_details_id )
} )
. await ? ? ,
) ,
None ,
)
2020-10-30 22:19:47 +00:00
} ;
2020-07-01 12:54:29 +00:00
let page = data . page ;
let limit = data . limit ;
let saved_only = data . saved_only ;
let community_id = data . community_id ;
2020-10-30 22:19:47 +00:00
2020-08-18 13:43:50 +00:00
let ( posts , comments ) = blocking ( context . pool ( ) , move | conn | {
2020-12-16 18:59:43 +00:00
let mut posts_query = PostQueryBuilder ::create ( conn )
2020-07-01 12:54:29 +00:00
. sort ( & sort )
. show_nsfw ( show_nsfw )
. saved_only ( saved_only )
2020-12-16 18:59:43 +00:00
. community_id ( community_id )
. my_user_id ( user_id )
2020-07-01 12:54:29 +00:00
. page ( page )
. limit ( limit ) ;
2020-12-16 18:59:43 +00:00
let mut comments_query = CommentQueryBuilder ::create ( conn )
. my_user_id ( user_id )
2020-07-01 12:54:29 +00:00
. sort ( & sort )
. saved_only ( saved_only )
. page ( page )
. limit ( limit ) ;
// If its saved only, you don't care what creator it was
// Or, if its not saved, then you only want it for that specific creator
if ! saved_only {
2020-12-16 18:59:43 +00:00
posts_query = posts_query . creator_id ( user_details_id ) ;
comments_query = comments_query . creator_id ( user_details_id ) ;
2020-07-01 12:54:29 +00:00
}
let posts = posts_query . list ( ) ? ;
let comments = comments_query . list ( ) ? ;
Ok ( ( posts , comments ) ) as Result < _ , LemmyError >
} )
. await ? ? ;
2020-08-18 13:43:50 +00:00
let follows = blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
CommunityFollowerView ::for_user ( conn , user_details_id )
} )
. await ? ? ;
2020-08-18 13:43:50 +00:00
let moderates = blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
CommunityModeratorView ::for_user ( conn , user_details_id )
} )
. await ? ? ;
2019-12-07 12:03:03 +00:00
2019-05-05 05:20:38 +00:00
// Return the jwt
2019-09-07 15:35:05 +00:00
Ok ( GetUserDetailsResponse {
2020-12-20 01:10:47 +00:00
user_view ,
2020-12-26 03:22:25 +00:00
user_view_dangerous ,
2019-12-09 19:08:19 +00:00
follows ,
moderates ,
comments ,
posts ,
2019-09-07 15:35:05 +00:00
} )
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 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 ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
// Make sure user is an admin
2020-08-18 13:43:50 +00:00
is_admin ( context . pool ( ) , user . id ) . await ? ;
2019-05-05 05:20:38 +00:00
2020-07-01 12:54:29 +00:00
let added = data . added ;
2020-07-07 14:54:44 +00:00
let added_user_id = data . user_id ;
let add_admin = move | conn : & '_ _ | User_ ::add_admin ( conn , added_user_id , added ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , add_admin ) . await ? . is_err ( ) {
2020-07-01 12:54:29 +00:00
return Err ( APIError ::err ( " couldnt_update_user " ) . into ( ) ) ;
}
2019-05-05 05:20:38 +00:00
// Mod tables
let form = ModAddForm {
2020-08-04 14:39:55 +00:00
mod_user_id : user . id ,
2019-05-05 05:20:38 +00:00
other_user_id : data . user_id ,
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
2020-12-04 00:47:58 +00:00
let mut admins = blocking ( context . pool ( ) , move | conn | UserViewSafe ::admins ( conn ) ) . await ? ? ;
2020-08-13 15:46:31 +00:00
let creator_index = admins
. iter ( )
2020-12-04 00:47:58 +00:00
. position ( | r | r . user . id = = site_creator_id )
2020-08-13 15:46:31 +00:00
. context ( location_info! ( ) ) ? ;
2019-08-24 02:40:41 +00:00
let creator_user = admins . remove ( creator_index ) ;
admins . insert ( 0 , creator_user ) ;
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) ]
2020-08-12 11:31:45 +00:00
impl Perform for BanUser {
2020-04-20 03:59:07 +00:00
type Response = BanUserResponse ;
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 < BanUserResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & BanUser = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
// Make sure user is an admin
2020-08-18 13:43:50 +00:00
is_admin ( context . pool ( ) , user . id ) . await ? ;
2019-05-05 05:20:38 +00:00
2020-07-01 12:54:29 +00:00
let ban = data . ban ;
2020-07-01 13:04:26 +00:00
let banned_user_id = data . user_id ;
let ban_user = move | conn : & '_ _ | User_ ::ban_user ( conn , banned_user_id , ban ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , ban_user ) . await ? . is_err ( ) {
2020-07-01 12:54:29 +00:00
return Err ( APIError ::err ( " couldnt_update_user " ) . into ( ) ) ;
}
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 : & '_ _ | {
2020-12-20 01:10:47 +00:00
Post ::update_removed_for_creator ( conn , banned_user_id , None , true )
2020-08-17 18:12:36 +00:00
} )
. await ? ? ;
// Communities
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn : & '_ _ | {
2020-12-20 01:10:47 +00:00
Community ::update_removed_for_creator ( conn , banned_user_id , true )
2020-08-17 18:12:36 +00:00
} )
. await ? ? ;
// Comments
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn : & '_ _ | {
2020-12-20 01:10:47 +00:00
Comment ::update_removed_for_creator ( conn , banned_user_id , true )
2020-08-17 18:12:36 +00:00
} )
. await ? ? ;
}
2019-05-05 05:20:38 +00:00
// Mod tables
let expires = match data . expires {
Some ( time ) = > Some ( naive_from_unix ( time ) ) ,
2019-09-07 15:35:05 +00:00
None = > None ,
2019-05-05 05:20:38 +00:00
} ;
let form = ModBanForm {
2020-08-04 14:39:55 +00:00
mod_user_id : user . id ,
2019-05-05 05:20:38 +00:00
other_user_id : data . user_id ,
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
2020-07-01 12:54:29 +00:00
let user_id = data . user_id ;
2020-08-18 13:43:50 +00:00
let user_view = blocking ( context . pool ( ) , move | conn | {
2020-12-04 00:47:58 +00:00
UserViewSafe ::read ( conn , user_id )
2020-08-18 13:43:50 +00:00
} )
. await ? ? ;
2019-05-05 05:20:38 +00:00
2020-04-19 22:08:25 +00:00
let res = BanUserResponse {
2020-12-20 01:10:47 +00:00
user_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 {
op : UserOperation ::BanUser ,
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 ;
2020-08-18 13:43:50 +00:00
let user = get_user_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 ;
2020-08-04 14:39:55 +00:00
let user_id = user . 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 )
2020-12-16 18:59:43 +00:00
. recipient_id ( user_id )
. my_user_id ( user_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) ]
2020-08-12 11:31:45 +00:00
impl Perform for GetUserMentions {
2020-04-20 03:59:07 +00:00
type Response = GetUserMentionsResponse ;
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 < GetUserMentionsResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & GetUserMentions = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_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 ;
2020-08-04 14:39:55 +00:00
let user_id = user . id ;
2020-08-18 13:43:50 +00:00
let mentions = blocking ( context . pool ( ) , move | conn | {
2020-12-16 18:59:43 +00:00
UserMentionQueryBuilder ::create ( conn )
. recipient_id ( user_id )
. my_user_id ( user_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
2020-01-16 14:39:08 +00:00
Ok ( GetUserMentionsResponse { mentions } )
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 MarkUserMentionAsRead {
2020-04-20 03:59:07 +00:00
type Response = UserMentionResponse ;
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 < UserMentionResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & MarkUserMentionAsRead = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-10-20 00:46:29 +00:00
2020-07-01 12:54:29 +00:00
let user_mention_id = data . user_mention_id ;
2020-08-18 13:43:50 +00:00
let read_user_mention = blocking ( context . pool ( ) , move | conn | {
UserMention ::read ( conn , user_mention_id )
} )
. await ? ? ;
2019-10-20 00:46:29 +00:00
2020-08-04 14:39:55 +00:00
if user . id ! = read_user_mention . recipient_id {
2020-07-14 16:12:04 +00:00
return Err ( APIError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
}
let user_mention_id = read_user_mention . id ;
2020-07-20 14:56:40 +00:00
let read = data . read ;
let update_mention = move | conn : & '_ _ | UserMention ::update_read ( conn , user_mention_id , read ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , update_mention ) . await ? . is_err ( ) {
2020-07-01 12:54:29 +00:00
return Err ( APIError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
} ;
2019-10-20 00:46:29 +00:00
2020-07-14 16:12:04 +00:00
let user_mention_id = read_user_mention . id ;
2020-08-04 14:39:55 +00:00
let user_id = user . id ;
2020-08-18 13:43:50 +00:00
let user_mention_view = blocking ( context . pool ( ) , move | conn | {
2020-12-16 16:09:21 +00:00
UserMentionView ::read ( conn , user_mention_id , Some ( user_id ) )
2020-07-01 12:54:29 +00:00
} )
. await ? ? ;
2019-10-20 00:46:29 +00:00
2020-12-16 16:09:21 +00:00
Ok ( UserMentionResponse { user_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 ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-05-05 05:20:38 +00:00
2020-08-04 14:39:55 +00:00
let user_id = user . 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 )
. my_user_id ( user_id )
. recipient_id ( user_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 ( ) {
2020-07-01 12:54:29 +00:00
return Err ( APIError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
}
2019-05-05 05:20:38 +00:00
}
2020-07-20 14:56:40 +00:00
// Mark all user mentions as read
let update_user_mentions = move | conn : & '_ _ | UserMention ::mark_all_as_read ( conn , user_id ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , update_user_mentions )
. await ?
. is_err ( )
{
2020-07-20 14:56:40 +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
let update_pm = move | conn : & '_ _ | PrivateMessage ::mark_all_as_read ( conn , user_id ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , update_pm ) . await ? . is_err ( ) {
2020-07-20 04:29:44 +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 DeleteAccount {
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 : & DeleteAccount = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2019-10-18 04:25:23 +00:00
// Verify the password
let valid : bool = verify ( & data . password , & user . password_encrypted ) . unwrap_or ( false ) ;
if ! valid {
2020-01-16 14:39:08 +00:00
return Err ( APIError ::err ( " password_incorrect " ) . into ( ) ) ;
2019-10-18 04:25:23 +00:00
}
2019-10-15 22:09:01 +00:00
// Comments
2020-08-04 14:39:55 +00:00
let user_id = user . id ;
2020-08-17 18:12:36 +00:00
let permadelete = move | conn : & '_ _ | Comment ::permadelete_for_creator ( conn , user_id ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , permadelete ) . await ? . is_err ( ) {
2020-08-17 18:12:36 +00:00
return Err ( APIError ::err ( " couldnt_update_comment " ) . into ( ) ) ;
2019-10-15 22:09:01 +00:00
}
// Posts
2020-08-17 18:12:36 +00:00
let permadelete = move | conn : & '_ _ | Post ::permadelete_for_creator ( conn , user_id ) ;
2020-08-18 13:43:50 +00:00
if blocking ( context . pool ( ) , permadelete ) . await ? . is_err ( ) {
2020-08-17 18:12:36 +00:00
return Err ( APIError ::err ( " couldnt_update_post " ) . into ( ) ) ;
2019-10-15 22:09:01 +00:00
}
2020-11-27 21:00:18 +00:00
blocking ( context . pool ( ) , move | conn | {
User_ ::delete_account ( conn , user_id )
} )
. await ? ? ;
2019-10-15 22:09:01 +00:00
Ok ( LoginResponse {
jwt : data . auth . to_owned ( ) ,
} )
}
}
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 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 ( ) ;
2020-08-18 13:43:50 +00:00
let user = match blocking ( context . pool ( ) , move | conn | {
User_ ::find_by_email ( conn , & email )
} )
. await ?
{
2019-10-30 03:35:39 +00:00
Ok ( user ) = > user ,
2020-01-16 14:39:08 +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 ( ) ;
let user_id = user . id ;
2020-08-18 13:43:50 +00:00
blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
PasswordResetRequest ::create_token ( conn , user_id , & token2 )
} )
. await ? ? ;
2019-10-30 03:35:39 +00:00
// Email the pure token to the user.
// TODO no i18n support here.
let user_email = & user . email . expect ( " email " ) ;
let subject = & format! ( " Password reset for {} " , user . name ) ;
2020-09-25 15:33:00 +00:00
let hostname = & Settings ::get ( ) . get_protocol_and_hostname ( ) ;
2019-11-02 06:41:57 +00:00
let html = & format! ( " <h1>Password Reset Request for {} </h1><br><a href= {} /password_change/ {} >Click here to reset your password</a> " , user . name , hostname , & token ) ;
2019-10-30 03:35:39 +00:00
match send_email ( subject , user_email , & user . name , html ) {
Ok ( _o ) = > _o ,
2020-01-16 14:39:08 +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 ( ) ;
2020-08-18 13:43:50 +00:00
let user_id = blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
PasswordResetRequest ::read_from_token ( conn , & token ) . map ( | p | p . user_id )
} )
. await ? ? ;
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 {
2020-01-16 14:39:08 +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 ( ) ;
2020-08-18 13:43:50 +00:00
let updated_user = match blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
User_ ::update_password ( conn , user_id , & password )
} )
. await ?
{
2019-10-30 03:35:39 +00:00
Ok ( user ) = > user ,
2020-01-16 14:39:08 +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 {
2020-08-13 15:46:31 +00:00
jwt : Claims ::jwt ( updated_user , Settings ::get ( ) . hostname ) ? ,
2019-10-30 03:35:39 +00:00
} )
}
}
2020-01-22 21:35: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 CreatePrivateMessage {
2020-04-20 03:59:07 +00:00
type Response = PrivateMessageResponse ;
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 < PrivateMessageResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & CreatePrivateMessage = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-01-22 21:35:29 +00:00
let content_slurs_removed = remove_slurs ( & data . content . to_owned ( ) ) ;
let private_message_form = PrivateMessageForm {
2020-05-06 02:06:24 +00:00
content : content_slurs_removed . to_owned ( ) ,
2020-08-04 14:39:55 +00:00
creator_id : user . id ,
2020-01-22 21:35:29 +00:00
recipient_id : data . recipient_id ,
deleted : None ,
read : None ,
updated : None ,
2020-08-31 13:48:02 +00:00
ap_id : None ,
2020-05-06 02:06:24 +00:00
local : true ,
published : None ,
2020-01-22 21:35:29 +00:00
} ;
2020-08-18 13:43:50 +00:00
let inserted_private_message = match blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
PrivateMessage ::create ( conn , & private_message_form )
} )
. await ?
{
2020-01-22 21:35:29 +00:00
Ok ( private_message ) = > private_message ,
Err ( _e ) = > {
2020-01-24 00:17:42 +00:00
return Err ( APIError ::err ( " couldnt_create_private_message " ) . into ( ) ) ;
2020-01-22 21:35:29 +00:00
}
} ;
2020-07-01 12:54:29 +00:00
let inserted_private_message_id = inserted_private_message . id ;
2020-08-18 13:43:50 +00:00
let updated_private_message = match blocking ( context . pool ( ) , move | conn | {
2020-07-10 18:15:41 +00:00
let apub_id = make_apub_endpoint (
EndpointType ::PrivateMessage ,
& inserted_private_message_id . to_string ( ) ,
)
. to_string ( ) ;
PrivateMessage ::update_ap_id ( & conn , inserted_private_message_id , apub_id )
2020-07-01 12:54:29 +00:00
} )
. await ?
{
Ok ( private_message ) = > private_message ,
Err ( _e ) = > return Err ( APIError ::err ( " couldnt_create_private_message " ) . into ( ) ) ,
} ;
2020-05-06 02:06:24 +00:00
2020-08-18 13:43:50 +00:00
updated_private_message . send_create ( & user , context ) . await ? ;
2020-05-06 02:06:24 +00:00
2020-01-22 21:35:29 +00:00
// Send notifications to the recipient
2020-07-01 12:54:29 +00:00
let recipient_id = data . recipient_id ;
2020-08-18 13:43:50 +00:00
let recipient_user =
blocking ( context . pool ( ) , move | conn | User_ ::read ( conn , recipient_id ) ) . await ? ? ;
2020-01-22 21:35:29 +00:00
if recipient_user . send_notifications_to_email {
2020-11-05 15:13:01 +00:00
send_email_to_user (
recipient_user ,
" Private Message from " ,
" Private Message " ,
& content_slurs_removed ,
) ;
2020-01-22 21:35:29 +00:00
}
2020-08-18 13:43:50 +00:00
let message = blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
PrivateMessageView ::read ( conn , inserted_private_message . id )
} )
. await ? ? ;
2020-01-22 21:35:29 +00:00
2020-12-20 01:10:47 +00:00
let res = PrivateMessageResponse {
private_message_view : message ,
} ;
2020-04-19 22:08:25 +00:00
2020-08-24 11:58:24 +00:00
context . chat_server ( ) . do_send ( SendUserRoomMessage {
op : UserOperation ::CreatePrivateMessage ,
response : res . clone ( ) ,
recipient_id ,
websocket_id ,
} ) ;
2020-04-19 22:08:25 +00:00
Ok ( res )
2020-01-22 21:35: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 EditPrivateMessage {
2020-04-20 03:59:07 +00:00
type Response = PrivateMessageResponse ;
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 < PrivateMessageResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & EditPrivateMessage = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-01-22 21:35:29 +00:00
2020-07-20 04:29:44 +00:00
// Checking permissions
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let orig_private_message = blocking ( context . pool ( ) , move | conn | {
PrivateMessage ::read ( conn , edit_id )
} )
. await ? ? ;
2020-08-04 14:39:55 +00:00
if user . id ! = orig_private_message . creator_id {
2020-01-24 00:17:42 +00:00
return Err ( APIError ::err ( " no_private_message_edit_allowed " ) . into ( ) ) ;
2020-01-22 21:35:29 +00:00
}
2020-07-20 04:29:44 +00:00
// Doing the update
let content_slurs_removed = remove_slurs ( & data . content ) ;
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let updated_private_message = match blocking ( context . pool ( ) , move | conn | {
2020-07-20 04:29:44 +00:00
PrivateMessage ::update_content ( conn , edit_id , & content_slurs_removed )
} )
. await ?
{
Ok ( private_message ) = > private_message ,
Err ( _e ) = > return Err ( APIError ::err ( " couldnt_update_private_message " ) . into ( ) ) ,
2020-01-22 21:35:29 +00:00
} ;
2020-07-20 04:29:44 +00:00
// Send the apub update
2020-08-18 13:43:50 +00:00
updated_private_message . send_update ( & user , context ) . await ? ;
2020-07-20 04:29:44 +00:00
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let message = blocking ( context . pool ( ) , move | conn | {
PrivateMessageView ::read ( conn , edit_id )
} )
. await ? ? ;
2020-12-16 22:16:48 +00:00
let recipient_id = message . recipient . id ;
2020-07-20 04:29:44 +00:00
2020-12-20 01:10:47 +00:00
let res = PrivateMessageResponse {
private_message_view : message ,
} ;
2020-07-20 04:29:44 +00:00
2020-08-24 11:58:24 +00:00
context . chat_server ( ) . do_send ( SendUserRoomMessage {
op : UserOperation ::EditPrivateMessage ,
response : res . clone ( ) ,
recipient_id ,
websocket_id ,
} ) ;
2020-07-20 04:29:44 +00:00
Ok ( res )
}
}
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for DeletePrivateMessage {
2020-07-20 04:29:44 +00:00
type Response = PrivateMessageResponse ;
async fn perform (
& 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-20 04:29:44 +00:00
) -> Result < PrivateMessageResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & DeletePrivateMessage = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-07-20 04:29:44 +00:00
// Checking permissions
2020-07-01 12:54:29 +00:00
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let orig_private_message = blocking ( context . pool ( ) , move | conn | {
PrivateMessage ::read ( conn , edit_id )
} )
. await ? ? ;
2020-08-04 14:39:55 +00:00
if user . id ! = orig_private_message . creator_id {
2020-07-20 04:29:44 +00:00
return Err ( APIError ::err ( " no_private_message_edit_allowed " ) . into ( ) ) ;
}
// Doing the update
let edit_id = data . edit_id ;
let deleted = data . deleted ;
2020-08-18 13:43:50 +00:00
let updated_private_message = match blocking ( context . pool ( ) , move | conn | {
2020-07-20 04:29:44 +00:00
PrivateMessage ::update_deleted ( conn , edit_id , deleted )
2020-07-01 12:54:29 +00:00
} )
. await ?
{
Ok ( private_message ) = > private_message ,
Err ( _e ) = > return Err ( APIError ::err ( " couldnt_update_private_message " ) . into ( ) ) ,
} ;
2020-01-22 21:35:29 +00:00
2020-07-20 04:29:44 +00:00
// Send the apub update
if data . deleted {
2020-08-18 13:43:50 +00:00
updated_private_message . send_delete ( & user , context ) . await ? ;
2020-05-06 02:06:24 +00:00
} else {
2020-07-01 12:54:29 +00:00
updated_private_message
2020-08-18 13:43:50 +00:00
. send_undo_delete ( & user , context )
2020-07-01 12:54:29 +00:00
. await ? ;
2020-05-06 02:06:24 +00:00
}
2020-07-01 12:54:29 +00:00
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let message = blocking ( context . pool ( ) , move | conn | {
PrivateMessageView ::read ( conn , edit_id )
} )
. await ? ? ;
2020-12-16 22:16:48 +00:00
let recipient_id = message . recipient . id ;
2020-01-22 21:35:29 +00:00
2020-12-20 01:10:47 +00:00
let res = PrivateMessageResponse {
private_message_view : message ,
} ;
2020-05-06 02:06:24 +00:00
2020-08-24 11:58:24 +00:00
context . chat_server ( ) . do_send ( SendUserRoomMessage {
op : UserOperation ::DeletePrivateMessage ,
response : res . clone ( ) ,
recipient_id ,
websocket_id ,
} ) ;
2020-07-20 04:29:44 +00:00
Ok ( res )
}
}
#[ async_trait::async_trait(?Send) ]
2020-08-12 11:31:45 +00:00
impl Perform for MarkPrivateMessageAsRead {
2020-07-20 04:29:44 +00:00
type Response = PrivateMessageResponse ;
async fn perform (
& 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-20 04:29:44 +00:00
) -> Result < PrivateMessageResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & MarkPrivateMessageAsRead = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-07-20 04:29:44 +00:00
// Checking permissions
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let orig_private_message = blocking ( context . pool ( ) , move | conn | {
PrivateMessage ::read ( conn , edit_id )
} )
. await ? ? ;
2020-08-04 14:39:55 +00:00
if user . id ! = orig_private_message . recipient_id {
2020-07-20 04:29:44 +00:00
return Err ( APIError ::err ( " couldnt_update_private_message " ) . into ( ) ) ;
}
// Doing the update
let edit_id = data . edit_id ;
let read = data . read ;
2020-08-18 13:43:50 +00:00
match blocking ( context . pool ( ) , move | conn | {
2020-07-20 04:29:44 +00:00
PrivateMessage ::update_read ( conn , edit_id , read )
} )
. await ?
{
Ok ( private_message ) = > private_message ,
Err ( _e ) = > return Err ( APIError ::err ( " couldnt_update_private_message " ) . into ( ) ) ,
} ;
// No need to send an apub update
let edit_id = data . edit_id ;
2020-08-18 13:43:50 +00:00
let message = blocking ( context . pool ( ) , move | conn | {
PrivateMessageView ::read ( conn , edit_id )
} )
. await ? ? ;
2020-12-16 22:16:48 +00:00
let recipient_id = message . recipient . id ;
2020-07-20 04:29:44 +00:00
2020-12-20 01:10:47 +00:00
let res = PrivateMessageResponse {
private_message_view : message ,
} ;
2020-07-20 04:29:44 +00:00
2020-08-24 11:58:24 +00:00
context . chat_server ( ) . do_send ( SendUserRoomMessage {
op : UserOperation ::MarkPrivateMessageAsRead ,
response : res . clone ( ) ,
recipient_id ,
websocket_id ,
} ) ;
2020-05-06 02:06:24 +00:00
Ok ( res )
2020-01-22 21:35: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 GetPrivateMessages {
2020-04-20 03:59:07 +00:00
type Response = PrivateMessagesResponse ;
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 < PrivateMessagesResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & GetPrivateMessages = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-08-04 14:39:55 +00:00
let user_id = user . id ;
2020-01-22 21:35:29 +00:00
2020-07-01 12:54:29 +00:00
let page = data . page ;
let limit = data . limit ;
let unread_only = data . unread_only ;
2020-08-18 13:43:50 +00:00
let messages = blocking ( context . pool ( ) , move | conn | {
2020-07-01 12:54:29 +00:00
PrivateMessageQueryBuilder ::create ( & conn , user_id )
. page ( page )
. limit ( limit )
. unread_only ( unread_only )
. list ( )
} )
. await ? ? ;
2020-01-22 21:35:29 +00:00
2020-12-20 01:10:47 +00:00
Ok ( PrivateMessagesResponse {
private_messages : messages ,
} )
2020-01-22 21:35:29 +00:00
}
}
2020-02-01 01:02:20 +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 UserJoin {
2020-04-20 03:59:07 +00:00
type Response = UserJoinResponse ;
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 < UserJoinResponse , LemmyError > {
2020-08-12 11:31:45 +00:00
let data : & UserJoin = & self ;
2020-08-18 13:43:50 +00:00
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
2020-04-19 22:08:25 +00:00
2020-08-24 11:58:24 +00:00
if let Some ( ws_id ) = websocket_id {
context . chat_server ( ) . do_send ( JoinUserRoom {
user_id : user . id ,
id : ws_id ,
} ) ;
2020-04-19 22:08:25 +00:00
}
2020-09-15 19:26:47 +00:00
Ok ( UserJoinResponse { joined : true } )
2020-02-01 01:02:20 +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 ;
let user = get_user_from_jwt ( & data . auth , context . pool ( ) ) . await ? ;
let user_id = user . id ;
let community_id = data . community ;
2020-11-04 02:15:11 +00:00
let community_ids =
collect_moderated_communities ( user_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 ( ) ,
recipient_id : user . id ,
websocket_id ,
} ) ;
Ok ( res )
}
}