2019-05-05 05:20:38 +00:00
use super ::* ;
2019-12-15 16:40:55 +00:00
use crate ::settings ::Settings ;
2019-11-02 06:43:21 +00:00
use crate ::{ generate_random_string , send_email } ;
2019-09-07 15:35:05 +00:00
use bcrypt ::verify ;
2020-01-12 15:31:51 +00:00
use diesel ::PgConnection ;
2019-05-05 05:20:38 +00:00
use std ::str ::FromStr ;
#[ derive(Serialize, Deserialize, Debug) ]
pub struct Login {
username_or_email : String ,
2019-09-07 15:35:05 +00:00
password : String ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct Register {
username : String ,
email : Option < String > ,
password : String ,
password_verify : String ,
admin : bool ,
2019-08-14 02:52:43 +00:00
show_nsfw : bool ,
}
#[ derive(Serialize, Deserialize) ]
pub struct SaveUserSettings {
show_nsfw : bool ,
2019-10-15 19:21:27 +00:00
theme : String ,
2019-10-21 04:21:54 +00:00
default_sort_type : i16 ,
default_listing_type : i16 ,
2019-12-09 08:24:53 +00:00
lang : String ,
2019-12-29 20:39:48 +00:00
avatar : Option < String > ,
2020-01-01 20:46:14 +00:00
email : Option < String > ,
2020-01-22 21:35:29 +00:00
matrix_user_id : Option < String > ,
2020-01-01 20:46:14 +00:00
new_password : Option < String > ,
new_password_verify : Option < String > ,
old_password : Option < String > ,
2020-01-02 21:55:54 +00:00
show_avatars : bool ,
send_notifications_to_email : bool ,
2019-08-14 02:52:43 +00:00
auth : String ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct LoginResponse {
op : String ,
2019-09-07 15:35:05 +00:00
jwt : String ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct GetUserDetails {
user_id : Option < i32 > ,
username : Option < String > ,
sort : String ,
page : Option < i64 > ,
limit : Option < i64 > ,
community_id : Option < i32 > ,
saved_only : bool ,
auth : Option < String > ,
}
#[ derive(Serialize, Deserialize) ]
pub struct GetUserDetailsResponse {
op : String ,
user : UserView ,
follows : Vec < CommunityFollowerView > ,
moderates : Vec < CommunityModeratorView > ,
comments : Vec < CommentView > ,
posts : Vec < PostView > ,
2019-10-14 00:36:35 +00:00
admins : Vec < UserView > ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct GetRepliesResponse {
op : String ,
replies : Vec < ReplyView > ,
}
2019-10-20 00:46:29 +00:00
#[ derive(Serialize, Deserialize) ]
pub struct GetUserMentionsResponse {
op : String ,
mentions : Vec < UserMentionView > ,
}
2019-05-05 05:20:38 +00:00
#[ derive(Serialize, Deserialize) ]
pub struct MarkAllAsRead {
2019-09-07 15:35:05 +00:00
auth : String ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct AddAdmin {
user_id : i32 ,
added : bool ,
2019-09-07 15:35:05 +00:00
auth : String ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct AddAdminResponse {
op : String ,
admins : Vec < UserView > ,
}
#[ derive(Serialize, Deserialize) ]
pub struct BanUser {
user_id : i32 ,
ban : bool ,
reason : Option < String > ,
expires : Option < i64 > ,
2019-09-07 15:35:05 +00:00
auth : String ,
2019-05-05 05:20:38 +00:00
}
#[ derive(Serialize, Deserialize) ]
pub struct BanUserResponse {
op : String ,
user : UserView ,
banned : bool ,
}
#[ derive(Serialize, Deserialize) ]
pub struct GetReplies {
sort : String ,
page : Option < i64 > ,
limit : Option < i64 > ,
unread_only : bool ,
2019-09-07 15:35:05 +00:00
auth : String ,
2019-05-05 05:20:38 +00:00
}
2019-10-20 00:46:29 +00:00
#[ derive(Serialize, Deserialize) ]
pub struct GetUserMentions {
sort : String ,
page : Option < i64 > ,
limit : Option < i64 > ,
unread_only : bool ,
auth : String ,
}
#[ derive(Serialize, Deserialize) ]
pub struct EditUserMention {
user_mention_id : i32 ,
read : Option < bool > ,
auth : String ,
}
#[ derive(Serialize, Deserialize, Clone) ]
pub struct UserMentionResponse {
op : String ,
mention : UserMentionView ,
}
2019-10-15 22:09:01 +00:00
#[ derive(Serialize, Deserialize) ]
pub struct DeleteAccount {
2019-10-18 04:25:23 +00:00
password : String ,
2019-10-15 22:09:01 +00:00
auth : String ,
}
2019-10-30 03:35:39 +00:00
#[ derive(Serialize, Deserialize) ]
pub struct PasswordReset {
email : String ,
}
#[ derive(Serialize, Deserialize, Clone) ]
pub struct PasswordResetResponse {
op : String ,
}
#[ derive(Serialize, Deserialize) ]
pub struct PasswordChange {
token : String ,
password : String ,
password_verify : String ,
}
2020-01-22 21:35:29 +00:00
#[ derive(Serialize, Deserialize) ]
pub struct CreatePrivateMessage {
content : String ,
recipient_id : i32 ,
auth : String ,
}
#[ derive(Serialize, Deserialize) ]
pub struct EditPrivateMessage {
edit_id : i32 ,
content : Option < String > ,
deleted : Option < bool > ,
read : Option < bool > ,
auth : String ,
}
#[ derive(Serialize, Deserialize) ]
pub struct GetPrivateMessages {
unread_only : bool ,
page : Option < i64 > ,
limit : Option < i64 > ,
auth : String ,
}
#[ derive(Serialize, Deserialize, Clone) ]
pub struct PrivateMessagesResponse {
op : String ,
messages : Vec < PrivateMessageView > ,
}
#[ derive(Serialize, Deserialize, Clone) ]
pub struct PrivateMessageResponse {
op : String ,
message : PrivateMessageView ,
}
2019-05-05 05:20:38 +00:00
impl Perform < LoginResponse > for Oper < Login > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < LoginResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & Login = & self . data ;
2019-05-05 05:20:38 +00:00
// Fetch that username / email
let user : User_ = match User_ ::find_by_email_or_username ( & conn , & data . username_or_email ) {
Ok ( user ) = > user ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " 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-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " password_incorrect " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
// Return the jwt
2019-09-07 15:35:05 +00:00
Ok ( LoginResponse {
op : self . op . to_string ( ) ,
jwt : user . jwt ( ) ,
} )
2019-05-05 05:20:38 +00:00
}
}
impl Perform < LoginResponse > for Oper < Register > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < LoginResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & Register = & self . data ;
2019-05-05 05:20:38 +00:00
2019-12-11 20:21:47 +00:00
// Make sure site has open registration
if let Ok ( site ) = SiteView ::read ( & conn ) {
if ! site . open_registration {
2020-01-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " registration_closed " ) . into ( ) ) ;
2019-12-11 20:21:47 +00:00
}
}
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 {
return Err ( APIError ::err ( & self . op , " passwords_dont_match " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
if has_slurs ( & data . username ) {
2020-01-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " no_slurs " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
// Make sure there are no admins
2020-01-02 11:30:00 +00:00
if data . admin & & ! UserView ::admins ( & conn ) ? . is_empty ( ) {
return Err ( APIError ::err ( & self . op , " admin_already_created " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
// Register the new user
let user_form = UserForm {
name : data . username . to_owned ( ) ,
2019-12-15 16:40:55 +00:00
fedi_name : Settings ::get ( ) . hostname . to_owned ( ) ,
2019-05-05 05:20:38 +00:00
email : 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 ,
2019-05-05 05:20:38 +00:00
password_encrypted : data . password . to_owned ( ) ,
preferred_username : None ,
updated : None ,
admin : data . admin ,
banned : false ,
2019-08-14 02:52:43 +00:00
show_nsfw : data . show_nsfw ,
2019-10-15 19:21:27 +00:00
theme : " darkly " . into ( ) ,
2019-10-21 04:21:54 +00:00
default_sort_type : SortType ::Hot as i16 ,
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 ,
2019-05-05 05:20:38 +00:00
} ;
// Create the user
let inserted_user = match User_ ::register ( & conn , & user_form ) {
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 "
} ;
return Err ( APIError ::err ( & self . op , err_type ) . into ( ) ) ;
}
2019-05-05 05:20:38 +00:00
} ;
2019-06-02 16:34:45 +00:00
// Create the main community if it doesn't exist
2019-08-17 21:39:20 +00:00
let main_community : Community = match Community ::read ( & conn , 2 ) {
2019-06-02 16:34:45 +00:00
Ok ( c ) = > c ,
Err ( _e ) = > {
let community_form = CommunityForm {
name : " main " . to_string ( ) ,
title : " The Default Community " . to_string ( ) ,
description : Some ( " The Default Community " . to_string ( ) ) ,
category_id : 1 ,
2019-08-14 02:52:43 +00:00
nsfw : false ,
2019-06-02 16:34:45 +00:00
creator_id : inserted_user . id ,
removed : None ,
deleted : None ,
updated : None ,
} ;
Community ::create ( & conn , & community_form ) . unwrap ( )
}
} ;
// Sign them up for main community no matter what
let community_follower_form = CommunityFollowerForm {
community_id : main_community . id ,
user_id : inserted_user . id ,
} ;
2019-09-07 15:35:05 +00:00
let _inserted_community_follower =
match CommunityFollower ::follow ( & conn , & community_follower_form ) {
Ok ( user ) = > user ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " community_follower_already_exists " ) . into ( ) ) ,
2019-09-07 15:35:05 +00:00
} ;
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 ,
} ;
2019-09-07 15:35:05 +00:00
let _inserted_community_moderator =
match CommunityModerator ::join ( & conn , & community_moderator_form ) {
Ok ( user ) = > user ,
Err ( _e ) = > {
2020-01-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " community_moderator_already_exists " ) . into ( ) )
2019-09-07 15:35:05 +00:00
}
} ;
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 {
op : self . op . to_string ( ) ,
jwt : inserted_user . jwt ( ) ,
} )
2019-05-05 05:20:38 +00:00
}
}
2019-08-14 02:52:43 +00:00
impl Perform < LoginResponse > for Oper < SaveUserSettings > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < LoginResponse , Error > {
2019-08-14 02:52:43 +00:00
let data : & SaveUserSettings = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-08-14 02:52:43 +00:00
} ;
let user_id = claims . id ;
2019-09-07 15:35:05 +00:00
2019-08-14 02:52:43 +00:00
let read_user = User_ ::read ( & conn , user_id ) ? ;
2020-01-01 20:46:14 +00:00
let email = match & data . email {
Some ( email ) = > Some ( email . to_owned ( ) ) ,
None = > read_user . email ,
} ;
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-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " passwords_dont_match " ) . into ( ) ) ;
2020-01-01 20:46:14 +00:00
}
// Check the old password
match & data . old_password {
Some ( old_password ) = > {
let valid : bool =
verify ( old_password , & read_user . password_encrypted ) . unwrap_or ( false ) ;
if ! valid {
2020-01-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " password_incorrect " ) . into ( ) ) ;
2020-01-01 20:46:14 +00:00
}
User_ ::update_password ( & conn , user_id , & new_password ) ? . password_encrypted
}
2020-01-02 11:30:00 +00:00
None = > return Err ( APIError ::err ( & self . op , " password_incorrect " ) . into ( ) ) ,
2020-01-01 20:46:14 +00:00
}
}
2020-01-02 11:30:00 +00:00
None = > return Err ( APIError ::err ( & self . op , " passwords_dont_match " ) . into ( ) ) ,
2020-01-01 20:46:14 +00:00
}
}
None = > read_user . password_encrypted ,
} ;
2019-08-14 02:52:43 +00:00
let user_form = UserForm {
name : read_user . name ,
fedi_name : read_user . fedi_name ,
2020-01-01 20:46:14 +00:00
email ,
2020-01-22 21:35:29 +00:00
matrix_user_id : data . matrix_user_id . to_owned ( ) ,
2019-12-29 20:39:48 +00:00
avatar : data . avatar . to_owned ( ) ,
2020-01-01 20:46:14 +00:00
password_encrypted ,
2019-08-14 02:52:43 +00:00
preferred_username : read_user . preferred_username ,
updated : Some ( naive_now ( ) ) ,
admin : read_user . admin ,
banned : read_user . banned ,
show_nsfw : data . show_nsfw ,
2019-10-15 19:21:27 +00:00
theme : data . theme . to_owned ( ) ,
2019-10-21 04:21:54 +00:00
default_sort_type : data . default_sort_type ,
default_listing_type : data . 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
} ;
let updated_user = match User_ ::update ( & conn , user_id , & user_form ) {
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 "
} ;
return Err ( APIError ::err ( & self . op , err_type ) . into ( ) ) ;
}
2019-08-14 02:52:43 +00:00
} ;
// Return the jwt
2019-09-07 15:35:05 +00:00
Ok ( LoginResponse {
op : self . op . to_string ( ) ,
jwt : updated_user . jwt ( ) ,
} )
2019-08-14 02:52:43 +00:00
}
}
2019-05-05 05:20:38 +00:00
impl Perform < GetUserDetailsResponse > for Oper < GetUserDetails > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < GetUserDetailsResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & GetUserDetails = & self . data ;
2019-05-05 05:20:38 +00:00
2019-08-14 02:52:43 +00:00
let user_claims : Option < Claims > = match & data . auth {
2019-09-07 15:35:05 +00:00
Some ( auth ) = > match Claims ::decode ( & auth ) {
Ok ( claims ) = > Some ( claims . claims ) ,
Err ( _e ) = > None ,
} ,
None = > None ,
2019-05-05 05:20:38 +00:00
} ;
2019-09-07 15:35:05 +00:00
2019-08-14 02:52:43 +00:00
let user_id = match & user_claims {
Some ( claims ) = > Some ( claims . id ) ,
2019-09-07 15:35:05 +00:00
None = > None ,
2019-08-14 02:52:43 +00:00
} ;
let show_nsfw = match & user_claims {
Some ( claims ) = > claims . 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 ) ? ;
let user_details_id = match data . user_id {
Some ( id ) = > id ,
2019-09-07 15:35:05 +00:00
None = > {
2019-12-29 01:58:01 +00:00
match User_ ::read_from_name (
2019-09-07 15:35:05 +00:00
& conn ,
2020-01-02 11:30:00 +00:00
data
. username
. to_owned ( )
. unwrap_or_else ( | | " admin " . to_string ( ) ) ,
2019-12-29 01:58:01 +00:00
) {
Ok ( user ) = > user . id ,
2019-12-29 20:39:48 +00:00
Err ( _e ) = > {
2020-01-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " couldnt_find_that_username_or_email " ) . into ( ) )
2019-12-29 20:39:48 +00:00
}
2019-12-29 01:58:01 +00:00
}
2019-09-07 15:35:05 +00:00
}
2019-05-05 05:20:38 +00:00
} ;
let user_view = UserView ::read ( & conn , user_details_id ) ? ;
2019-12-07 22:54:42 +00:00
let mut posts_query = PostQueryBuilder ::create ( & conn )
. sort ( & sort )
. show_nsfw ( show_nsfw )
. saved_only ( data . saved_only )
2019-12-10 23:10:39 +00:00
. for_community_id ( data . community_id )
. my_user_id ( user_id )
. page ( data . page )
. limit ( data . limit ) ;
2019-12-07 12:03:03 +00:00
2019-12-08 20:39:54 +00:00
let mut comments_query = CommentQueryBuilder ::create ( & conn )
. sort ( & sort )
. saved_only ( data . saved_only )
2019-12-10 23:10:39 +00:00
. my_user_id ( user_id )
. page ( data . page )
. limit ( data . limit ) ;
2019-12-08 20:39:54 +00:00
2019-05-05 05:20:38 +00:00
// If its saved only, you don't care what creator it was
2019-12-08 20:39:54 +00:00
// Or, if its not saved, then you only want it for that specific creator
2019-12-07 12:03:03 +00:00
if ! data . saved_only {
posts_query = posts_query . for_creator_id ( user_details_id ) ;
2019-12-08 20:39:54 +00:00
comments_query = comments_query . for_creator_id ( user_details_id ) ;
2019-12-07 12:03:03 +00:00
}
let posts = posts_query . list ( ) ? ;
2019-12-08 20:39:54 +00:00
let comments = comments_query . list ( ) ? ;
2019-05-05 05:20:38 +00:00
let follows = CommunityFollowerView ::for_user ( & conn , user_details_id ) ? ;
let moderates = CommunityModeratorView ::for_user ( & conn , user_details_id ) ? ;
2019-10-14 00:36:35 +00:00
let site_creator_id = Site ::read ( & conn , 1 ) ? . creator_id ;
let mut admins = UserView ::admins ( & conn ) ? ;
let creator_index = admins . iter ( ) . position ( | r | r . id = = site_creator_id ) . unwrap ( ) ;
let creator_user = admins . remove ( creator_index ) ;
admins . insert ( 0 , creator_user ) ;
2019-05-05 05:20:38 +00:00
// Return the jwt
2019-09-07 15:35:05 +00:00
Ok ( GetUserDetailsResponse {
op : self . op . to_string ( ) ,
user : user_view ,
2019-12-09 19:08:19 +00:00
follows ,
moderates ,
comments ,
posts ,
admins ,
2019-09-07 15:35:05 +00:00
} )
2019-05-05 05:20:38 +00:00
}
}
impl Perform < AddAdminResponse > for Oper < AddAdmin > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < AddAdminResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & AddAdmin = & self . data ;
2019-05-05 05:20:38 +00:00
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
let user_id = claims . id ;
// Make sure user is an admin
2020-01-02 11:30:00 +00:00
if ! UserView ::read ( & conn , user_id ) ? . admin {
return Err ( APIError ::err ( & self . op , " not_an_admin " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
let read_user = User_ ::read ( & conn , data . user_id ) ? ;
2020-01-22 21:35:29 +00:00
// TODO make addadmin easier
2019-05-05 05:20:38 +00:00
let user_form = UserForm {
name : read_user . name ,
fedi_name : read_user . fedi_name ,
email : read_user . email ,
2020-01-22 21:35:29 +00:00
matrix_user_id : read_user . matrix_user_id ,
2019-12-29 20:39:48 +00:00
avatar : read_user . avatar ,
2019-05-05 05:20:38 +00:00
password_encrypted : read_user . password_encrypted ,
preferred_username : read_user . preferred_username ,
updated : Some ( naive_now ( ) ) ,
admin : data . added ,
banned : read_user . banned ,
2019-08-14 02:52:43 +00:00
show_nsfw : read_user . show_nsfw ,
2019-10-15 19:21:27 +00:00
theme : read_user . theme ,
2019-10-21 04:21:54 +00:00
default_sort_type : read_user . default_sort_type ,
default_listing_type : read_user . default_listing_type ,
2019-12-09 08:24:53 +00:00
lang : read_user . lang ,
2020-01-02 21:55:54 +00:00
show_avatars : read_user . show_avatars ,
send_notifications_to_email : read_user . send_notifications_to_email ,
2019-05-05 05:20:38 +00:00
} ;
match User_ ::update ( & conn , data . user_id , & user_form ) {
Ok ( user ) = > user ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_user " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
// Mod tables
let form = ModAddForm {
mod_user_id : user_id ,
other_user_id : data . user_id ,
removed : Some ( ! data . added ) ,
} ;
ModAdd ::create ( & conn , & form ) ? ;
2019-08-24 02:40:41 +00:00
let site_creator_id = Site ::read ( & conn , 1 ) ? . creator_id ;
let mut admins = UserView ::admins ( & conn ) ? ;
let creator_index = admins . iter ( ) . position ( | r | r . id = = site_creator_id ) . unwrap ( ) ;
let creator_user = admins . remove ( creator_index ) ;
admins . insert ( 0 , creator_user ) ;
2019-05-05 05:20:38 +00:00
2019-09-07 15:35:05 +00:00
Ok ( AddAdminResponse {
op : self . op . to_string ( ) ,
2019-12-09 19:08:19 +00:00
admins ,
2019-09-07 15:35:05 +00:00
} )
2019-05-05 05:20:38 +00:00
}
}
impl Perform < BanUserResponse > for Oper < BanUser > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < BanUserResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & BanUser = & self . data ;
2019-05-05 05:20:38 +00:00
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
let user_id = claims . id ;
// Make sure user is an admin
2020-01-02 11:30:00 +00:00
if ! UserView ::read ( & conn , user_id ) ? . admin {
return Err ( APIError ::err ( & self . op , " not_an_admin " ) . into ( ) ) ;
2019-05-05 05:20:38 +00:00
}
let read_user = User_ ::read ( & conn , data . user_id ) ? ;
2020-01-22 21:35:29 +00:00
// TODO make bans and addadmins easier
2019-05-05 05:20:38 +00:00
let user_form = UserForm {
name : read_user . name ,
fedi_name : read_user . fedi_name ,
email : read_user . email ,
2020-01-22 21:35:29 +00:00
matrix_user_id : read_user . matrix_user_id ,
2019-12-29 20:39:48 +00:00
avatar : read_user . avatar ,
2019-05-05 05:20:38 +00:00
password_encrypted : read_user . password_encrypted ,
preferred_username : read_user . preferred_username ,
updated : Some ( naive_now ( ) ) ,
admin : read_user . admin ,
banned : data . ban ,
2019-08-14 02:52:43 +00:00
show_nsfw : read_user . show_nsfw ,
2019-10-15 19:21:27 +00:00
theme : read_user . theme ,
2019-10-21 04:21:54 +00:00
default_sort_type : read_user . default_sort_type ,
default_listing_type : read_user . default_listing_type ,
2019-12-09 08:24:53 +00:00
lang : read_user . lang ,
2020-01-02 21:55:54 +00:00
show_avatars : read_user . show_avatars ,
send_notifications_to_email : read_user . send_notifications_to_email ,
2019-05-05 05:20:38 +00:00
} ;
match User_ ::update ( & conn , data . user_id , & user_form ) {
Ok ( user ) = > user ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_user " ) . into ( ) ) ,
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 {
mod_user_id : user_id ,
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
} ;
ModBan ::create ( & conn , & form ) ? ;
let user_view = UserView ::read ( & conn , data . user_id ) ? ;
2019-09-07 15:35:05 +00:00
Ok ( BanUserResponse {
op : self . op . to_string ( ) ,
user : user_view ,
banned : data . ban ,
} )
2019-05-05 05:20:38 +00:00
}
}
impl Perform < GetRepliesResponse > for Oper < GetReplies > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < GetRepliesResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & GetReplies = & self . data ;
2019-05-05 05:20:38 +00:00
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
let user_id = claims . id ;
let sort = SortType ::from_str ( & data . sort ) ? ;
2019-12-08 20:39:54 +00:00
let replies = ReplyQueryBuilder ::create ( & conn , user_id )
. sort ( & sort )
. unread_only ( data . unread_only )
2019-12-10 23:10:39 +00:00
. page ( data . page )
. limit ( data . limit )
2019-12-08 20:39:54 +00:00
. list ( ) ? ;
2019-05-05 05:20:38 +00:00
2019-09-07 15:35:05 +00:00
Ok ( GetRepliesResponse {
op : self . op . to_string ( ) ,
2019-12-09 19:08:19 +00:00
replies ,
2019-09-07 15:35:05 +00:00
} )
2019-05-05 05:20:38 +00:00
}
}
2019-10-20 00:46:29 +00:00
impl Perform < GetUserMentionsResponse > for Oper < GetUserMentions > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < GetUserMentionsResponse , Error > {
2019-10-20 00:46:29 +00:00
let data : & GetUserMentions = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-10-20 00:46:29 +00:00
} ;
let user_id = claims . id ;
let sort = SortType ::from_str ( & data . sort ) ? ;
2019-12-08 20:39:54 +00:00
let mentions = UserMentionQueryBuilder ::create ( & conn , user_id )
. sort ( & sort )
. unread_only ( data . unread_only )
2019-12-10 23:10:39 +00:00
. page ( data . page )
. limit ( data . limit )
2019-12-08 20:39:54 +00:00
. list ( ) ? ;
2019-10-20 00:46:29 +00:00
Ok ( GetUserMentionsResponse {
op : self . op . to_string ( ) ,
2019-12-09 19:08:19 +00:00
mentions ,
2019-10-20 00:46:29 +00:00
} )
}
}
impl Perform < UserMentionResponse > for Oper < EditUserMention > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < UserMentionResponse , Error > {
2019-10-20 00:46:29 +00:00
let data : & EditUserMention = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-10-20 00:46:29 +00:00
} ;
let user_id = claims . id ;
let user_mention = UserMention ::read ( & conn , data . user_mention_id ) ? ;
let user_mention_form = UserMentionForm {
recipient_id : user_id ,
comment_id : user_mention . comment_id ,
read : data . read . to_owned ( ) ,
} ;
let _updated_user_mention =
match UserMention ::update ( & conn , user_mention . id , & user_mention_form ) {
Ok ( comment ) = > comment ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_comment " ) . into ( ) ) ,
2019-10-20 00:46:29 +00:00
} ;
let user_mention_view = UserMentionView ::read ( & conn , user_mention . id , user_id ) ? ;
Ok ( UserMentionResponse {
op : self . op . to_string ( ) ,
mention : user_mention_view ,
} )
}
}
2019-05-05 05:20:38 +00:00
impl Perform < GetRepliesResponse > for Oper < MarkAllAsRead > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < GetRepliesResponse , Error > {
2019-05-05 16:20:30 +00:00
let data : & MarkAllAsRead = & self . data ;
2019-05-05 05:20:38 +00:00
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
let user_id = claims . id ;
2019-12-08 20:39:54 +00:00
let replies = ReplyQueryBuilder ::create ( & conn , user_id )
. unread_only ( true )
. page ( 1 )
. limit ( 999 )
. list ( ) ? ;
2019-05-05 05:20:38 +00:00
for reply in & replies {
let comment_form = CommentForm {
content : reply . to_owned ( ) . content ,
parent_id : reply . to_owned ( ) . parent_id ,
post_id : reply . to_owned ( ) . post_id ,
creator_id : reply . to_owned ( ) . creator_id ,
removed : None ,
deleted : None ,
read : Some ( true ) ,
2019-09-07 15:35:05 +00:00
updated : reply . to_owned ( ) . updated ,
2019-05-05 05:20:38 +00:00
} ;
let _updated_comment = match Comment ::update ( & conn , reply . id , & comment_form ) {
Ok ( comment ) = > comment ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_comment " ) . into ( ) ) ,
2019-05-05 05:20:38 +00:00
} ;
}
2019-10-20 00:46:29 +00:00
// Mentions
2019-12-08 20:39:54 +00:00
let mentions = UserMentionQueryBuilder ::create ( & conn , user_id )
. unread_only ( true )
. page ( 1 )
. limit ( 999 )
. list ( ) ? ;
2019-10-20 00:46:29 +00:00
for mention in & mentions {
let mention_form = UserMentionForm {
recipient_id : mention . to_owned ( ) . recipient_id ,
comment_id : mention . to_owned ( ) . id ,
read : Some ( true ) ,
} ;
let _updated_mention =
match UserMention ::update ( & conn , mention . user_mention_id , & mention_form ) {
Ok ( mention ) = > mention ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_comment " ) . into ( ) ) ,
2019-10-20 00:46:29 +00:00
} ;
}
2019-05-05 05:20:38 +00:00
2020-01-22 21:35:29 +00:00
// messages
let messages = PrivateMessageQueryBuilder ::create ( & conn , user_id )
. page ( 1 )
. limit ( 999 )
. unread_only ( true )
. list ( ) ? ;
for message in & messages {
let private_message_form = PrivateMessageForm {
content : None ,
creator_id : message . to_owned ( ) . creator_id ,
recipient_id : message . to_owned ( ) . recipient_id ,
deleted : None ,
read : Some ( true ) ,
updated : None ,
} ;
let _updated_message = match PrivateMessage ::update ( & conn , message . id , & private_message_form )
{
Ok ( message ) = > message ,
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_private_message " ) . into ( ) ) ,
} ;
}
2019-09-07 15:35:05 +00:00
Ok ( GetRepliesResponse {
op : self . op . to_string ( ) ,
2019-10-20 00:46:29 +00:00
replies : vec ! [ ] ,
2019-09-07 15:35:05 +00:00
} )
2019-05-05 05:20:38 +00:00
}
}
2019-10-15 22:09:01 +00:00
impl Perform < LoginResponse > for Oper < DeleteAccount > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < LoginResponse , Error > {
2019-10-15 22:09:01 +00:00
let data : & DeleteAccount = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
2019-10-15 22:09:01 +00:00
} ;
let user_id = claims . id ;
2019-10-18 04:25:23 +00:00
let user : User_ = User_ ::read ( & conn , user_id ) ? ;
// Verify the password
let valid : bool = verify ( & data . password , & user . password_encrypted ) . unwrap_or ( false ) ;
if ! valid {
2020-01-02 11:30:00 +00:00
return Err ( APIError ::err ( & self . op , " password_incorrect " ) . into ( ) ) ;
2019-10-18 04:25:23 +00:00
}
2019-10-15 22:09:01 +00:00
// Comments
2019-12-08 20:39:54 +00:00
let comments = CommentQueryBuilder ::create ( & conn )
. for_creator_id ( user_id )
. limit ( std ::i64 ::MAX )
. list ( ) ? ;
2019-10-15 22:09:01 +00:00
for comment in & comments {
let comment_form = CommentForm {
content : " *Permananently Deleted* " . to_string ( ) ,
parent_id : comment . to_owned ( ) . parent_id ,
post_id : comment . to_owned ( ) . post_id ,
creator_id : comment . to_owned ( ) . creator_id ,
removed : None ,
deleted : Some ( true ) ,
read : None ,
updated : Some ( naive_now ( ) ) ,
} ;
let _updated_comment = match Comment ::update ( & conn , comment . id , & comment_form ) {
Ok ( comment ) = > comment ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_comment " ) . into ( ) ) ,
2019-10-15 22:09:01 +00:00
} ;
}
// Posts
2019-12-07 22:54:42 +00:00
let posts = PostQueryBuilder ::create ( & conn )
. sort ( & SortType ::New )
2019-12-07 12:03:03 +00:00
. for_creator_id ( user_id )
. limit ( std ::i64 ::MAX )
. list ( ) ? ;
2019-10-15 22:09:01 +00:00
for post in & posts {
let post_form = PostForm {
name : " *Permananently Deleted* " . to_string ( ) ,
url : Some ( " https://deleted.com " . to_string ( ) ) ,
body : Some ( " *Permananently Deleted* " . to_string ( ) ) ,
creator_id : post . to_owned ( ) . creator_id ,
community_id : post . to_owned ( ) . community_id ,
removed : None ,
deleted : Some ( true ) ,
nsfw : post . to_owned ( ) . nsfw ,
locked : None ,
stickied : None ,
updated : Some ( naive_now ( ) ) ,
} ;
let _updated_post = match Post ::update ( & conn , post . id , & post_form ) {
Ok ( post ) = > post ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_post " ) . into ( ) ) ,
2019-10-15 22:09:01 +00:00
} ;
}
Ok ( LoginResponse {
op : self . op . to_string ( ) ,
jwt : data . auth . to_owned ( ) ,
} )
}
}
2019-10-30 03:35:39 +00:00
impl Perform < PasswordResetResponse > for Oper < PasswordReset > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < PasswordResetResponse , Error > {
2019-10-30 03:35:39 +00:00
let data : & PasswordReset = & self . data ;
// Fetch that email
let user : User_ = match User_ ::find_by_email ( & conn , & data . email ) {
Ok ( user ) = > user ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " 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
PasswordResetRequest ::create_token ( & conn , user . id , & token ) ? ;
// 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 ) ;
2019-11-02 20:07:19 +00:00
let hostname = & format! ( " https:// {} " , Settings ::get ( ) . hostname ) ; //TODO add https for now.
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-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , & _e ) . into ( ) ) ,
2019-10-30 03:35:39 +00:00
} ;
Ok ( PasswordResetResponse {
op : self . op . to_string ( ) ,
} )
}
}
impl Perform < LoginResponse > for Oper < PasswordChange > {
2020-01-12 15:31:51 +00:00
fn perform ( & self , conn : & PgConnection ) -> Result < LoginResponse , Error > {
2019-10-30 03:35:39 +00:00
let data : & PasswordChange = & self . data ;
// Fetch the user_id from the token
let user_id = PasswordResetRequest ::read_from_token ( & conn , & data . token ) ? . user_id ;
// Make sure passwords match
2020-01-02 11:30:00 +00:00
if data . password ! = data . password_verify {
return Err ( APIError ::err ( & self . op , " passwords_dont_match " ) . into ( ) ) ;
2019-10-30 03:35:39 +00:00
}
// Update the user with the new password
2020-01-01 20:46:14 +00:00
let updated_user = match User_ ::update_password ( & conn , user_id , & data . password ) {
2019-10-30 03:35:39 +00:00
Ok ( user ) = > user ,
2020-01-02 11:30:00 +00:00
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_user " ) . into ( ) ) ,
2019-10-30 03:35:39 +00:00
} ;
// Return the jwt
Ok ( LoginResponse {
op : self . op . to_string ( ) ,
jwt : updated_user . jwt ( ) ,
} )
}
}
2020-01-22 21:35:29 +00:00
impl Perform < PrivateMessageResponse > for Oper < CreatePrivateMessage > {
fn perform ( & self , conn : & PgConnection ) -> Result < PrivateMessageResponse , Error > {
let data : & CreatePrivateMessage = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
} ;
let user_id = claims . id ;
let hostname = & format! ( " https:// {} " , Settings ::get ( ) . hostname ) ;
// Check for a site ban
if UserView ::read ( & conn , user_id ) ? . banned {
return Err ( APIError ::err ( & self . op , " site_ban " ) . into ( ) ) ;
}
let content_slurs_removed = remove_slurs ( & data . content . to_owned ( ) ) ;
let private_message_form = PrivateMessageForm {
content : Some ( content_slurs_removed . to_owned ( ) ) ,
creator_id : user_id ,
recipient_id : data . recipient_id ,
deleted : None ,
read : None ,
updated : None ,
} ;
let inserted_private_message = match PrivateMessage ::create ( & conn , & private_message_form ) {
Ok ( private_message ) = > private_message ,
Err ( _e ) = > {
return Err ( APIError ::err ( & self . op , " couldnt_create_private_message " ) . into ( ) ) ;
}
} ;
// Send notifications to the recipient
let recipient_user = User_ ::read ( & conn , data . recipient_id ) ? ;
if recipient_user . send_notifications_to_email {
if let Some ( email ) = recipient_user . email {
let subject = & format! (
" {} - Private Message from {} " ,
Settings ::get ( ) . hostname ,
claims . username
) ;
let html = & format! (
" <h1>Private Message</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a> " ,
claims . username , & content_slurs_removed , hostname
) ;
match send_email ( subject , & email , & recipient_user . name , html ) {
Ok ( _o ) = > _o ,
Err ( e ) = > eprintln! ( " {} " , e ) ,
} ;
}
}
let private_message_view = PrivateMessageView ::read ( & conn , inserted_private_message . id ) ? ;
Ok ( PrivateMessageResponse {
op : self . op . to_string ( ) ,
message : private_message_view ,
} )
}
}
impl Perform < PrivateMessageResponse > for Oper < EditPrivateMessage > {
fn perform ( & self , conn : & PgConnection ) -> Result < PrivateMessageResponse , Error > {
let data : & EditPrivateMessage = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
} ;
let user_id = claims . id ;
let orig_private_message = PrivateMessage ::read ( & conn , data . edit_id ) ? ;
// Check for a site ban
if UserView ::read ( & conn , user_id ) ? . banned {
return Err ( APIError ::err ( & self . op , " site_ban " ) . into ( ) ) ;
}
// Check to make sure they are the creator (or the recipient marking as read
if ! ( data . read . is_some ( ) & & orig_private_message . recipient_id . eq ( & user_id )
| | orig_private_message . creator_id . eq ( & user_id ) )
{
return Err ( APIError ::err ( & self . op , " no_private_message_edit_allowed " ) . into ( ) ) ;
}
let content_slurs_removed = match & data . content {
Some ( content ) = > Some ( remove_slurs ( content ) ) ,
None = > None ,
} ;
let private_message_form = PrivateMessageForm {
content : content_slurs_removed ,
creator_id : orig_private_message . creator_id ,
recipient_id : orig_private_message . recipient_id ,
deleted : data . deleted . to_owned ( ) ,
read : data . read . to_owned ( ) ,
updated : if data . read . is_some ( ) {
orig_private_message . updated
} else {
Some ( naive_now ( ) )
} ,
} ;
let _updated_private_message =
match PrivateMessage ::update ( & conn , data . edit_id , & private_message_form ) {
Ok ( private_message ) = > private_message ,
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " couldnt_update_private_message " ) . into ( ) ) ,
} ;
let private_message_view = PrivateMessageView ::read ( & conn , data . edit_id ) ? ;
Ok ( PrivateMessageResponse {
op : self . op . to_string ( ) ,
message : private_message_view ,
} )
}
}
impl Perform < PrivateMessagesResponse > for Oper < GetPrivateMessages > {
fn perform ( & self , conn : & PgConnection ) -> Result < PrivateMessagesResponse , Error > {
let data : & GetPrivateMessages = & self . data ;
let claims = match Claims ::decode ( & data . auth ) {
Ok ( claims ) = > claims . claims ,
Err ( _e ) = > return Err ( APIError ::err ( & self . op , " not_logged_in " ) . into ( ) ) ,
} ;
let user_id = claims . id ;
let messages = PrivateMessageQueryBuilder ::create ( & conn , user_id )
. page ( data . page )
. limit ( data . limit )
. unread_only ( data . unread_only )
. list ( ) ? ;
Ok ( PrivateMessagesResponse {
op : self . op . to_string ( ) ,
messages ,
} )
}
}