From 1e60e1e35106157bcf7ca52d25bacf8b6a7e15b0 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 15 Jan 2020 16:37:25 +0100 Subject: [PATCH 01/15] Implement HTTP API using generics (fixes #380) --- server/.rustfmt.toml | 2 +- server/src/main.rs | 3 +- server/src/routes/api.rs | 62 ++++++++++++++++++++++++++++++++++++++++ server/src/routes/mod.rs | 1 + 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 server/src/routes/api.rs diff --git a/server/.rustfmt.toml b/server/.rustfmt.toml index b1fce9c9..684a7f8a 100644 --- a/server/.rustfmt.toml +++ b/server/.rustfmt.toml @@ -1,2 +1,2 @@ tab_spaces = 2 -edition="2018" +edition="2018" \ No newline at end of file diff --git a/server/src/main.rs b/server/src/main.rs index 636182aa..601c2e0d 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -6,7 +6,7 @@ use actix::prelude::*; use actix_web::*; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; -use lemmy_server::routes::{federation, feeds, index, nodeinfo, webfinger, websocket}; +use lemmy_server::routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}; use lemmy_server::settings::Settings; use lemmy_server::websocket::server::*; use std::io; @@ -44,6 +44,7 @@ async fn main() -> io::Result<()> { .data(pool.clone()) .data(server.clone()) // The routes + .configure(api::config) .configure(federation::config) .configure(feeds::config) .configure(index::config) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs new file mode 100644 index 00000000..edf1ead6 --- /dev/null +++ b/server/src/routes/api.rs @@ -0,0 +1,62 @@ +use crate::api::community::{ + GetCommunity, GetCommunityResponse, ListCommunities, ListCommunitiesResponse, +}; +use crate::api::UserOperation; +use crate::api::{Oper, Perform}; +use actix_web::{web, HttpResponse}; +use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::PgConnection; +use failure::Error; +use serde::Serialize; + +type DbParam = web::Data>>; + +pub fn config(cfg: &mut web::ServiceConfig) { + cfg + // TODO: need to repeat this for every endpoint + .route( + "/api/v1/list_communities", + web::get().to(|info, db| { + route::(UserOperation::ListCommunities, info, db) + }), + ) + .route( + "/api/v1/get_community", + web::get().to(|info, db| { + route::(UserOperation::GetCommunity, info, db) + }), + ); +} + +fn perform( + op: UserOperation, + data: Request, + db: DbParam, +) -> Result +where + Response: Serialize, + Oper: Perform, +{ + let conn = match db.get() { + Ok(c) => c, + Err(e) => return Err(format_err!("{}", e)), + }; + let oper: Oper = Oper::new(op, data); + let response = oper.perform(&conn); + Ok(HttpResponse::Ok().json(response?)) +} + +async fn route( + op: UserOperation, + info: web::Query, + db: DbParam, +) -> Result +where + Data: Serialize, + Response: Serialize, + Oper: Perform, +{ + // TODO: want an implementation like this, where useroperation is passed without explicitly passing the other params + // maybe with a higher order functions? (but that would probably have worse performance) + perform::(op, info.0, db) +} diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 6556c8d5..27d9ea1b 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -1,3 +1,4 @@ +pub mod api; pub mod federation; pub mod feeds; pub mod index; From 19afdf993e55e42ae5f86882fe04ac108f71a6d6 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 15 Jan 2020 16:48:21 +0100 Subject: [PATCH 02/15] try to simplify code with higher order functions --- server/src/routes/api.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index edf1ead6..11919b8c 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -16,15 +16,15 @@ pub fn config(cfg: &mut web::ServiceConfig) { // TODO: need to repeat this for every endpoint .route( "/api/v1/list_communities", - web::get().to(|info, db| { - route::(UserOperation::ListCommunities, info, db) - }), + web::get().to( + route::(UserOperation::ListCommunities) + ), ) .route( "/api/v1/get_community", - web::get().to(|info, db| { - route::(UserOperation::GetCommunity, info, db) - }), + web::get().to(route::( + UserOperation::GetCommunity, + )), ); } @@ -46,11 +46,9 @@ where Ok(HttpResponse::Ok().json(response?)) } -async fn route( +fn route( op: UserOperation, - info: web::Query, - db: DbParam, -) -> Result +) -> Box<(dyn Fn(web::Query, DbParam) -> Result + 'static)> where Data: Serialize, Response: Serialize, @@ -58,5 +56,5 @@ where { // TODO: want an implementation like this, where useroperation is passed without explicitly passing the other params // maybe with a higher order functions? (but that would probably have worse performance) - perform::(op, info.0, db) + Box::new(|data, db| perform::(op, data.0, db)) } From f1035dacc2191385fe1e8695ab5a29e529aca380 Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 16 Jan 2020 15:39:08 +0100 Subject: [PATCH 03/15] working! --- server/src/api/comment.rs | 41 ++++---- server/src/api/community.rs | 97 ++++++------------ server/src/api/mod.rs | 54 +--------- server/src/api/post.rs | 78 ++++++-------- server/src/api/site.rs | 46 +++------ server/src/api/user.rs | 125 ++++++++-------------- server/src/routes/api.rs | 182 ++++++++++++++++++++++++++++----- server/src/websocket/mod.rs | 43 ++++++++ server/src/websocket/server.rs | 168 ++++++++++++++++-------------- 9 files changed, 436 insertions(+), 398 deletions(-) diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index 61cc9506..d26c2dce 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -35,7 +35,6 @@ pub struct SaveComment { #[derive(Serialize, Deserialize, Clone)] pub struct CommentResponse { - op: String, pub comment: CommentView, } @@ -53,7 +52,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -63,12 +62,12 @@ impl Perform for Oper { // Check for a community ban let post = Post::read(&conn, data.post_id)?; if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { - return Err(APIError::err(&self.op, "community_ban").into()); + return Err(APIError::err("community_ban").into()); } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } let content_slurs_removed = remove_slurs(&data.content.to_owned()); @@ -86,7 +85,7 @@ impl Perform for Oper { let inserted_comment = match Comment::create(&conn, &comment_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_create_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_create_comment").into()), }; // Scan the comment for user mentions, add those rows @@ -193,13 +192,12 @@ impl Perform for Oper { let _inserted_like = match CommentLike::like(&conn, &like_form) { Ok(like) => like, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_like_comment").into()), }; let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?; Ok(CommentResponse { - op: self.op.to_string(), comment: comment_view, }) } @@ -211,7 +209,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -231,17 +229,17 @@ impl Perform for Oper { editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect()); if !editors.contains(&user_id) { - return Err(APIError::err(&self.op, "no_comment_edit_allowed").into()); + return Err(APIError::err("no_comment_edit_allowed").into()); } // Check for a community ban if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() { - return Err(APIError::err(&self.op, "community_ban").into()); + return Err(APIError::err("community_ban").into()); } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } } @@ -264,7 +262,7 @@ impl Perform for Oper { let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), }; // Scan the comment for user mentions, add those rows @@ -310,7 +308,6 @@ impl Perform for Oper { let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?; Ok(CommentResponse { - op: self.op.to_string(), comment: comment_view, }) } @@ -322,7 +319,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -335,19 +332,18 @@ impl Perform for Oper { if data.save { match CommentSaved::save(&conn, &comment_saved_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_save_comment").into()), }; } else { match CommentSaved::unsave(&conn, &comment_saved_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_save_comment").into()), }; } let comment_view = CommentView::read(&conn, data.comment_id, Some(user_id))?; Ok(CommentResponse { - op: self.op.to_string(), comment: comment_view, }) } @@ -359,7 +355,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -368,19 +364,19 @@ impl Perform for Oper { if data.score == -1 { let site = SiteView::read(&conn)?; if !site.enable_downvotes { - return Err(APIError::err(&self.op, "downvotes_disabled").into()); + return Err(APIError::err("downvotes_disabled").into()); } } // Check for a community ban let post = Post::read(&conn, data.post_id)?; if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { - return Err(APIError::err(&self.op, "community_ban").into()); + return Err(APIError::err("community_ban").into()); } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } let like_form = CommentLikeForm { @@ -398,7 +394,7 @@ impl Perform for Oper { if do_add { let _inserted_like = match CommentLike::like(&conn, &like_form) { Ok(like) => like, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_like_comment").into()), }; } @@ -406,7 +402,6 @@ impl Perform for Oper { let liked_comment = CommentView::read(&conn, data.comment_id, Some(user_id))?; Ok(CommentResponse { - op: self.op.to_string(), comment: liked_comment, }) } diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 0bf846c3..c765aa9d 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -11,7 +11,6 @@ pub struct GetCommunity { #[derive(Serialize, Deserialize)] pub struct GetCommunityResponse { - op: String, community: CommunityView, moderators: Vec, admins: Vec, @@ -29,7 +28,6 @@ pub struct CreateCommunity { #[derive(Serialize, Deserialize, Clone)] pub struct CommunityResponse { - op: String, pub community: CommunityView, } @@ -43,7 +41,6 @@ pub struct ListCommunities { #[derive(Serialize, Deserialize)] pub struct ListCommunitiesResponse { - op: String, communities: Vec, } @@ -59,7 +56,6 @@ pub struct BanFromCommunity { #[derive(Serialize, Deserialize)] pub struct BanFromCommunityResponse { - op: String, user: UserView, banned: bool, } @@ -74,7 +70,6 @@ pub struct AddModToCommunity { #[derive(Serialize, Deserialize)] pub struct AddModToCommunityResponse { - op: String, moderators: Vec, } @@ -107,7 +102,6 @@ pub struct GetFollowedCommunities { #[derive(Serialize, Deserialize)] pub struct GetFollowedCommunitiesResponse { - op: String, communities: Vec, } @@ -141,19 +135,19 @@ impl Perform for Oper { data.name.to_owned().unwrap_or_else(|| "main".to_string()), ) { Ok(community) => community.id, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), + Err(_e) => return Err(APIError::err("couldnt_find_community").into()), } } }; let community_view = match CommunityView::read(&conn, community_id, user_id) { Ok(community) => community, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), + Err(_e) => return Err(APIError::err("couldnt_find_community").into()), }; let moderators = match CommunityModeratorView::for_community(&conn, community_id) { Ok(moderators) => moderators, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), + Err(_e) => return Err(APIError::err("couldnt_find_community").into()), }; let site_creator_id = Site::read(&conn, 1)?.creator_id; @@ -164,7 +158,6 @@ impl Perform for Oper { // Return the jwt Ok(GetCommunityResponse { - op: self.op.to_string(), community: community_view, moderators, admins, @@ -178,21 +171,21 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; if has_slurs(&data.name) || has_slurs(&data.title) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } let user_id = claims.id; // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } // When you create a community, make sure the user becomes a moderator and a follower @@ -210,7 +203,7 @@ impl Perform for Oper { let inserted_community = match Community::create(&conn, &community_form) { Ok(community) => community, - Err(_e) => return Err(APIError::err(&self.op, "community_already_exists").into()), + Err(_e) => return Err(APIError::err("community_already_exists").into()), }; let community_moderator_form = CommunityModeratorForm { @@ -221,9 +214,7 @@ impl Perform for Oper { let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) { Ok(user) => user, - Err(_e) => { - return Err(APIError::err(&self.op, "community_moderator_already_exists").into()) - } + Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()), }; let community_follower_form = CommunityFollowerForm { @@ -234,13 +225,12 @@ impl Perform for Oper { let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), + Err(_e) => return Err(APIError::err("community_follower_already_exists").into()), }; let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?; Ok(CommunityResponse { - op: self.op.to_string(), community: community_view, }) } @@ -251,19 +241,19 @@ impl Perform for Oper { let data: &EditCommunity = &self.data; if has_slurs(&data.name) || has_slurs(&data.title) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } // Verify its a mod @@ -276,7 +266,7 @@ impl Perform for Oper { ); editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect()); if !editors.contains(&user_id) { - return Err(APIError::err(&self.op, "no_community_edit_allowed").into()); + return Err(APIError::err("no_community_edit_allowed").into()); } let community_form = CommunityForm { @@ -293,7 +283,7 @@ impl Perform for Oper { let _updated_community = match Community::update(&conn, data.edit_id, &community_form) { Ok(community) => community, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()), + Err(_e) => return Err(APIError::err("couldnt_update_community").into()), }; // Mod tables @@ -315,7 +305,6 @@ impl Perform for Oper { let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?; Ok(CommunityResponse { - op: self.op.to_string(), community: community_view, }) } @@ -354,10 +343,7 @@ impl Perform for Oper { .list()?; // Return the jwt - Ok(ListCommunitiesResponse { - op: self.op.to_string(), - communities, - }) + Ok(ListCommunitiesResponse { communities }) } } @@ -367,7 +353,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -380,19 +366,18 @@ impl Perform for Oper { if data.follow { match CommunityFollower::follow(&conn, &community_follower_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), + Err(_e) => return Err(APIError::err("community_follower_already_exists").into()), }; } else { match CommunityFollower::ignore(&conn, &community_follower_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), + Err(_e) => return Err(APIError::err("community_follower_already_exists").into()), }; } let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?; Ok(CommunityResponse { - op: self.op.to_string(), community: community_view, }) } @@ -404,7 +389,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -412,14 +397,11 @@ impl Perform for Oper { let communities: Vec = match CommunityFollowerView::for_user(&conn, user_id) { Ok(communities) => communities, - Err(_e) => return Err(APIError::err(&self.op, "system_err_login").into()), + Err(_e) => return Err(APIError::err("system_err_login").into()), }; // Return the jwt - Ok(GetFollowedCommunitiesResponse { - op: self.op.to_string(), - communities, - }) + Ok(GetFollowedCommunitiesResponse { communities }) } } @@ -429,7 +411,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -442,12 +424,12 @@ impl Perform for Oper { if data.ban { match CommunityUserBan::ban(&conn, &community_user_ban_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()), + Err(_e) => return Err(APIError::err("community_user_already_banned").into()), }; } else { match CommunityUserBan::unban(&conn, &community_user_ban_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()), + Err(_e) => return Err(APIError::err("community_user_already_banned").into()), }; } @@ -470,7 +452,6 @@ impl Perform for Oper { let user_view = UserView::read(&conn, data.user_id)?; Ok(BanFromCommunityResponse { - op: self.op.to_string(), user: user_view, banned: data.ban, }) @@ -483,7 +464,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -496,16 +477,12 @@ impl Perform for Oper { if data.added { match CommunityModerator::join(&conn, &community_moderator_form) { Ok(user) => user, - Err(_e) => { - return Err(APIError::err(&self.op, "community_moderator_already_exists").into()) - } + Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()), }; } else { match CommunityModerator::leave(&conn, &community_moderator_form) { Ok(user) => user, - Err(_e) => { - return Err(APIError::err(&self.op, "community_moderator_already_exists").into()) - } + Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()), }; } @@ -520,10 +497,7 @@ impl Perform for Oper { let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?; - Ok(AddModToCommunityResponse { - op: self.op.to_string(), - moderators, - }) + Ok(AddModToCommunityResponse { moderators }) } } @@ -533,7 +507,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -548,7 +522,7 @@ impl Perform for Oper { // Make sure user is the creator, or an admin if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) { - return Err(APIError::err(&self.op, "not_an_admin").into()); + return Err(APIError::err("not_an_admin").into()); } let community_form = CommunityForm { @@ -565,7 +539,7 @@ impl Perform for Oper { let _updated_community = match Community::update(&conn, data.community_id, &community_form) { Ok(community) => community, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()), + Err(_e) => return Err(APIError::err("couldnt_update_community").into()), }; // You also have to re-do the community_moderator table, reordering it. @@ -588,9 +562,7 @@ impl Perform for Oper { let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) { Ok(user) => user, - Err(_e) => { - return Err(APIError::err(&self.op, "community_moderator_already_exists").into()) - } + Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()), }; } @@ -605,17 +577,16 @@ impl Perform for Oper { let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) { Ok(community) => community, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), + Err(_e) => return Err(APIError::err("couldnt_find_community").into()), }; let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) { Ok(moderators) => moderators, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), + Err(_e) => return Err(APIError::err("couldnt_find_community").into()), }; // Return the jwt Ok(GetCommunityResponse { - op: self.op.to_string(), community: community_view, moderators, admins, diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index e3580447..bee7b8fe 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -26,73 +26,27 @@ pub mod post; pub mod site; pub mod user; -#[derive(EnumString, ToString, Debug)] -pub enum UserOperation { - Login, - Register, - CreateCommunity, - CreatePost, - ListCommunities, - ListCategories, - GetPost, - GetCommunity, - CreateComment, - EditComment, - SaveComment, - CreateCommentLike, - GetPosts, - CreatePostLike, - EditPost, - SavePost, - EditCommunity, - FollowCommunity, - GetFollowedCommunities, - GetUserDetails, - GetReplies, - GetUserMentions, - EditUserMention, - GetModlog, - BanFromCommunity, - AddModToCommunity, - CreateSite, - EditSite, - GetSite, - AddAdmin, - BanUser, - Search, - MarkAllAsRead, - SaveUserSettings, - TransferCommunity, - TransferSite, - DeleteAccount, - PasswordReset, - PasswordChange, -} - #[derive(Fail, Debug)] -#[fail(display = "{{\"op\":\"{}\", \"error\":\"{}\"}}", op, message)] +#[fail(display = "{{\"error\":\"{}\"}}", message)] pub struct APIError { - pub op: String, pub message: String, } impl APIError { - pub fn err(op: &UserOperation, msg: &str) -> Self { + pub fn err(msg: &str) -> Self { APIError { - op: op.to_string(), message: msg.to_string(), } } } pub struct Oper { - op: UserOperation, data: T, } impl Oper { - pub fn new(op: UserOperation, data: T) -> Oper { - Oper { op, data } + pub fn new(data: T) -> Oper { + Oper { data } } } diff --git a/server/src/api/post.rs b/server/src/api/post.rs index b0fcdd0c..3f211453 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -14,7 +14,6 @@ pub struct CreatePost { #[derive(Serialize, Deserialize, Clone)] pub struct PostResponse { - op: String, pub post: PostView, } @@ -26,7 +25,6 @@ pub struct GetPost { #[derive(Serialize, Deserialize)] pub struct GetPostResponse { - op: String, post: PostView, comments: Vec, community: CommunityView, @@ -46,7 +44,6 @@ pub struct GetPosts { #[derive(Serialize, Deserialize)] pub struct GetPostsResponse { - op: String, posts: Vec, } @@ -59,7 +56,6 @@ pub struct CreatePostLike { #[derive(Serialize, Deserialize)] pub struct CreatePostLikeResponse { - op: String, post: PostView, } @@ -93,23 +89,23 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } let user_id = claims.id; // Check for a community ban if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { - return Err(APIError::err(&self.op, "community_ban").into()); + return Err(APIError::err("community_ban").into()); } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } let post_form = PostForm { @@ -128,7 +124,7 @@ impl Perform for Oper { let inserted_post = match Post::create(&conn, &post_form) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_create_post").into()), + Err(_e) => return Err(APIError::err("couldnt_create_post").into()), }; // They like their own post by default @@ -141,19 +137,16 @@ impl Perform for Oper { // Only add the like if the score isnt 0 let _inserted_like = match PostLike::like(&conn, &like_form) { Ok(like) => like, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_post").into()), + Err(_e) => return Err(APIError::err("couldnt_like_post").into()), }; // Refetch the view let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post").into()), + Err(_e) => return Err(APIError::err("couldnt_find_post").into()), }; - Ok(PostResponse { - op: self.op.to_string(), - post: post_view, - }) + Ok(PostResponse { post: post_view }) } } @@ -174,7 +167,7 @@ impl Perform for Oper { let post_view = match PostView::read(&conn, data.id, user_id) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post").into()), + Err(_e) => return Err(APIError::err("couldnt_find_post").into()), }; let comments = CommentQueryBuilder::create(&conn) @@ -195,7 +188,6 @@ impl Perform for Oper { // Return the jwt Ok(GetPostResponse { - op: self.op.to_string(), post: post_view, comments, community, @@ -241,13 +233,10 @@ impl Perform for Oper { .list() { Ok(posts) => posts, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_get_posts").into()), + Err(_e) => return Err(APIError::err("couldnt_get_posts").into()), }; - Ok(GetPostsResponse { - op: self.op.to_string(), - posts, - }) + Ok(GetPostsResponse { posts }) } } @@ -257,7 +246,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -266,19 +255,19 @@ impl Perform for Oper { if data.score == -1 { let site = SiteView::read(&conn)?; if !site.enable_downvotes { - return Err(APIError::err(&self.op, "downvotes_disabled").into()); + return Err(APIError::err("downvotes_disabled").into()); } } // Check for a community ban let post = Post::read(&conn, data.post_id)?; if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { - return Err(APIError::err(&self.op, "community_ban").into()); + return Err(APIError::err("community_ban").into()); } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } let like_form = PostLikeForm { @@ -295,20 +284,17 @@ impl Perform for Oper { if do_add { let _inserted_like = match PostLike::like(&conn, &like_form) { Ok(like) => like, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_post").into()), + Err(_e) => return Err(APIError::err("couldnt_like_post").into()), }; } let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post").into()), + Err(_e) => return Err(APIError::err("couldnt_find_post").into()), }; // just output the score - Ok(CreatePostLikeResponse { - op: self.op.to_string(), - post: post_view, - }) + Ok(CreatePostLikeResponse { post: post_view }) } } @@ -316,12 +302,12 @@ impl Perform for Oper { fn perform(&self, conn: &PgConnection) -> Result { let data: &EditPost = &self.data; if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -336,17 +322,17 @@ impl Perform for Oper { ); editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect()); if !editors.contains(&user_id) { - return Err(APIError::err(&self.op, "no_post_edit_allowed").into()); + return Err(APIError::err("no_post_edit_allowed").into()); } // Check for a community ban if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { - return Err(APIError::err(&self.op, "community_ban").into()); + return Err(APIError::err("community_ban").into()); } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(&self.op, "site_ban").into()); + return Err(APIError::err("site_ban").into()); } let post_form = PostForm { @@ -365,7 +351,7 @@ impl Perform for Oper { let _updated_post = match Post::update(&conn, data.edit_id, &post_form) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_post").into()), + Err(_e) => return Err(APIError::err("couldnt_update_post").into()), }; // Mod tables @@ -399,10 +385,7 @@ impl Perform for Oper { let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?; - Ok(PostResponse { - op: self.op.to_string(), - post: post_view, - }) + Ok(PostResponse { post: post_view }) } } @@ -412,7 +395,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -425,20 +408,17 @@ impl Perform for Oper { if data.save { match PostSaved::save(&conn, &post_saved_form) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_post").into()), + Err(_e) => return Err(APIError::err("couldnt_save_post").into()), }; } else { match PostSaved::unsave(&conn, &post_saved_form) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_post").into()), + Err(_e) => return Err(APIError::err("couldnt_save_post").into()), }; } let post_view = PostView::read(&conn, data.post_id, Some(user_id))?; - Ok(PostResponse { - op: self.op.to_string(), - post: post_view, - }) + Ok(PostResponse { post: post_view }) } } diff --git a/server/src/api/site.rs b/server/src/api/site.rs index a189a030..1291891b 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -7,7 +7,6 @@ pub struct ListCategories; #[derive(Serialize, Deserialize)] pub struct ListCategoriesResponse { - op: String, categories: Vec, } @@ -23,7 +22,6 @@ pub struct Search { #[derive(Serialize, Deserialize)] pub struct SearchResponse { - op: String, type_: String, comments: Vec, posts: Vec, @@ -41,7 +39,6 @@ pub struct GetModlog { #[derive(Serialize, Deserialize)] pub struct GetModlogResponse { - op: String, removed_posts: Vec, locked_posts: Vec, stickied_posts: Vec, @@ -78,13 +75,11 @@ pub struct GetSite; #[derive(Serialize, Deserialize)] pub struct SiteResponse { - op: String, site: SiteView, } #[derive(Serialize, Deserialize)] pub struct GetSiteResponse { - op: String, site: Option, admins: Vec, banned: Vec, @@ -104,10 +99,7 @@ impl Perform for Oper { let categories: Vec = Category::list_all(&conn)?; // Return the jwt - Ok(ListCategoriesResponse { - op: self.op.to_string(), - categories, - }) + Ok(ListCategoriesResponse { categories }) } } @@ -171,7 +163,6 @@ impl Perform for Oper { // Return the jwt Ok(GetModlogResponse { - op: self.op.to_string(), removed_posts, locked_posts, stickied_posts, @@ -191,20 +182,20 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; if has_slurs(&data.name) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } let user_id = claims.id; // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { - return Err(APIError::err(&self.op, "not_an_admin").into()); + return Err(APIError::err("not_an_admin").into()); } let site_form = SiteForm { @@ -219,15 +210,12 @@ impl Perform for Oper { match Site::create(&conn, &site_form) { Ok(site) => site, - Err(_e) => return Err(APIError::err(&self.op, "site_already_exists").into()), + Err(_e) => return Err(APIError::err("site_already_exists").into()), }; let site_view = SiteView::read(&conn)?; - Ok(SiteResponse { - op: self.op.to_string(), - site: site_view, - }) + Ok(SiteResponse { site: site_view }) } } @@ -237,20 +225,20 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; if has_slurs(&data.name) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } let user_id = claims.id; // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { - return Err(APIError::err(&self.op, "not_an_admin").into()); + return Err(APIError::err("not_an_admin").into()); } let found_site = Site::read(&conn, 1)?; @@ -267,15 +255,12 @@ impl Perform for Oper { match Site::update(&conn, 1, &site_form) { Ok(site) => site, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_site").into()), + Err(_e) => return Err(APIError::err("couldnt_update_site").into()), }; let site_view = SiteView::read(&conn)?; - Ok(SiteResponse { - op: self.op.to_string(), - site: site_view, - }) + Ok(SiteResponse { site: site_view }) } } @@ -300,7 +285,6 @@ impl Perform for Oper { let banned = UserView::banned(&conn)?; Ok(GetSiteResponse { - op: self.op.to_string(), site: site_view, admins, banned, @@ -403,7 +387,6 @@ impl Perform for Oper { // Return the jwt Ok(SearchResponse { - op: self.op.to_string(), type_: data.type_.to_owned(), comments, posts, @@ -419,7 +402,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -428,7 +411,7 @@ impl Perform for Oper { // Make sure user is the creator if read_site.creator_id != user_id { - return Err(APIError::err(&self.op, "not_an_admin").into()); + return Err(APIError::err("not_an_admin").into()); } let site_form = SiteForm { @@ -443,7 +426,7 @@ impl Perform for Oper { match Site::update(&conn, 1, &site_form) { Ok(site) => site, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_site").into()), + Err(_e) => return Err(APIError::err("couldnt_update_site").into()), }; // Mod tables @@ -468,7 +451,6 @@ impl Perform for Oper { let banned = UserView::banned(&conn)?; Ok(GetSiteResponse { - op: self.op.to_string(), site: Some(site_view), admins, banned, diff --git a/server/src/api/user.rs b/server/src/api/user.rs index ac700aca..e1ddb1ca 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -40,7 +40,6 @@ pub struct SaveUserSettings { #[derive(Serialize, Deserialize)] pub struct LoginResponse { - op: String, jwt: String, } @@ -58,7 +57,6 @@ pub struct GetUserDetails { #[derive(Serialize, Deserialize)] pub struct GetUserDetailsResponse { - op: String, user: UserView, follows: Vec, moderates: Vec, @@ -69,13 +67,11 @@ pub struct GetUserDetailsResponse { #[derive(Serialize, Deserialize)] pub struct GetRepliesResponse { - op: String, replies: Vec, } #[derive(Serialize, Deserialize)] pub struct GetUserMentionsResponse { - op: String, mentions: Vec, } @@ -93,7 +89,6 @@ pub struct AddAdmin { #[derive(Serialize, Deserialize)] pub struct AddAdminResponse { - op: String, admins: Vec, } @@ -108,7 +103,6 @@ pub struct BanUser { #[derive(Serialize, Deserialize)] pub struct BanUserResponse { - op: String, user: UserView, banned: bool, } @@ -140,7 +134,6 @@ pub struct EditUserMention { #[derive(Serialize, Deserialize, Clone)] pub struct UserMentionResponse { - op: String, mention: UserMentionView, } @@ -156,9 +149,7 @@ pub struct PasswordReset { } #[derive(Serialize, Deserialize, Clone)] -pub struct PasswordResetResponse { - op: String, -} +pub struct PasswordResetResponse {} #[derive(Serialize, Deserialize)] pub struct PasswordChange { @@ -174,20 +165,17 @@ impl Perform for Oper { // Fetch that username / email let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email").into()), + Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()), }; // Verify the password let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false); if !valid { - return Err(APIError::err(&self.op, "password_incorrect").into()); + return Err(APIError::err("password_incorrect").into()); } // Return the jwt - Ok(LoginResponse { - op: self.op.to_string(), - jwt: user.jwt(), - }) + Ok(LoginResponse { jwt: user.jwt() }) } } @@ -198,22 +186,22 @@ impl Perform for Oper { // Make sure site has open registration if let Ok(site) = SiteView::read(&conn) { if !site.open_registration { - return Err(APIError::err(&self.op, "registration_closed").into()); + return Err(APIError::err("registration_closed").into()); } } // Make sure passwords match if data.password != data.password_verify { - return Err(APIError::err(&self.op, "passwords_dont_match").into()); + return Err(APIError::err("passwords_dont_match").into()); } if has_slurs(&data.username) { - return Err(APIError::err(&self.op, "no_slurs").into()); + return Err(APIError::err("no_slurs").into()); } // Make sure there are no admins if data.admin && !UserView::admins(&conn)?.is_empty() { - return Err(APIError::err(&self.op, "admin_already_created").into()); + return Err(APIError::err("admin_already_created").into()); } // Register the new user @@ -248,7 +236,7 @@ impl Perform for Oper { "user_already_exists" }; - return Err(APIError::err(&self.op, err_type).into()); + return Err(APIError::err(err_type).into()); } }; @@ -280,7 +268,7 @@ impl Perform for Oper { let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), + Err(_e) => return Err(APIError::err("community_follower_already_exists").into()), }; // If its an admin, add them as a mod and follower to main @@ -293,15 +281,12 @@ impl Perform for Oper { let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) { Ok(user) => user, - Err(_e) => { - return Err(APIError::err(&self.op, "community_moderator_already_exists").into()) - } + Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()), }; } // Return the jwt Ok(LoginResponse { - op: self.op.to_string(), jwt: inserted_user.jwt(), }) } @@ -313,7 +298,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -331,7 +316,7 @@ impl Perform for Oper { Some(new_password_verify) => { // Make sure passwords match if new_password != new_password_verify { - return Err(APIError::err(&self.op, "passwords_dont_match").into()); + return Err(APIError::err("passwords_dont_match").into()); } // Check the old password @@ -340,14 +325,14 @@ impl Perform for Oper { let valid: bool = verify(old_password, &read_user.password_encrypted).unwrap_or(false); if !valid { - return Err(APIError::err(&self.op, "password_incorrect").into()); + return Err(APIError::err("password_incorrect").into()); } User_::update_password(&conn, user_id, &new_password)?.password_encrypted } - None => return Err(APIError::err(&self.op, "password_incorrect").into()), + None => return Err(APIError::err("password_incorrect").into()), } } - None => return Err(APIError::err(&self.op, "passwords_dont_match").into()), + None => return Err(APIError::err("passwords_dont_match").into()), } } None => read_user.password_encrypted, @@ -383,13 +368,12 @@ impl Perform for Oper { "user_already_exists" }; - return Err(APIError::err(&self.op, err_type).into()); + return Err(APIError::err(err_type).into()); } }; // Return the jwt Ok(LoginResponse { - op: self.op.to_string(), jwt: updated_user.jwt(), }) } @@ -430,9 +414,7 @@ impl Perform for Oper { .unwrap_or_else(|| "admin".to_string()), ) { Ok(user) => user.id, - Err(_e) => { - return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email").into()) - } + Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()), } } }; @@ -475,7 +457,6 @@ impl Perform for Oper { // Return the jwt Ok(GetUserDetailsResponse { - op: self.op.to_string(), user: user_view, follows, moderates, @@ -492,14 +473,14 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { - return Err(APIError::err(&self.op, "not_an_admin").into()); + return Err(APIError::err("not_an_admin").into()); } let read_user = User_::read(&conn, data.user_id)?; @@ -525,7 +506,7 @@ impl Perform for Oper { match User_::update(&conn, data.user_id, &user_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user").into()), + Err(_e) => return Err(APIError::err("couldnt_update_user").into()), }; // Mod tables @@ -543,10 +524,7 @@ impl Perform for Oper { let creator_user = admins.remove(creator_index); admins.insert(0, creator_user); - Ok(AddAdminResponse { - op: self.op.to_string(), - admins, - }) + Ok(AddAdminResponse { admins }) } } @@ -556,14 +534,14 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { - return Err(APIError::err(&self.op, "not_an_admin").into()); + return Err(APIError::err("not_an_admin").into()); } let read_user = User_::read(&conn, data.user_id)?; @@ -589,7 +567,7 @@ impl Perform for Oper { match User_::update(&conn, data.user_id, &user_form) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user").into()), + Err(_e) => return Err(APIError::err("couldnt_update_user").into()), }; // Mod tables @@ -611,7 +589,6 @@ impl Perform for Oper { let user_view = UserView::read(&conn, data.user_id)?; Ok(BanUserResponse { - op: self.op.to_string(), user: user_view, banned: data.ban, }) @@ -624,7 +601,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -638,10 +615,7 @@ impl Perform for Oper { .limit(data.limit) .list()?; - Ok(GetRepliesResponse { - op: self.op.to_string(), - replies, - }) + Ok(GetRepliesResponse { replies }) } } @@ -651,7 +625,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -665,10 +639,7 @@ impl Perform for Oper { .limit(data.limit) .list()?; - Ok(GetUserMentionsResponse { - op: self.op.to_string(), - mentions, - }) + Ok(GetUserMentionsResponse { mentions }) } } @@ -678,7 +649,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -694,13 +665,12 @@ impl Perform for Oper { let _updated_user_mention = match UserMention::update(&conn, user_mention.id, &user_mention_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), }; let user_mention_view = UserMentionView::read(&conn, user_mention.id, user_id)?; Ok(UserMentionResponse { - op: self.op.to_string(), mention: user_mention_view, }) } @@ -712,7 +682,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -737,7 +707,7 @@ impl Perform for Oper { let _updated_comment = match Comment::update(&conn, reply.id, &comment_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), }; } @@ -758,14 +728,11 @@ impl Perform for Oper { let _updated_mention = match UserMention::update(&conn, mention.user_mention_id, &mention_form) { Ok(mention) => mention, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), }; } - Ok(GetRepliesResponse { - op: self.op.to_string(), - replies: vec![], - }) + Ok(GetRepliesResponse { replies: vec![] }) } } @@ -775,7 +742,7 @@ impl Perform for Oper { let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, - Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), + Err(_e) => return Err(APIError::err("not_logged_in").into()), }; let user_id = claims.id; @@ -785,7 +752,7 @@ impl Perform for Oper { // Verify the password let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false); if !valid { - return Err(APIError::err(&self.op, "password_incorrect").into()); + return Err(APIError::err("password_incorrect").into()); } // Comments @@ -808,7 +775,7 @@ impl Perform for Oper { let _updated_comment = match Comment::update(&conn, comment.id, &comment_form) { Ok(comment) => comment, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), + Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), }; } @@ -836,12 +803,11 @@ impl Perform for Oper { let _updated_post = match Post::update(&conn, post.id, &post_form) { Ok(post) => post, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_post").into()), + Err(_e) => return Err(APIError::err("couldnt_update_post").into()), }; } Ok(LoginResponse { - op: self.op.to_string(), jwt: data.auth.to_owned(), }) } @@ -854,7 +820,7 @@ impl Perform for Oper { // Fetch that email let user: User_ = match User_::find_by_email(&conn, &data.email) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email").into()), + Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()), }; // Generate a random token @@ -871,12 +837,10 @@ impl Perform for Oper { let html = &format!("

Password Reset Request for {}


Click here to reset your password", user.name, hostname, &token); match send_email(subject, user_email, &user.name, html) { Ok(_o) => _o, - Err(_e) => return Err(APIError::err(&self.op, &_e).into()), + Err(_e) => return Err(APIError::err(&_e).into()), }; - Ok(PasswordResetResponse { - op: self.op.to_string(), - }) + Ok(PasswordResetResponse {}) } } @@ -889,18 +853,17 @@ impl Perform for Oper { // Make sure passwords match if data.password != data.password_verify { - return Err(APIError::err(&self.op, "passwords_dont_match").into()); + return Err(APIError::err("passwords_dont_match").into()); } // Update the user with the new password let updated_user = match User_::update_password(&conn, user_id, &data.password) { Ok(user) => user, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user").into()), + Err(_e) => return Err(APIError::err("couldnt_update_user").into()), }; // Return the jwt Ok(LoginResponse { - op: self.op.to_string(), jwt: updated_user.jwt(), }) } diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 11919b8c..70e68688 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -1,7 +1,8 @@ -use crate::api::community::{ - GetCommunity, GetCommunityResponse, ListCommunities, ListCommunitiesResponse, -}; -use crate::api::UserOperation; +use crate::api::comment::*; +use crate::api::community::*; +use crate::api::post::*; +use crate::api::site::*; +use crate::api::user::*; use crate::api::{Oper, Perform}; use actix_web::{web, HttpResponse}; use diesel::r2d2::{ConnectionManager, Pool}; @@ -13,26 +14,165 @@ type DbParam = web::Data>>; pub fn config(cfg: &mut web::ServiceConfig) { cfg - // TODO: need to repeat this for every endpoint + .route( + "/api/v1/login", + web::post().to(route::), + ) + .route( + "/api/v1/register", + web::post().to(route::), + ) + .route( + "/api/v1/create_community", + web::post().to(route::), + ) + .route( + "/api/v1/create_post", + web::post().to(route::), + ) .route( "/api/v1/list_communities", - web::get().to( - route::(UserOperation::ListCommunities) - ), + web::get().to(route::), + ) + .route( + "/api/v1/list_categories", + web::get().to(route::), + ) + .route( + "/api/v1/get_post", + web::get().to(route::), ) .route( "/api/v1/get_community", - web::get().to(route::( - UserOperation::GetCommunity, - )), + web::get().to(route::), + ) + .route( + "/api/v1/create_communent", + web::post().to(route::), + ) + .route( + "/api/v1/edit_comment", + web::post().to(route::), + ) + .route( + "/api/v1/save_comment", + web::post().to(route::), + ) + .route( + "/api/v1/create_comment_like", + web::post().to(route::), + ) + .route( + "/api/v1/get_posts", + web::get().to(route::), + ) + .route( + "/api/v1/create_post_like", + web::post().to(route::), + ) + .route( + "/api/v1/edit_post", + web::post().to(route::), + ) + .route( + "/api/v1/save_post", + web::post().to(route::), + ) + .route( + "/api/v1/edit_community", + web::post().to(route::), + ) + .route( + "/api/v1/follow_community", + web::post().to(route::), + ) + .route( + "/api/v1/get_followed_communities", + web::get().to(route::), + ) + .route( + "/api/v1/get_user_details", + web::get().to(route::), + ) + .route( + "/api/v1/get_replies", + web::get().to(route::), + ) + .route( + "/api/v1/get_user_mentions", + web::get().to(route::), + ) + .route( + "/api/v1/edit_user_mention", + web::post().to(route::), + ) + .route( + "/api/v1/get_modlog", + web::get().to(route::), + ) + .route( + "/api/v1/ban_from_community", + web::post().to(route::), + ) + .route( + "/api/v1/add_mod_to_community", + web::post().to(route::), + ) + .route( + "/api/v1/create_site", + web::post().to(route::), + ) + .route( + "/api/v1/edit_site", + web::post().to(route::), + ) + .route( + "/api/v1/get_site", + web::get().to(route::), + ) + .route( + "/api/v1/add_admin", + web::post().to(route::), + ) + .route( + "/api/v1/ban_user", + web::post().to(route::), + ) + .route( + "/api/v1/search", + web::post().to(route::), + ) + .route( + "/api/v1/mark_all_as_read", + web::post().to(route::), + ) + .route( + "/api/v1/save_user_settings", + web::post().to(route::), + ) + .route( + "/api/v1/transfer_community", + web::post().to(route::), + ) + .route( + "/api/v1/transfer_site", + web::post().to(route::), + ) + .route( + "/api/v1/delete_account", + web::post().to(route::), + ) + .route( + "/api/v1/password_reset", + web::post().to(route::), + ) + .route( + "/api/v1/password_change", + web::post().to(route::), ); } -fn perform( - op: UserOperation, - data: Request, - db: DbParam, -) -> Result +fn perform(data: Request, db: DbParam) -> Result where Response: Serialize, Oper: Perform, @@ -41,20 +181,16 @@ where Ok(c) => c, Err(e) => return Err(format_err!("{}", e)), }; - let oper: Oper = Oper::new(op, data); + let oper: Oper = Oper::new(data); let response = oper.perform(&conn); Ok(HttpResponse::Ok().json(response?)) } -fn route( - op: UserOperation, -) -> Box<(dyn Fn(web::Query, DbParam) -> Result + 'static)> +async fn route(data: web::Query, db: DbParam) -> Result where Data: Serialize, Response: Serialize, Oper: Perform, { - // TODO: want an implementation like this, where useroperation is passed without explicitly passing the other params - // maybe with a higher order functions? (but that would probably have worse performance) - Box::new(|data, db| perform::(op, data.0, db)) + perform::(data.0, db) } diff --git a/server/src/websocket/mod.rs b/server/src/websocket/mod.rs index 74f47ad3..1be3a8e0 100644 --- a/server/src/websocket/mod.rs +++ b/server/src/websocket/mod.rs @@ -1 +1,44 @@ pub mod server; + +#[derive(EnumString, ToString, Debug)] +pub enum UserOperation { + Login, + Register, + CreateCommunity, + CreatePost, + ListCommunities, + ListCategories, + GetPost, + GetCommunity, + CreateComment, + EditComment, + SaveComment, + CreateCommentLike, + GetPosts, + CreatePostLike, + EditPost, + SavePost, + EditCommunity, + FollowCommunity, + GetFollowedCommunities, + GetUserDetails, + GetReplies, + GetUserMentions, + EditUserMention, + GetModlog, + BanFromCommunity, + AddModToCommunity, + CreateSite, + EditSite, + GetSite, + AddAdmin, + BanUser, + Search, + MarkAllAsRead, + SaveUserSettings, + TransferCommunity, + TransferSite, + DeleteAccount, + PasswordReset, + PasswordChange, +} diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 957c5f64..0386ba4e 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -19,6 +19,7 @@ use crate::api::post::*; use crate::api::site::*; use crate::api::user::*; use crate::api::*; +use crate::websocket::UserOperation; use crate::Settings; /// Chat server sends this messages to session @@ -201,7 +202,6 @@ impl ChatServer { ); Err( APIError { - op: "Rate Limit".to_string(), message: format!("Too many requests. {} per {} seconds", rate, per), } .into(), @@ -295,11 +295,25 @@ impl Handler for ChatServer { } } +fn to_json_string(op: UserOperation, data: T) -> Result +where + T: Serialize, +{ + dbg!(&op); + let mut json = serde_json::to_value(&data)?; + match json.as_object_mut() { + Some(j) => j.insert("op".to_string(), serde_json::to_value(op.to_string())?), + None => return Err(format_err!("")), + }; + // TODO: it seems like this is never called? + let x = serde_json::to_string(&json)?; + Ok(x) +} + fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let json: Value = serde_json::from_str(&msg.msg)?; let data = &json["data"].to_string(); let op = &json["op"].as_str().ok_or(APIError { - op: "Unknown op type".to_string(), message: "Unknown op type".to_string(), })?; @@ -310,12 +324,12 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let login: Login = serde_json::from_str(data)?; - let res = Oper::new(user_operation, login).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(login).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::Register => { let register: Register = serde_json::from_str(data)?; - let res = Oper::new(user_operation, register).perform(&conn); + let res = Oper::new(register).perform(&conn); if res.is_ok() { chat.check_rate_limit_register(msg.id)?; } @@ -323,84 +337,84 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let get_user_details: GetUserDetails = serde_json::from_str(data)?; - let res = Oper::new(user_operation, get_user_details).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_user_details).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::SaveUserSettings => { let save_user_settings: SaveUserSettings = serde_json::from_str(data)?; - let res = Oper::new(user_operation, save_user_settings).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(save_user_settings).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::AddAdmin => { let add_admin: AddAdmin = serde_json::from_str(data)?; - let res = Oper::new(user_operation, add_admin).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(add_admin).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::BanUser => { let ban_user: BanUser = serde_json::from_str(data)?; - let res = Oper::new(user_operation, ban_user).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(ban_user).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetReplies => { let get_replies: GetReplies = serde_json::from_str(data)?; - let res = Oper::new(user_operation, get_replies).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_replies).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetUserMentions => { let get_user_mentions: GetUserMentions = serde_json::from_str(data)?; - let res = Oper::new(user_operation, get_user_mentions).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_user_mentions).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::EditUserMention => { let edit_user_mention: EditUserMention = serde_json::from_str(data)?; - let res = Oper::new(user_operation, edit_user_mention).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(edit_user_mention).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::MarkAllAsRead => { let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?; - let res = Oper::new(user_operation, mark_all_as_read).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(mark_all_as_read).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetCommunity => { let get_community: GetCommunity = serde_json::from_str(data)?; - let res = Oper::new(user_operation, get_community).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_community).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::ListCommunities => { let list_communities: ListCommunities = serde_json::from_str(data)?; - let res = Oper::new(user_operation, list_communities).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(list_communities).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::CreateCommunity => { chat.check_rate_limit_register(msg.id)?; let create_community: CreateCommunity = serde_json::from_str(data)?; - let res = Oper::new(user_operation, create_community).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(create_community).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::EditCommunity => { let edit_community: EditCommunity = serde_json::from_str(data)?; - let res = Oper::new(user_operation, edit_community).perform(&conn)?; + let res = Oper::new(edit_community).perform(&conn)?; let mut community_sent: CommunityResponse = res.clone(); community_sent.community.user_id = None; community_sent.community.subscribed = None; let community_sent_str = serde_json::to_string(&community_sent)?; chat.send_community_message(community_sent.community.id, &community_sent_str, msg.id)?; - Ok(serde_json::to_string(&res)?) + to_json_string(user_operation, &res) } UserOperation::FollowCommunity => { let follow_community: FollowCommunity = serde_json::from_str(data)?; - let res = Oper::new(user_operation, follow_community).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(follow_community).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetFollowedCommunities => { let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?; - let res = Oper::new(user_operation, followed_communities).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(followed_communities).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::BanFromCommunity => { let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; let community_id = ban_from_community.community_id; - let res = Oper::new(user_operation, ban_from_community).perform(&conn)?; + let res = Oper::new(ban_from_community).perform(&conn)?; let res_str = serde_json::to_string(&res)?; chat.send_community_message(community_id, &res_str, msg.id)?; Ok(res_str) @@ -408,144 +422,144 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?; let community_id = mod_add_to_community.community_id; - let res = Oper::new(user_operation, mod_add_to_community).perform(&conn)?; + let res = Oper::new(mod_add_to_community).perform(&conn)?; let res_str = serde_json::to_string(&res)?; chat.send_community_message(community_id, &res_str, msg.id)?; Ok(res_str) } UserOperation::ListCategories => { let list_categories: ListCategories = ListCategories; - let res = Oper::new(user_operation, list_categories).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(list_categories).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::CreatePost => { chat.check_rate_limit_post(msg.id)?; let create_post: CreatePost = serde_json::from_str(data)?; - let res = Oper::new(user_operation, create_post).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(create_post).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetPost => { let get_post: GetPost = serde_json::from_str(data)?; chat.join_room(get_post.id, msg.id); - let res = Oper::new(user_operation, get_post).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_post).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetPosts => { let get_posts: GetPosts = serde_json::from_str(data)?; - let res = Oper::new(user_operation, get_posts).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_posts).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::CreatePostLike => { chat.check_rate_limit_message(msg.id)?; let create_post_like: CreatePostLike = serde_json::from_str(data)?; - let res = Oper::new(user_operation, create_post_like).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(create_post_like).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::EditPost => { let edit_post: EditPost = serde_json::from_str(data)?; - let res = Oper::new(user_operation, edit_post).perform(&conn)?; + let res = Oper::new(edit_post).perform(&conn)?; let mut post_sent = res.clone(); post_sent.post.my_vote = None; let post_sent_str = serde_json::to_string(&post_sent)?; chat.send_room_message(post_sent.post.id, &post_sent_str, msg.id); - Ok(serde_json::to_string(&res)?) + to_json_string(user_operation, &res) } UserOperation::SavePost => { let save_post: SavePost = serde_json::from_str(data)?; - let res = Oper::new(user_operation, save_post).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(save_post).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::CreateComment => { chat.check_rate_limit_message(msg.id)?; let create_comment: CreateComment = serde_json::from_str(data)?; let post_id = create_comment.post_id; - let res = Oper::new(user_operation, create_comment).perform(&conn)?; + let res = Oper::new(create_comment).perform(&conn)?; let mut comment_sent = res.clone(); comment_sent.comment.my_vote = None; comment_sent.comment.user_id = None; let comment_sent_str = serde_json::to_string(&comment_sent)?; chat.send_room_message(post_id, &comment_sent_str, msg.id); - Ok(serde_json::to_string(&res)?) + to_json_string(user_operation, &res) } UserOperation::EditComment => { let edit_comment: EditComment = serde_json::from_str(data)?; let post_id = edit_comment.post_id; - let res = Oper::new(user_operation, edit_comment).perform(&conn)?; + let res = Oper::new(edit_comment).perform(&conn)?; let mut comment_sent = res.clone(); comment_sent.comment.my_vote = None; comment_sent.comment.user_id = None; let comment_sent_str = serde_json::to_string(&comment_sent)?; chat.send_room_message(post_id, &comment_sent_str, msg.id); - Ok(serde_json::to_string(&res)?) + to_json_string(user_operation, &res) } UserOperation::SaveComment => { let save_comment: SaveComment = serde_json::from_str(data)?; - let res = Oper::new(user_operation, save_comment).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(save_comment).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::CreateCommentLike => { chat.check_rate_limit_message(msg.id)?; let create_comment_like: CreateCommentLike = serde_json::from_str(data)?; let post_id = create_comment_like.post_id; - let res = Oper::new(user_operation, create_comment_like).perform(&conn)?; + let res = Oper::new(create_comment_like).perform(&conn)?; let mut comment_sent = res.clone(); comment_sent.comment.my_vote = None; comment_sent.comment.user_id = None; let comment_sent_str = serde_json::to_string(&comment_sent)?; chat.send_room_message(post_id, &comment_sent_str, msg.id); - Ok(serde_json::to_string(&res)?) + to_json_string(user_operation, &res) } UserOperation::GetModlog => { let get_modlog: GetModlog = serde_json::from_str(data)?; - let res = Oper::new(user_operation, get_modlog).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(get_modlog).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::CreateSite => { let create_site: CreateSite = serde_json::from_str(data)?; - let res = Oper::new(user_operation, create_site).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(create_site).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::EditSite => { let edit_site: EditSite = serde_json::from_str(data)?; - let res = Oper::new(user_operation, edit_site).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(edit_site).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::GetSite => { let online: usize = chat.sessions.len(); let get_site: GetSite = serde_json::from_str(data)?; - let mut res = Oper::new(user_operation, get_site).perform(&conn)?; + let mut res = Oper::new(get_site).perform(&conn)?; res.online = online; - Ok(serde_json::to_string(&res)?) + to_json_string(user_operation, &res) } UserOperation::Search => { let search: Search = serde_json::from_str(data)?; - let res = Oper::new(user_operation, search).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(search).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::TransferCommunity => { let transfer_community: TransferCommunity = serde_json::from_str(data)?; - let res = Oper::new(user_operation, transfer_community).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(transfer_community).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::TransferSite => { let transfer_site: TransferSite = serde_json::from_str(data)?; - let res = Oper::new(user_operation, transfer_site).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(transfer_site).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::DeleteAccount => { let delete_account: DeleteAccount = serde_json::from_str(data)?; - let res = Oper::new(user_operation, delete_account).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(delete_account).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::PasswordReset => { let password_reset: PasswordReset = serde_json::from_str(data)?; - let res = Oper::new(user_operation, password_reset).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(password_reset).perform(&conn)?; + to_json_string(user_operation, &res) } UserOperation::PasswordChange => { let password_change: PasswordChange = serde_json::from_str(data)?; - let res = Oper::new(user_operation, password_change).perform(&conn)?; - Ok(serde_json::to_string(&res)?) + let res = Oper::new(password_change).perform(&conn)?; + to_json_string(user_operation, &res) } } } From 9121d741940e9f1a8bf38793ecb2498831f17175 Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 16 Jan 2020 16:09:01 +0100 Subject: [PATCH 04/15] small fix --- server/src/websocket/server.rs | 91 +++++++++++++++++----------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 0386ba4e..2d9ddcd4 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -295,11 +295,10 @@ impl Handler for ChatServer { } } -fn to_json_string(op: UserOperation, data: T) -> Result +fn to_json_string(op: &UserOperation, data: T) -> Result where T: Serialize, { - dbg!(&op); let mut json = serde_json::to_value(&data)?; match json.as_object_mut() { Some(j) => j.insert("op".to_string(), serde_json::to_value(op.to_string())?), @@ -325,7 +324,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let login: Login = serde_json::from_str(data)?; let res = Oper::new(login).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::Register => { let register: Register = serde_json::from_str(data)?; @@ -333,63 +332,63 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let get_user_details: GetUserDetails = serde_json::from_str(data)?; let res = Oper::new(get_user_details).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::SaveUserSettings => { let save_user_settings: SaveUserSettings = serde_json::from_str(data)?; let res = Oper::new(save_user_settings).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::AddAdmin => { let add_admin: AddAdmin = serde_json::from_str(data)?; let res = Oper::new(add_admin).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::BanUser => { let ban_user: BanUser = serde_json::from_str(data)?; let res = Oper::new(ban_user).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetReplies => { let get_replies: GetReplies = serde_json::from_str(data)?; let res = Oper::new(get_replies).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetUserMentions => { let get_user_mentions: GetUserMentions = serde_json::from_str(data)?; let res = Oper::new(get_user_mentions).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::EditUserMention => { let edit_user_mention: EditUserMention = serde_json::from_str(data)?; let res = Oper::new(edit_user_mention).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::MarkAllAsRead => { let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?; let res = Oper::new(mark_all_as_read).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetCommunity => { let get_community: GetCommunity = serde_json::from_str(data)?; let res = Oper::new(get_community).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::ListCommunities => { let list_communities: ListCommunities = serde_json::from_str(data)?; let res = Oper::new(list_communities).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::CreateCommunity => { chat.check_rate_limit_register(msg.id)?; let create_community: CreateCommunity = serde_json::from_str(data)?; let res = Oper::new(create_community).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::EditCommunity => { let edit_community: EditCommunity = serde_json::from_str(data)?; @@ -397,25 +396,25 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let follow_community: FollowCommunity = serde_json::from_str(data)?; let res = Oper::new(follow_community).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetFollowedCommunities => { let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?; let res = Oper::new(followed_communities).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::BanFromCommunity => { let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; let community_id = ban_from_community.community_id; let res = Oper::new(ban_from_community).perform(&conn)?; - let res_str = serde_json::to_string(&res)?; + let res_str = to_json_string(&user_operation, &res)?; chat.send_community_message(community_id, &res_str, msg.id)?; Ok(res_str) } @@ -423,51 +422,51 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let list_categories: ListCategories = ListCategories; let res = Oper::new(list_categories).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::CreatePost => { chat.check_rate_limit_post(msg.id)?; let create_post: CreatePost = serde_json::from_str(data)?; let res = Oper::new(create_post).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetPost => { let get_post: GetPost = serde_json::from_str(data)?; chat.join_room(get_post.id, msg.id); let res = Oper::new(get_post).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetPosts => { let get_posts: GetPosts = serde_json::from_str(data)?; let res = Oper::new(get_posts).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::CreatePostLike => { chat.check_rate_limit_message(msg.id)?; let create_post_like: CreatePostLike = serde_json::from_str(data)?; let res = Oper::new(create_post_like).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::EditPost => { let edit_post: EditPost = serde_json::from_str(data)?; let res = Oper::new(edit_post).perform(&conn)?; let mut post_sent = res.clone(); post_sent.post.my_vote = None; - let post_sent_str = serde_json::to_string(&post_sent)?; + let post_sent_str = to_json_string(&user_operation, &post_sent)?; chat.send_room_message(post_sent.post.id, &post_sent_str, msg.id); - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::SavePost => { let save_post: SavePost = serde_json::from_str(data)?; let res = Oper::new(save_post).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::CreateComment => { chat.check_rate_limit_message(msg.id)?; @@ -477,9 +476,9 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let edit_comment: EditComment = serde_json::from_str(data)?; @@ -488,14 +487,14 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let save_comment: SaveComment = serde_json::from_str(data)?; let res = Oper::new(save_comment).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::CreateCommentLike => { chat.check_rate_limit_message(msg.id)?; @@ -505,61 +504,61 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let get_modlog: GetModlog = serde_json::from_str(data)?; let res = Oper::new(get_modlog).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::CreateSite => { let create_site: CreateSite = serde_json::from_str(data)?; let res = Oper::new(create_site).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::EditSite => { let edit_site: EditSite = serde_json::from_str(data)?; let res = Oper::new(edit_site).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::GetSite => { let online: usize = chat.sessions.len(); let get_site: GetSite = serde_json::from_str(data)?; let mut res = Oper::new(get_site).perform(&conn)?; res.online = online; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::Search => { let search: Search = serde_json::from_str(data)?; let res = Oper::new(search).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::TransferCommunity => { let transfer_community: TransferCommunity = serde_json::from_str(data)?; let res = Oper::new(transfer_community).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::TransferSite => { let transfer_site: TransferSite = serde_json::from_str(data)?; let res = Oper::new(transfer_site).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::DeleteAccount => { let delete_account: DeleteAccount = serde_json::from_str(data)?; let res = Oper::new(delete_account).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::PasswordReset => { let password_reset: PasswordReset = serde_json::from_str(data)?; let res = Oper::new(password_reset).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } UserOperation::PasswordChange => { let password_change: PasswordChange = serde_json::from_str(data)?; let res = Oper::new(password_change).perform(&conn)?; - to_json_string(user_operation, &res) + to_json_string(&user_operation, &res) } } } From 2c5529254462ebd02232a0d1d0128d3fd6bca3eb Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 16 Jan 2020 16:47:38 +0100 Subject: [PATCH 05/15] fix formatting --- server/src/routes/api.rs | 196 ++++++++------------------------------- 1 file changed, 40 insertions(+), 156 deletions(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 70e68688..b7fb773f 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -12,164 +12,48 @@ use serde::Serialize; type DbParam = web::Data>>; +#[rustfmt::skip] pub fn config(cfg: &mut web::ServiceConfig) { cfg - .route( - "/api/v1/login", - web::post().to(route::), - ) - .route( - "/api/v1/register", - web::post().to(route::), - ) - .route( - "/api/v1/create_community", - web::post().to(route::), - ) - .route( - "/api/v1/create_post", - web::post().to(route::), - ) - .route( - "/api/v1/list_communities", - web::get().to(route::), - ) - .route( - "/api/v1/list_categories", - web::get().to(route::), - ) - .route( - "/api/v1/get_post", - web::get().to(route::), - ) - .route( - "/api/v1/get_community", - web::get().to(route::), - ) - .route( - "/api/v1/create_communent", - web::post().to(route::), - ) - .route( - "/api/v1/edit_comment", - web::post().to(route::), - ) - .route( - "/api/v1/save_comment", - web::post().to(route::), - ) - .route( - "/api/v1/create_comment_like", - web::post().to(route::), - ) - .route( - "/api/v1/get_posts", - web::get().to(route::), - ) - .route( - "/api/v1/create_post_like", - web::post().to(route::), - ) - .route( - "/api/v1/edit_post", - web::post().to(route::), - ) - .route( - "/api/v1/save_post", - web::post().to(route::), - ) - .route( - "/api/v1/edit_community", - web::post().to(route::), - ) - .route( - "/api/v1/follow_community", - web::post().to(route::), - ) - .route( - "/api/v1/get_followed_communities", - web::get().to(route::), - ) - .route( - "/api/v1/get_user_details", - web::get().to(route::), - ) - .route( - "/api/v1/get_replies", - web::get().to(route::), - ) - .route( - "/api/v1/get_user_mentions", - web::get().to(route::), - ) - .route( - "/api/v1/edit_user_mention", - web::post().to(route::), - ) - .route( - "/api/v1/get_modlog", - web::get().to(route::), - ) - .route( - "/api/v1/ban_from_community", - web::post().to(route::), - ) - .route( - "/api/v1/add_mod_to_community", - web::post().to(route::), - ) - .route( - "/api/v1/create_site", - web::post().to(route::), - ) - .route( - "/api/v1/edit_site", - web::post().to(route::), - ) - .route( - "/api/v1/get_site", - web::get().to(route::), - ) - .route( - "/api/v1/add_admin", - web::post().to(route::), - ) - .route( - "/api/v1/ban_user", - web::post().to(route::), - ) - .route( - "/api/v1/search", - web::post().to(route::), - ) - .route( - "/api/v1/mark_all_as_read", - web::post().to(route::), - ) - .route( - "/api/v1/save_user_settings", - web::post().to(route::), - ) - .route( - "/api/v1/transfer_community", - web::post().to(route::), - ) - .route( - "/api/v1/transfer_site", - web::post().to(route::), - ) - .route( - "/api/v1/delete_account", - web::post().to(route::), - ) - .route( - "/api/v1/password_reset", - web::post().to(route::), - ) - .route( - "/api/v1/password_change", - web::post().to(route::), - ); + .route("/api/v1/login", web::post().to(route::)) + .route("/api/v1/register", web::post().to(route::)) + .route("/api/v1/create_community", web::post().to(route::)) + .route("/api/v1/create_post", web::post().to(route::)) + .route("/api/v1/list_communities", web::get().to(route::)) + .route("/api/v1/list_categories", web::get().to(route::)) + .route("/api/v1/get_post", web::get().to(route::)) + .route("/api/v1/get_community", web::get().to(route::)) + .route("/api/v1/create_communent", web::post().to(route::)) + .route("/api/v1/edit_comment", web::post().to(route::)) + .route("/api/v1/save_comment", web::post().to(route::)) + .route("/api/v1/create_comment_like", web::post().to(route::)) + .route("/api/v1/get_posts", web::get().to(route::)) + .route("/api/v1/create_post_like", web::post().to(route::)) + .route("/api/v1/edit_post", web::post().to(route::)) + .route("/api/v1/save_post", web::post().to(route::)) + .route("/api/v1/edit_community", web::post().to(route::)) + .route("/api/v1/follow_community", web::post().to(route::)) + .route("/api/v1/get_followed_communities", web::get().to(route::)) + .route("/api/v1/get_user_details", web::get().to(route::)) + .route("/api/v1/get_replies", web::get().to(route::)) + .route("/api/v1/get_user_mentions", web::get().to(route::)) + .route("/api/v1/edit_user_mention", web::post().to(route::)) + .route("/api/v1/get_modlog", web::get().to(route::)) + .route("/api/v1/ban_from_community", web::post().to(route::)) + .route("/api/v1/add_mod_to_community", web::post().to(route::)) + .route("/api/v1/create_site", web::post().to(route::)) + .route("/api/v1/edit_site", web::post().to(route::)) + .route("/api/v1/get_site", web::get().to(route::)) + .route("/api/v1/add_admin", web::post().to(route::)) + .route("/api/v1/ban_user", web::post().to(route::)) + .route("/api/v1/search", web::post().to(route::)) + .route("/api/v1/mark_all_as_read", web::post().to(route::)) + .route("/api/v1/save_user_settings", web::post().to(route::)) + .route("/api/v1/transfer_community", web::post().to(route::)) + .route("/api/v1/transfer_site", web::post().to(route::)) + .route("/api/v1/delete_account", web::post().to(route::)) + .route("/api/v1/password_reset", web::post().to(route::)) + .route("/api/v1/password_change", web::post().to(route::)); } fn perform(data: Request, db: DbParam) -> Result From 643189160ec598d0c093561a100be6afd081013a Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 16 Jan 2020 17:04:37 +0100 Subject: [PATCH 06/15] fix typo --- server/src/routes/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index b7fb773f..41d92dfd 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -23,7 +23,7 @@ pub fn config(cfg: &mut web::ServiceConfig) { .route("/api/v1/list_categories", web::get().to(route::)) .route("/api/v1/get_post", web::get().to(route::)) .route("/api/v1/get_community", web::get().to(route::)) - .route("/api/v1/create_communent", web::post().to(route::)) + .route("/api/v1/create_comment", web::post().to(route::)) .route("/api/v1/edit_comment", web::post().to(route::)) .route("/api/v1/save_comment", web::post().to(route::)) .route("/api/v1/create_comment_like", web::post().to(route::)) From cde4a8a977b202fbc0ca10c802fc31fe41c9388b Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 18 Jan 2020 14:22:25 +0100 Subject: [PATCH 07/15] rewrite api endpoint urls --- server/src/routes/api.rs | 84 ++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 41d92dfd..6ba62766 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -15,45 +15,53 @@ type DbParam = web::Data>>; #[rustfmt::skip] pub fn config(cfg: &mut web::ServiceConfig) { cfg - .route("/api/v1/login", web::post().to(route::)) - .route("/api/v1/register", web::post().to(route::)) - .route("/api/v1/create_community", web::post().to(route::)) - .route("/api/v1/create_post", web::post().to(route::)) - .route("/api/v1/list_communities", web::get().to(route::)) - .route("/api/v1/list_categories", web::get().to(route::)) - .route("/api/v1/get_post", web::get().to(route::)) - .route("/api/v1/get_community", web::get().to(route::)) - .route("/api/v1/create_comment", web::post().to(route::)) - .route("/api/v1/edit_comment", web::post().to(route::)) - .route("/api/v1/save_comment", web::post().to(route::)) - .route("/api/v1/create_comment_like", web::post().to(route::)) - .route("/api/v1/get_posts", web::get().to(route::)) - .route("/api/v1/create_post_like", web::post().to(route::)) - .route("/api/v1/edit_post", web::post().to(route::)) - .route("/api/v1/save_post", web::post().to(route::)) - .route("/api/v1/edit_community", web::post().to(route::)) - .route("/api/v1/follow_community", web::post().to(route::)) - .route("/api/v1/get_followed_communities", web::get().to(route::)) - .route("/api/v1/get_user_details", web::get().to(route::)) - .route("/api/v1/get_replies", web::get().to(route::)) - .route("/api/v1/get_user_mentions", web::get().to(route::)) - .route("/api/v1/edit_user_mention", web::post().to(route::)) - .route("/api/v1/get_modlog", web::get().to(route::)) - .route("/api/v1/ban_from_community", web::post().to(route::)) - .route("/api/v1/add_mod_to_community", web::post().to(route::)) - .route("/api/v1/create_site", web::post().to(route::)) - .route("/api/v1/edit_site", web::post().to(route::)) - .route("/api/v1/get_site", web::get().to(route::)) - .route("/api/v1/add_admin", web::post().to(route::)) - .route("/api/v1/ban_user", web::post().to(route::)) + // Site + .route("/api/v1/site", web::get().to(route::)) + .route("/api/v1/categories", web::get().to(route::)) + .route("/api/v1/modlog", web::get().to(route::)) .route("/api/v1/search", web::post().to(route::)) - .route("/api/v1/mark_all_as_read", web::post().to(route::)) - .route("/api/v1/save_user_settings", web::post().to(route::)) - .route("/api/v1/transfer_community", web::post().to(route::)) - .route("/api/v1/transfer_site", web::post().to(route::)) - .route("/api/v1/delete_account", web::post().to(route::)) - .route("/api/v1/password_reset", web::post().to(route::)) - .route("/api/v1/password_change", web::post().to(route::)); + // Community + .route("/api/v1/community", web::post().to(route::)) + .route("/api/v1/community", web::get().to(route::)) + .route("/api/v1/community", web::put().to(route::)) + .route("/api/v1/community/list", web::get().to(route::)) + .route("/api/v1/community/follow", web::post().to(route::)) + // Post + .route("/api/v1/post", web::post().to(route::)) + .route("/api/v1/post", web::put().to(route::)) + .route("/api/v1/post", web::get().to(route::)) + .route("/api/v1/post/list", web::get().to(route::)) + .route("/api/v1/post/like", web::post().to(route::)) + .route("/api/v1/post/save", web::post().to(route::)) + .route("/api/v1/post/replies", web::get().to(route::)) + // Comment + .route("/api/v1/comment", web::post().to(route::)) + .route("/api/v1/comment", web::put().to(route::)) + .route("/api/v1/comment/like", web::post().to(route::)) + .route("/api/v1/comment/save", web::post().to(route::)) + // User + .route("/api/v1/user", web::get().to(route::)) + .route("/api/v1/user/mentions", web::get().to(route::)) + .route("/api/v1/user/mentions", web::put().to(route::)) + .route("/api/v1/user/followed-communities", web::get().to(route::)) + // Mod actions + .route("/api/v1/community/transfer", web::post().to(route::)) + .route("/api/v1/community/ban-user", web::post().to(route::)) + .route("/api/v1/community/mod", web::post().to(route::)) + // Admin actions + .route("/api/v1/site", web::post().to(route::)) + .route("/api/v1/site", web::put().to(route::)) + .route("/api/v1/site/transfer", web::post().to(route::)) + .route("/api/v1/admin/add", web::post().to(route::)) + .route("/api/v1/user/ban", web::post().to(route::)) + // User account actions + .route("/api/v1/user/login", web::post().to(route::)) + .route("/api/v1/user/register", web::post().to(route::)) + .route("/api/v1/user/delete_account", web::post().to(route::)) + .route("/api/v1/user/password_reset", web::post().to(route::)) + .route("/api/v1/user/password_change", web::post().to(route::)) + .route("/api/v1/user/mark_all_as_read", web::post().to(route::)) + .route("/api/v1/user/save_user_settings", web::post().to(route::)); } fn perform(data: Request, db: DbParam) -> Result From 05ee5a8b600fdd7a64ad66bb52978befed3a7614 Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 18 Jan 2020 17:25:45 +0100 Subject: [PATCH 08/15] simplify json serialization code --- server/src/websocket/server.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 2d9ddcd4..9afbff25 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -295,18 +295,21 @@ impl Handler for ChatServer { } } +#[derive(Serialize)] +struct WebsocketResponse { + op: String, + data: T, +} + fn to_json_string(op: &UserOperation, data: T) -> Result where T: Serialize, { - let mut json = serde_json::to_value(&data)?; - match json.as_object_mut() { - Some(j) => j.insert("op".to_string(), serde_json::to_value(op.to_string())?), - None => return Err(format_err!("")), + let response = WebsocketResponse { + op: op.to_string(), + data, }; - // TODO: it seems like this is never called? - let x = serde_json::to_string(&json)?; - Ok(x) + Ok(serde_json::to_string(&response)?) } fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { From a044718066623f19d51196d444bd199c1231cebe Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 18 Jan 2020 23:54:10 -0500 Subject: [PATCH 09/15] First pass at fixing UI to work with new websocketresponses. --- ui/src/components/communities.tsx | 33 ++--- ui/src/components/community-form.tsx | 47 +++---- ui/src/components/community.tsx | 53 ++++---- ui/src/components/inbox.tsx | 102 +++++++-------- ui/src/components/login.tsx | 37 +++--- ui/src/components/main.tsx | 62 ++++----- ui/src/components/modlog.tsx | 25 ++-- ui/src/components/navbar.tsx | 30 ++--- ui/src/components/password_change.tsx | 19 +-- ui/src/components/post-form.tsx | 48 +++---- ui/src/components/post.tsx | 174 +++++++++++++------------- ui/src/components/search.tsx | 16 ++- ui/src/components/setup.tsx | 26 ++-- ui/src/components/user.tsx | 96 +++++++------- ui/src/interfaces.ts | 54 ++++---- ui/src/utils.ts | 8 +- 16 files changed, 397 insertions(+), 433 deletions(-) diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index 7e4a0ef8..683d495e 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -12,7 +12,7 @@ import { SortType, } from '../interfaces'; import { WebSocketService } from '../services'; -import { msgOp } from '../utils'; +import { wsJsonToRes } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -36,14 +36,7 @@ export class Communities extends Component { super(props, context); this.state = this.emptyState; this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -236,13 +229,13 @@ export class Communities extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); return; - } else if (op == UserOperation.ListCommunities) { - let res: ListCommunitiesResponse = msg; - this.state.communities = res.communities; + } else if (res.op == UserOperation.ListCommunities) { + let data = res.data as ListCommunitiesResponse; + this.state.communities = data.communities; this.state.communities.sort( (a, b) => b.number_of_subscribers - a.number_of_subscribers ); @@ -251,11 +244,11 @@ export class Communities extends Component { this.setState(this.state); let table = document.querySelector('#community_table'); Sortable.initTable(table); - } else if (op == UserOperation.FollowCommunity) { - let res: CommunityResponse = msg; - let found = this.state.communities.find(c => c.id == res.community.id); - found.subscribed = res.community.subscribed; - found.number_of_subscribers = res.community.number_of_subscribers; + } else if (res.op == UserOperation.FollowCommunity) { + let data = res.data as CommunityResponse; + let found = this.state.communities.find(c => c.id == data.community.id); + found.subscribed = data.community.subscribed; + found.number_of_subscribers = data.community.number_of_subscribers; this.setState(this.state); } } diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx index 2085da28..93f4d649 100644 --- a/ui/src/components/community-form.tsx +++ b/ui/src/components/community-form.tsx @@ -10,8 +10,8 @@ import { GetSiteResponse, } from '../interfaces'; import { WebSocketService } from '../services'; -import { msgOp, capitalizeFirstLetter } from '../utils'; -import * as autosize from 'autosize'; +import { wsJsonToRes, capitalizeFirstLetter } from '../utils'; +import autosize from 'autosize'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -67,14 +67,7 @@ export class CommunityForm extends Component< } this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -247,33 +240,33 @@ export class CommunityForm extends Component< } parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); + let res = wsJsonToRes(msg); console.log(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + if (res.error) { + alert(i18n.t(res.error)); this.state.loading = false; this.setState(this.state); return; - } else if (op == UserOperation.ListCategories) { - let res: ListCategoriesResponse = msg; - this.state.categories = res.categories; + } else if (res.op == UserOperation.ListCategories) { + let data = res.data as ListCategoriesResponse; + this.state.categories = data.categories; if (!this.props.community) { - this.state.communityForm.category_id = res.categories[0].id; + this.state.communityForm.category_id = data.categories[0].id; } this.setState(this.state); - } else if (op == UserOperation.CreateCommunity) { - let res: CommunityResponse = msg; + } else if (res.op == UserOperation.CreateCommunity) { + let data = res.data as CommunityResponse; this.state.loading = false; - this.props.onCreate(res.community); + this.props.onCreate(data.community); } - // TODO is ths necessary - else if (op == UserOperation.EditCommunity) { - let res: CommunityResponse = msg; + // TODO is this necessary + else if (res.op == UserOperation.EditCommunity) { + let data = res.data as CommunityResponse; this.state.loading = false; - this.props.onEdit(res.community); - } else if (op == UserOperation.GetSite) { - let res: GetSiteResponse = msg; - this.state.enable_nsfw = res.site.enable_nsfw; + this.props.onEdit(data.community); + } else if (res.op == UserOperation.GetSite) { + let data = res.data as GetSiteResponse; + this.state.enable_nsfw = data.site.enable_nsfw; this.setState(this.state); } } diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 6bcd88b1..a48f468b 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -20,7 +20,7 @@ import { PostListings } from './post-listings'; import { SortSelect } from './sort-select'; import { Sidebar } from './sidebar'; import { - msgOp, + wsJsonToRes, routeSortTypeToEnum, fetchLimit, postRefetchSeconds, @@ -253,46 +253,47 @@ export class Community extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); this.context.router.history.push('/'); return; - } else if (op == UserOperation.GetCommunity) { - let res: GetCommunityResponse = msg; - this.state.community = res.community; - this.state.moderators = res.moderators; - this.state.admins = res.admins; + } else if (res.op == UserOperation.GetCommunity) { + let data = res.data as GetCommunityResponse; + this.state.community = data.community; + this.state.moderators = data.moderators; + this.state.admins = data.admins; document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`; this.setState(this.state); this.keepFetchingPosts(); - } else if (op == UserOperation.EditCommunity) { - let res: CommunityResponse = msg; - this.state.community = res.community; + } else if (res.op == UserOperation.EditCommunity) { + let data = res.data as CommunityResponse; + this.state.community = data.community; this.setState(this.state); - } else if (op == UserOperation.FollowCommunity) { - let res: CommunityResponse = msg; - this.state.community.subscribed = res.community.subscribed; + } else if (res.op == UserOperation.FollowCommunity) { + let data = res.data as CommunityResponse; + this.state.community.subscribed = data.community.subscribed; this.state.community.number_of_subscribers = - res.community.number_of_subscribers; + data.community.number_of_subscribers; this.setState(this.state); - } else if (op == UserOperation.GetPosts) { - let res: GetPostsResponse = msg; + } else if (res.op == UserOperation.GetPosts) { + let data = res.data as GetPostsResponse; + // TODO rework this // This is needed to refresh the view this.state.posts = undefined; this.setState(this.state); - this.state.posts = res.posts; + this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); - } else if (op == UserOperation.CreatePostLike) { - let res: CreatePostLikeResponse = msg; - let found = this.state.posts.find(c => c.id == res.post.id); - found.my_vote = res.post.my_vote; - found.score = res.post.score; - found.upvotes = res.post.upvotes; - found.downvotes = res.post.downvotes; + } else if (res.op == UserOperation.CreatePostLike) { + let data = res.data as CreatePostLikeResponse; + let found = this.state.posts.find(c => c.id == data.post.id); + found.my_vote = data.post.my_vote; + found.score = data.post.score; + found.upvotes = data.post.upvotes; + found.downvotes = data.post.downvotes; this.setState(this.state); } } diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index a302b834..a46017e2 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -14,7 +14,7 @@ import { CommentResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { msgOp, fetchLimit } from '../utils'; +import { wsJsonToRes, fetchLimit } from '../utils'; import { CommentNodes } from './comment-nodes'; import { SortSelect } from './sort-select'; import { i18n } from '../i18next'; @@ -298,92 +298,92 @@ export class Inbox extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); return; - } else if (op == UserOperation.GetReplies) { - let res: GetRepliesResponse = msg; - this.state.replies = res.replies; + } else if (res.op == UserOperation.GetReplies) { + let data = res.data as GetRepliesResponse; + this.state.replies = data.replies; this.sendUnreadCount(); window.scrollTo(0, 0); this.setState(this.state); - } else if (op == UserOperation.GetUserMentions) { - let res: GetUserMentionsResponse = msg; - this.state.mentions = res.mentions; + } else if (res.op == UserOperation.GetUserMentions) { + let data = res.data as GetUserMentionsResponse; + this.state.mentions = data.mentions; this.sendUnreadCount(); window.scrollTo(0, 0); this.setState(this.state); - } else if (op == UserOperation.MarkAllAsRead) { + } else if (res.op == UserOperation.MarkAllAsRead) { this.state.replies = []; this.state.mentions = []; window.scrollTo(0, 0); this.setState(this.state); - } else if (op == UserOperation.EditComment) { - let res: CommentResponse = msg; + } else if (res.op == UserOperation.EditComment) { + let data = res.data as CommentResponse; - let found = this.state.replies.find(c => c.id == res.comment.id); - found.content = res.comment.content; - found.updated = res.comment.updated; - found.removed = res.comment.removed; - found.deleted = res.comment.deleted; - found.upvotes = res.comment.upvotes; - found.downvotes = res.comment.downvotes; - found.score = res.comment.score; + let found = this.state.replies.find(c => c.id == data.comment.id); + found.content = data.comment.content; + found.updated = data.comment.updated; + found.removed = data.comment.removed; + found.deleted = data.comment.deleted; + found.upvotes = data.comment.upvotes; + found.downvotes = data.comment.downvotes; + found.score = data.comment.score; // If youre in the unread view, just remove it from the list - if (this.state.unreadOrAll == UnreadOrAll.Unread && res.comment.read) { + if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) { this.state.replies = this.state.replies.filter( - r => r.id !== res.comment.id + r => r.id !== data.comment.id ); } else { - let found = this.state.replies.find(c => c.id == res.comment.id); - found.read = res.comment.read; + let found = this.state.replies.find(c => c.id == data.comment.id); + found.read = data.comment.read; } this.sendUnreadCount(); this.setState(this.state); - } else if (op == UserOperation.EditUserMention) { - let res: UserMentionResponse = msg; + } else if (res.op == UserOperation.EditUserMention) { + let data = res.data as UserMentionResponse; - let found = this.state.mentions.find(c => c.id == res.mention.id); - found.content = res.mention.content; - found.updated = res.mention.updated; - found.removed = res.mention.removed; - found.deleted = res.mention.deleted; - found.upvotes = res.mention.upvotes; - found.downvotes = res.mention.downvotes; - found.score = res.mention.score; + let found = this.state.mentions.find(c => c.id == data.mention.id); + found.content = data.mention.content; + found.updated = data.mention.updated; + found.removed = data.mention.removed; + found.deleted = data.mention.deleted; + found.upvotes = data.mention.upvotes; + found.downvotes = data.mention.downvotes; + found.score = data.mention.score; // If youre in the unread view, just remove it from the list - if (this.state.unreadOrAll == UnreadOrAll.Unread && res.mention.read) { + if (this.state.unreadOrAll == UnreadOrAll.Unread && data.mention.read) { this.state.mentions = this.state.mentions.filter( - r => r.id !== res.mention.id + r => r.id !== data.mention.id ); } else { - let found = this.state.mentions.find(c => c.id == res.mention.id); - found.read = res.mention.read; + let found = this.state.mentions.find(c => c.id == data.mention.id); + found.read = data.mention.read; } this.sendUnreadCount(); this.setState(this.state); - } else if (op == UserOperation.CreateComment) { + } else if (res.op == UserOperation.CreateComment) { // let res: CommentResponse = msg; alert(i18n.t('reply_sent')); // this.state.replies.unshift(res.comment); // TODO do this right // this.setState(this.state); - } else if (op == UserOperation.SaveComment) { - let res: CommentResponse = msg; - let found = this.state.replies.find(c => c.id == res.comment.id); - found.saved = res.comment.saved; + } else if (res.op == UserOperation.SaveComment) { + let data = res.data as CommentResponse; + let found = this.state.replies.find(c => c.id == data.comment.id); + found.saved = data.comment.saved; this.setState(this.state); - } else if (op == UserOperation.CreateCommentLike) { - let res: CommentResponse = msg; + } else if (res.op == UserOperation.CreateCommentLike) { + let data = res.data as CommentResponse; let found: Comment = this.state.replies.find( - c => c.id === res.comment.id + c => c.id === data.comment.id ); - found.score = res.comment.score; - found.upvotes = res.comment.upvotes; - found.downvotes = res.comment.downvotes; - if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; + found.score = data.comment.score; + found.upvotes = data.comment.upvotes; + found.downvotes = data.comment.downvotes; + if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote; this.setState(this.state); } } diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx index 53b7a22f..42d5e05f 100644 --- a/ui/src/components/login.tsx +++ b/ui/src/components/login.tsx @@ -10,7 +10,7 @@ import { GetSiteResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { msgOp, validEmail } from '../utils'; +import { wsJsonToRes, validEmail } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -48,14 +48,7 @@ export class Login extends Component { this.state = this.emptyState; this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -300,30 +293,30 @@ export class Login extends Component { } parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); this.state = this.emptyState; this.setState(this.state); return; } else { - if (op == UserOperation.Login) { + if (res.op == UserOperation.Login) { + let data = res.data as LoginResponse; this.state = this.emptyState; this.setState(this.state); - let res: LoginResponse = msg; - UserService.Instance.login(res); + UserService.Instance.login(data); this.props.history.push('/'); - } else if (op == UserOperation.Register) { + } else if (res.op == UserOperation.Register) { + let data = res.data as LoginResponse; this.state = this.emptyState; this.setState(this.state); - let res: LoginResponse = msg; - UserService.Instance.login(res); + UserService.Instance.login(data); this.props.history.push('/communities'); - } else if (op == UserOperation.PasswordReset) { + } else if (res.op == UserOperation.PasswordReset) { alert(i18n.t('reset_password_mail_sent')); - } else if (op == UserOperation.GetSite) { - let res: GetSiteResponse = msg; - this.state.enable_nsfw = res.site.enable_nsfw; + } else if (res.op == UserOperation.GetSite) { + let data = res.data as GetSiteResponse; + this.state.enable_nsfw = data.site.enable_nsfw; this.setState(this.state); document.title = `${i18n.t('login')} - ${ WebSocketService.Instance.site.name diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 9e7d690d..63519056 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -24,7 +24,7 @@ import { SortSelect } from './sort-select'; import { ListingTypeSelect } from './listing-type-select'; import { SiteForm } from './site-form'; import { - msgOp, + wsJsonToRes, repoUrl, mdToHtml, fetchLimit, @@ -56,7 +56,6 @@ export class Main extends Component { subscribedCommunities: [], trendingCommunities: [], site: { - op: null, site: { id: null, name: null, @@ -564,53 +563,54 @@ export class Main extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); return; - } else if (op == UserOperation.GetFollowedCommunities) { - let res: GetFollowedCommunitiesResponse = msg; - this.state.subscribedCommunities = res.communities; + } else if (res.op == UserOperation.GetFollowedCommunities) { + let data = res.data as GetFollowedCommunitiesResponse; + this.state.subscribedCommunities = data.communities; this.setState(this.state); - } else if (op == UserOperation.ListCommunities) { - let res: ListCommunitiesResponse = msg; - this.state.trendingCommunities = res.communities; + } else if (res.op == UserOperation.ListCommunities) { + let data = res.data as ListCommunitiesResponse; + this.state.trendingCommunities = data.communities; this.setState(this.state); - } else if (op == UserOperation.GetSite) { - let res: GetSiteResponse = msg; + } else if (res.op == UserOperation.GetSite) { + let data = res.data as GetSiteResponse; // This means it hasn't been set up yet - if (!res.site) { + if (!data.site) { this.context.router.history.push('/setup'); } - this.state.site.admins = res.admins; - this.state.site.site = res.site; - this.state.site.banned = res.banned; - this.state.site.online = res.online; + this.state.site.admins = data.admins; + this.state.site.site = data.site; + this.state.site.banned = data.banned; + this.state.site.online = data.online; this.setState(this.state); document.title = `${WebSocketService.Instance.site.name}`; - } else if (op == UserOperation.EditSite) { - let res: SiteResponse = msg; - this.state.site.site = res.site; + } else if (res.op == UserOperation.EditSite) { + let data = res.data as SiteResponse; + this.state.site.site = data.site; this.state.showEditSite = false; this.setState(this.state); - } else if (op == UserOperation.GetPosts) { - let res: GetPostsResponse = msg; + } else if (res.op == UserOperation.GetPosts) { + let data = res.data as GetPostsResponse; // This is needed to refresh the view + // TODO mess with this this.state.posts = undefined; this.setState(this.state); - this.state.posts = res.posts; + this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); - } else if (op == UserOperation.CreatePostLike) { - let res: CreatePostLikeResponse = msg; - let found = this.state.posts.find(c => c.id == res.post.id); - found.my_vote = res.post.my_vote; - found.score = res.post.score; - found.upvotes = res.post.upvotes; - found.downvotes = res.post.downvotes; + } else if (res.op == UserOperation.CreatePostLike) { + let data = res.data as CreatePostLikeResponse; + let found = this.state.posts.find(c => c.id == data.post.id); + found.my_vote = data.post.my_vote; + found.score = data.post.score; + found.upvotes = data.post.upvotes; + found.downvotes = data.post.downvotes; this.setState(this.state); } } diff --git a/ui/src/components/modlog.tsx b/ui/src/components/modlog.tsx index 425710dd..5a66744b 100644 --- a/ui/src/components/modlog.tsx +++ b/ui/src/components/modlog.tsx @@ -17,9 +17,9 @@ import { ModAdd, } from '../interfaces'; import { WebSocketService } from '../services'; -import { msgOp, addTypeInfo, fetchLimit } from '../utils'; +import { wsJsonToRes, addTypeInfo, fetchLimit } from '../utils'; import { MomentTime } from './moment-time'; -import * as moment from 'moment'; +import moment from 'moment'; import { i18n } from '../i18next'; interface ModlogState { @@ -55,14 +55,7 @@ export class Modlog extends Component { ? Number(this.props.match.params.community_id) : undefined; this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -431,15 +424,15 @@ export class Modlog extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); return; - } else if (op == UserOperation.GetModlog) { - let res: GetModlogResponse = msg; + } else if (res.op == UserOperation.GetModlog) { + let data = res.data as GetModlogResponse; this.state.loading = false; window.scrollTo(0, 0); - this.setCombined(res); + this.setCombined(data); } } } diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 9fa1e3dc..50bf19c7 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -14,7 +14,7 @@ import { Comment, } from '../interfaces'; import { - msgOp, + wsJsonToRes, pictshareAvatarThumbnail, showAvatars, fetchLimit, @@ -182,16 +182,16 @@ export class Navbar extends Component { } parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); - if (msg.error) { - if (msg.error == 'not_logged_in') { + let res = wsJsonToRes(msg); + if (res.error) { + if (res.error == 'not_logged_in') { UserService.Instance.logout(); location.reload(); } return; - } else if (op == UserOperation.GetReplies) { - let res: GetRepliesResponse = msg; - let unreadReplies = res.replies.filter(r => !r.read); + } else if (res.op == UserOperation.GetReplies) { + let data = res.data as GetRepliesResponse; + let unreadReplies = data.replies.filter(r => !r.read); if ( unreadReplies.length > 0 && this.state.fetchCount > 1 && @@ -203,9 +203,9 @@ export class Navbar extends Component { this.state.replies = unreadReplies; this.setState(this.state); this.sendUnreadCount(); - } else if (op == UserOperation.GetUserMentions) { - let res: GetUserMentionsResponse = msg; - let unreadMentions = res.mentions.filter(r => !r.read); + } else if (res.op == UserOperation.GetUserMentions) { + let data = res.data as GetUserMentionsResponse; + let unreadMentions = data.mentions.filter(r => !r.read); if ( unreadMentions.length > 0 && this.state.fetchCount > 1 && @@ -217,12 +217,12 @@ export class Navbar extends Component { this.state.mentions = unreadMentions; this.setState(this.state); this.sendUnreadCount(); - } else if (op == UserOperation.GetSite) { - let res: GetSiteResponse = msg; + } else if (res.op == UserOperation.GetSite) { + let data = res.data as GetSiteResponse; - if (res.site) { - this.state.siteName = res.site.name; - WebSocketService.Instance.site = res.site; + if (data.site) { + this.state.siteName = data.site.name; + WebSocketService.Instance.site = data.site; this.setState(this.state); } } diff --git a/ui/src/components/password_change.tsx b/ui/src/components/password_change.tsx index 3e542f7b..d25c0daf 100644 --- a/ui/src/components/password_change.tsx +++ b/ui/src/components/password_change.tsx @@ -7,7 +7,7 @@ import { PasswordChangeForm, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { msgOp, capitalizeFirstLetter } from '../utils'; +import { wsJsonToRes, capitalizeFirstLetter } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -34,14 +34,7 @@ export class PasswordChange extends Component { this.state = this.emptyState; this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -141,18 +134,18 @@ export class PasswordChange extends Component { } parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); + let res = wsJsonToRes(msg); if (msg.error) { alert(i18n.t(msg.error)); this.state.loading = false; this.setState(this.state); return; } else { - if (op == UserOperation.PasswordChange) { + if (res.op == UserOperation.PasswordChange) { + let data = res.data as LoginResponse; this.state = this.emptyState; this.setState(this.state); - let res: LoginResponse = msg; - UserService.Instance.login(res); + UserService.Instance.login(data); this.props.history.push('/'); } } diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx index fe633a01..a2541504 100644 --- a/ui/src/components/post-form.tsx +++ b/ui/src/components/post-form.tsx @@ -19,7 +19,7 @@ import { } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { - msgOp, + wsJsonToRes, getPageTitle, validURL, capitalizeFirstLetter, @@ -458,46 +458,46 @@ export class PostForm extends Component { } parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); this.state.loading = false; this.setState(this.state); return; - } else if (op == UserOperation.ListCommunities) { - let res: ListCommunitiesResponse = msg; - this.state.communities = res.communities; + } else if (res.op == UserOperation.ListCommunities) { + let data = res.data as ListCommunitiesResponse; + this.state.communities = data.communities; if (this.props.post) { this.state.postForm.community_id = this.props.post.community_id; } else if (this.props.params && this.props.params.community) { - let foundCommunityId = res.communities.find( + let foundCommunityId = data.communities.find( r => r.name == this.props.params.community ).id; this.state.postForm.community_id = foundCommunityId; } else { - this.state.postForm.community_id = res.communities[0].id; + this.state.postForm.community_id = data.communities[0].id; } this.setState(this.state); - } else if (op == UserOperation.CreatePost) { + } else if (res.op == UserOperation.CreatePost) { + let data = res.data as PostResponse; this.state.loading = false; - let res: PostResponse = msg; - this.props.onCreate(res.post.id); - } else if (op == UserOperation.EditPost) { + this.props.onCreate(data.post.id); + } else if (res.op == UserOperation.EditPost) { + let data = res.data as PostResponse; this.state.loading = false; - let res: PostResponse = msg; - this.props.onEdit(res.post); - } else if (op == UserOperation.Search) { - let res: SearchResponse = msg; + this.props.onEdit(data.post); + } else if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; - if (res.type_ == SearchType[SearchType.Posts]) { - this.state.suggestedPosts = res.posts; - } else if (res.type_ == SearchType[SearchType.Url]) { - this.state.crossPosts = res.posts; + if (data.type_ == SearchType[SearchType.Posts]) { + this.state.suggestedPosts = data.posts; + } else if (data.type_ == SearchType[SearchType.Url]) { + this.state.crossPosts = data.posts; } this.setState(this.state); - } else if (op == UserOperation.GetSite) { - let res: GetSiteResponse = msg; - this.state.enable_nsfw = res.site.enable_nsfw; + } else if (res.op == UserOperation.GetSite) { + let data = res.data as GetSiteResponse; + this.state.enable_nsfw = data.site.enable_nsfw; this.setState(this.state); } } diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index d4869386..866894a9 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -28,7 +28,7 @@ import { GetCommunityResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { msgOp, hotRank } from '../utils'; +import { wsJsonToRes, hotRank } from '../utils'; import { PostListing } from './post-listing'; import { PostListings } from './post-listings'; import { Sidebar } from './sidebar'; @@ -343,17 +343,17 @@ export class Post extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); return; - } else if (op == UserOperation.GetPost) { - let res: GetPostResponse = msg; - this.state.post = res.post; - this.state.comments = res.comments; - this.state.community = res.community; - this.state.moderators = res.moderators; - this.state.admins = res.admins; + } else if (res.op == UserOperation.GetPost) { + let data = res.data as GetPostResponse; + this.state.post = data.post; + this.state.comments = data.comments; + this.state.community = data.community; + this.state.moderators = data.moderators; + this.state.admins = data.admins; this.state.loading = false; document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`; @@ -370,105 +370,107 @@ export class Post extends Component { } this.setState(this.state); - } else if (op == UserOperation.CreateComment) { - let res: CommentResponse = msg; - this.state.comments.unshift(res.comment); + } else if (res.op == UserOperation.CreateComment) { + let data = res.data as CommentResponse; + this.state.comments.unshift(data.comment); this.setState(this.state); - } else if (op == UserOperation.EditComment) { - let res: CommentResponse = msg; - let found = this.state.comments.find(c => c.id == res.comment.id); - found.content = res.comment.content; - found.updated = res.comment.updated; - found.removed = res.comment.removed; - found.deleted = res.comment.deleted; - found.upvotes = res.comment.upvotes; - found.downvotes = res.comment.downvotes; - found.score = res.comment.score; - found.read = res.comment.read; + } else if (res.op == UserOperation.EditComment) { + let data = res.data as CommentResponse; + let found = this.state.comments.find(c => c.id == data.comment.id); + found.content = data.comment.content; + found.updated = data.comment.updated; + found.removed = data.comment.removed; + found.deleted = data.comment.deleted; + found.upvotes = data.comment.upvotes; + found.downvotes = data.comment.downvotes; + found.score = data.comment.score; + found.read = data.comment.read; this.setState(this.state); - } else if (op == UserOperation.SaveComment) { - let res: CommentResponse = msg; - let found = this.state.comments.find(c => c.id == res.comment.id); - found.saved = res.comment.saved; + } else if (res.op == UserOperation.SaveComment) { + let data = res.data as CommentResponse; + let found = this.state.comments.find(c => c.id == data.comment.id); + found.saved = data.comment.saved; this.setState(this.state); - } else if (op == UserOperation.CreateCommentLike) { - let res: CommentResponse = msg; + } else if (res.op == UserOperation.CreateCommentLike) { + let data = res.data as CommentResponse; let found: Comment = this.state.comments.find( - c => c.id === res.comment.id + c => c.id === data.comment.id ); - found.score = res.comment.score; - found.upvotes = res.comment.upvotes; - found.downvotes = res.comment.downvotes; - if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; + found.score = data.comment.score; + found.upvotes = data.comment.upvotes; + found.downvotes = data.comment.downvotes; + if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote; this.setState(this.state); - } else if (op == UserOperation.CreatePostLike) { - let res: CreatePostLikeResponse = msg; - this.state.post.my_vote = res.post.my_vote; - this.state.post.score = res.post.score; - this.state.post.upvotes = res.post.upvotes; - this.state.post.downvotes = res.post.downvotes; + } else if (res.op == UserOperation.CreatePostLike) { + let data = res.data as CreatePostLikeResponse; + this.state.post.my_vote = data.post.my_vote; + this.state.post.score = data.post.score; + this.state.post.upvotes = data.post.upvotes; + this.state.post.downvotes = data.post.downvotes; this.setState(this.state); - } else if (op == UserOperation.EditPost) { - let res: PostResponse = msg; - this.state.post = res.post; + } else if (res.op == UserOperation.EditPost) { + let data = res.data as PostResponse; + this.state.post = data.post; this.setState(this.state); - } else if (op == UserOperation.SavePost) { - let res: PostResponse = msg; - this.state.post = res.post; + } else if (res.op == UserOperation.SavePost) { + let data = res.data as PostResponse; + this.state.post = data.post; this.setState(this.state); - } else if (op == UserOperation.EditCommunity) { - let res: CommunityResponse = msg; - this.state.community = res.community; - this.state.post.community_id = res.community.id; - this.state.post.community_name = res.community.name; + } else if (res.op == UserOperation.EditCommunity) { + let data = res.data as CommunityResponse; + this.state.community = data.community; + this.state.post.community_id = data.community.id; + this.state.post.community_name = data.community.name; this.setState(this.state); - } else if (op == UserOperation.FollowCommunity) { - let res: CommunityResponse = msg; - this.state.community.subscribed = res.community.subscribed; + } else if (res.op == UserOperation.FollowCommunity) { + let data = res.data as CommunityResponse; + this.state.community.subscribed = data.community.subscribed; this.state.community.number_of_subscribers = - res.community.number_of_subscribers; + data.community.number_of_subscribers; this.setState(this.state); - } else if (op == UserOperation.BanFromCommunity) { - let res: BanFromCommunityResponse = msg; + } else if (res.op == UserOperation.BanFromCommunity) { + let data = res.data as BanFromCommunityResponse; this.state.comments - .filter(c => c.creator_id == res.user.id) - .forEach(c => (c.banned_from_community = res.banned)); - if (this.state.post.creator_id == res.user.id) { - this.state.post.banned_from_community = res.banned; + .filter(c => c.creator_id == data.user.id) + .forEach(c => (c.banned_from_community = data.banned)); + if (this.state.post.creator_id == data.user.id) { + this.state.post.banned_from_community = data.banned; } this.setState(this.state); - } else if (op == UserOperation.AddModToCommunity) { - let res: AddModToCommunityResponse = msg; - this.state.moderators = res.moderators; + } else if (res.op == UserOperation.AddModToCommunity) { + let data = res.data as AddModToCommunityResponse; + this.state.moderators = data.moderators; this.setState(this.state); - } else if (op == UserOperation.BanUser) { - let res: BanUserResponse = msg; + } else if (res.op == UserOperation.BanUser) { + let data = res.data as BanUserResponse; this.state.comments - .filter(c => c.creator_id == res.user.id) - .forEach(c => (c.banned = res.banned)); - if (this.state.post.creator_id == res.user.id) { - this.state.post.banned = res.banned; + .filter(c => c.creator_id == data.user.id) + .forEach(c => (c.banned = data.banned)); + if (this.state.post.creator_id == data.user.id) { + this.state.post.banned = data.banned; } this.setState(this.state); - } else if (op == UserOperation.AddAdmin) { - let res: AddAdminResponse = msg; - this.state.admins = res.admins; + } else if (res.op == UserOperation.AddAdmin) { + let data = res.data as AddAdminResponse; + this.state.admins = data.admins; this.setState(this.state); - } else if (op == UserOperation.Search) { - let res: SearchResponse = msg; - this.state.crossPosts = res.posts.filter(p => p.id != this.state.post.id); + } else if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; + this.state.crossPosts = data.posts.filter( + p => p.id != this.state.post.id + ); this.setState(this.state); - } else if (op == UserOperation.TransferSite) { - let res: GetSiteResponse = msg; + } else if (res.op == UserOperation.TransferSite) { + let data = res.data as GetSiteResponse; - this.state.admins = res.admins; + this.state.admins = data.admins; this.setState(this.state); - } else if (op == UserOperation.TransferCommunity) { - let res: GetCommunityResponse = msg; - this.state.community = res.community; - this.state.moderators = res.moderators; - this.state.admins = res.admins; + } else if (res.op == UserOperation.TransferCommunity) { + let data = res.data as GetCommunityResponse; + this.state.community = data.community; + this.state.moderators = data.moderators; + this.state.admins = data.admins; this.setState(this.state); } } diff --git a/ui/src/components/search.tsx b/ui/src/components/search.tsx index 70219d4e..f310b80c 100644 --- a/ui/src/components/search.tsx +++ b/ui/src/components/search.tsx @@ -15,7 +15,7 @@ import { } from '../interfaces'; import { WebSocketService } from '../services'; import { - msgOp, + wsJsonToRes, fetchLimit, routeSearchTypeToEnum, routeSortTypeToEnum, @@ -45,7 +45,6 @@ export class Search extends Component { sort: this.getSortTypeFromProps(this.props), page: this.getPageFromProps(this.props), searchResponse: { - op: null, type_: null, posts: [], comments: [], @@ -386,7 +385,6 @@ export class Search extends Component { return (
{res && - res.op && res.posts.length == 0 && res.comments.length == 0 && res.communities.length == 0 && @@ -464,13 +462,13 @@ export class Search extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); return; - } else if (op == UserOperation.Search) { - let res: SearchResponse = msg; - this.state.searchResponse = res; + } else if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; + this.state.searchResponse = data; this.state.loading = false; document.title = `${i18n.t('search')} - ${this.state.q} - ${ WebSocketService.Instance.site.name diff --git a/ui/src/components/setup.tsx b/ui/src/components/setup.tsx index d421e46f..5b081111 100644 --- a/ui/src/components/setup.tsx +++ b/ui/src/components/setup.tsx @@ -3,7 +3,7 @@ import { Subscription } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; import { RegisterForm, LoginResponse, UserOperation } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { msgOp } from '../utils'; +import { wsJsonToRes } from '../utils'; import { SiteForm } from './site-form'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -35,14 +35,7 @@ export class Setup extends Component { this.state = this.emptyState; this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -189,20 +182,19 @@ export class Setup extends Component { } parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); this.state.userLoading = false; this.setState(this.state); return; - } else if (op == UserOperation.Register) { + } else if (res.op == UserOperation.Register) { + let data = res.data as LoginResponse; this.state.userLoading = false; this.state.doneRegisteringUser = true; - let res: LoginResponse = msg; - UserService.Instance.login(res); - console.log(res); + UserService.Instance.login(data); this.setState(this.state); - } else if (op == UserOperation.CreateSite) { + } else if (res.op == UserOperation.CreateSite) { this.props.history.push('/'); } } diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index c95b18bb..8987fdfc 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -21,7 +21,7 @@ import { } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { - msgOp, + wsJsonToRes, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter, @@ -970,25 +970,25 @@ export class User extends Component { parseMessage(msg: any) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + alert(i18n.t(res.error)); this.state.deleteAccountLoading = false; this.state.avatarLoading = false; this.state.userSettingsLoading = false; - if (msg.error == 'couldnt_find_that_username_or_email') { + if (res.error == 'couldnt_find_that_username_or_email') { this.context.router.history.push('/'); } this.setState(this.state); return; - } else if (op == UserOperation.GetUserDetails) { - let res: UserDetailsResponse = msg; - this.state.user = res.user; - this.state.comments = res.comments; - this.state.follows = res.follows; - this.state.moderates = res.moderates; - this.state.posts = res.posts; - this.state.admins = res.admins; + } else if (res.op == UserOperation.GetUserDetails) { + let data = res.data as UserDetailsResponse; + this.state.user = data.user; + this.state.comments = data.comments; + this.state.follows = data.follows; + this.state.moderates = data.moderates; + this.state.posts = data.posts; + this.state.admins = data.admins; this.state.loading = false; if (this.isCurrentUser) { this.state.userSettingsForm.show_nsfw = @@ -1010,59 +1010,59 @@ export class User extends Component { document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`; window.scrollTo(0, 0); this.setState(this.state); - } else if (op == UserOperation.EditComment) { - let res: CommentResponse = msg; + } else if (res.op == UserOperation.EditComment) { + let data = res.data as CommentResponse; - let found = this.state.comments.find(c => c.id == res.comment.id); - found.content = res.comment.content; - found.updated = res.comment.updated; - found.removed = res.comment.removed; - found.deleted = res.comment.deleted; - found.upvotes = res.comment.upvotes; - found.downvotes = res.comment.downvotes; - found.score = res.comment.score; + let found = this.state.comments.find(c => c.id == data.comment.id); + found.content = data.comment.content; + found.updated = data.comment.updated; + found.removed = data.comment.removed; + found.deleted = data.comment.deleted; + found.upvotes = data.comment.upvotes; + found.downvotes = data.comment.downvotes; + found.score = data.comment.score; this.setState(this.state); - } else if (op == UserOperation.CreateComment) { + } else if (res.op == UserOperation.CreateComment) { // let res: CommentResponse = msg; alert(i18n.t('reply_sent')); // this.state.comments.unshift(res.comment); // TODO do this right // this.setState(this.state); - } else if (op == UserOperation.SaveComment) { - let res: CommentResponse = msg; - let found = this.state.comments.find(c => c.id == res.comment.id); - found.saved = res.comment.saved; + } else if (res.op == UserOperation.SaveComment) { + let data = res.data as CommentResponse; + let found = this.state.comments.find(c => c.id == data.comment.id); + found.saved = data.comment.saved; this.setState(this.state); - } else if (op == UserOperation.CreateCommentLike) { - let res: CommentResponse = msg; + } else if (res.op == UserOperation.CreateCommentLike) { + let data = res.data as CommentResponse; let found: Comment = this.state.comments.find( - c => c.id === res.comment.id + c => c.id === data.comment.id ); - found.score = res.comment.score; - found.upvotes = res.comment.upvotes; - found.downvotes = res.comment.downvotes; - if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; + found.score = data.comment.score; + found.upvotes = data.comment.upvotes; + found.downvotes = data.comment.downvotes; + if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote; this.setState(this.state); - } else if (op == UserOperation.BanUser) { - let res: BanUserResponse = msg; + } else if (res.op == UserOperation.BanUser) { + let data = res.data as BanUserResponse; this.state.comments - .filter(c => c.creator_id == res.user.id) - .forEach(c => (c.banned = res.banned)); + .filter(c => c.creator_id == data.user.id) + .forEach(c => (c.banned = data.banned)); this.state.posts - .filter(c => c.creator_id == res.user.id) - .forEach(c => (c.banned = res.banned)); + .filter(c => c.creator_id == data.user.id) + .forEach(c => (c.banned = data.banned)); this.setState(this.state); - } else if (op == UserOperation.AddAdmin) { - let res: AddAdminResponse = msg; - this.state.admins = res.admins; + } else if (res.op == UserOperation.AddAdmin) { + let data = res.data as AddAdminResponse; + this.state.admins = data.admins; this.setState(this.state); - } else if (op == UserOperation.SaveUserSettings) { + } else if (res.op == UserOperation.SaveUserSettings) { + let data = res.data as LoginResponse; this.state = this.emptyState; this.state.userSettingsLoading = false; this.setState(this.state); - let res: LoginResponse = msg; - UserService.Instance.login(res); - } else if (op == UserOperation.DeleteAccount) { + UserService.Instance.login(data); + } else if (res.op == UserOperation.DeleteAccount) { this.state.deleteAccountLoading = false; this.state.deleteAccountShowConfirm = false; this.setState(this.state); diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 7fc7a252..3af5be02 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -226,7 +226,6 @@ export interface FollowCommunityForm { } export interface GetFollowedCommunitiesResponse { - op: string; communities: Array; } @@ -241,7 +240,6 @@ export interface GetUserDetailsForm { } export interface UserDetailsResponse { - op: string; user: UserView; follows: Array; moderates: Array; @@ -259,7 +257,6 @@ export interface GetRepliesForm { } export interface GetRepliesResponse { - op: string; replies: Array; } @@ -272,7 +269,6 @@ export interface GetUserMentionsForm { } export interface GetUserMentionsResponse { - op: string; mentions: Array; } @@ -283,7 +279,6 @@ export interface EditUserMentionForm { } export interface UserMentionResponse { - op: string; mention: Comment; } @@ -297,7 +292,6 @@ export interface BanFromCommunityForm { } export interface BanFromCommunityResponse { - op: string; user: UserView; banned: boolean; } @@ -321,7 +315,6 @@ export interface TransferSiteForm { } export interface AddModToCommunityResponse { - op: string; moderators: Array; } @@ -333,7 +326,6 @@ export interface GetModlogForm { } export interface GetModlogResponse { - op: string; removed_posts: Array; locked_posts: Array; stickied_posts: Array; @@ -474,7 +466,6 @@ export interface RegisterForm { } export interface LoginResponse { - op: string; jwt: string; } @@ -509,14 +500,12 @@ export interface CommunityForm { } export interface GetCommunityResponse { - op: string; community: Community; moderators: Array; admins: Array; } export interface CommunityResponse { - op: string; community: Community; } @@ -528,12 +517,10 @@ export interface ListCommunitiesForm { } export interface ListCommunitiesResponse { - op: string; communities: Array; } export interface ListCategoriesResponse { - op: string; categories: Array; } @@ -562,7 +549,6 @@ export interface PostFormParams { } export interface GetPostResponse { - op: string; post: Post; comments: Array; community: Community; @@ -577,7 +563,6 @@ export interface SavePostForm { } export interface PostResponse { - op: string; post: Post; } @@ -601,7 +586,6 @@ export interface SaveCommentForm { } export interface CommentResponse { - op: string; comment: Comment; } @@ -627,7 +611,6 @@ export interface GetPostsForm { } export interface GetPostsResponse { - op: string; posts: Array; } @@ -638,7 +621,6 @@ export interface CreatePostLikeForm { } export interface CreatePostLikeResponse { - op: string; post: Post; } @@ -652,7 +634,6 @@ export interface SiteForm { } export interface GetSiteResponse { - op: string; site: Site; admins: Array; banned: Array; @@ -660,7 +641,6 @@ export interface GetSiteResponse { } export interface SiteResponse { - op: string; site: Site; } @@ -673,7 +653,6 @@ export interface BanUserForm { } export interface BanUserResponse { - op: string; user: UserView; banned: boolean; } @@ -685,7 +664,6 @@ export interface AddAdminForm { } export interface AddAdminResponse { - op: string; admins: Array; } @@ -699,7 +677,6 @@ export interface SearchForm { } export interface SearchResponse { - op: string; type_: string; posts?: Array; comments?: Array; @@ -715,12 +692,37 @@ export interface PasswordResetForm { email: string; } -export interface PasswordResetResponse { - op: string; -} +// export interface PasswordResetResponse { +// } export interface PasswordChangeForm { token: string; password: string; password_verify: string; } + +type ResponseType = + | SiteResponse + | GetFollowedCommunitiesResponse + | ListCommunitiesResponse + | GetPostsResponse + | CreatePostLikeResponse + | GetRepliesResponse + | GetUserMentionsResponse + | ListCategoriesResponse + | CommunityResponse + | CommentResponse + | UserMentionResponse + | LoginResponse + | GetModlogResponse + | SearchResponse + | BanFromCommunityResponse + | AddModToCommunityResponse + | BanUserResponse + | AddAdminResponse; + +export interface WebSocketResponse { + op: UserOperation; + data: ResponseType; + error?: string; +} diff --git a/ui/src/utils.ts b/ui/src/utils.ts index dc221716..363de663 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -15,6 +15,7 @@ import { SortType, ListingType, SearchType, + WebSocketResponse, } from './interfaces'; import { UserService } from './services/UserService'; import markdown_it from 'markdown-it'; @@ -38,9 +39,12 @@ export function randomStr() { .substr(2, 10); } -export function msgOp(msg: any): UserOperation { +export function wsJsonToRes(msg: any): WebSocketResponse { let opStr: string = msg.op; - return UserOperation[opStr]; + return { + op: UserOperation[opStr], + data: msg.data, + }; } export const md = new markdown_it({ From dcd60228ed753a146bcb56bb5bf51166835acfb7 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 19 Jan 2020 00:38:45 -0500 Subject: [PATCH 10/15] Strongly typing WebsocketJsonResponse. Forgot comment-form.tsx --- ui/src/components/comment-form.tsx | 18 +++++++++--------- ui/src/components/communities.tsx | 3 ++- ui/src/components/community-form.tsx | 2 +- ui/src/components/community.tsx | 3 ++- ui/src/components/inbox.tsx | 3 ++- ui/src/components/login.tsx | 3 ++- ui/src/components/main.tsx | 3 ++- ui/src/components/modlog.tsx | 2 +- ui/src/components/moment-time.tsx | 2 +- ui/src/components/navbar.tsx | 3 ++- ui/src/components/password_change.tsx | 3 ++- ui/src/components/post-form.tsx | 3 ++- ui/src/components/post.tsx | 3 ++- ui/src/components/search.tsx | 3 ++- ui/src/components/setup.tsx | 9 +++++++-- ui/src/components/user.tsx | 3 ++- ui/src/interfaces.ts | 6 ++++++ ui/src/utils.ts | 3 ++- 18 files changed, 49 insertions(+), 26 deletions(-) diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index f5816899..dddcbe72 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -10,9 +10,9 @@ import { } from '../interfaces'; import { Subscription } from 'rxjs'; import { + wsJsonToRes, capitalizeFirstLetter, mentionDropdownFetchLimit, - msgOp, mdToHtml, randomStr, markdownHelpUrl, @@ -311,10 +311,10 @@ export class CommentForm extends Component { this.userSub = WebSocketService.Instance.subject.subscribe( msg => { - let op: UserOperation = msgOp(msg); - if (op == UserOperation.Search) { - let res: SearchResponse = msg; - let users = res.users.map(u => { + let res = wsJsonToRes(msg); + if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; + let users = data.users.map(u => { return { key: u.name }; }); cb(users); @@ -343,10 +343,10 @@ export class CommentForm extends Component { this.communitySub = WebSocketService.Instance.subject.subscribe( msg => { - let op: UserOperation = msgOp(msg); - if (op == UserOperation.Search) { - let res: SearchResponse = msg; - let communities = res.communities.map(u => { + let res = wsJsonToRes(msg); + if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; + let communities = data.communities.map(u => { return { key: u.name }; }); cb(communities); diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index 683d495e..ebcbc345 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -10,6 +10,7 @@ import { FollowCommunityForm, ListCommunitiesForm, SortType, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService } from '../services'; import { wsJsonToRes } from '../utils'; @@ -227,7 +228,7 @@ export class Communities extends Component { WebSocketService.Instance.listCommunities(listCommunitiesForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx index 93f4d649..14cd8e4f 100644 --- a/ui/src/components/community-form.tsx +++ b/ui/src/components/community-form.tsx @@ -239,7 +239,7 @@ export class CommunityForm extends Component< i.props.onCancel(); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); console.log(msg); if (res.error) { diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index a48f468b..357fe260 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -14,6 +14,7 @@ import { ListingType, GetPostsResponse, CreatePostLikeResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { PostListings } from './post-listings'; @@ -251,7 +252,7 @@ export class Community extends Component { WebSocketService.Instance.getPosts(getPostsForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index a46017e2..4aa9cebe 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -12,6 +12,7 @@ import { GetUserMentionsResponse, UserMentionResponse, CommentResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { wsJsonToRes, fetchLimit } from '../utils'; @@ -296,7 +297,7 @@ export class Inbox extends Component { WebSocketService.Instance.markAllAsRead(); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx index 42d5e05f..29482f45 100644 --- a/ui/src/components/login.tsx +++ b/ui/src/components/login.tsx @@ -8,6 +8,7 @@ import { UserOperation, PasswordResetForm, GetSiteResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { wsJsonToRes, validEmail } from '../utils'; @@ -292,7 +293,7 @@ export class Login extends Component { WebSocketService.Instance.passwordReset(resetForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); if (res.error) { alert(i18n.t(res.error)); diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 63519056..1ccebc80 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -17,6 +17,7 @@ import { CreatePostLikeResponse, Post, GetPostsForm, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { PostListings } from './post-listings'; @@ -561,7 +562,7 @@ export class Main extends Component { WebSocketService.Instance.getPosts(getPostsForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/modlog.tsx b/ui/src/components/modlog.tsx index 5a66744b..b2011af5 100644 --- a/ui/src/components/modlog.tsx +++ b/ui/src/components/modlog.tsx @@ -422,7 +422,7 @@ export class Modlog extends Component { WebSocketService.Instance.getModlog(modlogForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/moment-time.tsx b/ui/src/components/moment-time.tsx index 6bb4d99c..fd2a7efa 100644 --- a/ui/src/components/moment-time.tsx +++ b/ui/src/components/moment-time.tsx @@ -1,5 +1,5 @@ import { Component } from 'inferno'; -import * as moment from 'moment'; +import moment from 'moment'; import { getMomentLanguage } from '../utils'; import { i18n } from '../i18next'; diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 50bf19c7..fac54212 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -12,6 +12,7 @@ import { SortType, GetSiteResponse, Comment, + WebSocketJsonResponse, } from '../interfaces'; import { wsJsonToRes, @@ -181,7 +182,7 @@ export class Navbar extends Component { i.setState(i.state); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); if (res.error) { if (res.error == 'not_logged_in') { diff --git a/ui/src/components/password_change.tsx b/ui/src/components/password_change.tsx index d25c0daf..97f10888 100644 --- a/ui/src/components/password_change.tsx +++ b/ui/src/components/password_change.tsx @@ -5,6 +5,7 @@ import { UserOperation, LoginResponse, PasswordChangeForm, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { wsJsonToRes, capitalizeFirstLetter } from '../utils'; @@ -133,7 +134,7 @@ export class PasswordChange extends Component { WebSocketService.Instance.passwordChange(i.state.passwordChangeForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); if (msg.error) { alert(i18n.t(msg.error)); diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx index a2541504..454a569f 100644 --- a/ui/src/components/post-form.tsx +++ b/ui/src/components/post-form.tsx @@ -16,6 +16,7 @@ import { SearchType, SearchResponse, GetSiteResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { @@ -457,7 +458,7 @@ export class PostForm extends Component { }); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); if (res.error) { alert(i18n.t(res.error)); diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index 866894a9..1e334b1d 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -26,6 +26,7 @@ import { SearchResponse, GetSiteResponse, GetCommunityResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { wsJsonToRes, hotRank } from '../utils'; @@ -341,7 +342,7 @@ export class Post extends Component { ); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/search.tsx b/ui/src/components/search.tsx index f310b80c..ae0f8dbc 100644 --- a/ui/src/components/search.tsx +++ b/ui/src/components/search.tsx @@ -12,6 +12,7 @@ import { SearchForm, SearchResponse, SearchType, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService } from '../services'; import { @@ -460,7 +461,7 @@ export class Search extends Component { ); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/components/setup.tsx b/ui/src/components/setup.tsx index 5b081111..c4c9dc63 100644 --- a/ui/src/components/setup.tsx +++ b/ui/src/components/setup.tsx @@ -1,7 +1,12 @@ import { Component, linkEvent } from 'inferno'; import { Subscription } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; -import { RegisterForm, LoginResponse, UserOperation } from '../interfaces'; +import { + RegisterForm, + LoginResponse, + UserOperation, + WebSocketJsonResponse, +} from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { wsJsonToRes } from '../utils'; import { SiteForm } from './site-form'; @@ -181,7 +186,7 @@ export class Setup extends Component { i.setState(i.state); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { let res = wsJsonToRes(msg); if (res.error) { alert(i18n.t(res.error)); diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index 8987fdfc..606d85ab 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -18,6 +18,7 @@ import { BanUserResponse, AddAdminResponse, DeleteAccountForm, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { @@ -968,7 +969,7 @@ export class User extends Component { WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (res.error) { diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 3af5be02..63c60856 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -726,3 +726,9 @@ export interface WebSocketResponse { data: ResponseType; error?: string; } + +export interface WebSocketJsonResponse { + op: string; + data: ResponseType; + error?: string; +} diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 363de663..90ef6899 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -16,6 +16,7 @@ import { ListingType, SearchType, WebSocketResponse, + WebSocketJsonResponse, } from './interfaces'; import { UserService } from './services/UserService'; import markdown_it from 'markdown-it'; @@ -39,7 +40,7 @@ export function randomStr() { .substr(2, 10); } -export function wsJsonToRes(msg: any): WebSocketResponse { +export function wsJsonToRes(msg: WebSocketJsonResponse): WebSocketResponse { let opStr: string = msg.op; return { op: UserOperation[opStr], From 4b029865d71a03cf288e2dae89c26481013fa432 Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 19 Jan 2020 14:25:50 +0100 Subject: [PATCH 11/15] Use generics to reduce code duplication in websocket --- server/src/websocket/server.rs | 150 ++++++++++++--------------------- 1 file changed, 52 insertions(+), 98 deletions(-) diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 9afbff25..3015a80d 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -3,7 +3,7 @@ //! room through `ChatServer`. use actix::prelude::*; -use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::r2d2::{ConnectionManager, Pool, PooledConnection}; use diesel::PgConnection; use failure::Error; use rand::{rngs::ThreadRng, Rng}; @@ -312,6 +312,21 @@ where Ok(serde_json::to_string(&response)?) } +fn do_user_operation<'a, Data, Response>( + op: UserOperation, + data: &str, + conn: &PooledConnection>, +) -> Result +where + for<'de> Data: Deserialize<'de> + 'a, + Response: Serialize, + Oper: Perform, +{ + let parsed_data: Data = serde_json::from_str(data)?; + let res = Oper::new(parsed_data).perform(&conn)?; + to_json_string(&op, &res) +} + fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let json: Value = serde_json::from_str(&msg.msg)?; let data = &json["data"].to_string(); @@ -323,75 +338,47 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let login: Login = serde_json::from_str(data)?; - let res = Oper::new(login).perform(&conn)?; - to_json_string(&user_operation, &res) - } + UserOperation::Login => do_user_operation::(user_operation, data, &conn), UserOperation::Register => { - let register: Register = serde_json::from_str(data)?; - let res = Oper::new(register).perform(&conn); - if res.is_ok() { - chat.check_rate_limit_register(msg.id)?; - } - to_json_string(&user_operation, &res?) + chat.check_rate_limit_register(msg.id)?; + do_user_operation::(user_operation, data, &conn) } UserOperation::GetUserDetails => { - let get_user_details: GetUserDetails = serde_json::from_str(data)?; - let res = Oper::new(get_user_details).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::SaveUserSettings => { - let save_user_settings: SaveUserSettings = serde_json::from_str(data)?; - let res = Oper::new(save_user_settings).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::AddAdmin => { - let add_admin: AddAdmin = serde_json::from_str(data)?; - let res = Oper::new(add_admin).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::BanUser => { - let ban_user: BanUser = serde_json::from_str(data)?; - let res = Oper::new(ban_user).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::GetReplies => { - let get_replies: GetReplies = serde_json::from_str(data)?; - let res = Oper::new(get_replies).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::GetUserMentions => { - let get_user_mentions: GetUserMentions = serde_json::from_str(data)?; - let res = Oper::new(get_user_mentions).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::EditUserMention => { - let edit_user_mention: EditUserMention = serde_json::from_str(data)?; - let res = Oper::new(edit_user_mention).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::MarkAllAsRead => { - let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?; - let res = Oper::new(mark_all_as_read).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::GetCommunity => { - let get_community: GetCommunity = serde_json::from_str(data)?; - let res = Oper::new(get_community).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::ListCommunities => { - let list_communities: ListCommunities = serde_json::from_str(data)?; - let res = Oper::new(list_communities).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::CreateCommunity => { chat.check_rate_limit_register(msg.id)?; - let create_community: CreateCommunity = serde_json::from_str(data)?; - let res = Oper::new(create_community).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::EditCommunity => { let edit_community: EditCommunity = serde_json::from_str(data)?; @@ -404,15 +391,12 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let follow_community: FollowCommunity = serde_json::from_str(data)?; - let res = Oper::new(follow_community).perform(&conn)?; - to_json_string(&user_operation, &res) - } - UserOperation::GetFollowedCommunities => { - let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?; - let res = Oper::new(followed_communities).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } + UserOperation::GetFollowedCommunities => do_user_operation::< + GetFollowedCommunities, + GetFollowedCommunitiesResponse, + >(user_operation, data, &conn), UserOperation::BanFromCommunity => { let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; let community_id = ban_from_community.community_id; @@ -430,15 +414,11 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let list_categories: ListCategories = ListCategories; - let res = Oper::new(list_categories).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::CreatePost => { chat.check_rate_limit_post(msg.id)?; - let create_post: CreatePost = serde_json::from_str(data)?; - let res = Oper::new(create_post).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::GetPost => { let get_post: GetPost = serde_json::from_str(data)?; @@ -447,15 +427,11 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let get_posts: GetPosts = serde_json::from_str(data)?; - let res = Oper::new(get_posts).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::CreatePostLike => { chat.check_rate_limit_message(msg.id)?; - let create_post_like: CreatePostLike = serde_json::from_str(data)?; - let res = Oper::new(create_post_like).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::EditPost => { let edit_post: EditPost = serde_json::from_str(data)?; @@ -467,9 +443,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let save_post: SavePost = serde_json::from_str(data)?; - let res = Oper::new(save_post).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::CreateComment => { chat.check_rate_limit_message(msg.id)?; @@ -495,9 +469,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let save_comment: SaveComment = serde_json::from_str(data)?; - let res = Oper::new(save_comment).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::CreateCommentLike => { chat.check_rate_limit_message(msg.id)?; @@ -512,19 +484,13 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let get_modlog: GetModlog = serde_json::from_str(data)?; - let res = Oper::new(get_modlog).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::CreateSite => { - let create_site: CreateSite = serde_json::from_str(data)?; - let res = Oper::new(create_site).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::EditSite => { - let edit_site: EditSite = serde_json::from_str(data)?; - let res = Oper::new(edit_site).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::GetSite => { let online: usize = chat.sessions.len(); @@ -534,34 +500,22 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let search: Search = serde_json::from_str(data)?; - let res = Oper::new(search).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::TransferCommunity => { - let transfer_community: TransferCommunity = serde_json::from_str(data)?; - let res = Oper::new(transfer_community).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::TransferSite => { - let transfer_site: TransferSite = serde_json::from_str(data)?; - let res = Oper::new(transfer_site).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::DeleteAccount => { - let delete_account: DeleteAccount = serde_json::from_str(data)?; - let res = Oper::new(delete_account).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::PasswordReset => { - let password_reset: PasswordReset = serde_json::from_str(data)?; - let res = Oper::new(password_reset).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } UserOperation::PasswordChange => { - let password_change: PasswordChange = serde_json::from_str(data)?; - let res = Oper::new(password_change).perform(&conn)?; - to_json_string(&user_operation, &res) + do_user_operation::(user_operation, data, &conn) } } } From e094ae0208774732a547bb41892f0df6e46ec3c2 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 19 Jan 2020 13:31:37 -0500 Subject: [PATCH 12/15] Updating API docs. --- docs/src/SUMMARY.md | 2 +- ....md => contributing_websocket_http_api.md} | 575 +++++++++++++----- 2 files changed, 438 insertions(+), 139 deletions(-) rename docs/src/{contributing_websocket_api.md => contributing_websocket_http_api.md} (69%) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index d8916974..c2df6223 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -12,5 +12,5 @@ - [Contributing](contributing.md) - [Docker Development](contributing_docker_development.md) - [Local Development](contributing_local_development.md) - - [Websocket API](contributing_websocket_api.md) + - [Websocket/HTTP API](contributing_websocket_http_api.md) - [ActivityPub API Outline](contributing_apub_api_outline.md) diff --git a/docs/src/contributing_websocket_api.md b/docs/src/contributing_websocket_http_api.md similarity index 69% rename from docs/src/contributing_websocket_api.md rename to docs/src/contributing_websocket_http_api.md index 16383d53..c6db490b 100644 --- a/docs/src/contributing_websocket_api.md +++ b/docs/src/contributing_websocket_http_api.md @@ -5,126 +5,171 @@ - [Data types](#data-types) - [Basic usage](#basic-usage) - * [WebSocket Endpoint](#websocket-endpoint) - * [Testing with Websocat](#testing-with-websocat) - * [Testing with the WebSocket JavaScript API](#testing-with-the-websocket-javascript-api) + * [WebSocket](#websocket) + + [Testing with Websocat](#testing-with-websocat) + + [Testing with the WebSocket JavaScript API](#testing-with-the-websocket-javascript-api) + * [HTTP](#http) + + [Testing with Curl](#testing-with-curl) + - [Get Example](#get-example) + - [Post Example](#post-example) - [Rate limits](#rate-limits) - [Errors](#errors) - [API documentation](#api-documentation) * [Sort Types](#sort-types) + * [Websocket vs HTTP](#websocket-vs-http) * [User / Authentication / Admin actions](#user--authentication--admin-actions) + [Login](#login) - [Request](#request) - [Response](#response) + - [HTTP](#http-1) + [Register](#register) - [Request](#request-1) - [Response](#response-1) + - [HTTP](#http-2) + [Get User Details](#get-user-details) - [Request](#request-2) - [Response](#response-2) + - [HTTP](#http-3) + [Save User Settings](#save-user-settings) - [Request](#request-3) - [Response](#response-3) + - [HTTP](#http-4) + [Get Replies / Inbox](#get-replies--inbox) - [Request](#request-4) - [Response](#response-4) + - [HTTP](#http-5) + [Get User Mentions](#get-user-mentions) - [Request](#request-5) - [Response](#response-5) - + [Mark All As Read](#mark-all-as-read) + - [HTTP](#http-6) + + [Edit User Mention](#edit-user-mention) - [Request](#request-6) - [Response](#response-6) - + [Delete Account](#delete-account) + - [HTTP](#http-7) + + [Mark All As Read](#mark-all-as-read) - [Request](#request-7) - [Response](#response-7) - + [Add admin](#add-admin) + - [HTTP](#http-8) + + [Delete Account](#delete-account) - [Request](#request-8) - [Response](#response-8) - + [Ban user](#ban-user) + - [HTTP](#http-9) + + [Add admin](#add-admin) - [Request](#request-9) - [Response](#response-9) - * [Site](#site) - + [List Categories](#list-categories) + - [HTTP](#http-10) + + [Ban user](#ban-user) - [Request](#request-10) - [Response](#response-10) - + [Search](#search) + - [HTTP](#http-11) + * [Site](#site) + + [List Categories](#list-categories) - [Request](#request-11) - [Response](#response-11) - + [Get Modlog](#get-modlog) + - [HTTP](#http-12) + + [Search](#search) - [Request](#request-12) - [Response](#response-12) - + [Create Site](#create-site) + - [HTTP](#http-13) + + [Get Modlog](#get-modlog) - [Request](#request-13) - [Response](#response-13) - + [Edit Site](#edit-site) + - [HTTP](#http-14) + + [Create Site](#create-site) - [Request](#request-14) - [Response](#response-14) - + [Get Site](#get-site) + - [HTTP](#http-15) + + [Edit Site](#edit-site) - [Request](#request-15) - [Response](#response-15) - + [Transfer Site](#transfer-site) + - [HTTP](#http-16) + + [Get Site](#get-site) - [Request](#request-16) - [Response](#response-16) - * [Community](#community) - + [Get Community](#get-community) + - [HTTP](#http-17) + + [Transfer Site](#transfer-site) - [Request](#request-17) - [Response](#response-17) - + [Create Community](#create-community) + - [HTTP](#http-18) + * [Community](#community) + + [Get Community](#get-community) - [Request](#request-18) - [Response](#response-18) - + [List Communities](#list-communities) + - [HTTP](#http-19) + + [Create Community](#create-community) - [Request](#request-19) - [Response](#response-19) - + [Ban from Community](#ban-from-community) + - [HTTP](#http-20) + + [List Communities](#list-communities) - [Request](#request-20) - [Response](#response-20) - + [Add Mod to Community](#add-mod-to-community) + - [HTTP](#http-21) + + [Ban from Community](#ban-from-community) - [Request](#request-21) - [Response](#response-21) - + [Edit Community](#edit-community) + - [HTTP](#http-22) + + [Add Mod to Community](#add-mod-to-community) - [Request](#request-22) - [Response](#response-22) - + [Follow Community](#follow-community) + - [HTTP](#http-23) + + [Edit Community](#edit-community) - [Request](#request-23) - [Response](#response-23) - + [Get Followed Communities](#get-followed-communities) + - [HTTP](#http-24) + + [Follow Community](#follow-community) - [Request](#request-24) - [Response](#response-24) - + [Transfer Community](#transfer-community) + - [HTTP](#http-25) + + [Get Followed Communities](#get-followed-communities) - [Request](#request-25) - [Response](#response-25) - * [Post](#post) - + [Create Post](#create-post) + - [HTTP](#http-26) + + [Transfer Community](#transfer-community) - [Request](#request-26) - [Response](#response-26) - + [Get Post](#get-post) + - [HTTP](#http-27) + * [Post](#post) + + [Create Post](#create-post) - [Request](#request-27) - [Response](#response-27) - + [Get Posts](#get-posts) + - [HTTP](#http-28) + + [Get Post](#get-post) - [Request](#request-28) - [Response](#response-28) - + [Create Post Like](#create-post-like) + - [HTTP](#http-29) + + [Get Posts](#get-posts) - [Request](#request-29) - [Response](#response-29) - + [Edit Post](#edit-post) + - [HTTP](#http-30) + + [Create Post Like](#create-post-like) - [Request](#request-30) - [Response](#response-30) - + [Save Post](#save-post) + - [HTTP](#http-31) + + [Edit Post](#edit-post) - [Request](#request-31) - [Response](#response-31) - * [Comment](#comment) - + [Create Comment](#create-comment) + - [HTTP](#http-32) + + [Save Post](#save-post) - [Request](#request-32) - [Response](#response-32) - + [Edit Comment](#edit-comment) + - [HTTP](#http-33) + * [Comment](#comment) + + [Create Comment](#create-comment) - [Request](#request-33) - [Response](#response-33) - + [Save Comment](#save-comment) + - [HTTP](#http-34) + + [Edit Comment](#edit-comment) - [Request](#request-34) - [Response](#response-34) - + [Create Comment Like](#create-comment-like) + - [HTTP](#http-35) + + [Save Comment](#save-comment) - [Request](#request-35) - [Response](#response-35) + - [HTTP](#http-36) + + [Create Comment Like](#create-comment-like) + - [Request](#request-36) + - [Response](#response-36) + - [HTTP](#http-37) * [RSS / Atom feeds](#rss--atom-feeds) + [All](#all) + [Community](#community-1) @@ -144,13 +189,13 @@ Request and response strings are in [JSON format](https://www.json.org). -### WebSocket Endpoint +### WebSocket Connect to ws://***host***/api/v1/ws to get started. If the ***`host`*** supports secure connections, you can use wss://***host***/api/v1/ws. -### Testing with Websocat +#### Testing with Websocat [Websocat link](https://github.com/vi/websocat) @@ -159,7 +204,7 @@ If the ***`host`*** supports secure connections, you can use wss://***host A simple test command: `{"op": "ListCategories"}` -### Testing with the WebSocket JavaScript API +#### Testing with the WebSocket JavaScript API [WebSocket JavaScript API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) ```javascript @@ -171,6 +216,32 @@ ws.onopen = function () { })); }; ``` +### HTTP + +Endpoints are at http://***host***/api/v1/***endpoint***. They'll be listed below for each action. + +#### Testing with Curl + +##### Get Example + +``` +curl /community/list?sort=Hot +``` + +##### Post Example + +``` +curl -i -H \ +"Content-Type: application/json" \ +-X POST \ +-d '{ + comment_id: X, + pub post_id: X, + score: X, + auth: X, +}' \ +/comment/like +``` ## Rate limits @@ -201,6 +272,11 @@ These go wherever there is a `sort` field. The available sort types are: - `TopYear` - the most upvoted posts/communities of the current year. - `TopAll` - the most upvoted posts/communities on the current instance. +### Websocket vs HTTP + +- Below are the websocket JSON requests / responses. For HTTP, ignore all fields except those inside `data`. +- For example, an http login will be a `POST` `{username_or_email: X, password: X}` + ### User / Authentication / Admin actions #### Login @@ -220,13 +296,19 @@ The `jwt` string should be stored and used anywhere `auth` is called for. ##### Response ```rust { - op: String, - jwt: String + op: "Login", + data: { + jwt: String, + } } ``` +##### HTTP + +`POST /user/login` #### Register + Only the first user will be able to be the admin. ##### Request @@ -245,11 +327,17 @@ Only the first user will be able to be the admin. ##### Response ```rust { - op: String, - jwt: String + op: "Register", + data: { + jwt: String, + } } ``` +##### HTTP + +`POST /user/register` + #### Get User Details ##### Request ```rust @@ -270,14 +358,20 @@ Only the first user will be able to be the admin. ##### Response ```rust { - op: String, - user: UserView, - follows: Vec, - moderates: Vec, - comments: Vec, - posts: Vec, + op: "GetUserDetails", + data: { + user: UserView, + follows: Vec, + moderates: Vec, + comments: Vec, + posts: Vec, + } } ``` +##### HTTP + +`GET /user` + #### Save User Settings ##### Request ```rust @@ -295,10 +389,16 @@ Only the first user will be able to be the admin. ##### Response ```rust { - op: String, - jwt: String + op: "SaveUserSettings", + data: { + jwt: String + } } ``` +##### HTTP + +`PUT /save_user_settings` + #### Get Replies / Inbox ##### Request ```rust @@ -316,10 +416,16 @@ Only the first user will be able to be the admin. ##### Response ```rust { - op: String, - replies: Vec, + op: "GetReplies", + data: { + replies: Vec, + } } ``` +##### HTTP + +`GET /user/replies` + #### Get User Mentions ##### Request @@ -338,11 +444,42 @@ Only the first user will be able to be the admin. ##### Response ```rust { - op: String, - mentions: Vec, + op: "GetUserMentions", + data: { + mentions: Vec, + } } ``` +##### HTTP + +`GET /user/mentions` + +#### Edit User Mention +##### Request +```rust +{ + op: "EditUserMention", + data: { + user_mention_id: i32, + read: Option, + auth: String, + } +} +``` +##### Response +```rust +{ + op: "EditUserMention", + data: { + mention: UserMentionView, + } +} +``` +##### HTTP + +`PUT /user/mention` + #### Mark All As Read Marks all user replies and mentions as read. @@ -359,11 +496,17 @@ Marks all user replies and mentions as read. ##### Response ```rust { - op: String, - replies: Vec, + op: "MarkAllAsRead", + data: { + replies: Vec, + } } ``` +##### HTTP + +`POST /user/mark_all_as_read` + #### Delete Account *Permananently deletes your posts and comments* @@ -381,11 +524,17 @@ Marks all user replies and mentions as read. ##### Response ```rust { - op: String, - jwt: String, + op: "DeleteAccount", + data: { + jwt: String, + } } ``` +##### HTTP + +`POST /user/delete_account` + #### Add admin ##### Request ```rust @@ -401,10 +550,15 @@ Marks all user replies and mentions as read. ##### Response ```rust { - op: String, - admins: Vec, + op: "AddAdmin", + data: { + admins: Vec, + } } ``` +##### HTTP + +`POST /admin/add` #### Ban user ##### Request @@ -423,11 +577,16 @@ Marks all user replies and mentions as read. ##### Response ```rust { - op: String, - user: UserView, - banned: bool, + op: "BanUser", + data: { + user: UserView, + banned: bool, + } } ``` +##### HTTP + +`POST /user/ban` ### Site #### List Categories @@ -440,12 +599,18 @@ Marks all user replies and mentions as read. ##### Response ```rust { - op: String, - categories: Vec + op: "ListCategories", + data: { + categories: Vec + } } ``` +##### HTTP + +`GET /categories` #### Search + Search types are `Both, Comments, Posts`. ##### Request @@ -465,11 +630,16 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - comments: Vec, - posts: Vec, + op: "Search", + data: { + comments: Vec, + posts: Vec, + } } ``` +##### HTTP + +`POST /search` #### Get Modlog ##### Request @@ -487,18 +657,24 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - removed_posts: Vec, - locked_posts: Vec, - removed_comments: Vec, - removed_communities: Vec, - banned_from_community: Vec, - banned: Vec, - added_to_community: Vec, - added: Vec, + op: "GetModlog", + data: { + removed_posts: Vec, + locked_posts: Vec, + removed_comments: Vec, + removed_communities: Vec, + banned_from_community: Vec, + banned: Vec, + added_to_community: Vec, + added: Vec, + } } ``` +##### HTTP + +`GET /modlog` + #### Create Site ##### Request ```rust @@ -514,11 +690,17 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - site: SiteView, + op: "CreateSite", + data: { + site: SiteView, + } } ``` +##### HTTP + +`POST /site` + #### Edit Site ##### Request ```rust @@ -534,10 +716,15 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - site: SiteView, + op: "EditSite", + data: { + site: SiteView, + } } ``` +##### HTTP + +`PUT /site` #### Get Site ##### Request @@ -549,12 +736,17 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - site: Option, - admins: Vec, - banned: Vec, + op: "GetSite", + data: { + site: Option, + admins: Vec, + banned: Vec, + } } ``` +##### HTTP + +`GET /site` #### Transfer Site ##### Request @@ -570,12 +762,17 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - site: Option, - admins: Vec, - banned: Vec, + op: "TransferSite", + data: { + site: Option, + admins: Vec, + banned: Vec, + } } ``` +##### HTTP + +`POST /site/transfer` ### Community #### Get Community @@ -593,12 +790,17 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - community: CommunityView, - moderators: Vec, - admins: Vec, + op: "GetCommunity", + data: { + community: CommunityView, + moderators: Vec, + admins: Vec, + } } ``` +##### HTTP + +`GET /community` #### Create Community ##### Request @@ -617,10 +819,15 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - community: CommunityView + op: "CreateCommunity", + data: { + community: CommunityView + } } ``` +##### HTTP + +`POST /community` #### List Communities ##### Request @@ -638,10 +845,15 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - communities: Vec + op: "ListCommunities", + data: { + communities: Vec + } } ``` +##### HTTP + +`GET /community/list` #### Ban from Community ##### Request @@ -661,11 +873,16 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - user: UserView, - banned: bool, + op: "BanFromCommunity", + data: { + user: UserView, + banned: bool, + } } ``` +##### HTTP + +`POST /community/ban_user` #### Add Mod to Community ##### Request @@ -683,10 +900,15 @@ Search types are `Both, Comments, Posts`. ##### Response ```rust { - op: String, - moderators: Vec, + op: "AddModToCommunity", + data: { + moderators: Vec, + } } ``` +##### HTTP + +`POST /community/mod` #### Edit Community Mods and admins can remove and lock a community, creators can delete it. @@ -712,10 +934,15 @@ Mods and admins can remove and lock a community, creators can delete it. ##### Response ```rust { - op: String, - community: CommunityView + op: "EditCommunity", + data: { + community: CommunityView + } } ``` +##### HTTP + +`PUT /community` #### Follow Community ##### Request @@ -732,10 +959,15 @@ Mods and admins can remove and lock a community, creators can delete it. ##### Response ```rust { - op: String, - community: CommunityView + op: "FollowCommunity", + data: { + community: CommunityView + } } ``` +##### HTTP + +`POST /community/follow` #### Get Followed Communities ##### Request @@ -750,10 +982,15 @@ Mods and admins can remove and lock a community, creators can delete it. ##### Response ```rust { - op: String, - communities: Vec + op: "GetFollowedCommunities", + data: { + communities: Vec + } } ``` +##### HTTP + +`GET /user/followed_communities` #### Transfer Community ##### Request @@ -770,12 +1007,17 @@ Mods and admins can remove and lock a community, creators can delete it. ##### Response ```rust { - op: String, - community: CommunityView, - moderators: Vec, - admins: Vec, + op: "TransferCommunity", + data: { + community: CommunityView, + moderators: Vec, + admins: Vec, + } } ``` +##### HTTP + +`POST /community/transfer` ### Post #### Create Post @@ -795,10 +1037,15 @@ Mods and admins can remove and lock a community, creators can delete it. ##### Response ```rust { - op: String, - post: PostView + op: "CreatePost", + data: { + post: PostView + } } ``` +##### HTTP + +`POST /post` #### Get Post ##### Request @@ -814,16 +1061,22 @@ Mods and admins can remove and lock a community, creators can delete it. ##### Response ```rust { - op: String, - post: PostView, - comments: Vec, - community: CommunityView, - moderators: Vec, - admins: Vec, + op: "GetPost", + data: { + post: PostView, + comments: Vec, + community: CommunityView, + moderators: Vec, + admins: Vec, + } } ``` +##### HTTP + +`GET /post` #### Get Posts + Post listing types are `All, Subscribed, Community` ##### Request @@ -843,12 +1096,18 @@ Post listing types are `All, Subscribed, Community` ##### Response ```rust { - op: String, - posts: Vec, + op: "GetPosts", + data: { + posts: Vec, + } } ``` +##### HTTP + +`GET /post/list` #### Create Post Like + `score` can be 0, -1, or 1 ##### Request @@ -865,12 +1124,18 @@ Post listing types are `All, Subscribed, Community` ##### Response ```rust { - op: String, - post: PostView + op: "CreatePostLike", + data: { + post: PostView + } } ``` +##### HTTP + +`POST /post/like` #### Edit Post + Mods and admins can remove and lock a post, creators can delete it. ##### Request @@ -895,11 +1160,17 @@ Mods and admins can remove and lock a post, creators can delete it. ##### Response ```rust { - op: String, - post: PostView + op: "EditPost", + data: { + post: PostView + } } ``` +##### HTTP + +`PUT /post` + #### Save Post ##### Request ```rust @@ -915,10 +1186,15 @@ Mods and admins can remove and lock a post, creators can delete it. ##### Response ```rust { - op: String, - post: PostView + op: "SavePost", + data: { + post: PostView + } } ``` +##### HTTP + +`POST /post/save` ### Comment #### Create Comment @@ -938,12 +1214,19 @@ Mods and admins can remove and lock a post, creators can delete it. ##### Response ```rust { - op: String, - comment: CommentView + op: "CreateComment", + data: { + comment: CommentView + } } ``` +##### HTTP + +`POST /comment` + #### Edit Comment + Mods and admins can remove a comment, creators can delete it. ##### Request @@ -967,10 +1250,15 @@ Mods and admins can remove a comment, creators can delete it. ##### Response ```rust { - op: String, - comment: CommentView + op: "EditComment", + data: { + comment: CommentView + } } ``` +##### HTTP + +`PUT /comment` #### Save Comment ##### Request @@ -987,12 +1275,18 @@ Mods and admins can remove a comment, creators can delete it. ##### Response ```rust { - op: String, - comment: CommentView + op: "SaveComment", + data: { + comment: CommentView + } } ``` +##### HTTP + +`POST /comment/save` #### Create Comment Like + `score` can be 0, -1, or 1 ##### Request @@ -1010,10 +1304,15 @@ Mods and admins can remove a comment, creators can delete it. ##### Response ```rust { - op: String, - comment: CommentView + op: "CreateCommentLike", + data: { + comment: CommentView + } } ``` +##### HTTP + +`POST /comment/like` ### RSS / Atom feeds From 7914fd949cc13ba3c5aba5b66458591086bd67ae Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 20 Jan 2020 18:49:54 -0500 Subject: [PATCH 13/15] Fixing last. --- docs/src/contributing_websocket_http_api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/src/contributing_websocket_http_api.md b/docs/src/contributing_websocket_http_api.md index c6db490b..eea77e8b 100644 --- a/docs/src/contributing_websocket_http_api.md +++ b/docs/src/contributing_websocket_http_api.md @@ -611,7 +611,7 @@ Marks all user replies and mentions as read. #### Search -Search types are `Both, Comments, Posts`. +Search types are `All, Comments, Posts, Communities, Users, Url` ##### Request ```rust @@ -624,6 +624,7 @@ Search types are `Both, Comments, Posts`. sort: String, page: Option, limit: Option, + auth?: Option, } } ``` @@ -632,8 +633,11 @@ Search types are `Both, Comments, Posts`. { op: "Search", data: { + type_: String, comments: Vec, posts: Vec, + communities: Vec, + users: Vec, } } ``` From 2f21071606de35e7d5eb9c44fc868d9e831cabaf Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 23 Jan 2020 15:22:17 +0100 Subject: [PATCH 14/15] api fixes --- server/src/routes/api.rs | 95 +++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 6ba62766..5121d240 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -16,52 +16,52 @@ type DbParam = web::Data>>; pub fn config(cfg: &mut web::ServiceConfig) { cfg // Site - .route("/api/v1/site", web::get().to(route::)) - .route("/api/v1/categories", web::get().to(route::)) - .route("/api/v1/modlog", web::get().to(route::)) - .route("/api/v1/search", web::post().to(route::)) + .route("/api/v1/site", web::get().to(route_get::)) + .route("/api/v1/categories", web::get().to(route_get::)) + .route("/api/v1/modlog", web::get().to(route_get::)) + .route("/api/v1/search", web::get().to(route_get::)) // Community - .route("/api/v1/community", web::post().to(route::)) - .route("/api/v1/community", web::get().to(route::)) - .route("/api/v1/community", web::put().to(route::)) - .route("/api/v1/community/list", web::get().to(route::)) - .route("/api/v1/community/follow", web::post().to(route::)) + .route("/api/v1/community", web::post().to(route_post::)) + .route("/api/v1/community", web::get().to(route_get::)) + .route("/api/v1/community", web::put().to(route_post::)) + .route("/api/v1/community/list", web::get().to(route_get::)) + .route("/api/v1/community/follow", web::post().to(route_post::)) // Post - .route("/api/v1/post", web::post().to(route::)) - .route("/api/v1/post", web::put().to(route::)) - .route("/api/v1/post", web::get().to(route::)) - .route("/api/v1/post/list", web::get().to(route::)) - .route("/api/v1/post/like", web::post().to(route::)) - .route("/api/v1/post/save", web::post().to(route::)) - .route("/api/v1/post/replies", web::get().to(route::)) + .route("/api/v1/post", web::post().to(route_post::)) + .route("/api/v1/post", web::put().to(route_post::)) + .route("/api/v1/post", web::get().to(route_get::)) + .route("/api/v1/post/list", web::get().to(route_get::)) + .route("/api/v1/post/like", web::post().to(route_post::)) + .route("/api/v1/post/save", web::put().to(route_post::)) // Comment - .route("/api/v1/comment", web::post().to(route::)) - .route("/api/v1/comment", web::put().to(route::)) - .route("/api/v1/comment/like", web::post().to(route::)) - .route("/api/v1/comment/save", web::post().to(route::)) + .route("/api/v1/comment", web::post().to(route_post::)) + .route("/api/v1/comment", web::put().to(route_post::)) + .route("/api/v1/comment/like", web::post().to(route_post::)) + .route("/api/v1/comment/save", web::put().to(route_post::)) // User - .route("/api/v1/user", web::get().to(route::)) - .route("/api/v1/user/mentions", web::get().to(route::)) - .route("/api/v1/user/mentions", web::put().to(route::)) - .route("/api/v1/user/followed-communities", web::get().to(route::)) + .route("/api/v1/user", web::get().to(route_get::)) + .route("/api/v1/user/mention", web::get().to(route_get::)) + .route("/api/v1/user/mention", web::put().to(route_post::)) + .route("/api/v1/user/replies", web::get().to(route_get::)) + .route("/api/v1/user/followed_communities", web::get().to(route_get::)) // Mod actions - .route("/api/v1/community/transfer", web::post().to(route::)) - .route("/api/v1/community/ban-user", web::post().to(route::)) - .route("/api/v1/community/mod", web::post().to(route::)) + .route("/api/v1/community/transfer", web::post().to(route_post::)) + .route("/api/v1/community/ban_user", web::post().to(route_post::)) + .route("/api/v1/community/mod", web::post().to(route_post::)) // Admin actions - .route("/api/v1/site", web::post().to(route::)) - .route("/api/v1/site", web::put().to(route::)) - .route("/api/v1/site/transfer", web::post().to(route::)) - .route("/api/v1/admin/add", web::post().to(route::)) - .route("/api/v1/user/ban", web::post().to(route::)) + .route("/api/v1/site", web::post().to(route_post::)) + .route("/api/v1/site", web::put().to(route_post::)) + .route("/api/v1/site/transfer", web::post().to(route_post::)) + .route("/api/v1/admin/add", web::post().to(route_post::)) + .route("/api/v1/user/ban", web::post().to(route_post::)) // User account actions - .route("/api/v1/user/login", web::post().to(route::)) - .route("/api/v1/user/register", web::post().to(route::)) - .route("/api/v1/user/delete_account", web::post().to(route::)) - .route("/api/v1/user/password_reset", web::post().to(route::)) - .route("/api/v1/user/password_change", web::post().to(route::)) - .route("/api/v1/user/mark_all_as_read", web::post().to(route::)) - .route("/api/v1/user/save_user_settings", web::post().to(route::)); + .route("/api/v1/user/login", web::post().to(route_post::)) + .route("/api/v1/user/register", web::post().to(route_post::)) + .route("/api/v1/user/delete_account", web::post().to(route_post::)) + .route("/api/v1/user/password_reset", web::post().to(route_post::)) + .route("/api/v1/user/password_change", web::post().to(route_post::)) + .route("/api/v1/user/mark_all_as_read", web::post().to(route_post::)) + .route("/api/v1/user/save_user_settings", web::put().to(route_post::)); } fn perform(data: Request, db: DbParam) -> Result @@ -78,7 +78,22 @@ where Ok(HttpResponse::Ok().json(response?)) } -async fn route(data: web::Query, db: DbParam) -> Result +async fn route_get( + data: web::Query, + db: DbParam, +) -> Result +where + Data: Serialize, + Response: Serialize, + Oper: Perform, +{ + perform::(data.0, db) +} + +async fn route_post( + data: web::Json, + db: DbParam, +) -> Result where Data: Serialize, Response: Serialize, From b45c83682c187737b092080e83ba741c39cfa695 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 23 Jan 2020 11:21:21 -0500 Subject: [PATCH 15/15] Fixing http curl POST docs. --- docs/src/contributing_websocket_http_api.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/contributing_websocket_http_api.md b/docs/src/contributing_websocket_http_api.md index eea77e8b..9e87d4fa 100644 --- a/docs/src/contributing_websocket_http_api.md +++ b/docs/src/contributing_websocket_http_api.md @@ -235,10 +235,10 @@ curl -i -H \ "Content-Type: application/json" \ -X POST \ -d '{ - comment_id: X, - pub post_id: X, - score: X, - auth: X, + "comment_id": X, + "post_id": X, + "score": X, + "auth": "..." }' \ /comment/like ```