From cbec13eeccd7fe1b18ca6f1f2c6c77c54c96a55f Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 2 May 2019 18:34:21 -0700 Subject: [PATCH 1/5] Reorganizing files before splitting out API --- Dockerfile | 2 +- server/Cargo.lock | 48 +++++++++---------- server/Cargo.toml | 7 +-- server/src/apub.rs | 2 +- server/src/{actions => db}/category.rs | 0 server/src/{actions => db}/comment.rs | 8 ++-- server/src/{actions => db}/comment_view.rs | 14 +++--- server/src/{actions => db}/community.rs | 2 +- server/src/{actions => db}/community_view.rs | 20 ++++---- server/src/{actions => db}/mod.rs | 0 server/src/{actions => db}/moderator.rs | 8 ++-- server/src/{actions => db}/moderator_views.rs | 16 +++---- server/src/{actions => db}/post.rs | 6 +-- server/src/{actions => db}/post_view.rs | 10 ++-- server/src/{actions => db}/user.rs | 0 server/src/{actions => db}/user_view.rs | 6 +-- server/src/lib.rs | 4 +- server/src/{bin => }/main.rs | 13 +++-- .../{websocket_server => websocket}/mod.rs | 0 .../{websocket_server => websocket}/server.rs | 22 ++++----- 20 files changed, 91 insertions(+), 97 deletions(-) rename server/src/{actions => db}/category.rs (100%) rename server/src/{actions => db}/comment.rs (98%) rename server/src/{actions => db}/comment_view.rs (97%) rename server/src/{actions => db}/community.rs (99%) rename server/src/{actions => db}/community_view.rs (91%) rename server/src/{actions => db}/mod.rs (100%) rename server/src/{actions => db}/moderator.rs (99%) rename server/src/{actions => db}/moderator_views.rs (95%) rename server/src/{actions => db}/post.rs (99%) rename server/src/{actions => db}/post_view.rs (98%) rename server/src/{actions => db}/user.rs (100%) rename server/src/{actions => db}/user_view.rs (89%) rename server/src/{bin => }/main.rs (96%) rename server/src/{websocket_server => websocket}/mod.rs (100%) rename server/src/{websocket_server => websocket}/server.rs (99%) diff --git a/Dockerfile b/Dockerfile index 3f3aa4e76..e794b249b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ COPY server/Cargo.toml server/Cargo.lock ./ RUN mkdir -p ./src/bin \ && echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs RUN cargo build --release --bin lemmy -RUN rm -r ./target/release/.fingerprint/server-* +RUN rm -r ./target/release/.fingerprint/lemmy_server-* # copy your source tree # RUN rm -rf ./src/ diff --git a/server/Cargo.lock b/server/Cargo.lock index 36d2a9014..659bd235c 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -752,6 +752,30 @@ name = "lazycell" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lemmy_server" +version = "0.0.1" +dependencies = [ + "activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-web 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)", + "bcrypt 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonwebtoken 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.49" @@ -1344,30 +1368,6 @@ dependencies = [ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "server" -version = "0.0.1" -dependencies = [ - "activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-web 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)", - "bcrypt 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonwebtoken 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "sha1" version = "0.6.0" diff --git a/server/Cargo.toml b/server/Cargo.toml index 1daaca663..5ee8b8a63 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,12 +1,7 @@ [package] -name = "server" +name = "lemmy_server" version = "0.0.1" authors = ["Dessalines "] -autobins = false - -[[bin]] -name = "lemmy" -path = "src/bin/main.rs" [dependencies] diesel = { version = "1.4.2", features = ["postgres","chrono"] } diff --git a/server/src/apub.rs b/server/src/apub.rs index 4fc0ba334..3d9595c81 100644 --- a/server/src/apub.rs +++ b/server/src/apub.rs @@ -1,6 +1,6 @@ extern crate activitypub; use self::activitypub::{context, actor::Person}; -use actions::user::User_; +use db::user::User_; impl User_ { pub fn person(&self) -> Person { diff --git a/server/src/actions/category.rs b/server/src/db/category.rs similarity index 100% rename from server/src/actions/category.rs rename to server/src/db/category.rs diff --git a/server/src/actions/comment.rs b/server/src/db/comment.rs similarity index 98% rename from server/src/actions/comment.rs rename to server/src/db/comment.rs index 4bbc7c04d..d63837a09 100644 --- a/server/src/actions/comment.rs +++ b/server/src/db/comment.rs @@ -4,7 +4,7 @@ use diesel::*; use diesel::result::Error; use serde::{Deserialize, Serialize}; use {Crud, Likeable, Saveable}; -use actions::post::Post; +use super::post::Post; // WITH RECURSIVE MyTree AS ( // SELECT * FROM comment WHERE parent_id IS NULL @@ -160,9 +160,9 @@ impl Saveable for CommentSaved { mod tests { use establish_connection; use super::*; - use actions::post::*; - use actions::community::*; - use actions::user::*; + use super::super::post::*; + use super::super::community::*; + use super::super::user::*; use Crud; #[test] fn test_crud() { diff --git a/server/src/actions/comment_view.rs b/server/src/db/comment_view.rs similarity index 97% rename from server/src/actions/comment_view.rs rename to server/src/db/comment_view.rs index eb6276cc2..903c4839d 100644 --- a/server/src/actions/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -68,7 +68,7 @@ impl CommentView { page: Option, limit: Option, ) -> Result, Error> { - use actions::comment_view::comment_view::dsl::*; + use super::comment_view::comment_view::dsl::*; let (limit, offset) = limit_and_offset(page, limit); @@ -125,7 +125,7 @@ impl CommentView { } pub fn read(conn: &PgConnection, from_comment_id: i32, my_user_id: Option) -> Result { - use actions::comment_view::comment_view::dsl::*; + use super::comment_view::comment_view::dsl::*; let mut query = comment_view.into_boxed(); @@ -206,7 +206,7 @@ impl ReplyView { page: Option, limit: Option, ) -> Result, Error> { - use actions::comment_view::reply_view::dsl::*; + use super::comment_view::reply_view::dsl::*; let (limit, offset) = limit_and_offset(page, limit); @@ -251,10 +251,10 @@ impl ReplyView { mod tests { use establish_connection; use super::*; - use actions::post::*; - use actions::community::*; - use actions::user::*; - use actions::comment::*; + use super::super::post::*; + use super::super::community::*; + use super::super::user::*; + use super::super::comment::*; use {Crud,Likeable}; #[test] fn test_crud() { diff --git a/server/src/actions/community.rs b/server/src/db/community.rs similarity index 99% rename from server/src/actions/community.rs rename to server/src/db/community.rs index db53ceb97..2e75828fc 100644 --- a/server/src/actions/community.rs +++ b/server/src/db/community.rs @@ -221,7 +221,7 @@ impl Crud for Site { mod tests { use establish_connection; use super::*; - use actions::user::*; + use super::super::user::*; use Crud; #[test] fn test_crud() { diff --git a/server/src/actions/community_view.rs b/server/src/db/community_view.rs similarity index 91% rename from server/src/actions/community_view.rs rename to server/src/db/community_view.rs index 9a162746a..5ec22f4e3 100644 --- a/server/src/actions/community_view.rs +++ b/server/src/db/community_view.rs @@ -100,7 +100,7 @@ pub struct CommunityView { impl CommunityView { pub fn read(conn: &PgConnection, from_community_id: i32, from_user_id: Option) -> Result { - use actions::community_view::community_view::dsl::*; + use super::community_view::community_view::dsl::*; let mut query = community_view.into_boxed(); @@ -122,7 +122,7 @@ impl CommunityView { page: Option, limit: Option, ) -> Result, Error> { - use actions::community_view::community_view::dsl::*; + use super::community_view::community_view::dsl::*; let mut query = community_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -163,12 +163,12 @@ pub struct CommunityModeratorView { impl CommunityModeratorView { pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result, Error> { - use actions::community_view::community_moderator_view::dsl::*; + use super::community_view::community_moderator_view::dsl::*; community_moderator_view.filter(community_id.eq(from_community_id)).load::(conn) } pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result, Error> { - use actions::community_view::community_moderator_view::dsl::*; + use super::community_view::community_moderator_view::dsl::*; community_moderator_view.filter(user_id.eq(from_user_id)).load::(conn) } } @@ -186,12 +186,12 @@ pub struct CommunityFollowerView { impl CommunityFollowerView { pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result, Error> { - use actions::community_view::community_follower_view::dsl::*; + use super::community_view::community_follower_view::dsl::*; community_follower_view.filter(community_id.eq(from_community_id)).load::(conn) } pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result, Error> { - use actions::community_view::community_follower_view::dsl::*; + use super::community_view::community_follower_view::dsl::*; community_follower_view.filter(user_id.eq(from_user_id)).load::(conn) } } @@ -210,17 +210,17 @@ pub struct CommunityUserBanView { impl CommunityUserBanView { pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result, Error> { - use actions::community_view::community_user_ban_view::dsl::*; + use super::community_view::community_user_ban_view::dsl::*; community_user_ban_view.filter(community_id.eq(from_community_id)).load::(conn) } pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result, Error> { - use actions::community_view::community_user_ban_view::dsl::*; + use super::community_view::community_user_ban_view::dsl::*; community_user_ban_view.filter(user_id.eq(from_user_id)).load::(conn) } pub fn get(conn: &PgConnection, from_user_id: i32, from_community_id: i32) -> Result { - use actions::community_view::community_user_ban_view::dsl::*; + use super::community_view::community_user_ban_view::dsl::*; community_user_ban_view .filter(user_id.eq(from_user_id)) .filter(community_id.eq(from_community_id)) @@ -246,7 +246,7 @@ pub struct SiteView { impl SiteView { pub fn read(conn: &PgConnection) -> Result { - use actions::community_view::site_view::dsl::*; + use super::community_view::site_view::dsl::*; site_view.first::(conn) } } diff --git a/server/src/actions/mod.rs b/server/src/db/mod.rs similarity index 100% rename from server/src/actions/mod.rs rename to server/src/db/mod.rs diff --git a/server/src/actions/moderator.rs b/server/src/db/moderator.rs similarity index 99% rename from server/src/actions/moderator.rs rename to server/src/db/moderator.rs index 794d91e74..74068e0a6 100644 --- a/server/src/actions/moderator.rs +++ b/server/src/db/moderator.rs @@ -400,10 +400,10 @@ impl Crud for ModAdd { mod tests { use establish_connection; use super::*; - use actions::user::*; - use actions::post::*; - use actions::community::*; - use actions::comment::*; + use super::super::user::*; + use super::super::post::*; + use super::super::community::*; + use super::super::comment::*; // use Crud; #[test] fn test_crud() { diff --git a/server/src/actions/moderator_views.rs b/server/src/db/moderator_views.rs similarity index 95% rename from server/src/actions/moderator_views.rs rename to server/src/db/moderator_views.rs index 894182771..56881ef54 100644 --- a/server/src/actions/moderator_views.rs +++ b/server/src/db/moderator_views.rs @@ -41,7 +41,7 @@ impl ModRemovePostView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_remove_post_view::dsl::*; + use super::moderator_views::mod_remove_post_view::dsl::*; let mut query = mod_remove_post_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -94,7 +94,7 @@ impl ModLockPostView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_lock_post_view::dsl::*; + use super::moderator_views::mod_lock_post_view::dsl::*; let mut query = mod_lock_post_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -156,7 +156,7 @@ impl ModRemoveCommentView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_remove_comment_view::dsl::*; + use super::moderator_views::mod_remove_comment_view::dsl::*; let mut query = mod_remove_comment_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -207,7 +207,7 @@ impl ModRemoveCommunityView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_remove_community_view::dsl::*; + use super::moderator_views::mod_remove_community_view::dsl::*; let mut query = mod_remove_community_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -260,7 +260,7 @@ impl ModBanFromCommunityView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_ban_from_community_view::dsl::*; + use super::moderator_views::mod_ban_from_community_view::dsl::*; let mut query = mod_ban_from_community_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -311,7 +311,7 @@ impl ModBanView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_ban_view::dsl::*; + use super::moderator_views::mod_ban_view::dsl::*; let mut query = mod_ban_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -359,7 +359,7 @@ impl ModAddCommunityView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_add_community_view::dsl::*; + use super::moderator_views::mod_add_community_view::dsl::*; let mut query = mod_add_community_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); @@ -406,7 +406,7 @@ impl ModAddView { page: Option, limit: Option, ) -> Result, Error> { - use actions::moderator_views::mod_add_view::dsl::*; + use super::moderator_views::mod_add_view::dsl::*; let mut query = mod_add_view.into_boxed(); let (limit, offset) = limit_and_offset(page, limit); diff --git a/server/src/actions/post.rs b/server/src/db/post.rs similarity index 99% rename from server/src/actions/post.rs rename to server/src/db/post.rs index 495a8b09e..59a7a1d44 100644 --- a/server/src/actions/post.rs +++ b/server/src/db/post.rs @@ -173,10 +173,10 @@ impl Readable for PostRead { #[cfg(test)] mod tests { use establish_connection; - use super::*; use Crud; - use actions::community::*; - use actions::user::*; + use super::*; + use super::super::community::*; + use super::super::user::*; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/actions/post_view.rs b/server/src/db/post_view.rs similarity index 98% rename from server/src/actions/post_view.rs rename to server/src/db/post_view.rs index 18287651d..6dc6b6ef8 100644 --- a/server/src/actions/post_view.rs +++ b/server/src/db/post_view.rs @@ -85,7 +85,7 @@ impl PostView { page: Option, limit: Option, ) -> Result, Error> { - use actions::post_view::post_view::dsl::*; + use super::post_view::post_view::dsl::*; let (limit, offset) = limit_and_offset(page, limit); @@ -158,7 +158,7 @@ impl PostView { pub fn read(conn: &PgConnection, from_post_id: i32, my_user_id: Option) -> Result { - use actions::post_view::post_view::dsl::*; + use super::post_view::post_view::dsl::*; use diesel::prelude::*; let mut query = post_view.into_boxed(); @@ -181,9 +181,9 @@ impl PostView { mod tests { use {establish_connection, Crud, Likeable}; use super::*; - use actions::community::*; - use actions::user::*; - use actions::post::*; + use super::super::community::*; + use super::super::user::*; + use super::super::post::*; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/actions/user.rs b/server/src/db/user.rs similarity index 100% rename from server/src/actions/user.rs rename to server/src/db/user.rs diff --git a/server/src/actions/user_view.rs b/server/src/db/user_view.rs similarity index 89% rename from server/src/actions/user_view.rs rename to server/src/db/user_view.rs index a5187aee5..f17fd8c95 100644 --- a/server/src/actions/user_view.rs +++ b/server/src/db/user_view.rs @@ -35,20 +35,20 @@ pub struct UserView { impl UserView { pub fn read(conn: &PgConnection, from_user_id: i32) -> Result { - use actions::user_view::user_view::dsl::*; + use super::user_view::user_view::dsl::*; user_view.find(from_user_id) .first::(conn) } pub fn admins(conn: &PgConnection) -> Result, Error> { - use actions::user_view::user_view::dsl::*; + use super::user_view::user_view::dsl::*; user_view.filter(admin.eq(true)) .load::(conn) } pub fn banned(conn: &PgConnection) -> Result, Error> { - use actions::user_view::user_view::dsl::*; + use super::user_view::user_view::dsl::*; user_view.filter(banned.eq(true)) .load::(conn) } diff --git a/server/src/lib.rs b/server/src/lib.rs index 71b72ac32..a453633e2 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -17,8 +17,8 @@ pub extern crate regex; pub mod schema; pub mod apub; -pub mod actions; -pub mod websocket_server; +pub mod db; +pub mod websocket; use diesel::*; use diesel::pg::PgConnection; diff --git a/server/src/bin/main.rs b/server/src/main.rs similarity index 96% rename from server/src/bin/main.rs rename to server/src/main.rs index c2fde341d..3e51785b3 100644 --- a/server/src/bin/main.rs +++ b/server/src/main.rs @@ -1,14 +1,13 @@ -extern crate server; +extern crate lemmy_server; #[macro_use] extern crate diesel_migrations; use std::time::{Instant, Duration}; use std::env; -use server::actix::*; -use server::actix_web::server::HttpServer; -use server::actix_web::{ws, App, Error, HttpRequest, HttpResponse, fs::NamedFile, fs}; - -use server::websocket_server::server::*; -use server::establish_connection; +use lemmy_server::actix::*; +use lemmy_server::actix_web::server::HttpServer; +use lemmy_server::actix_web::{ws, App, Error, HttpRequest, HttpResponse, fs::NamedFile, fs}; +use lemmy_server::websocket::server::*; +use lemmy_server::establish_connection; embed_migrations!(); diff --git a/server/src/websocket_server/mod.rs b/server/src/websocket/mod.rs similarity index 100% rename from server/src/websocket_server/mod.rs rename to server/src/websocket/mod.rs diff --git a/server/src/websocket_server/server.rs b/server/src/websocket/server.rs similarity index 99% rename from server/src/websocket_server/server.rs rename to server/src/websocket/server.rs index 82c4007d7..a530b9a65 100644 --- a/server/src/websocket_server/server.rs +++ b/server/src/websocket/server.rs @@ -14,17 +14,17 @@ use failure::Error; use std::time::{SystemTime}; use {Crud, Joinable, Likeable, Followable, Bannable, Saveable, establish_connection, naive_now, naive_from_unix, SortType, SearchType, has_slurs, remove_slurs, Settings}; -use actions::community::*; -use actions::user::*; -use actions::post::*; -use actions::comment::*; -use actions::post_view::*; -use actions::comment_view::*; -use actions::category::*; -use actions::community_view::*; -use actions::user_view::*; -use actions::moderator_views::*; -use actions::moderator::*; +use db::community::*; +use db::user::*; +use db::post::*; +use db::comment::*; +use db::post_view::*; +use db::comment_view::*; +use db::category::*; +use db::community_view::*; +use db::user_view::*; +use db::moderator_views::*; +use db::moderator::*; const RATE_LIMIT_MESSAGES: i32 = 30; const RATE_LIMIT_PER_SECOND: i32 = 60; From 779e5964fadc51fd59de1ca53d9a65e7c4f367ad Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 4 May 2019 08:53:29 -0700 Subject: [PATCH 2/5] More reorg --- server/src/main.rs | 8 +++++++- server/src/websocket/server.rs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/server/src/main.rs b/server/src/main.rs index 3e51785b3..add17cfd6 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -29,7 +29,13 @@ fn chat_route(req: &HttpRequest) -> Result Date: Sat, 4 May 2019 22:20:38 -0700 Subject: [PATCH 3/5] Mostly done with reorg. --- server/src/api/comment.rs | 316 ++++ server/src/api/community.rs | 559 ++++++ server/src/api/mod.rs | 60 + server/src/api/post.rs | 469 +++++ server/src/api/site.rs | 336 ++++ server/src/api/user.rs | 503 ++++++ server/src/db/category.rs | 13 +- server/src/db/comment.rs | 6 +- server/src/db/comment_view.rs | 7 +- server/src/db/community.rs | 6 +- server/src/db/community_view.rs | 6 +- server/src/db/mod.rs | 72 + server/src/db/moderator.rs | 6 +- server/src/db/moderator_views.rs | 6 +- server/src/db/post.rs | 6 +- server/src/db/post_view.rs | 7 +- server/src/db/user.rs | 7 +- server/src/db/user_view.rs | 5 +- server/src/lib.rs | 83 +- server/src/websocket/server.rs | 2812 +++--------------------------- 20 files changed, 2535 insertions(+), 2750 deletions(-) create mode 100644 server/src/api/comment.rs create mode 100644 server/src/api/community.rs create mode 100644 server/src/api/mod.rs create mode 100644 server/src/api/post.rs create mode 100644 server/src/api/site.rs create mode 100644 server/src/api/user.rs diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs new file mode 100644 index 000000000..36a44b363 --- /dev/null +++ b/server/src/api/comment.rs @@ -0,0 +1,316 @@ +use super::*; + +#[derive(Serialize, Deserialize)] +pub struct CreateComment { + content: String, + parent_id: Option, + edit_id: Option, + pub post_id: i32, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct EditComment { + content: String, + parent_id: Option, + edit_id: i32, + creator_id: i32, + pub post_id: i32, + removed: Option, + deleted: Option, + reason: Option, + read: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct SaveComment { + comment_id: i32, + save: bool, + auth: String +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct CommentResponse { + op: String, + pub comment: CommentView +} + +#[derive(Serialize, Deserialize)] +pub struct CreateCommentLike { + comment_id: i32, + pub post_id: i32, + score: i16, + auth: String +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: CreateComment = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // 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, "You have been banned from this community"))? + } + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + let content_slurs_removed = remove_slurs(&data.content.to_owned()); + + let comment_form = CommentForm { + content: content_slurs_removed, + parent_id: data.parent_id.to_owned(), + post_id: data.post_id, + creator_id: user_id, + removed: None, + deleted: None, + read: None, + updated: None + }; + + let inserted_comment = match Comment::create(&conn, &comment_form) { + Ok(comment) => comment, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't create Comment"))? + } + }; + + // You like your own comment by default + let like_form = CommentLikeForm { + comment_id: inserted_comment.id, + post_id: data.post_id, + user_id: user_id, + score: 1 + }; + + let _inserted_like = match CommentLike::like(&conn, &like_form) { + Ok(like) => like, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't like comment."))? + } + }; + + let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?; + + Ok( + CommentResponse { + op: self.op.to_string(), + comment: comment_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: EditComment = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let orig_comment = CommentView::read(&conn, data.edit_id, None)?; + + // You are allowed to mark the comment as read even if you're banned. + if data.read.is_none() { + + // Verify its the creator or a mod, or an admin + let mut editors: Vec = vec![data.creator_id]; + editors.append( + &mut CommunityModeratorView::for_community(&conn, orig_comment.community_id) + ? + .into_iter() + .map(|m| m.user_id) + .collect() + ); + editors.append( + &mut UserView::admins(&conn) + ? + .into_iter() + .map(|a| a.id) + .collect() + ); + + if !editors.contains(&user_id) { + return Err(APIError::err(self.op, "Not allowed to edit comment."))? + } + + // Check for a community ban + if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() { + return Err(APIError::err(self.op, "You have been banned from this community"))? + } + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + } + + let content_slurs_removed = remove_slurs(&data.content.to_owned()); + + let comment_form = CommentForm { + content: content_slurs_removed, + parent_id: data.parent_id, + post_id: data.post_id, + creator_id: data.creator_id, + removed: data.removed.to_owned(), + deleted: data.deleted.to_owned(), + read: data.read.to_owned(), + updated: if data.read.is_some() { orig_comment.updated } else {Some(naive_now())} + }; + + let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) { + Ok(comment) => comment, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update Comment"))? + } + }; + + // Mod tables + if let Some(removed) = data.removed.to_owned() { + let form = ModRemoveCommentForm { + mod_user_id: user_id, + comment_id: data.edit_id, + removed: Some(removed), + reason: data.reason.to_owned(), + }; + ModRemoveComment::create(&conn, &form)?; + } + + + let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?; + + Ok( + CommentResponse { + op: self.op.to_string(), + comment: comment_view + } + ) + + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: SaveComment = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let comment_saved_form = CommentSavedForm { + comment_id: data.comment_id, + user_id: user_id, + }; + + if data.save { + match CommentSaved::save(&conn, &comment_saved_form) { + Ok(comment) => comment, + Err(_e) => { + return Err(APIError::err(self.op, "Couldnt do comment save"))? + } + }; + } else { + match CommentSaved::unsave(&conn, &comment_saved_form) { + Ok(comment) => comment, + Err(_e) => { + return Err(APIError::err(self.op, "Couldnt do comment save"))? + } + }; + } + + let comment_view = CommentView::read(&conn, data.comment_id, Some(user_id))?; + + Ok( + CommentResponse { + op: self.op.to_string(), + comment: comment_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: CreateCommentLike = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // 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, "You have been banned from this community"))? + } + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + let like_form = CommentLikeForm { + comment_id: data.comment_id, + post_id: data.post_id, + user_id: user_id, + score: data.score + }; + + // Remove any likes first + CommentLike::remove(&conn, &like_form)?; + + // Only add the like if the score isnt 0 + if &like_form.score != &0 { + let _inserted_like = match CommentLike::like(&conn, &like_form) { + Ok(like) => like, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't like comment."))? + } + }; + } + + // Have to refetch the comment to get the current state + 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 new file mode 100644 index 000000000..5059b17f0 --- /dev/null +++ b/server/src/api/community.rs @@ -0,0 +1,559 @@ +use super::*; +use std::str::FromStr; + +#[derive(Serialize, Deserialize)] +pub struct GetCommunity { + id: Option, + name: Option, + auth: Option +} + +#[derive(Serialize, Deserialize)] +pub struct GetCommunityResponse { + op: String, + community: CommunityView, + moderators: Vec, + admins: Vec, +} + + +#[derive(Serialize, Deserialize)] +pub struct CreateCommunity { + name: String, + title: String, + description: Option, + category_id: i32 , + auth: String +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct CommunityResponse { + op: String, + pub community: CommunityView +} + +#[derive(Serialize, Deserialize)] +pub struct ListCommunities { + sort: String, + page: Option, + limit: Option, + auth: Option +} + +#[derive(Serialize, Deserialize)] +pub struct ListCommunitiesResponse { + op: String, + communities: Vec +} + +#[derive(Serialize, Deserialize)] +pub struct BanFromCommunity { + pub community_id: i32, + user_id: i32, + ban: bool, + reason: Option, + expires: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct BanFromCommunityResponse { + op: String, + user: UserView, + banned: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct AddModToCommunity { + pub community_id: i32, + user_id: i32, + added: bool, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct AddModToCommunityResponse { + op: String, + moderators: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct EditCommunity { + pub edit_id: i32, + name: String, + title: String, + description: Option, + category_id: i32, + removed: Option, + deleted: Option, + reason: Option, + expires: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct FollowCommunity { + community_id: i32, + follow: bool, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct GetFollowedCommunities { + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct GetFollowedCommunitiesResponse { + op: String, + communities: Vec +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetCommunity = self.data; + let conn = establish_connection(); + + let user_id: Option = match &data.auth { + Some(auth) => { + match Claims::decode(&auth) { + Ok(claims) => { + let user_id = claims.claims.id; + Some(user_id) + } + Err(_e) => None + } + } + None => None + }; + + let community_id = match data.id { + Some(id) => id, + None => Community::read_from_name(&conn, data.name.to_owned().unwrap_or("main".to_string()))?.id + }; + + let community_view = match CommunityView::read(&conn, community_id, user_id) { + Ok(community) => community, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't find Community"))? + } + }; + + let moderators = match CommunityModeratorView::for_community(&conn, community_id) { + Ok(moderators) => moderators, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't find Community"))? + } + }; + + let admins = UserView::admins(&conn)?; + + // Return the jwt + Ok( + GetCommunityResponse { + op: self.op.to_string(), + community: community_view, + moderators: moderators, + admins: admins, + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: CreateCommunity = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + 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"))? + } + + let user_id = claims.id; + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + // When you create a community, make sure the user becomes a moderator and a follower + let community_form = CommunityForm { + name: data.name.to_owned(), + title: data.title.to_owned(), + description: data.description.to_owned(), + category_id: data.category_id, + creator_id: user_id, + removed: None, + deleted: None, + updated: None, + }; + + let inserted_community = match Community::create(&conn, &community_form) { + Ok(community) => community, + Err(_e) => { + return Err(APIError::err(self.op, "Community already exists."))? + } + }; + + let community_moderator_form = CommunityModeratorForm { + community_id: inserted_community.id, + user_id: user_id + }; + + 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."))? + } + }; + + let community_follower_form = CommunityFollowerForm { + community_id: inserted_community.id, + user_id: user_id + }; + + 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."))? + } + }; + + let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?; + + Ok( + CommunityResponse { + op: self.op.to_string(), + community: community_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: EditCommunity = self.data; + + if has_slurs(&data.name) || has_slurs(&data.title) { + return Err(APIError::err(self.op, "No slurs"))? + } + + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + // Verify its a mod + let mut editors: Vec = Vec::new(); + editors.append( + &mut CommunityModeratorView::for_community(&conn, data.edit_id) + ? + .into_iter() + .map(|m| m.user_id) + .collect() + ); + editors.append( + &mut UserView::admins(&conn) + ? + .into_iter() + .map(|a| a.id) + .collect() + ); + if !editors.contains(&user_id) { + return Err(APIError::err(self.op, "Not allowed to edit community"))? + } + + let community_form = CommunityForm { + name: data.name.to_owned(), + title: data.title.to_owned(), + description: data.description.to_owned(), + category_id: data.category_id.to_owned(), + creator_id: user_id, + removed: data.removed.to_owned(), + deleted: data.deleted.to_owned(), + updated: Some(naive_now()) + }; + + let _updated_community = match Community::update(&conn, data.edit_id, &community_form) { + Ok(community) => community, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update Community"))? + } + }; + + // Mod tables + if let Some(removed) = data.removed.to_owned() { + let expires = match data.expires { + Some(time) => Some(naive_from_unix(time)), + None => None + }; + let form = ModRemoveCommunityForm { + mod_user_id: user_id, + community_id: data.edit_id, + removed: Some(removed), + reason: data.reason.to_owned(), + expires: expires + }; + ModRemoveCommunity::create(&conn, &form)?; + } + + let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?; + + Ok( + CommunityResponse { + op: self.op.to_string(), + community: community_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: ListCommunities = self.data; + let conn = establish_connection(); + + let user_id: Option = match &data.auth { + Some(auth) => { + match Claims::decode(&auth) { + Ok(claims) => { + let user_id = claims.claims.id; + Some(user_id) + } + Err(_e) => None + } + } + None => None + }; + + let sort = SortType::from_str(&data.sort)?; + + let communities: Vec = CommunityView::list(&conn, user_id, sort, data.page, data.limit)?; + + // Return the jwt + Ok( + ListCommunitiesResponse { + op: self.op.to_string(), + communities: communities + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: FollowCommunity = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let community_follower_form = CommunityFollowerForm { + community_id: data.community_id, + user_id: user_id + }; + + 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."))? + } + }; + } else { + match CommunityFollower::ignore(&conn, &community_follower_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "Community follower already exists."))? + } + }; + } + + let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?; + + Ok( + CommunityResponse { + op: self.op.to_string(), + community: community_view + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetFollowedCommunities = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let communities: Vec = match CommunityFollowerView::for_user(&conn, user_id) { + Ok(communities) => communities, + Err(_e) => { + return Err(APIError::err(self.op, "System error, try logging out and back in."))? + } + }; + + // Return the jwt + Ok( + GetFollowedCommunitiesResponse { + op: self.op.to_string(), + communities: communities + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: BanFromCommunity = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let community_user_ban_form = CommunityUserBanForm { + community_id: data.community_id, + user_id: data.user_id, + }; + + if data.ban { + match CommunityUserBan::ban(&conn, &community_user_ban_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "Community user ban already exists"))? + } + }; + } else { + match CommunityUserBan::unban(&conn, &community_user_ban_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "Community user ban already exists"))? + } + }; + } + + // Mod tables + let expires = match data.expires { + Some(time) => Some(naive_from_unix(time)), + None => None + }; + + let form = ModBanFromCommunityForm { + mod_user_id: user_id, + other_user_id: data.user_id, + community_id: data.community_id, + reason: data.reason.to_owned(), + banned: Some(data.ban), + expires: expires, + }; + ModBanFromCommunity::create(&conn, &form)?; + + let user_view = UserView::read(&conn, data.user_id)?; + + Ok( + BanFromCommunityResponse { + op: self.op.to_string(), + user: user_view, + banned: data.ban + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: AddModToCommunity = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let community_moderator_form = CommunityModeratorForm { + community_id: data.community_id, + user_id: data.user_id + }; + + 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."))? + } + }; + } else { + match CommunityModerator::leave(&conn, &community_moderator_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "Community moderator already exists."))? + } + }; + } + + // Mod tables + let form = ModAddCommunityForm { + mod_user_id: user_id, + other_user_id: data.user_id, + community_id: data.community_id, + removed: Some(!data.added), + }; + ModAddCommunity::create(&conn, &form)?; + + let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?; + + Ok( + AddModToCommunityResponse { + op: self.op.to_string(), + moderators: moderators, + } + ) + } +} diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs new file mode 100644 index 000000000..4bf6f7fee --- /dev/null +++ b/server/src/api/mod.rs @@ -0,0 +1,60 @@ +use serde::{Deserialize, Serialize}; +use failure::Error; +use db::*; +use db::community::*; +use db::user::*; +use db::post::*; +use db::comment::*; +use db::post_view::*; +use db::comment_view::*; +use db::category::*; +use db::community_view::*; +use db::user_view::*; +use db::moderator_views::*; +use db::moderator::*; +use {has_slurs, remove_slurs, Settings, naive_now, naive_from_unix}; + +pub mod user; +pub mod community; +pub mod post; +pub mod comment; +pub mod site; + +#[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, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead +} + +#[derive(Fail, Debug)] +#[fail(display = "{{\"op\":\"{}\", \"error\":\"{}\"}}", op, message)] +pub struct APIError { + op: String, + message: String, +} + +impl APIError { + pub fn err(op: UserOperation, 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: op, + data: data + } + } +} + +pub trait Perform { + fn perform(&self) -> Result where T: Sized; +} diff --git a/server/src/api/post.rs b/server/src/api/post.rs new file mode 100644 index 000000000..e7bec69e8 --- /dev/null +++ b/server/src/api/post.rs @@ -0,0 +1,469 @@ +use super::*; +use std::str::FromStr; + +#[derive(Serialize, Deserialize)] +pub struct CreatePost { + name: String, + url: Option, + body: Option, + community_id: i32, + auth: String +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct PostResponse { + op: String, + pub post: PostView +} + + +#[derive(Serialize, Deserialize)] +pub struct GetPost { + pub id: i32, + auth: Option +} + +#[derive(Serialize, Deserialize)] +pub struct GetPostResponse { + op: String, + post: PostView, + comments: Vec, + community: CommunityView, + moderators: Vec, + admins: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct GetPosts { + type_: String, + sort: String, + page: Option, + limit: Option, + community_id: Option, + auth: Option +} + +#[derive(Serialize, Deserialize)] +pub struct GetPostsResponse { + op: String, + posts: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct CreatePostLike { + post_id: i32, + score: i16, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct CreatePostLikeResponse { + op: String, + post: PostView +} + + +#[derive(Serialize, Deserialize)] +pub struct EditPost { + pub edit_id: i32, + creator_id: i32, + community_id: i32, + name: String, + url: Option, + body: Option, + removed: Option, + deleted: Option, + locked: Option, + reason: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct SavePost { + post_id: i32, + save: bool, + auth: String +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: CreatePost = self.data; + let conn = establish_connection(); + + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + if has_slurs(&data.name) || + (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) { + return Err(APIError::err(self.op, "No slurs"))? + } + + 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, "You have been banned from this community"))? + } + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + let post_form = PostForm { + name: data.name.to_owned(), + url: data.url.to_owned(), + body: data.body.to_owned(), + community_id: data.community_id, + creator_id: user_id, + removed: None, + deleted: None, + locked: None, + updated: None + }; + + let inserted_post = match Post::create(&conn, &post_form) { + Ok(post) => post, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't create Post"))? + } + }; + + // They like their own post by default + let like_form = PostLikeForm { + post_id: inserted_post.id, + user_id: user_id, + score: 1 + }; + + // 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, "Couldn't like post."))? + } + }; + + // 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, "Couldn't find Post"))? + } + }; + + Ok( + PostResponse { + op: self.op.to_string(), + post: post_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetPost = self.data; + let conn = establish_connection(); + + let user_id: Option = match &data.auth { + Some(auth) => { + match Claims::decode(&auth) { + Ok(claims) => { + let user_id = claims.claims.id; + Some(user_id) + } + Err(_e) => None + } + } + None => None + }; + + let post_view = match PostView::read(&conn, data.id, user_id) { + Ok(post) => post, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't find Post"))? + } + }; + + let comments = CommentView::list(&conn, &SortType::New, Some(data.id), None, None, user_id, false, None, Some(9999))?; + + let community = CommunityView::read(&conn, post_view.community_id, user_id)?; + + let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id)?; + + let admins = UserView::admins(&conn)?; + + // Return the jwt + Ok( + GetPostResponse { + op: self.op.to_string(), + post: post_view, + comments: comments, + community: community, + moderators: moderators, + admins: admins, + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetPosts = self.data; + let conn = establish_connection(); + + let user_id: Option = match &data.auth { + Some(auth) => { + match Claims::decode(&auth) { + Ok(claims) => { + let user_id = claims.claims.id; + Some(user_id) + } + Err(_e) => None + } + } + None => None + }; + + let type_ = PostListingType::from_str(&data.type_)?; + let sort = SortType::from_str(&data.sort)?; + + let posts = match PostView::list(&conn, + type_, + &sort, + data.community_id, + None, + None, + user_id, + false, + false, + data.page, + data.limit) { + Ok(posts) => posts, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't get posts"))? + } + }; + + // Return the jwt + Ok( + GetPostsResponse { + op: self.op.to_string(), + posts: posts + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: CreatePostLike = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // 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, "You have been banned from this community"))? + } + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + let like_form = PostLikeForm { + post_id: data.post_id, + user_id: user_id, + score: data.score + }; + + // Remove any likes first + PostLike::remove(&conn, &like_form)?; + + // Only add the like if the score isnt 0 + if &like_form.score != &0 { + let _inserted_like = match PostLike::like(&conn, &like_form) { + Ok(like) => like, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't like post."))? + } + }; + } + + let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) { + Ok(post) => post, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't find Post"))? + } + }; + + // just output the score + Ok( + CreatePostLikeResponse { + op: self.op.to_string(), + post: post_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> 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"))? + } + + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // Verify its the creator or a mod or admin + let mut editors: Vec = vec![data.creator_id]; + editors.append( + &mut CommunityModeratorView::for_community(&conn, data.community_id) + ? + .into_iter() + .map(|m| m.user_id) + .collect() + ); + editors.append( + &mut UserView::admins(&conn) + ? + .into_iter() + .map(|a| a.id) + .collect() + ); + if !editors.contains(&user_id) { + return Err(APIError::err(self.op, "Not allowed to edit post."))? + } + + // Check for a community ban + if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { + return Err(APIError::err(self.op, "You have been banned from this community"))? + } + + // Check for a site ban + if UserView::read(&conn, user_id)?.banned { + return Err(APIError::err(self.op, "You have been banned from the site"))? + } + + let post_form = PostForm { + name: data.name.to_owned(), + url: data.url.to_owned(), + body: data.body.to_owned(), + creator_id: data.creator_id.to_owned(), + community_id: data.community_id, + removed: data.removed.to_owned(), + deleted: data.deleted.to_owned(), + locked: data.locked.to_owned(), + updated: Some(naive_now()) + }; + + let _updated_post = match Post::update(&conn, data.edit_id, &post_form) { + Ok(post) => post, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update Post"))? + } + }; + + // Mod tables + if let Some(removed) = data.removed.to_owned() { + let form = ModRemovePostForm { + mod_user_id: user_id, + post_id: data.edit_id, + removed: Some(removed), + reason: data.reason.to_owned(), + }; + ModRemovePost::create(&conn, &form)?; + } + + if let Some(locked) = data.locked.to_owned() { + let form = ModLockPostForm { + mod_user_id: user_id, + post_id: data.edit_id, + locked: Some(locked), + }; + ModLockPost::create(&conn, &form)?; + } + + let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?; + + Ok( + PostResponse { + op: self.op.to_string(), + post: post_view + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: SavePost = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let post_saved_form = PostSavedForm { + post_id: data.post_id, + user_id: user_id, + }; + + if data.save { + match PostSaved::save(&conn, &post_saved_form) { + Ok(post) => post, + Err(_e) => { + return Err(APIError::err(self.op, "Couldnt do post save"))? + } + }; + } else { + match PostSaved::unsave(&conn, &post_saved_form) { + Ok(post) => post, + Err(_e) => { + return Err(APIError::err(self.op, "Couldnt do post save"))? + } + }; + } + + let post_view = PostView::read(&conn, data.post_id, Some(user_id))?; + + Ok( + PostResponse { + op: self.op.to_string(), + post: post_view + } + ) + } +} diff --git a/server/src/api/site.rs b/server/src/api/site.rs new file mode 100644 index 000000000..3140788da --- /dev/null +++ b/server/src/api/site.rs @@ -0,0 +1,336 @@ +use super::*; +use std::str::FromStr; + +#[derive(Serialize, Deserialize)] +pub struct ListCategories; + +#[derive(Serialize, Deserialize)] +pub struct ListCategoriesResponse { + op: String, + categories: Vec +} + +#[derive(Serialize, Deserialize)] +pub struct Search { + q: String, + type_: String, + community_id: Option, + sort: String, + page: Option, + limit: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct SearchResponse { + op: String, + comments: Vec, + posts: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct GetModlog { + mod_user_id: Option, + community_id: Option, + page: Option, + limit: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct GetModlogResponse { + 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, +} + + +#[derive(Serialize, Deserialize)] +pub struct CreateSite { + name: String, + description: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct EditSite { + name: String, + description: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +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, +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: ListCategories = self.data; + let conn = establish_connection(); + + let categories: Vec = Category::list_all(&conn)?; + + // Return the jwt + Ok( + ListCategoriesResponse { + op: self.op.to_string(), + categories: categories + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetModlog = self.data; + let conn = establish_connection(); + + let removed_posts = ModRemovePostView::list(&conn, data.community_id, data.mod_user_id, data.page, data.limit)?; + let locked_posts = ModLockPostView::list(&conn, data.community_id, data.mod_user_id, data.page, data.limit)?; + let removed_comments = ModRemoveCommentView::list(&conn, data.community_id, data.mod_user_id, data.page, data.limit)?; + let banned_from_community = ModBanFromCommunityView::list(&conn, data.community_id, data.mod_user_id, data.page, data.limit)?; + let added_to_community = ModAddCommunityView::list(&conn, data.community_id, data.mod_user_id, data.page, data.limit)?; + + // These arrays are only for the full modlog, when a community isn't given + let mut removed_communities = Vec::new(); + let mut banned = Vec::new(); + let mut added = Vec::new(); + + if data.community_id.is_none() { + removed_communities = ModRemoveCommunityView::list(&conn, data.mod_user_id, data.page, data.limit)?; + banned = ModBanView::list(&conn, data.mod_user_id, data.page, data.limit)?; + added = ModAddView::list(&conn, data.mod_user_id, data.page, data.limit)?; + } + + // Return the jwt + Ok( + GetModlogResponse { + op: self.op.to_string(), + removed_posts: removed_posts, + locked_posts: locked_posts, + removed_comments: removed_comments, + removed_communities: removed_communities, + banned_from_community: banned_from_community, + banned: banned, + added_to_community: added_to_community, + added: added, + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: CreateSite = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + if has_slurs(&data.name) || + (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { + return Err(APIError::err(self.op, "No slurs"))? + } + + 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."))? + } + + let site_form = SiteForm { + name: data.name.to_owned(), + description: data.description.to_owned(), + creator_id: user_id, + updated: None, + }; + + match Site::create(&conn, &site_form) { + Ok(site) => site, + Err(_e) => { + return Err(APIError::err(self.op, "Site exists already"))? + } + }; + + let site_view = SiteView::read(&conn)?; + + Ok( + SiteResponse { + op: self.op.to_string(), + site: site_view, + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: EditSite = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + if has_slurs(&data.name) || + (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { + return Err(APIError::err(self.op, "No slurs"))? + } + + let user_id = claims.id; + + // Make sure user is an admin + if UserView::read(&conn, user_id)?.admin == false { + return Err(APIError::err(self.op, "Not an admin."))? + } + + let found_site = Site::read(&conn, 1)?; + + let site_form = SiteForm { + name: data.name.to_owned(), + description: data.description.to_owned(), + creator_id: found_site.creator_id, + updated: Some(naive_now()), + }; + + match Site::update(&conn, 1, &site_form) { + Ok(site) => site, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update site."))? + } + }; + + let site_view = SiteView::read(&conn)?; + + Ok( + SiteResponse { + op: self.op.to_string(), + site: site_view, + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetSite = self.data; + let conn = establish_connection(); + + // It can return a null site in order to redirect + let site_view = match Site::read(&conn, 1) { + Ok(_site) => Some(SiteView::read(&conn)?), + Err(_e) => None + }; + + let admins = UserView::admins(&conn)?; + let banned = UserView::banned(&conn)?; + + Ok( + GetSiteResponse { + op: self.op.to_string(), + site: site_view, + admins: admins, + banned: banned, + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: Search = self.data; + let conn = establish_connection(); + + let sort = SortType::from_str(&data.sort)?; + let type_ = SearchType::from_str(&data.type_)?; + + let mut posts = Vec::new(); + let mut comments = Vec::new(); + + match type_ { + SearchType::Posts => { + posts = PostView::list(&conn, + PostListingType::All, + &sort, + data.community_id, + None, + Some(data.q.to_owned()), + None, + false, + false, + data.page, + data.limit)?; + }, + SearchType::Comments => { + comments = CommentView::list(&conn, + &sort, + None, + None, + Some(data.q.to_owned()), + None, + false, + data.page, + data.limit)?; + }, + SearchType::Both => { + posts = PostView::list(&conn, + PostListingType::All, + &sort, + data.community_id, + None, + Some(data.q.to_owned()), + None, + false, + false, + data.page, + data.limit)?; + comments = CommentView::list(&conn, + &sort, + None, + None, + Some(data.q.to_owned()), + None, + false, + data.page, + data.limit)?; + } + }; + + + // Return the jwt + Ok( + SearchResponse { + op: self.op.to_string(), + comments: comments, + posts: posts, + } + ) + } +} diff --git a/server/src/api/user.rs b/server/src/api/user.rs new file mode 100644 index 000000000..82ec11cc9 --- /dev/null +++ b/server/src/api/user.rs @@ -0,0 +1,503 @@ +use super::*; +use std::str::FromStr; +use bcrypt::{verify}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Login { + username_or_email: String, + password: String +} + +#[derive(Serialize, Deserialize)] +pub struct Register { + username: String, + email: Option, + password: String, + password_verify: String, + admin: bool, + spam_timeri: i64, +} + +#[derive(Serialize, Deserialize)] +pub struct LoginResponse { + op: String, + jwt: String +} + +#[derive(Serialize, Deserialize)] +pub struct GetUserDetails { + user_id: Option, + username: Option, + sort: String, + page: Option, + limit: Option, + community_id: Option, + saved_only: bool, + auth: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct GetUserDetailsResponse { + op: String, + user: UserView, + follows: Vec, + moderates: Vec, + comments: Vec, + posts: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct GetRepliesResponse { + op: String, + replies: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct MarkAllAsRead { + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct AddAdmin { + user_id: i32, + added: bool, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct AddAdminResponse { + op: String, + admins: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct BanUser { + user_id: i32, + ban: bool, + reason: Option, + expires: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct BanUserResponse { + op: String, + user: UserView, + banned: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct GetReplies { + sort: String, + page: Option, + limit: Option, + unread_only: bool, + auth: String +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: Login = self.data; + let conn = establish_connection(); + + // 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, "Couldn't find that username or email"))? + }; + + // 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"))? + } + + // Return the jwt + Ok( + LoginResponse { + op: self.op.to_string(), + jwt: user.jwt() + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: Register = self.data; + let conn = establish_connection(); + + // Make sure passwords match + if &data.password != &data.password_verify { + return Err(APIError::err(self.op, "Passwords do not match."))? + } + + if data.spam_timeri < 1142 { + return Err(APIError::err(self.op, "Too fast"))? + } + + if has_slurs(&data.username) { + return Err(APIError::err(self.op, "No slurs"))? + } + + // Make sure there are no admins + if data.admin && UserView::admins(&conn)?.len() > 0 { + return Err(APIError::err(self.op, "Sorry, there's already an admin."))? + } + + // Register the new user + let user_form = UserForm { + name: data.username.to_owned(), + fedi_name: Settings::get().hostname.into(), + email: data.email.to_owned(), + password_encrypted: data.password.to_owned(), + preferred_username: None, + updated: None, + admin: data.admin, + banned: false, + }; + + // Create the user + let inserted_user = match User_::register(&conn, &user_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "User already exists."))? + } + }; + + // Sign them up for main community no matter what + let community_follower_form = CommunityFollowerForm { + community_id: 1, + user_id: inserted_user.id, + }; + + 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."))? + } + }; + + // If its an admin, add them as a mod and follower to main + if data.admin { + let community_moderator_form = CommunityModeratorForm { + community_id: 1, + user_id: inserted_user.id, + }; + + 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."))? + } + }; + + } + + // Return the jwt + Ok( + LoginResponse { + op: self.op.to_string(), + jwt: inserted_user.jwt() + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetUserDetails = self.data; + let conn = establish_connection(); + + let user_id: Option = match &data.auth { + Some(auth) => { + match Claims::decode(&auth) { + Ok(claims) => { + let user_id = claims.claims.id; + Some(user_id) + } + Err(_e) => None + } + } + None => None + }; + + //TODO add save + let sort = SortType::from_str(&data.sort)?; + + let user_details_id = match data.user_id { + Some(id) => id, + None => User_::read_from_name(&conn, data.username.to_owned().unwrap_or("admin".to_string()))?.id + }; + + let user_view = UserView::read(&conn, user_details_id)?; + + // If its saved only, you don't care what creator it was + let posts = if data.saved_only { + PostView::list(&conn, + PostListingType::All, + &sort, + data.community_id, + None, + None, + Some(user_details_id), + data.saved_only, + false, + data.page, + data.limit)? + } else { + PostView::list(&conn, + PostListingType::All, + &sort, + data.community_id, + Some(user_details_id), + None, + user_id, + data.saved_only, + false, + data.page, + data.limit)? + }; + let comments = if data.saved_only { + CommentView::list(&conn, + &sort, + None, + None, + None, + Some(user_details_id), + data.saved_only, + data.page, + data.limit)? + } else { + CommentView::list(&conn, + &sort, + None, + Some(user_details_id), + None, + user_id, + data.saved_only, + data.page, + data.limit)? + }; + + let follows = CommunityFollowerView::for_user(&conn, user_details_id)?; + let moderates = CommunityModeratorView::for_user(&conn, user_details_id)?; + + // Return the jwt + Ok( + GetUserDetailsResponse { + op: self.op.to_string(), + user: user_view, + follows: follows, + moderates: moderates, + comments: comments, + posts: posts, + } + ) + } +} + + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: AddAdmin = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // Make sure user is an admin + if UserView::read(&conn, user_id)?.admin == false { + return Err(APIError::err(self.op, "Not an admin."))? + } + + let read_user = User_::read(&conn, data.user_id)?; + + let user_form = UserForm { + name: read_user.name, + fedi_name: read_user.fedi_name, + email: read_user.email, + password_encrypted: read_user.password_encrypted, + preferred_username: read_user.preferred_username, + updated: Some(naive_now()), + admin: data.added, + banned: read_user.banned, + }; + + match User_::update(&conn, data.user_id, &user_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update user"))? + } + }; + + // Mod tables + let form = ModAddForm { + mod_user_id: user_id, + other_user_id: data.user_id, + removed: Some(!data.added), + }; + + ModAdd::create(&conn, &form)?; + + let admins = UserView::admins(&conn)?; + + Ok( + AddAdminResponse { + op: self.op.to_string(), + admins: admins, + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: BanUser = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + // Make sure user is an admin + if UserView::read(&conn, user_id)?.admin == false { + return Err(APIError::err(self.op, "Not an admin."))? + } + + let read_user = User_::read(&conn, data.user_id)?; + + let user_form = UserForm { + name: read_user.name, + fedi_name: read_user.fedi_name, + email: read_user.email, + password_encrypted: read_user.password_encrypted, + preferred_username: read_user.preferred_username, + updated: Some(naive_now()), + admin: read_user.admin, + banned: data.ban, + }; + + match User_::update(&conn, data.user_id, &user_form) { + Ok(user) => user, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update user"))? + } + }; + + // Mod tables + let expires = match data.expires { + Some(time) => Some(naive_from_unix(time)), + None => None + }; + + let form = ModBanForm { + mod_user_id: user_id, + other_user_id: data.user_id, + reason: data.reason.to_owned(), + banned: Some(data.ban), + expires: expires, + }; + + ModBan::create(&conn, &form)?; + + let user_view = UserView::read(&conn, data.user_id)?; + + Ok( + BanUserResponse { + op: self.op.to_string(), + user: user_view, + banned: data.ban + } + ) + + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: GetReplies = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let sort = SortType::from_str(&data.sort)?; + + let replies = ReplyView::get_replies(&conn, user_id, &sort, data.unread_only, data.page, data.limit)?; + + // Return the jwt + Ok( + GetRepliesResponse { + op: self.op.to_string(), + replies: replies, + } + ) + } +} + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: MarkAllAsRead = self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return Err(APIError::err(self.op, "Not logged in."))? + } + }; + + let user_id = claims.id; + + let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?; + + for reply in &replies { + let comment_form = CommentForm { + content: reply.to_owned().content, + parent_id: reply.to_owned().parent_id, + post_id: reply.to_owned().post_id, + creator_id: reply.to_owned().creator_id, + removed: None, + deleted: None, + read: Some(true), + updated: reply.to_owned().updated + }; + + let _updated_comment = match Comment::update(&conn, reply.id, &comment_form) { + Ok(comment) => comment, + Err(_e) => { + return Err(APIError::err(self.op, "Couldn't update Comment"))? + } + }; + } + + let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?; + + Ok( + GetRepliesResponse { + op: self.op.to_string(), + replies: replies, + } + ) + } +} diff --git a/server/src/db/category.rs b/server/src/db/category.rs index 8491f1ec4..7835fedd8 100644 --- a/server/src/db/category.rs +++ b/server/src/db/category.rs @@ -1,9 +1,6 @@ -extern crate diesel; use schema::{category}; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {Crud}; +use schema::category::dsl::*; +use super::*; #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] #[table_name="category"] @@ -20,26 +17,22 @@ pub struct CategoryForm { impl Crud for Category { fn read(conn: &PgConnection, category_id: i32) -> Result { - use schema::category::dsl::*; category.find(category_id) .first::(conn) } fn delete(conn: &PgConnection, category_id: i32) -> Result { - use schema::category::dsl::*; diesel::delete(category.find(category_id)) .execute(conn) } fn create(conn: &PgConnection, new_category: &CategoryForm) -> Result { - use schema::category::dsl::*; insert_into(category) .values(new_category) .get_result::(conn) } fn update(conn: &PgConnection, category_id: i32, new_category: &CategoryForm) -> Result { - use schema::category::dsl::*; diesel::update(category.find(category_id)) .set(new_category) .get_result::(conn) @@ -48,7 +41,6 @@ impl Crud for Category { impl Category { pub fn list_all(conn: &PgConnection) -> Result, Error> { - use schema::category::dsl::*; category.load::(conn) } } @@ -57,7 +49,6 @@ impl Category { mod tests { use establish_connection; use super::*; - // use Crud; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs index d63837a09..4e57aaa42 100644 --- a/server/src/db/comment.rs +++ b/server/src/db/comment.rs @@ -1,9 +1,5 @@ -extern crate diesel; use schema::{comment, comment_like, comment_saved}; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {Crud, Likeable, Saveable}; +use super::*; use super::post::Post; // WITH RECURSIVE MyTree AS ( diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index 903c4839d..eb1d302f7 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -1,9 +1,4 @@ -extern crate diesel; -use diesel::*; -use diesel::result::Error; -use diesel::dsl::*; -use serde::{Deserialize, Serialize}; -use { SortType, limit_and_offset, fuzzy_search }; +use super::*; // The faked schema since diesel doesn't do views table! { diff --git a/server/src/db/community.rs b/server/src/db/community.rs index 2e75828fc..a2c300f29 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -1,9 +1,5 @@ -extern crate diesel; use schema::{community, community_moderator, community_follower, community_user_ban, site}; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {Crud, Followable, Joinable, Bannable}; +use super::*; #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] #[table_name="community"] diff --git a/server/src/db/community_view.rs b/server/src/db/community_view.rs index 5ec22f4e3..ec77cc8fe 100644 --- a/server/src/db/community_view.rs +++ b/server/src/db/community_view.rs @@ -1,8 +1,4 @@ -extern crate diesel; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {SortType, limit_and_offset}; +use super::*; table! { community_view (id) { diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs index ece1e885a..a3edb1b9c 100644 --- a/server/src/db/mod.rs +++ b/server/src/db/mod.rs @@ -1,3 +1,9 @@ +use diesel::*; +use diesel::dsl::*; +use diesel::result::Error; +use {Settings}; +use serde::{Deserialize, Serialize}; + pub mod user; pub mod community; pub mod post; @@ -9,3 +15,69 @@ pub mod community_view; pub mod user_view; pub mod moderator; pub mod moderator_views; + +pub trait Crud { + fn create(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn read(conn: &PgConnection, id: i32) -> Result where Self: Sized; + fn update(conn: &PgConnection, id: i32, form: &T) -> Result where Self: Sized; + fn delete(conn: &PgConnection, id: i32) -> Result where Self: Sized; +} + +pub trait Followable { + fn follow(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn ignore(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + +pub trait Joinable { + fn join(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn leave(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + +pub trait Likeable { + fn read(conn: &PgConnection, id: i32) -> Result, Error> where Self: Sized; + fn like(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn remove(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + +pub trait Bannable { + fn ban(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn unban(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + +pub trait Saveable { + fn save(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn unsave(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + +pub trait Readable { + fn mark_as_read(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn mark_as_unread(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + +pub fn establish_connection() -> PgConnection { + let db_url = Settings::get().db_url; + PgConnection::establish(&db_url) + .expect(&format!("Error connecting to {}", db_url)) +} + +#[derive(EnumString,ToString,Debug, Serialize, Deserialize)] +pub enum SortType { + Hot, New, TopDay, TopWeek, TopMonth, TopYear, TopAll +} + +#[derive(EnumString,ToString,Debug, Serialize, Deserialize)] +pub enum SearchType { + Both, Comments, Posts +} + +pub fn fuzzy_search(q: &str) -> String { + let replaced = q.replace(" ", "%"); + format!("%{}%", replaced) +} + +pub fn limit_and_offset(page: Option, limit: Option) -> (i64, i64) { + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + (limit, offset) +} diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs index 74068e0a6..c23cf9e64 100644 --- a/server/src/db/moderator.rs +++ b/server/src/db/moderator.rs @@ -1,9 +1,5 @@ -extern crate diesel; use schema::{mod_remove_post, mod_lock_post, mod_remove_comment, mod_remove_community, mod_ban_from_community, mod_ban, mod_add_community, mod_add}; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {Crud}; +use super::*; #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] #[table_name="mod_remove_post"] diff --git a/server/src/db/moderator_views.rs b/server/src/db/moderator_views.rs index 56881ef54..3c8e2e9a6 100644 --- a/server/src/db/moderator_views.rs +++ b/server/src/db/moderator_views.rs @@ -1,8 +1,4 @@ -extern crate diesel; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {limit_and_offset}; +use super::*; table! { mod_remove_post_view (id) { diff --git a/server/src/db/post.rs b/server/src/db/post.rs index 59a7a1d44..009543d3b 100644 --- a/server/src/db/post.rs +++ b/server/src/db/post.rs @@ -1,9 +1,5 @@ -extern crate diesel; use schema::{post, post_like, post_saved, post_read}; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; -use {Crud, Likeable, Saveable, Readable}; +use super::*; #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] #[table_name="post"] diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs index 6dc6b6ef8..f51eab10d 100644 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@ -1,9 +1,4 @@ -extern crate diesel; -use diesel::*; -use diesel::result::Error; -use diesel::dsl::*; -use serde::{Deserialize, Serialize}; -use { SortType, limit_and_offset, fuzzy_search }; +use super::*; #[derive(EnumString,ToString,Debug, Serialize, Deserialize)] pub enum PostListingType { diff --git a/server/src/db/user.rs b/server/src/db/user.rs index 9c9e0a52e..254030691 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -1,9 +1,7 @@ use schema::user_; -use diesel::*; -use diesel::result::Error; use schema::user_::dsl::*; -use serde::{Serialize, Deserialize}; -use {Crud,is_email_regex, Settings}; +use super::*; +use {Settings, is_email_regex}; use jsonwebtoken::{encode, decode, Header, Validation, TokenData}; use bcrypt::{DEFAULT_COST, hash}; @@ -38,6 +36,7 @@ pub struct UserForm { impl Crud for User_ { fn read(conn: &PgConnection, user_id: i32) -> Result { + use schema::user_::dsl::*; user_.find(user_id) .first::(conn) } diff --git a/server/src/db/user_view.rs b/server/src/db/user_view.rs index f17fd8c95..3d78ae1a0 100644 --- a/server/src/db/user_view.rs +++ b/server/src/db/user_view.rs @@ -1,7 +1,4 @@ -extern crate diesel; -use diesel::*; -use diesel::result::Error; -use serde::{Deserialize, Serialize}; +use super::*; table! { user_view (id) { diff --git a/server/src/lib.rs b/server/src/lib.rs index a453633e2..9d87d704d 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,5 +1,7 @@ -#[macro_use] -pub extern crate diesel; +#[macro_use] pub extern crate strum_macros; +#[macro_use] pub extern crate lazy_static; +#[macro_use] pub extern crate failure; +#[macro_use] pub extern crate diesel; pub extern crate dotenv; pub extern crate chrono; pub extern crate serde; @@ -11,68 +13,18 @@ pub extern crate strum; pub extern crate jsonwebtoken; pub extern crate bcrypt; pub extern crate regex; -#[macro_use] pub extern crate strum_macros; -#[macro_use] pub extern crate lazy_static; -#[macro_use] extern crate failure; pub mod schema; +pub mod api; pub mod apub; pub mod db; pub mod websocket; -use diesel::*; -use diesel::pg::PgConnection; -use diesel::result::Error; use dotenv::dotenv; use std::env; use regex::Regex; -use serde::{Deserialize, Serialize}; use chrono::{DateTime, NaiveDateTime, Utc}; -pub trait Crud { - fn create(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn read(conn: &PgConnection, id: i32) -> Result where Self: Sized; - fn update(conn: &PgConnection, id: i32, form: &T) -> Result where Self: Sized; - fn delete(conn: &PgConnection, id: i32) -> Result where Self: Sized; -} - -pub trait Followable { - fn follow(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn ignore(conn: &PgConnection, form: &T) -> Result where Self: Sized; -} - -pub trait Joinable { - fn join(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn leave(conn: &PgConnection, form: &T) -> Result where Self: Sized; -} - -pub trait Likeable { - fn read(conn: &PgConnection, id: i32) -> Result, Error> where Self: Sized; - fn like(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn remove(conn: &PgConnection, form: &T) -> Result where Self: Sized; -} - -pub trait Bannable { - fn ban(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn unban(conn: &PgConnection, form: &T) -> Result where Self: Sized; -} - -pub trait Saveable { - fn save(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn unsave(conn: &PgConnection, form: &T) -> Result where Self: Sized; -} - -pub trait Readable { - fn mark_as_read(conn: &PgConnection, form: &T) -> Result where Self: Sized; - fn mark_as_unread(conn: &PgConnection, form: &T) -> Result where Self: Sized; -} - -pub fn establish_connection() -> PgConnection { - let db_url = Settings::get().db_url; - PgConnection::establish(&db_url) - .expect(&format!("Error connecting to {}", db_url)) -} - pub struct Settings { db_url: String, hostname: String, @@ -94,16 +46,6 @@ impl Settings { } } -#[derive(EnumString,ToString,Debug, Serialize, Deserialize)] -pub enum SortType { - Hot, New, TopDay, TopWeek, TopMonth, TopYear, TopAll -} - -#[derive(EnumString,ToString,Debug, Serialize, Deserialize)] -pub enum SearchType { - Both, Comments, Posts -} - pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime { DateTime::::from_utc(ndt, Utc) } @@ -128,18 +70,6 @@ pub fn has_slurs(test: &str) -> bool { SLUR_REGEX.is_match(test) } -pub fn fuzzy_search(q: &str) -> String { - let replaced = q.replace(" ", "%"); - format!("%{}%", replaced) -} - -pub fn limit_and_offset(page: Option, limit: Option) -> (i64, i64) { - let page = page.unwrap_or(1); - let limit = limit.unwrap_or(10); - let offset = limit * (page - 1); - (limit, offset) -} - #[cfg(test)] mod tests { use {Settings, is_email_regex, remove_slurs, has_slurs, fuzzy_search}; @@ -167,10 +97,7 @@ mod tests { } } - - lazy_static! { static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap(); static ref SLUR_REGEX: Regex = Regex::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|\bnig(\b|g?(a|er)?s?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btrann?(y|ies?)|ladyboy(s?))").unwrap(); } - diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 07378d37a..857c626a8 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -7,41 +7,22 @@ use rand::{rngs::ThreadRng, Rng}; use std::collections::{HashMap, HashSet}; use serde::{Deserialize, Serialize}; use serde_json::{Value}; -use bcrypt::{verify}; use std::str::FromStr; -use diesel::PgConnection; use failure::Error; use std::time::{SystemTime}; -use {Crud, Joinable, Likeable, Followable, Bannable, Saveable, establish_connection, naive_now, naive_from_unix, SortType, SearchType, has_slurs, remove_slurs, Settings}; -use db::community::*; -use db::user::*; -use db::post::*; -use db::comment::*; -use db::post_view::*; -use db::comment_view::*; -use db::category::*; -use db::community_view::*; -use db::user_view::*; -use db::moderator_views::*; -use db::moderator::*; +use api::*; +use api::user::*; +use api::community::*; +use api::post::*; +use api::comment::*; +use api::site::*; const RATE_LIMIT_MESSAGES: i32 = 30; const RATE_LIMIT_PER_SECOND: i32 = 60; const RATE_LIMIT_REGISTER_MESSAGES: i32 = 1; const RATE_LIMIT_REGISTER_PER_SECOND: i32 = 60; -#[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, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead -} - -#[derive(Fail, Debug)] -#[fail(display = "{{\"op\":\"{}\", \"error\":\"{}\"}}", op, message)] -pub struct ErrorMessage { - op: String, - message: String -} /// Chat server sends this messages to session #[derive(Message)] @@ -87,414 +68,6 @@ impl actix::Message for StandardMessage { type Result = String; } -#[derive(Serialize, Deserialize)] -pub struct Login { - pub username_or_email: String, - pub password: String -} - -#[derive(Serialize, Deserialize)] -pub struct Register { - username: String, - email: Option, - password: String, - password_verify: String, - admin: bool, - spam_timeri: i64, -} - -#[derive(Serialize, Deserialize)] -pub struct LoginResponse { - op: String, - jwt: String -} - -#[derive(Serialize, Deserialize)] -pub struct CreateCommunity { - name: String, - title: String, - description: Option, - category_id: i32 , - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct CommunityResponse { - op: String, - community: CommunityView -} - -#[derive(Serialize, Deserialize)] -pub struct ListCommunities { - sort: String, - page: Option, - limit: Option, - auth: Option -} - -#[derive(Serialize, Deserialize)] -pub struct ListCommunitiesResponse { - op: String, - communities: Vec -} - -#[derive(Serialize, Deserialize)] -pub struct ListCategories; - -#[derive(Serialize, Deserialize)] -pub struct ListCategoriesResponse { - op: String, - categories: Vec -} - -#[derive(Serialize, Deserialize)] -pub struct CreatePost { - name: String, - url: Option, - body: Option, - community_id: i32, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct PostResponse { - op: String, - post: PostView -} - - -#[derive(Serialize, Deserialize)] -pub struct GetPost { - id: i32, - auth: Option -} - -#[derive(Serialize, Deserialize)] -pub struct GetPostResponse { - op: String, - post: PostView, - comments: Vec, - community: CommunityView, - moderators: Vec, - admins: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct GetPosts { - type_: String, - sort: String, - page: Option, - limit: Option, - community_id: Option, - auth: Option -} - -#[derive(Serialize, Deserialize)] -pub struct GetPostsResponse { - op: String, - posts: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct GetCommunity { - id: Option, - name: Option, - auth: Option -} - -#[derive(Serialize, Deserialize)] -pub struct GetCommunityResponse { - op: String, - community: CommunityView, - moderators: Vec, - admins: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct CreateComment { - content: String, - parent_id: Option, - edit_id: Option, - post_id: i32, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct EditComment { - content: String, - parent_id: Option, - edit_id: i32, - creator_id: i32, - post_id: i32, - removed: Option, - deleted: Option, - reason: Option, - read: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct SaveComment { - comment_id: i32, - save: bool, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct CommentResponse { - op: String, - comment: CommentView -} - -#[derive(Serialize, Deserialize)] -pub struct CreateCommentLike { - comment_id: i32, - post_id: i32, - score: i16, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct CreatePostLike { - post_id: i32, - score: i16, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct CreatePostLikeResponse { - op: String, - post: PostView -} - - -#[derive(Serialize, Deserialize)] -pub struct EditPost { - edit_id: i32, - creator_id: i32, - community_id: i32, - name: String, - url: Option, - body: Option, - removed: Option, - deleted: Option, - locked: Option, - reason: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct SavePost { - post_id: i32, - save: bool, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct EditCommunity { - edit_id: i32, - name: String, - title: String, - description: Option, - category_id: i32, - removed: Option, - deleted: Option, - reason: Option, - expires: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct FollowCommunity { - community_id: i32, - follow: bool, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct GetFollowedCommunities { - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct GetFollowedCommunitiesResponse { - op: String, - communities: Vec -} - -#[derive(Serialize, Deserialize)] -pub struct GetUserDetails { - user_id: Option, - username: Option, - sort: String, - page: Option, - limit: Option, - community_id: Option, - saved_only: bool, - auth: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct GetUserDetailsResponse { - op: String, - user: UserView, - follows: Vec, - moderates: Vec, - comments: Vec, - posts: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct GetModlog { - mod_user_id: Option, - community_id: Option, - page: Option, - limit: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct GetModlogResponse { - 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, -} - -#[derive(Serialize, Deserialize)] -pub struct BanFromCommunity { - community_id: i32, - user_id: i32, - ban: bool, - reason: Option, - expires: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct BanFromCommunityResponse { - op: String, - user: UserView, - banned: bool, -} - - -#[derive(Serialize, Deserialize)] -pub struct AddModToCommunity { - community_id: i32, - user_id: i32, - added: bool, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct AddModToCommunityResponse { - op: String, - moderators: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct CreateSite { - name: String, - description: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct EditSite { - name: String, - description: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -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, -} - -#[derive(Serialize, Deserialize)] -pub struct AddAdmin { - user_id: i32, - added: bool, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct AddAdminResponse { - op: String, - admins: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct BanUser { - user_id: i32, - ban: bool, - reason: Option, - expires: Option, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct BanUserResponse { - op: String, - user: UserView, - banned: bool, -} - -#[derive(Serialize, Deserialize)] -pub struct GetReplies { - sort: String, - page: Option, - limit: Option, - unread_only: bool, - auth: String -} - -#[derive(Serialize, Deserialize)] -pub struct GetRepliesResponse { - op: String, - replies: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct Search { - q: String, - type_: String, - community_id: Option, - sort: String, - page: Option, - limit: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct SearchResponse { - op: String, - comments: Vec, - posts: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct MarkAllAsRead { - auth: String -} - #[derive(Debug)] pub struct RateLimitBucket { last_checked: SystemTime, @@ -543,8 +116,25 @@ impl ChatServer { } } - fn send_community_message(&self, conn: &PgConnection, community_id: i32, message: &str, skip_id: usize) -> Result<(), Error> { - let posts = PostView::list(conn, + fn join_room(&self, room_id: i32, id: usize) { + // remove session from all rooms + for (_n, sessions) in &mut self.rooms { + sessions.remove(&id); + } + + // If the room doesn't exist yet + if self.rooms.get_mut(&room_id).is_none() { + self.rooms.insert(room_id, HashSet::new()); + } + + self.rooms.get_mut(&room_id).unwrap().insert(id); + } + + fn send_community_message(&self, community_id: i32, message: &str, skip_id: usize) -> Result<(), Error> { + use db::*; + use db::post_view::*; + let conn = establish_connection(); + let posts = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(community_id), @@ -562,16 +152,16 @@ impl ChatServer { Ok(()) } - fn check_rate_limit_register(&mut self, addr: usize) -> Result<(), Error> { - self.check_rate_limit_full(addr, RATE_LIMIT_REGISTER_MESSAGES, RATE_LIMIT_REGISTER_PER_SECOND) + fn check_rate_limit_register(&mut self, id: usize) -> Result<(), Error> { + self.check_rate_limit_full(id, RATE_LIMIT_REGISTER_MESSAGES, RATE_LIMIT_REGISTER_PER_SECOND) } - fn check_rate_limit(&mut self, addr: usize) -> Result<(), Error> { - self.check_rate_limit_full(addr, RATE_LIMIT_MESSAGES, RATE_LIMIT_PER_SECOND) + fn check_rate_limit(&mut self, id: usize) -> Result<(), Error> { + self.check_rate_limit_full(id, RATE_LIMIT_MESSAGES, RATE_LIMIT_PER_SECOND) } - fn check_rate_limit_full(&mut self, addr: usize, rate: i32, per: i32) -> Result<(), Error> { - if let Some(info) = self.sessions.get(&addr) { + fn check_rate_limit_full(&mut self, id: usize, rate: i32, per: i32) -> Result<(), Error> { + if let Some(info) = self.sessions.get(&id) { if let Some(rate_limit) = self.rate_limits.get_mut(&info.ip) { // The initial value if rate_limit.allowance == -2f64 { @@ -588,7 +178,7 @@ impl ChatServer { if rate_limit.allowance < 1.0 { println!("Rate limited IP: {}, time_passed: {}, allowance: {}", &info.ip, time_passed, rate_limit.allowance); - Err(ErrorMessage { + Err(APIError { op: "Rate Limit".to_string(), message: format!("Too many requests. {} per {} seconds", rate, per), })? @@ -652,7 +242,6 @@ impl Handler for ChatServer { } } - /// Handler for Disconnect message. impl Handler for ChatServer { type Result = (); @@ -700,2192 +289,193 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { let login: Login = serde_json::from_str(data)?; - login.perform(chat, msg.id) + let res = Oper::new(user_operation, login).perform()?; + Ok(serde_json::to_string(&res)?) }, UserOperation::Register => { + chat.check_rate_limit_register(msg.id)?; let register: Register = serde_json::from_str(data)?; - register.perform(chat, msg.id) - }, - UserOperation::CreateCommunity => { - let create_community: CreateCommunity = serde_json::from_str(data)?; - create_community.perform(chat, msg.id) - }, - UserOperation::ListCommunities => { - let list_communities: ListCommunities = serde_json::from_str(data)?; - list_communities.perform(chat, msg.id) - }, - UserOperation::ListCategories => { - let list_categories: ListCategories = ListCategories; - list_categories.perform(chat, msg.id) - }, - UserOperation::CreatePost => { - let create_post: CreatePost = serde_json::from_str(data)?; - create_post.perform(chat, msg.id) - }, - UserOperation::GetPost => { - let get_post: GetPost = serde_json::from_str(data)?; - get_post.perform(chat, msg.id) - }, - UserOperation::GetCommunity => { - let get_community: GetCommunity = serde_json::from_str(data)?; - get_community.perform(chat, msg.id) - }, - UserOperation::CreateComment => { - let create_comment: CreateComment = serde_json::from_str(data)?; - create_comment.perform(chat, msg.id) - }, - UserOperation::EditComment => { - let edit_comment: EditComment = serde_json::from_str(data)?; - edit_comment.perform(chat, msg.id) - }, - UserOperation::SaveComment => { - let save_post: SaveComment = serde_json::from_str(data)?; - save_post.perform(chat, msg.id) - }, - UserOperation::CreateCommentLike => { - let create_comment_like: CreateCommentLike = serde_json::from_str(data)?; - create_comment_like.perform(chat, msg.id) - }, - UserOperation::GetPosts => { - let get_posts: GetPosts = serde_json::from_str(data)?; - get_posts.perform(chat, msg.id) - }, - UserOperation::CreatePostLike => { - let create_post_like: CreatePostLike = serde_json::from_str(data)?; - create_post_like.perform(chat, msg.id) - }, - UserOperation::EditPost => { - let edit_post: EditPost = serde_json::from_str(data)?; - edit_post.perform(chat, msg.id) - }, - UserOperation::SavePost => { - let save_post: SavePost = serde_json::from_str(data)?; - save_post.perform(chat, msg.id) - }, - UserOperation::EditCommunity => { - let edit_community: EditCommunity = serde_json::from_str(data)?; - edit_community.perform(chat, msg.id) - }, - UserOperation::FollowCommunity => { - let follow_community: FollowCommunity = serde_json::from_str(data)?; - follow_community.perform(chat, msg.id) - }, - UserOperation::GetFollowedCommunities => { - let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?; - followed_communities.perform(chat, msg.id) + let res = Oper::new(user_operation, register).perform()?; + Ok(serde_json::to_string(&res)?) }, UserOperation::GetUserDetails => { let get_user_details: GetUserDetails = serde_json::from_str(data)?; - get_user_details.perform(chat, msg.id) - }, - UserOperation::GetModlog => { - let get_modlog: GetModlog = serde_json::from_str(data)?; - get_modlog.perform(chat, msg.id) - }, - UserOperation::BanFromCommunity => { - let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; - ban_from_community.perform(chat, msg.id) - }, - UserOperation::AddModToCommunity => { - let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?; - mod_add_to_community.perform(chat, msg.id) - }, - UserOperation::CreateSite => { - let create_site: CreateSite = serde_json::from_str(data)?; - create_site.perform(chat, msg.id) - }, - UserOperation::EditSite => { - let edit_site: EditSite = serde_json::from_str(data)?; - edit_site.perform(chat, msg.id) - }, - UserOperation::GetSite => { - let get_site: GetSite = serde_json::from_str(data)?; - get_site.perform(chat, msg.id) + let res = Oper::new(user_operation, get_user_details).perform()?; + Ok(serde_json::to_string(&res)?) }, UserOperation::AddAdmin => { let add_admin: AddAdmin = serde_json::from_str(data)?; - add_admin.perform(chat, msg.id) + let res = Oper::new(user_operation, add_admin).perform()?; + Ok(serde_json::to_string(&res)?) }, UserOperation::BanUser => { let ban_user: BanUser = serde_json::from_str(data)?; - ban_user.perform(chat, msg.id) + let res = Oper::new(user_operation, ban_user).perform()?; + Ok(serde_json::to_string(&res)?) }, UserOperation::GetReplies => { let get_replies: GetReplies = serde_json::from_str(data)?; - get_replies.perform(chat, msg.id) - }, - UserOperation::Search => { - let search: Search = serde_json::from_str(data)?; - search.perform(chat, msg.id) + let res = Oper::new(user_operation, get_replies).perform()?; + Ok(serde_json::to_string(&res)?) }, UserOperation::MarkAllAsRead => { let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?; - mark_all_as_read.perform(chat, msg.id) + let res = Oper::new(user_operation, mark_all_as_read).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::GetCommunity => { + let get_community: GetCommunity = serde_json::from_str(data)?; + let res = Oper::new(user_operation, get_community).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::ListCommunities => { + let list_communities: ListCommunities = serde_json::from_str(data)?; + let res = Oper::new(user_operation, list_communities).perform()?; + Ok(serde_json::to_string(&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()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::EditCommunity => { + let edit_community: EditCommunity = serde_json::from_str(data)?; + let res = Oper::new(user_operation, edit_community).perform()?; + 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(edit_community.edit_id, &community_sent_str, msg.id)?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::FollowCommunity => { + let follow_community: FollowCommunity = serde_json::from_str(data)?; + let res = Oper::new(user_operation, follow_community).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::GetFollowedCommunities => { + let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?; + let res = Oper::new(user_operation, followed_communities).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::BanFromCommunity => { + let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; + let res = Oper::new(user_operation, ban_from_community).perform()?; + let res_str = serde_json::to_string(&res)?; + chat.send_community_message(ban_from_community.community_id, &res_str, msg.id)?; + Ok(res_str) + }, + UserOperation::AddModToCommunity => { + let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?; + let res = Oper::new(user_operation, mod_add_to_community).perform()?; + let res_str = serde_json::to_string(&res)?; + chat.send_community_message(mod_add_to_community.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()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::CreatePost => { + chat.check_rate_limit_register(msg.id)?; + let create_post: CreatePost = serde_json::from_str(data)?; + let res = Oper::new(user_operation, create_post).perform()?; + Ok(serde_json::to_string(&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()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::GetPosts => { + let get_posts: GetPosts = serde_json::from_str(data)?; + let res = Oper::new(user_operation, get_posts).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::CreatePostLike => { + chat.check_rate_limit(msg.id)?; + let create_post_like: CreatePostLike = serde_json::from_str(data)?; + let res = Oper::new(user_operation, create_post_like).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::EditPost => { + let edit_post: EditPost = serde_json::from_str(data)?; + let res = Oper::new(user_operation, edit_post).perform()?; + 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(edit_post.edit_id, &post_sent_str, msg.id); + Ok(serde_json::to_string(&res)?) + }, + UserOperation::SavePost => { + let save_post: SavePost = serde_json::from_str(data)?; + let res = Oper::new(user_operation, save_post).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::CreateComment => { + chat.check_rate_limit(msg.id)?; + let create_comment: CreateComment = serde_json::from_str(data)?; + let res = Oper::new(user_operation, create_comment).perform()?; + 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(create_comment.post_id, &comment_sent_str, msg.id); + Ok(serde_json::to_string(&res)?) + }, + UserOperation::EditComment => { + let edit_comment: EditComment = serde_json::from_str(data)?; + let res = Oper::new(user_operation, edit_comment).perform()?; + 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(edit_comment.post_id, &comment_sent_str, msg.id); + Ok(serde_json::to_string(&res)?) + }, + UserOperation::SaveComment => { + let save_comment: SaveComment = serde_json::from_str(data)?; + let res = Oper::new(user_operation, save_comment).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::CreateCommentLike => { + chat.check_rate_limit(msg.id)?; + let create_comment_like: CreateCommentLike = serde_json::from_str(data)?; + let res = Oper::new(user_operation, create_comment_like).perform()?; + 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(create_comment_like.post_id, &comment_sent_str, msg.id); + Ok(serde_json::to_string(&res)?) + }, + UserOperation::GetModlog => { + let get_modlog: GetModlog = serde_json::from_str(data)?; + let res = Oper::new(user_operation, get_modlog).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::CreateSite => { + let create_site: CreateSite = serde_json::from_str(data)?; + let res = Oper::new(user_operation, create_site).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::EditSite => { + let edit_site: EditSite = serde_json::from_str(data)?; + let res = Oper::new(user_operation, edit_site).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::GetSite => { + let get_site: GetSite = serde_json::from_str(data)?; + let res = Oper::new(user_operation, get_site).perform()?; + Ok(serde_json::to_string(&res)?) + }, + UserOperation::Search => { + let search: Search = serde_json::from_str(data)?; + let res = Oper::new(user_operation, search).perform()?; + Ok(serde_json::to_string(&res)?) }, } } - -pub trait Perform { - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result; - fn op_type(&self) -> UserOperation; - fn error(&self, error_msg: &str) -> ErrorMessage { - ErrorMessage { - op: self.op_type().to_string(), - message: error_msg.to_string() - } - } -} - -impl Perform for Login { - - fn op_type(&self) -> UserOperation { - UserOperation::Login - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - // Fetch that username / email - let user: User_ = match User_::find_by_email_or_username(&conn, &self.username_or_email) { - Ok(user) => user, - Err(_e) => return Err(self.error("Couldn't find that username or email"))? - }; - - // Verify the password - let valid: bool = verify(&self.password, &user.password_encrypted).unwrap_or(false); - if !valid { - return Err(self.error("Password incorrect"))? - } - - // Return the jwt - Ok( - serde_json::to_string( - &LoginResponse { - op: self.op_type().to_string(), - jwt: user.jwt() - } - )? - ) - } - -} - -impl Perform for Register { - fn op_type(&self) -> UserOperation { - UserOperation::Register - } - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - chat.check_rate_limit_register(addr)?; - - // Make sure passwords match - if &self.password != &self.password_verify { - return Err(self.error("Passwords do not match."))? - } - - if self.spam_timeri < 1142 { - return Err(self.error("Too fast"))? - } - - if has_slurs(&self.username) { - return Err(self.error("No slurs"))? - } - - // Make sure there are no admins - if self.admin && UserView::admins(&conn)?.len() > 0 { - return Err(self.error("Sorry, there's already an admin."))? - } - - // Register the new user - let user_form = UserForm { - name: self.username.to_owned(), - fedi_name: Settings::get().hostname.into(), - email: self.email.to_owned(), - password_encrypted: self.password.to_owned(), - preferred_username: None, - updated: None, - admin: self.admin, - banned: false, - }; - - // Create the user - let inserted_user = match User_::register(&conn, &user_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("User already exists."))? - } - }; - - // Sign them up for main community no matter what - let community_follower_form = CommunityFollowerForm { - community_id: 1, - user_id: inserted_user.id, - }; - - let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community follower already exists."))? - } - }; - - // If its an admin, add them as a mod and follower to main - if self.admin { - let community_moderator_form = CommunityModeratorForm { - community_id: 1, - user_id: inserted_user.id, - }; - - let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community moderator already exists."))? - } - }; - - } - - - // Return the jwt - Ok( - serde_json::to_string( - &LoginResponse { - op: self.op_type().to_string(), - jwt: inserted_user.jwt() - } - )? - ) - - } -} - -impl Perform for CreateCommunity { - fn op_type(&self) -> UserOperation { - UserOperation::CreateCommunity - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - chat.check_rate_limit_register(addr)?; - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - if has_slurs(&self.name) || - has_slurs(&self.title) || - (self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) { - return Err(self.error("No slurs"))? - } - - let user_id = claims.id; - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - // When you create a community, make sure the user becomes a moderator and a follower - let community_form = CommunityForm { - name: self.name.to_owned(), - title: self.title.to_owned(), - description: self.description.to_owned(), - category_id: self.category_id, - creator_id: user_id, - removed: None, - deleted: None, - updated: None, - }; - - let inserted_community = match Community::create(&conn, &community_form) { - Ok(community) => community, - Err(_e) => { - return Err(self.error("Community already exists."))? - } - }; - - let community_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - user_id: user_id - }; - - let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community moderator already exists."))? - } - }; - - let community_follower_form = CommunityFollowerForm { - community_id: inserted_community.id, - user_id: user_id - }; - - let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community follower already exists."))? - } - }; - - let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?; - - Ok( - serde_json::to_string( - &CommunityResponse { - op: self.op_type().to_string(), - community: community_view - } - )? - ) - } -} - -impl Perform for ListCommunities { - fn op_type(&self) -> UserOperation { - UserOperation::ListCommunities - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let user_id: Option = match &self.auth { - Some(auth) => { - match Claims::decode(&auth) { - Ok(claims) => { - let user_id = claims.claims.id; - Some(user_id) - } - Err(_e) => None - } - } - None => None - }; - - let sort = SortType::from_str(&self.sort)?; - - let communities: Vec = CommunityView::list(&conn, user_id, sort, self.page, self.limit)?; - - // Return the jwt - Ok( - serde_json::to_string( - &ListCommunitiesResponse { - op: self.op_type().to_string(), - communities: communities - } - )? - ) - } -} - -impl Perform for ListCategories { - fn op_type(&self) -> UserOperation { - UserOperation::ListCategories - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let categories: Vec = Category::list_all(&conn)?; - - // Return the jwt - Ok( - serde_json::to_string( - &ListCategoriesResponse { - op: self.op_type().to_string(), - categories: categories - } - )? - ) - } -} - -impl Perform for CreatePost { - fn op_type(&self) -> UserOperation { - UserOperation::CreatePost - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - chat.check_rate_limit_register(addr)?; - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - if has_slurs(&self.name) || - (self.body.is_some() && has_slurs(&self.body.to_owned().unwrap())) { - return Err(self.error("No slurs"))? - } - - let user_id = claims.id; - - // Check for a community ban - if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() { - return Err(self.error("You have been banned from this community"))? - } - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - let post_form = PostForm { - name: self.name.to_owned(), - url: self.url.to_owned(), - body: self.body.to_owned(), - community_id: self.community_id, - creator_id: user_id, - removed: None, - deleted: None, - locked: None, - updated: None - }; - - let inserted_post = match Post::create(&conn, &post_form) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldn't create Post"))? - } - }; - - // They like their own post by default - let like_form = PostLikeForm { - post_id: inserted_post.id, - user_id: user_id, - score: 1 - }; - - // 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(self.error("Couldn't like post."))? - } - }; - - // Refetch the view - let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldn't find Post"))? - } - }; - - Ok( - serde_json::to_string( - &PostResponse { - op: self.op_type().to_string(), - post: post_view - } - )? - ) - } -} - - -impl Perform for GetPost { - fn op_type(&self) -> UserOperation { - UserOperation::GetPost - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - let user_id: Option = match &self.auth { - Some(auth) => { - match Claims::decode(&auth) { - Ok(claims) => { - let user_id = claims.claims.id; - Some(user_id) - } - Err(_e) => None - } - } - None => None - }; - - let post_view = match PostView::read(&conn, self.id, user_id) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldn't find Post"))? - } - }; - - // remove session from all rooms - for (_n, sessions) in &mut chat.rooms { - sessions.remove(&addr); - } - - // If the room doesn't exist yet - if chat.rooms.get_mut(&self.id).is_none() { - chat.rooms.insert(self.id, HashSet::new()); - } - - chat.rooms.get_mut(&self.id).unwrap().insert(addr); - - let comments = CommentView::list(&conn, &SortType::New, Some(self.id), None, None, user_id, false, None, Some(9999))?; - - let community = CommunityView::read(&conn, post_view.community_id, user_id)?; - - let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id)?; - - let admins = UserView::admins(&conn)?; - - // Return the jwt - Ok( - serde_json::to_string( - &GetPostResponse { - op: self.op_type().to_string(), - post: post_view, - comments: comments, - community: community, - moderators: moderators, - admins: admins, - } - )? - ) - } -} - -impl Perform for GetCommunity { - fn op_type(&self) -> UserOperation { - UserOperation::GetCommunity - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let user_id: Option = match &self.auth { - Some(auth) => { - match Claims::decode(&auth) { - Ok(claims) => { - let user_id = claims.claims.id; - Some(user_id) - } - Err(_e) => None - } - } - None => None - }; - - let community_id = match self.id { - Some(id) => id, - None => Community::read_from_name(&conn, self.name.to_owned().unwrap_or("main".to_string()))?.id - }; - - let community_view = match CommunityView::read(&conn, community_id, user_id) { - Ok(community) => community, - Err(_e) => { - return Err(self.error("Couldn't find Community"))? - } - }; - - let moderators = match CommunityModeratorView::for_community(&conn, community_id) { - Ok(moderators) => moderators, - Err(_e) => { - return Err(self.error("Couldn't find Community"))? - } - }; - - let admins = UserView::admins(&conn)?; - - // Return the jwt - Ok( - serde_json::to_string( - &GetCommunityResponse { - op: self.op_type().to_string(), - community: community_view, - moderators: moderators, - admins: admins, - } - )? - ) - } -} - -impl Perform for CreateComment { - fn op_type(&self) -> UserOperation { - UserOperation::CreateComment - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - chat.check_rate_limit(addr)?; - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Check for a community ban - let post = Post::read(&conn, self.post_id)?; - if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { - return Err(self.error("You have been banned from this community"))? - } - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - let content_slurs_removed = remove_slurs(&self.content.to_owned()); - - let comment_form = CommentForm { - content: content_slurs_removed, - parent_id: self.parent_id.to_owned(), - post_id: self.post_id, - creator_id: user_id, - removed: None, - deleted: None, - read: None, - updated: None - }; - - let inserted_comment = match Comment::create(&conn, &comment_form) { - Ok(comment) => comment, - Err(_e) => { - return Err(self.error("Couldn't create Comment"))? - } - }; - - // You like your own comment by default - let like_form = CommentLikeForm { - comment_id: inserted_comment.id, - post_id: self.post_id, - user_id: user_id, - score: 1 - }; - - let _inserted_like = match CommentLike::like(&conn, &like_form) { - Ok(like) => like, - Err(_e) => { - return Err(self.error("Couldn't like comment."))? - } - }; - - let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?; - - let mut comment_sent = comment_view.clone(); - comment_sent.my_vote = None; - comment_sent.user_id = None; - - let comment_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: comment_view - } - )?; - - let comment_sent_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: comment_sent - } - )?; - - chat.send_room_message(self.post_id, &comment_sent_out, addr); - - Ok(comment_out) - } -} - -impl Perform for EditComment { - fn op_type(&self) -> UserOperation { - UserOperation::EditComment - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let orig_comment = CommentView::read(&conn, self.edit_id, None)?; - - // You are allowed to mark the comment as read even if you're banned. - if self.read.is_none() { - - // Verify its the creator or a mod, or an admin - let mut editors: Vec = vec![self.creator_id]; - editors.append( - &mut CommunityModeratorView::for_community(&conn, orig_comment.community_id) - ? - .into_iter() - .map(|m| m.user_id) - .collect() - ); - editors.append( - &mut UserView::admins(&conn) - ? - .into_iter() - .map(|a| a.id) - .collect() - ); - - if !editors.contains(&user_id) { - return Err(self.error("Not allowed to edit comment."))? - } - - // Check for a community ban - if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() { - return Err(self.error("You have been banned from this community"))? - } - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - } - - let content_slurs_removed = remove_slurs(&self.content.to_owned()); - - let comment_form = CommentForm { - content: content_slurs_removed, - parent_id: self.parent_id, - post_id: self.post_id, - creator_id: self.creator_id, - removed: self.removed.to_owned(), - deleted: self.deleted.to_owned(), - read: self.read.to_owned(), - updated: if self.read.is_some() { orig_comment.updated } else {Some(naive_now())} - }; - - let _updated_comment = match Comment::update(&conn, self.edit_id, &comment_form) { - Ok(comment) => comment, - Err(_e) => { - return Err(self.error("Couldn't update Comment"))? - } - }; - - // Mod tables - if let Some(removed) = self.removed.to_owned() { - let form = ModRemoveCommentForm { - mod_user_id: user_id, - comment_id: self.edit_id, - removed: Some(removed), - reason: self.reason.to_owned(), - }; - ModRemoveComment::create(&conn, &form)?; - } - - - let comment_view = CommentView::read(&conn, self.edit_id, Some(user_id))?; - - let mut comment_sent = comment_view.clone(); - comment_sent.my_vote = None; - comment_sent.user_id = None; - - let comment_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: comment_view - } - )?; - - let comment_sent_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: comment_sent - } - )?; - - chat.send_room_message(self.post_id, &comment_sent_out, addr); - - Ok(comment_out) - } -} - -impl Perform for SaveComment { - fn op_type(&self) -> UserOperation { - UserOperation::SaveComment - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let comment_saved_form = CommentSavedForm { - comment_id: self.comment_id, - user_id: user_id, - }; - - if self.save { - match CommentSaved::save(&conn, &comment_saved_form) { - Ok(comment) => comment, - Err(_e) => { - return Err(self.error("Couldnt do comment save"))? - } - }; - } else { - match CommentSaved::unsave(&conn, &comment_saved_form) { - Ok(comment) => comment, - Err(_e) => { - return Err(self.error("Couldnt do comment save"))? - } - }; - } - - let comment_view = CommentView::read(&conn, self.comment_id, Some(user_id))?; - - let comment_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: comment_view - } - ) - ?; - - Ok(comment_out) - } -} - - -impl Perform for CreateCommentLike { - fn op_type(&self) -> UserOperation { - UserOperation::CreateCommentLike - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - chat.check_rate_limit(addr)?; - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Check for a community ban - let post = Post::read(&conn, self.post_id)?; - if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { - return Err(self.error("You have been banned from this community"))? - } - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - let like_form = CommentLikeForm { - comment_id: self.comment_id, - post_id: self.post_id, - user_id: user_id, - score: self.score - }; - - // Remove any likes first - CommentLike::remove(&conn, &like_form)?; - - // Only add the like if the score isnt 0 - if &like_form.score != &0 { - let _inserted_like = match CommentLike::like(&conn, &like_form) { - Ok(like) => like, - Err(_e) => { - return Err(self.error("Couldn't like comment."))? - } - }; - } - - // Have to refetch the comment to get the current state - let liked_comment = CommentView::read(&conn, self.comment_id, Some(user_id))?; - - let mut liked_comment_sent = liked_comment.clone(); - liked_comment_sent.my_vote = None; - liked_comment_sent.user_id = None; - - let like_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: liked_comment - } - )?; - - let like_sent_out = serde_json::to_string( - &CommentResponse { - op: self.op_type().to_string(), - comment: liked_comment_sent - } - )?; - - chat.send_room_message(self.post_id, &like_sent_out, addr); - - Ok(like_out) - } -} - - -impl Perform for GetPosts { - fn op_type(&self) -> UserOperation { - UserOperation::GetPosts - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let user_id: Option = match &self.auth { - Some(auth) => { - match Claims::decode(&auth) { - Ok(claims) => { - let user_id = claims.claims.id; - Some(user_id) - } - Err(_e) => None - } - } - None => None - }; - - let type_ = PostListingType::from_str(&self.type_)?; - let sort = SortType::from_str(&self.sort)?; - - let posts = match PostView::list(&conn, - type_, - &sort, - self.community_id, - None, - None, - user_id, - false, - false, - self.page, - self.limit) { - Ok(posts) => posts, - Err(_e) => { - return Err(self.error("Couldn't get posts"))? - } - }; - - // Return the jwt - Ok( - serde_json::to_string( - &GetPostsResponse { - op: self.op_type().to_string(), - posts: posts - } - )? - ) - } -} - - -impl Perform for CreatePostLike { - fn op_type(&self) -> UserOperation { - UserOperation::CreatePostLike - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - chat.check_rate_limit(addr)?; - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Check for a community ban - let post = Post::read(&conn, self.post_id)?; - if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { - return Err(self.error("You have been banned from this community"))? - } - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - let like_form = PostLikeForm { - post_id: self.post_id, - user_id: user_id, - score: self.score - }; - - // Remove any likes first - PostLike::remove(&conn, &like_form)?; - - // Only add the like if the score isnt 0 - if &like_form.score != &0 { - let _inserted_like = match PostLike::like(&conn, &like_form) { - Ok(like) => like, - Err(_e) => { - return Err(self.error("Couldn't like post."))? - } - }; - } - - let post_view = match PostView::read(&conn, self.post_id, Some(user_id)) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldn't find Post"))? - } - }; - - // just output the score - - let like_out = serde_json::to_string( - &CreatePostLikeResponse { - op: self.op_type().to_string(), - post: post_view - } - )?; - - Ok(like_out) - } -} - -impl Perform for EditPost { - fn op_type(&self) -> UserOperation { - UserOperation::EditPost - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - if has_slurs(&self.name) || - (self.body.is_some() && has_slurs(&self.body.to_owned().unwrap())) { - return Err(self.error("No slurs"))? - } - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Verify its the creator or a mod or admin - let mut editors: Vec = vec![self.creator_id]; - editors.append( - &mut CommunityModeratorView::for_community(&conn, self.community_id) - ? - .into_iter() - .map(|m| m.user_id) - .collect() - ); - editors.append( - &mut UserView::admins(&conn) - ? - .into_iter() - .map(|a| a.id) - .collect() - ); - if !editors.contains(&user_id) { - return Err(self.error("Not allowed to edit post."))? - } - - // Check for a community ban - if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() { - return Err(self.error("You have been banned from this community"))? - } - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - let post_form = PostForm { - name: self.name.to_owned(), - url: self.url.to_owned(), - body: self.body.to_owned(), - creator_id: self.creator_id.to_owned(), - community_id: self.community_id, - removed: self.removed.to_owned(), - deleted: self.deleted.to_owned(), - locked: self.locked.to_owned(), - updated: Some(naive_now()) - }; - - let _updated_post = match Post::update(&conn, self.edit_id, &post_form) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldn't update Post"))? - } - }; - - // Mod tables - if let Some(removed) = self.removed.to_owned() { - let form = ModRemovePostForm { - mod_user_id: user_id, - post_id: self.edit_id, - removed: Some(removed), - reason: self.reason.to_owned(), - }; - ModRemovePost::create(&conn, &form)?; - } - - if let Some(locked) = self.locked.to_owned() { - let form = ModLockPostForm { - mod_user_id: user_id, - post_id: self.edit_id, - locked: Some(locked), - }; - ModLockPost::create(&conn, &form)?; - } - - let post_view = PostView::read(&conn, self.edit_id, Some(user_id))?; - - let mut post_sent = post_view.clone(); - post_sent.my_vote = None; - - let post_out = serde_json::to_string( - &PostResponse { - op: self.op_type().to_string(), - post: post_view - } - ) - ?; - - let post_sent_out = serde_json::to_string( - &PostResponse { - op: self.op_type().to_string(), - post: post_sent - } - ) - ?; - - chat.send_room_message(self.edit_id, &post_sent_out, addr); - - Ok(post_out) - } -} - -impl Perform for SavePost { - fn op_type(&self) -> UserOperation { - UserOperation::SavePost - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let post_saved_form = PostSavedForm { - post_id: self.post_id, - user_id: user_id, - }; - - if self.save { - match PostSaved::save(&conn, &post_saved_form) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldnt do post save"))? - } - }; - } else { - match PostSaved::unsave(&conn, &post_saved_form) { - Ok(post) => post, - Err(_e) => { - return Err(self.error("Couldnt do post save"))? - } - }; - } - - let post_view = PostView::read(&conn, self.post_id, Some(user_id))?; - - let post_out = serde_json::to_string( - &PostResponse { - op: self.op_type().to_string(), - post: post_view - } - ) - ?; - - Ok(post_out) - } -} - -impl Perform for EditCommunity { - fn op_type(&self) -> UserOperation { - UserOperation::EditCommunity - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - if has_slurs(&self.name) || has_slurs(&self.title) { - return Err(self.error("No slurs"))? - } - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Check for a site ban - if UserView::read(&conn, user_id)?.banned { - return Err(self.error("You have been banned from the site"))? - } - - // Verify its a mod - let mut editors: Vec = Vec::new(); - editors.append( - &mut CommunityModeratorView::for_community(&conn, self.edit_id) - ? - .into_iter() - .map(|m| m.user_id) - .collect() - ); - editors.append( - &mut UserView::admins(&conn) - ? - .into_iter() - .map(|a| a.id) - .collect() - ); - if !editors.contains(&user_id) { - return Err(self.error("Not allowed to edit community"))? - } - - let community_form = CommunityForm { - name: self.name.to_owned(), - title: self.title.to_owned(), - description: self.description.to_owned(), - category_id: self.category_id.to_owned(), - creator_id: user_id, - removed: self.removed.to_owned(), - deleted: self.deleted.to_owned(), - updated: Some(naive_now()) - }; - - let _updated_community = match Community::update(&conn, self.edit_id, &community_form) { - Ok(community) => community, - Err(_e) => { - return Err(self.error("Couldn't update Community"))? - } - }; - - // Mod tables - if let Some(removed) = self.removed.to_owned() { - let expires = match self.expires { - Some(time) => Some(naive_from_unix(time)), - None => None - }; - let form = ModRemoveCommunityForm { - mod_user_id: user_id, - community_id: self.edit_id, - removed: Some(removed), - reason: self.reason.to_owned(), - expires: expires - }; - ModRemoveCommunity::create(&conn, &form)?; - } - - let community_view = CommunityView::read(&conn, self.edit_id, Some(user_id))?; - - let community_out = serde_json::to_string( - &CommunityResponse { - op: self.op_type().to_string(), - community: community_view - } - ) - ?; - - let community_view_sent = CommunityView::read(&conn, self.edit_id, None)?; - - let community_sent = serde_json::to_string( - &CommunityResponse { - op: self.op_type().to_string(), - community: community_view_sent - } - ) - ?; - - chat.send_community_message(&conn, self.edit_id, &community_sent, addr)?; - - Ok(community_out) - } -} - - -impl Perform for FollowCommunity { - fn op_type(&self) -> UserOperation { - UserOperation::FollowCommunity - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let community_follower_form = CommunityFollowerForm { - community_id: self.community_id, - user_id: user_id - }; - - if self.follow { - - match CommunityFollower::follow(&conn, &community_follower_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community follower already exists."))? - } - }; - } else { - match CommunityFollower::ignore(&conn, &community_follower_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community follower already exists."))? - } - }; - } - - let community_view = CommunityView::read(&conn, self.community_id, Some(user_id))?; - - Ok( - serde_json::to_string( - &CommunityResponse { - op: self.op_type().to_string(), - community: community_view - } - )? - ) - } -} - -impl Perform for GetFollowedCommunities { - fn op_type(&self) -> UserOperation { - UserOperation::GetFollowedCommunities - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let communities: Vec = match CommunityFollowerView::for_user(&conn, user_id) { - Ok(communities) => communities, - Err(_e) => { - return Err(self.error("System error, try logging out and back in."))? - } - }; - - // Return the jwt - Ok( - serde_json::to_string( - &GetFollowedCommunitiesResponse { - op: self.op_type().to_string(), - communities: communities - } - )? - ) - } -} - -impl Perform for GetUserDetails { - fn op_type(&self) -> UserOperation { - UserOperation::GetUserDetails - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let user_id: Option = match &self.auth { - Some(auth) => { - match Claims::decode(&auth) { - Ok(claims) => { - let user_id = claims.claims.id; - Some(user_id) - } - Err(_e) => None - } - } - None => None - }; - - //TODO add save - let sort = SortType::from_str(&self.sort)?; - - let user_details_id = match self.user_id { - Some(id) => id, - None => User_::read_from_name(&conn, self.username.to_owned().unwrap_or("admin".to_string()))?.id - }; - - let user_view = UserView::read(&conn, user_details_id)?; - - // If its saved only, you don't care what creator it was - let posts = if self.saved_only { - PostView::list(&conn, - PostListingType::All, - &sort, - self.community_id, - None, - None, - Some(user_details_id), - self.saved_only, - false, - self.page, - self.limit)? - } else { - PostView::list(&conn, - PostListingType::All, - &sort, - self.community_id, - Some(user_details_id), - None, - user_id, - self.saved_only, - false, - self.page, - self.limit)? - }; - let comments = if self.saved_only { - CommentView::list(&conn, - &sort, - None, - None, - None, - Some(user_details_id), - self.saved_only, - self.page, - self.limit)? - } else { - CommentView::list(&conn, - &sort, - None, - Some(user_details_id), - None, - user_id, - self.saved_only, - self.page, - self.limit)? - }; - - let follows = CommunityFollowerView::for_user(&conn, user_details_id)?; - let moderates = CommunityModeratorView::for_user(&conn, user_details_id)?; - - // Return the jwt - Ok( - serde_json::to_string( - &GetUserDetailsResponse { - op: self.op_type().to_string(), - user: user_view, - follows: follows, - moderates: moderates, - comments: comments, - posts: posts, - } - )? - ) - } -} - -impl Perform for GetModlog { - fn op_type(&self) -> UserOperation { - UserOperation::GetModlog - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let removed_posts = ModRemovePostView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit)?; - let locked_posts = ModLockPostView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit)?; - let removed_comments = ModRemoveCommentView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit)?; - let banned_from_community = ModBanFromCommunityView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit)?; - let added_to_community = ModAddCommunityView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit)?; - - // These arrays are only for the full modlog, when a community isn't given - let mut removed_communities = Vec::new(); - let mut banned = Vec::new(); - let mut added = Vec::new(); - - if self.community_id.is_none() { - removed_communities = ModRemoveCommunityView::list(&conn, self.mod_user_id, self.page, self.limit)?; - banned = ModBanView::list(&conn, self.mod_user_id, self.page, self.limit)?; - added = ModAddView::list(&conn, self.mod_user_id, self.page, self.limit)?; - } - - // Return the jwt - Ok( - serde_json::to_string( - &GetModlogResponse { - op: self.op_type().to_string(), - removed_posts: removed_posts, - locked_posts: locked_posts, - removed_comments: removed_comments, - removed_communities: removed_communities, - banned_from_community: banned_from_community, - banned: banned, - added_to_community: added_to_community, - added: added, - } - )? - ) - } -} - -impl Perform for GetReplies { - fn op_type(&self) -> UserOperation { - UserOperation::GetReplies - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let sort = SortType::from_str(&self.sort)?; - - let replies = ReplyView::get_replies(&conn, user_id, &sort, self.unread_only, self.page, self.limit)?; - - // Return the jwt - Ok( - serde_json::to_string( - &GetRepliesResponse { - op: self.op_type().to_string(), - replies: replies, - } - )? - ) - } -} - -impl Perform for BanFromCommunity { - fn op_type(&self) -> UserOperation { - UserOperation::BanFromCommunity - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let community_user_ban_form = CommunityUserBanForm { - community_id: self.community_id, - user_id: self.user_id, - }; - - if self.ban { - match CommunityUserBan::ban(&conn, &community_user_ban_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community user ban already exists"))? - } - }; - } else { - match CommunityUserBan::unban(&conn, &community_user_ban_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community user ban already exists"))? - } - }; - } - - // Mod tables - let expires = match self.expires { - Some(time) => Some(naive_from_unix(time)), - None => None - }; - - let form = ModBanFromCommunityForm { - mod_user_id: user_id, - other_user_id: self.user_id, - community_id: self.community_id, - reason: self.reason.to_owned(), - banned: Some(self.ban), - expires: expires, - }; - ModBanFromCommunity::create(&conn, &form)?; - - let user_view = UserView::read(&conn, self.user_id)?; - - let res = serde_json::to_string( - &BanFromCommunityResponse { - op: self.op_type().to_string(), - user: user_view, - banned: self.ban - } - ) - ?; - - - chat.send_community_message(&conn, self.community_id, &res, addr)?; - - Ok(res) - } -} - -impl Perform for AddModToCommunity { - fn op_type(&self) -> UserOperation { - UserOperation::AddModToCommunity - } - - fn perform(&self, chat: &mut ChatServer, addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let community_moderator_form = CommunityModeratorForm { - community_id: self.community_id, - user_id: self.user_id - }; - - if self.added { - match CommunityModerator::join(&conn, &community_moderator_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community moderator already exists."))? - } - }; - } else { - match CommunityModerator::leave(&conn, &community_moderator_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Community moderator already exists."))? - } - }; - } - - // Mod tables - let form = ModAddCommunityForm { - mod_user_id: user_id, - other_user_id: self.user_id, - community_id: self.community_id, - removed: Some(!self.added), - }; - ModAddCommunity::create(&conn, &form)?; - - let moderators = CommunityModeratorView::for_community(&conn, self.community_id)?; - - let res = serde_json::to_string( - &AddModToCommunityResponse { - op: self.op_type().to_string(), - moderators: moderators, - } - ) - ?; - - - chat.send_community_message(&conn, self.community_id, &res, addr)?; - - Ok(res) - - } -} - -impl Perform for CreateSite { - fn op_type(&self) -> UserOperation { - UserOperation::CreateSite - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - if has_slurs(&self.name) || - (self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) { - return Err(self.error("No slurs"))? - } - - let user_id = claims.id; - - // Make sure user is an admin - if !UserView::read(&conn, user_id)?.admin { - return Err(self.error("Not an admin."))? - } - - let site_form = SiteForm { - name: self.name.to_owned(), - description: self.description.to_owned(), - creator_id: user_id, - updated: None, - }; - - match Site::create(&conn, &site_form) { - Ok(site) => site, - Err(_e) => { - return Err(self.error("Site exists already"))? - } - }; - - let site_view = SiteView::read(&conn)?; - - Ok( - serde_json::to_string( - &SiteResponse { - op: self.op_type().to_string(), - site: site_view, - } - )? - ) - } -} - -impl Perform for EditSite { - fn op_type(&self) -> UserOperation { - UserOperation::EditSite - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - if has_slurs(&self.name) || - (self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) { - return Err(self.error("No slurs"))? - } - - let user_id = claims.id; - - // Make sure user is an admin - if UserView::read(&conn, user_id)?.admin == false { - return Err(self.error("Not an admin."))? - } - - let found_site = Site::read(&conn, 1)?; - - let site_form = SiteForm { - name: self.name.to_owned(), - description: self.description.to_owned(), - creator_id: found_site.creator_id, - updated: Some(naive_now()), - }; - - match Site::update(&conn, 1, &site_form) { - Ok(site) => site, - Err(_e) => { - return Err(self.error("Couldn't update site."))? - } - }; - - let site_view = SiteView::read(&conn)?; - - Ok( - serde_json::to_string( - &SiteResponse { - op: self.op_type().to_string(), - site: site_view, - } - )? - ) - } -} - -impl Perform for GetSite { - fn op_type(&self) -> UserOperation { - UserOperation::GetSite - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - // It can return a null site in order to redirect - let site_view = match Site::read(&conn, 1) { - Ok(_site) => Some(SiteView::read(&conn)?), - Err(_e) => None - }; - - let admins = UserView::admins(&conn)?; - let banned = UserView::banned(&conn)?; - - Ok( - serde_json::to_string( - &GetSiteResponse { - op: self.op_type().to_string(), - site: site_view, - admins: admins, - banned: banned, - } - )? - ) - } -} - -impl Perform for AddAdmin { - fn op_type(&self) -> UserOperation { - UserOperation::AddAdmin - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Make sure user is an admin - if UserView::read(&conn, user_id)?.admin == false { - return Err(self.error("Not an admin."))? - } - - let read_user = User_::read(&conn, self.user_id)?; - - let user_form = UserForm { - name: read_user.name, - fedi_name: read_user.fedi_name, - email: read_user.email, - password_encrypted: read_user.password_encrypted, - preferred_username: read_user.preferred_username, - updated: Some(naive_now()), - admin: self.added, - banned: read_user.banned, - }; - - match User_::update(&conn, self.user_id, &user_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Couldn't update user"))? - } - }; - - // Mod tables - let form = ModAddForm { - mod_user_id: user_id, - other_user_id: self.user_id, - removed: Some(!self.added), - }; - - ModAdd::create(&conn, &form)?; - - let admins = UserView::admins(&conn)?; - - let res = serde_json::to_string( - &AddAdminResponse { - op: self.op_type().to_string(), - admins: admins, - } - ) - ?; - - - Ok(res) - - } -} - -impl Perform for BanUser { - fn op_type(&self) -> UserOperation { - UserOperation::BanUser - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - // Make sure user is an admin - if UserView::read(&conn, user_id)?.admin == false { - return Err(self.error("Not an admin."))? - } - - let read_user = User_::read(&conn, self.user_id)?; - - let user_form = UserForm { - name: read_user.name, - fedi_name: read_user.fedi_name, - email: read_user.email, - password_encrypted: read_user.password_encrypted, - preferred_username: read_user.preferred_username, - updated: Some(naive_now()), - admin: read_user.admin, - banned: self.ban, - }; - - match User_::update(&conn, self.user_id, &user_form) { - Ok(user) => user, - Err(_e) => { - return Err(self.error("Couldn't update user"))? - } - }; - - // Mod tables - let expires = match self.expires { - Some(time) => Some(naive_from_unix(time)), - None => None - }; - - let form = ModBanForm { - mod_user_id: user_id, - other_user_id: self.user_id, - reason: self.reason.to_owned(), - banned: Some(self.ban), - expires: expires, - }; - - ModBan::create(&conn, &form)?; - - let user_view = UserView::read(&conn, self.user_id)?; - - let res = serde_json::to_string( - &BanUserResponse { - op: self.op_type().to_string(), - user: user_view, - banned: self.ban - } - ) - ?; - - Ok(res) - - } -} - -impl Perform for Search { - fn op_type(&self) -> UserOperation { - UserOperation::Search - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let sort = SortType::from_str(&self.sort)?; - let type_ = SearchType::from_str(&self.type_)?; - - let mut posts = Vec::new(); - let mut comments = Vec::new(); - - match type_ { - SearchType::Posts => { - posts = PostView::list(&conn, - PostListingType::All, - &sort, - self.community_id, - None, - Some(self.q.to_owned()), - None, - false, - false, - self.page, - self.limit)?; - }, - SearchType::Comments => { - comments = CommentView::list(&conn, - &sort, - None, - None, - Some(self.q.to_owned()), - None, - false, - self.page, - self.limit)?; - }, - SearchType::Both => { - posts = PostView::list(&conn, - PostListingType::All, - &sort, - self.community_id, - None, - Some(self.q.to_owned()), - None, - false, - false, - self.page, - self.limit)?; - comments = CommentView::list(&conn, - &sort, - None, - None, - Some(self.q.to_owned()), - None, - false, - self.page, - self.limit)?; - } - }; - - - // Return the jwt - Ok( - serde_json::to_string( - &SearchResponse { - op: self.op_type().to_string(), - comments: comments, - posts: posts, - } - )? - ) - } -} - - -impl Perform for MarkAllAsRead { - fn op_type(&self) -> UserOperation { - UserOperation::MarkAllAsRead - } - - fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result { - - let conn = establish_connection(); - - let claims = match Claims::decode(&self.auth) { - Ok(claims) => claims.claims, - Err(_e) => { - return Err(self.error("Not logged in."))? - } - }; - - let user_id = claims.id; - - let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?; - - for reply in &replies { - let comment_form = CommentForm { - content: reply.to_owned().content, - parent_id: reply.to_owned().parent_id, - post_id: reply.to_owned().post_id, - creator_id: reply.to_owned().creator_id, - removed: None, - deleted: None, - read: Some(true), - updated: reply.to_owned().updated - }; - - let _updated_comment = match Comment::update(&conn, reply.id, &comment_form) { - Ok(comment) => comment, - Err(_e) => { - return Err(self.error("Couldn't update Comment"))? - } - }; - } - - let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?; - - Ok( - serde_json::to_string( - &GetRepliesResponse { - op: self.op_type().to_string(), - replies: replies, - } - )? - ) - } -} From f8615b6178ed2c2863de6cb98988b97d01b46786 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 5 May 2019 09:20:30 -0700 Subject: [PATCH 4/5] Done with reorg --- server/src/api/comment.rs | 42 ++++++++++---------- server/src/api/community.rs | 66 +++++++++++++++---------------- server/src/api/mod.rs | 6 +-- server/src/api/post.rs | 58 +++++++++++++-------------- server/src/api/site.rs | 28 +++++++------- server/src/api/user.rs | 50 ++++++++++++------------ server/src/db/category.rs | 1 - server/src/db/comment.rs | 2 - server/src/db/comment_view.rs | 2 - server/src/db/community.rs | 2 - server/src/db/mod.rs | 9 +++++ server/src/db/moderator.rs | 1 - server/src/db/post.rs | 2 - server/src/db/post_view.rs | 1 - server/src/db/user.rs | 4 +- server/src/lib.rs | 6 +-- server/src/main.rs | 71 +--------------------------------- server/src/websocket/server.rs | 35 ++++++++++------- 18 files changed, 158 insertions(+), 228 deletions(-) diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index 36a44b363..65516aca2 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -47,13 +47,13 @@ pub struct CreateCommentLike { impl Perform for Oper { fn perform(&self) -> Result { - let data: CreateComment = self.data; + let data: &CreateComment = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -62,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, "You have been banned from this community"))? + return Err(APIError::err(&self.op, "You have been banned from this community"))? } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } let content_slurs_removed = remove_slurs(&data.content.to_owned()); @@ -86,7 +86,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, "Couldn't create Comment"))? + return Err(APIError::err(&self.op, "Couldn't create Comment"))? } }; @@ -101,7 +101,7 @@ impl Perform for Oper { let _inserted_like = match CommentLike::like(&conn, &like_form) { Ok(like) => like, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't like comment."))? + return Err(APIError::err(&self.op, "Couldn't like comment."))? } }; @@ -118,13 +118,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: EditComment = self.data; + let data: &EditComment = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -153,17 +153,17 @@ impl Perform for Oper { ); if !editors.contains(&user_id) { - return Err(APIError::err(self.op, "Not allowed to edit comment."))? + return Err(APIError::err(&self.op, "Not allowed to edit comment."))? } // Check for a community ban if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() { - return Err(APIError::err(self.op, "You have been banned from this community"))? + return Err(APIError::err(&self.op, "You have been banned from this community"))? } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } } @@ -184,7 +184,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, "Couldn't update Comment"))? + return Err(APIError::err(&self.op, "Couldn't update Comment"))? } }; @@ -214,13 +214,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: SaveComment = self.data; + let data: &SaveComment = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -235,14 +235,14 @@ impl Perform for Oper { match CommentSaved::save(&conn, &comment_saved_form) { Ok(comment) => comment, Err(_e) => { - return Err(APIError::err(self.op, "Couldnt do comment save"))? + return Err(APIError::err(&self.op, "Couldnt do comment save"))? } }; } else { match CommentSaved::unsave(&conn, &comment_saved_form) { Ok(comment) => comment, Err(_e) => { - return Err(APIError::err(self.op, "Couldnt do comment save"))? + return Err(APIError::err(&self.op, "Couldnt do comment save"))? } }; } @@ -260,13 +260,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: CreateCommentLike = self.data; + let data: &CreateCommentLike = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -275,12 +275,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, "You have been banned from this community"))? + return Err(APIError::err(&self.op, "You have been banned from this community"))? } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } let like_form = CommentLikeForm { @@ -298,7 +298,7 @@ impl Perform for Oper { let _inserted_like = match CommentLike::like(&conn, &like_form) { Ok(like) => like, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't like comment."))? + return Err(APIError::err(&self.op, "Couldn't like comment."))? } }; } diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 5059b17f0..be4bb41aa 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -46,7 +46,7 @@ pub struct ListCommunitiesResponse { communities: Vec } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct BanFromCommunity { pub community_id: i32, user_id: i32, @@ -111,7 +111,7 @@ pub struct GetFollowedCommunitiesResponse { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetCommunity = self.data; + let data: &GetCommunity = &self.data; let conn = establish_connection(); let user_id: Option = match &data.auth { @@ -135,14 +135,14 @@ impl Perform for Oper { let community_view = match CommunityView::read(&conn, community_id, user_id) { Ok(community) => community, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't find Community"))? + return Err(APIError::err(&self.op, "Couldn't find Community"))? } }; let moderators = match CommunityModeratorView::for_community(&conn, community_id) { Ok(moderators) => moderators, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't find Community"))? + return Err(APIError::err(&self.op, "Couldn't find Community"))? } }; @@ -162,27 +162,27 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: CreateCommunity = self.data; + let data: &CreateCommunity = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; 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"))? + return Err(APIError::err(&self.op, "No slurs"))? } let user_id = claims.id; // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } // When you create a community, make sure the user becomes a moderator and a follower @@ -200,7 +200,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."))? + return Err(APIError::err(&self.op, "Community already exists."))? } }; @@ -212,7 +212,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."))? + return Err(APIError::err(&self.op, "Community moderator already exists."))? } }; @@ -224,7 +224,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."))? + return Err(APIError::err(&self.op, "Community follower already exists."))? } }; @@ -241,10 +241,10 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: EditCommunity = self.data; + let data: &EditCommunity = &self.data; if has_slurs(&data.name) || has_slurs(&data.title) { - return Err(APIError::err(self.op, "No slurs"))? + return Err(APIError::err(&self.op, "No slurs"))? } let conn = establish_connection(); @@ -252,7 +252,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."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -260,7 +260,7 @@ impl Perform for Oper { // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } // Verify its a mod @@ -280,7 +280,7 @@ impl Perform for Oper { .collect() ); if !editors.contains(&user_id) { - return Err(APIError::err(self.op, "Not allowed to edit community"))? + return Err(APIError::err(&self.op, "Not allowed to edit community"))? } let community_form = CommunityForm { @@ -297,7 +297,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, "Couldn't update Community"))? + return Err(APIError::err(&self.op, "Couldn't update Community"))? } }; @@ -330,7 +330,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: ListCommunities = self.data; + let data: &ListCommunities = &self.data; let conn = establish_connection(); let user_id: Option = match &data.auth { @@ -363,13 +363,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: FollowCommunity = self.data; + let data: &FollowCommunity = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -384,14 +384,14 @@ impl Perform for Oper { match CommunityFollower::follow(&conn, &community_follower_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "Community follower already exists."))? + return Err(APIError::err(&self.op, "Community follower already exists."))? } }; } else { match CommunityFollower::ignore(&conn, &community_follower_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "Community follower already exists."))? + return Err(APIError::err(&self.op, "Community follower already exists."))? } }; } @@ -410,13 +410,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetFollowedCommunities = self.data; + let data: &GetFollowedCommunities = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -425,7 +425,7 @@ 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 error, try logging out and back in."))? + return Err(APIError::err(&self.op, "System error, try logging out and back in."))? } }; @@ -442,13 +442,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: BanFromCommunity = self.data; + let data: &BanFromCommunity = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -463,14 +463,14 @@ impl Perform for Oper { match CommunityUserBan::ban(&conn, &community_user_ban_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "Community user ban already exists"))? + return Err(APIError::err(&self.op, "Community user ban already exists"))? } }; } else { match CommunityUserBan::unban(&conn, &community_user_ban_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "Community user ban already exists"))? + return Err(APIError::err(&self.op, "Community user ban already exists"))? } }; } @@ -505,13 +505,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: AddModToCommunity = self.data; + let data: &AddModToCommunity = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -526,14 +526,14 @@ impl Perform for Oper { match CommunityModerator::join(&conn, &community_moderator_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "Community moderator already exists."))? + return Err(APIError::err(&self.op, "Community moderator already exists."))? } }; } else { match CommunityModerator::leave(&conn, &community_moderator_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "Community moderator already exists."))? + return Err(APIError::err(&self.op, "Community moderator already exists."))? } }; } diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 4bf6f7fee..6e3e8269d 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -28,12 +28,12 @@ pub enum UserOperation { #[derive(Fail, Debug)] #[fail(display = "{{\"op\":\"{}\", \"error\":\"{}\"}}", op, message)] pub struct APIError { - op: String, - message: String, + pub op: String, + pub message: String, } impl APIError { - pub fn err(op: UserOperation, msg: &str) -> Self { + pub fn err(op: &UserOperation, msg: &str) -> Self { APIError { op: op.to_string(), message: msg.to_string(), diff --git a/server/src/api/post.rs b/server/src/api/post.rs index e7bec69e8..39df95462 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -87,32 +87,32 @@ pub struct SavePost { impl Perform for Oper { fn perform(&self) -> Result { - let data: CreatePost = self.data; + let data: &CreatePost = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) { - return Err(APIError::err(self.op, "No slurs"))? + return Err(APIError::err(&self.op, "No slurs"))? } 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, "You have been banned from this community"))? + return Err(APIError::err(&self.op, "You have been banned from this community"))? } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } let post_form = PostForm { @@ -130,7 +130,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, "Couldn't create Post"))? + return Err(APIError::err(&self.op, "Couldn't create Post"))? } }; @@ -145,7 +145,7 @@ impl Perform for Oper { let _inserted_like = match PostLike::like(&conn, &like_form) { Ok(like) => like, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't like post."))? + return Err(APIError::err(&self.op, "Couldn't like post."))? } }; @@ -153,7 +153,7 @@ impl Perform for Oper { let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) { Ok(post) => post, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't find Post"))? + return Err(APIError::err(&self.op, "Couldn't find Post"))? } }; @@ -168,7 +168,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetPost = self.data; + let data: &GetPost = &self.data; let conn = establish_connection(); let user_id: Option = match &data.auth { @@ -187,7 +187,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, "Couldn't find Post"))? + return Err(APIError::err(&self.op, "Couldn't find Post"))? } }; @@ -216,7 +216,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetPosts = self.data; + let data: &GetPosts = &self.data; let conn = establish_connection(); let user_id: Option = match &data.auth { @@ -248,7 +248,7 @@ impl Perform for Oper { data.limit) { Ok(posts) => posts, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't get posts"))? + return Err(APIError::err(&self.op, "Couldn't get posts"))? } }; @@ -264,13 +264,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: CreatePostLike = self.data; + let data: &CreatePostLike = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -279,12 +279,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, "You have been banned from this community"))? + return Err(APIError::err(&self.op, "You have been banned from this community"))? } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } let like_form = PostLikeForm { @@ -301,7 +301,7 @@ impl Perform for Oper { let _inserted_like = match PostLike::like(&conn, &like_form) { Ok(like) => like, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't like post."))? + return Err(APIError::err(&self.op, "Couldn't like post."))? } }; } @@ -309,7 +309,7 @@ impl Perform for Oper { let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) { Ok(post) => post, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't find Post"))? + return Err(APIError::err(&self.op, "Couldn't find Post"))? } }; @@ -325,10 +325,10 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: EditPost = self.data; + 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"))? + return Err(APIError::err(&self.op, "No slurs"))? } let conn = establish_connection(); @@ -336,7 +336,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."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -359,17 +359,17 @@ impl Perform for Oper { .collect() ); if !editors.contains(&user_id) { - return Err(APIError::err(self.op, "Not allowed to edit post."))? + return Err(APIError::err(&self.op, "Not allowed to edit post."))? } // Check for a community ban if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { - return Err(APIError::err(self.op, "You have been banned from this community"))? + return Err(APIError::err(&self.op, "You have been banned from this community"))? } // Check for a site ban if UserView::read(&conn, user_id)?.banned { - return Err(APIError::err(self.op, "You have been banned from the site"))? + return Err(APIError::err(&self.op, "You have been banned from the site"))? } let post_form = PostForm { @@ -387,7 +387,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, "Couldn't update Post"))? + return Err(APIError::err(&self.op, "Couldn't update Post"))? } }; @@ -424,13 +424,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: SavePost = self.data; + let data: &SavePost = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -445,14 +445,14 @@ impl Perform for Oper { match PostSaved::save(&conn, &post_saved_form) { Ok(post) => post, Err(_e) => { - return Err(APIError::err(self.op, "Couldnt do post save"))? + return Err(APIError::err(&self.op, "Couldnt do post save"))? } }; } else { match PostSaved::unsave(&conn, &post_saved_form) { Ok(post) => post, Err(_e) => { - return Err(APIError::err(self.op, "Couldnt do post save"))? + return Err(APIError::err(&self.op, "Couldnt do post save"))? } }; } diff --git a/server/src/api/site.rs b/server/src/api/site.rs index 3140788da..03ee90ff1 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -83,7 +83,7 @@ pub struct GetSiteResponse { impl Perform for Oper { fn perform(&self) -> Result { - let data: ListCategories = self.data; + let _data: &ListCategories = &self.data; let conn = establish_connection(); let categories: Vec = Category::list_all(&conn)?; @@ -100,7 +100,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetModlog = self.data; + let data: &GetModlog = &self.data; let conn = establish_connection(); let removed_posts = ModRemovePostView::list(&conn, data.community_id, data.mod_user_id, data.page, data.limit)?; @@ -139,26 +139,26 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: CreateSite = self.data; + let data: &CreateSite = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; if has_slurs(&data.name) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { - return Err(APIError::err(self.op, "No slurs"))? + return Err(APIError::err(&self.op, "No slurs"))? } 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."))? + return Err(APIError::err(&self.op, "Not an admin."))? } let site_form = SiteForm { @@ -171,7 +171,7 @@ impl Perform for Oper { match Site::create(&conn, &site_form) { Ok(site) => site, Err(_e) => { - return Err(APIError::err(self.op, "Site exists already"))? + return Err(APIError::err(&self.op, "Site exists already"))? } }; @@ -189,26 +189,26 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: EditSite = self.data; + let data: &EditSite = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; if has_slurs(&data.name) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) { - return Err(APIError::err(self.op, "No slurs"))? + return Err(APIError::err(&self.op, "No slurs"))? } let user_id = claims.id; // Make sure user is an admin if UserView::read(&conn, user_id)?.admin == false { - return Err(APIError::err(self.op, "Not an admin."))? + return Err(APIError::err(&self.op, "Not an admin."))? } let found_site = Site::read(&conn, 1)?; @@ -223,7 +223,7 @@ impl Perform for Oper { match Site::update(&conn, 1, &site_form) { Ok(site) => site, Err(_e) => { - return Err(APIError::err(self.op, "Couldn't update site."))? + return Err(APIError::err(&self.op, "Couldn't update site."))? } }; @@ -240,7 +240,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetSite = self.data; + let _data: &GetSite = &self.data; let conn = establish_connection(); // It can return a null site in order to redirect @@ -265,7 +265,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: Search = self.data; + let data: &Search = &self.data; let conn = establish_connection(); let sort = SortType::from_str(&data.sort)?; diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 82ec11cc9..ab0f24a5a 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -97,19 +97,19 @@ pub struct GetReplies { impl Perform for Oper { fn perform(&self) -> Result { - let data: Login = self.data; + let data: &Login = &self.data; let conn = establish_connection(); // 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, "Couldn't find that username or email"))? + Err(_e) => return Err(APIError::err(&self.op, "Couldn't find that username or email"))? }; // 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"))? + return Err(APIError::err(&self.op, "Password incorrect"))? } // Return the jwt @@ -125,25 +125,25 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: Register = self.data; + let data: &Register = &self.data; let conn = establish_connection(); // Make sure passwords match if &data.password != &data.password_verify { - return Err(APIError::err(self.op, "Passwords do not match."))? + return Err(APIError::err(&self.op, "Passwords do not match."))? } if data.spam_timeri < 1142 { - return Err(APIError::err(self.op, "Too fast"))? + return Err(APIError::err(&self.op, "Too fast"))? } if has_slurs(&data.username) { - return Err(APIError::err(self.op, "No slurs"))? + return Err(APIError::err(&self.op, "No slurs"))? } // Make sure there are no admins if data.admin && UserView::admins(&conn)?.len() > 0 { - return Err(APIError::err(self.op, "Sorry, there's already an admin."))? + return Err(APIError::err(&self.op, "Sorry, there's already an admin."))? } // Register the new user @@ -162,7 +162,7 @@ impl Perform for Oper { let inserted_user = match User_::register(&conn, &user_form) { Ok(user) => user, Err(_e) => { - return Err(APIError::err(self.op, "User already exists."))? + return Err(APIError::err(&self.op, "User already exists."))? } }; @@ -175,7 +175,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."))? + return Err(APIError::err(&self.op, "Community follower already exists."))? } }; @@ -189,7 +189,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."))? + return Err(APIError::err(&self.op, "Community moderator already exists."))? } }; @@ -208,7 +208,7 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetUserDetails = self.data; + let data: &GetUserDetails = &self.data; let conn = establish_connection(); let user_id: Option = match &data.auth { @@ -302,13 +302,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: AddAdmin = self.data; + let data: &AddAdmin = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -316,7 +316,7 @@ impl Perform for Oper { // Make sure user is an admin if UserView::read(&conn, user_id)?.admin == false { - return Err(APIError::err(self.op, "Not an admin."))? + return Err(APIError::err(&self.op, "Not an admin."))? } let read_user = User_::read(&conn, data.user_id)?; @@ -335,7 +335,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, "Couldn't update user"))? + return Err(APIError::err(&self.op, "Couldn't update user"))? } }; @@ -361,13 +361,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: BanUser = self.data; + let data: &BanUser = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -375,7 +375,7 @@ impl Perform for Oper { // Make sure user is an admin if UserView::read(&conn, user_id)?.admin == false { - return Err(APIError::err(self.op, "Not an admin."))? + return Err(APIError::err(&self.op, "Not an admin."))? } let read_user = User_::read(&conn, data.user_id)?; @@ -394,7 +394,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, "Couldn't update user"))? + return Err(APIError::err(&self.op, "Couldn't update user"))? } }; @@ -429,13 +429,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: GetReplies = self.data; + let data: &GetReplies = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -457,13 +457,13 @@ impl Perform for Oper { impl Perform for Oper { fn perform(&self) -> Result { - let data: MarkAllAsRead = self.data; + let data: &MarkAllAsRead = &self.data; let conn = establish_connection(); let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => { - return Err(APIError::err(self.op, "Not logged in."))? + return Err(APIError::err(&self.op, "Not logged in."))? } }; @@ -486,7 +486,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, "Couldn't update Comment"))? + return Err(APIError::err(&self.op, "Couldn't update Comment"))? } }; } diff --git a/server/src/db/category.rs b/server/src/db/category.rs index 7835fedd8..99f906d41 100644 --- a/server/src/db/category.rs +++ b/server/src/db/category.rs @@ -47,7 +47,6 @@ impl Category { #[cfg(test)] mod tests { - use establish_connection; use super::*; #[test] fn test_crud() { diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs index 4e57aaa42..a924bd4c8 100644 --- a/server/src/db/comment.rs +++ b/server/src/db/comment.rs @@ -154,12 +154,10 @@ impl Saveable for CommentSaved { #[cfg(test)] mod tests { - use establish_connection; use super::*; use super::super::post::*; use super::super::community::*; use super::super::user::*; - use Crud; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index eb1d302f7..9cd61e33e 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -244,13 +244,11 @@ impl ReplyView { #[cfg(test)] mod tests { - use establish_connection; use super::*; use super::super::post::*; use super::super::community::*; use super::super::user::*; use super::super::comment::*; - use {Crud,Likeable}; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/db/community.rs b/server/src/db/community.rs index a2c300f29..4540f731a 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -215,10 +215,8 @@ impl Crud for Site { #[cfg(test)] mod tests { - use establish_connection; use super::*; use super::super::user::*; - use Crud; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs index a3edb1b9c..c3587c476 100644 --- a/server/src/db/mod.rs +++ b/server/src/db/mod.rs @@ -81,3 +81,12 @@ pub fn limit_and_offset(page: Option, limit: Option) -> (i64, i64) { let offset = limit * (page - 1); (limit, offset) } +#[cfg(test)] +mod tests { + use super::fuzzy_search; + #[test] fn test_fuzzy_search() { + let test = "This is a fuzzy search"; + assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string()); + } +} + diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs index c23cf9e64..8b85e6631 100644 --- a/server/src/db/moderator.rs +++ b/server/src/db/moderator.rs @@ -394,7 +394,6 @@ impl Crud for ModAdd { #[cfg(test)] mod tests { - use establish_connection; use super::*; use super::super::user::*; use super::super::post::*; diff --git a/server/src/db/post.rs b/server/src/db/post.rs index 009543d3b..f03022717 100644 --- a/server/src/db/post.rs +++ b/server/src/db/post.rs @@ -168,8 +168,6 @@ impl Readable for PostRead { #[cfg(test)] mod tests { - use establish_connection; - use Crud; use super::*; use super::super::community::*; use super::super::user::*; diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs index f51eab10d..bfe730a2b 100644 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@ -174,7 +174,6 @@ impl PostView { #[cfg(test)] mod tests { - use {establish_connection, Crud, Likeable}; use super::*; use super::super::community::*; use super::super::user::*; diff --git a/server/src/db/user.rs b/server/src/db/user.rs index 254030691..a4a7be43f 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -120,9 +120,7 @@ impl User_ { #[cfg(test)] mod tests { - use establish_connection; - use super::{User_, UserForm}; - use Crud; + use super::*; #[test] fn test_crud() { let conn = establish_connection(); diff --git a/server/src/lib.rs b/server/src/lib.rs index 9d87d704d..7dd090d67 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -72,7 +72,7 @@ pub fn has_slurs(test: &str) -> bool { #[cfg(test)] mod tests { - use {Settings, is_email_regex, remove_slurs, has_slurs, fuzzy_search}; + use {Settings, is_email_regex, remove_slurs, has_slurs}; #[test] fn test_api() { assert_eq!(Settings::get().api_endpoint(), "rrr/api/v1"); @@ -91,10 +91,6 @@ mod tests { assert!(!has_slurs(slur_free)); } - #[test] fn test_fuzzy_search() { - let test = "This is a fuzzy search"; - assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string()); - } } lazy_static! { diff --git a/server/src/main.rs b/server/src/main.rs index add17cfd6..fc27531b9 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -7,7 +7,7 @@ use lemmy_server::actix::*; use lemmy_server::actix_web::server::HttpServer; use lemmy_server::actix_web::{ws, App, Error, HttpRequest, HttpResponse, fs::NamedFile, fs}; use lemmy_server::websocket::server::*; -use lemmy_server::establish_connection; +use lemmy_server::db::establish_connection; embed_migrations!(); @@ -117,6 +117,7 @@ impl StreamHandler for WSSession { } ws::Message::Text(text) => { let m = text.trim().to_owned(); + println!("WEBSOCKET MESSAGE: {:?} from id: {}", &m, self.id); ctx.state() .addr @@ -130,79 +131,11 @@ impl StreamHandler for WSSession { Ok(res) => ctx.text(res), Err(e) => { eprintln!("{}", &e); - // ctx.text(e); } } - // Ok(res) => ctx.text(res), - // // something is wrong with chat server - // _ => ctx.stop(), fut::ok(()) }) .wait(ctx); - - // we check for /sss type of messages - // if m.starts_with('/') { - // let v: Vec<&str> = m.splitn(2, ' ').collect(); - // match v[0] { - // "/list" => { - // // Send ListRooms message to chat server and wait for - // // response - // println!("List rooms"); - // ctx.state() - // .addr - // .send(ListRooms) - // .into_actor(self) - // .then(|res, _, ctx| { - // match res { - // Ok(rooms) => { - // for room in rooms { - // ctx.text(room); - // } - // } - // _ => println!("Something is wrong"), - // } - // fut::ok(()) - // }) - // .wait(ctx) - // .wait(ctx) pauses all events in context, - // so actor wont receive any new messages until it get list - // of rooms back - // } - // "/join" => { - // if v.len() == 2 { - // self.room = v[1].to_owned(); - // ctx.state().addr.do_send(Join { - // id: self.id, - // name: self.room.clone(), - // }); - - // ctx.text("joined"); - // } else { - // ctx.text("!!! room name is required"); - // } - // } - // "/name" => { - // if v.len() == 2 { - // self.name = Some(v[1].to_owned()); - // } else { - // ctx.text("!!! name is required"); - // } - // } - // _ => ctx.text(format!("!!! unknown command: {:?}", m)), - // } - // } else { - // let msg = if let Some(ref name) = self.name { - // format!("{}: {}", name, m) - // } else { - // m.to_owned() - // }; - // send message to chat server - // ctx.state().addr.do_send(ClientMessage { - // id: self.id, - // msg: msg, - // room: self.room.clone(), - // }) - // } } ws::Message::Binary(_bin) => println!("Unexpected binary"), ws::Message::Close(_) => { diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 857c626a8..cd931e1fa 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -104,8 +104,8 @@ impl Default for ChatServer { impl ChatServer { /// Send message to all users in the room - fn send_room_message(&self, room: i32, message: &str, skip_id: usize) { - if let Some(sessions) = self.rooms.get(&room) { + fn send_room_message(&self, room: &i32, message: &str, skip_id: usize) { + if let Some(sessions) = self.rooms.get(room) { for id in sessions { if *id != skip_id { if let Some(info) = self.sessions.get(id) { @@ -116,9 +116,9 @@ impl ChatServer { } } - fn join_room(&self, room_id: i32, id: usize) { + fn join_room(&mut self, room_id: i32, id: usize) { // remove session from all rooms - for (_n, sessions) in &mut self.rooms { + for (_n, mut sessions) in &mut self.rooms { sessions.remove(&id); } @@ -127,17 +127,17 @@ impl ChatServer { self.rooms.insert(room_id, HashSet::new()); } - self.rooms.get_mut(&room_id).unwrap().insert(id); + &self.rooms.get_mut(&room_id).unwrap().insert(id); } - fn send_community_message(&self, community_id: i32, message: &str, skip_id: usize) -> Result<(), Error> { + fn send_community_message(&self, community_id: &i32, message: &str, skip_id: usize) -> Result<(), Error> { use db::*; use db::post_view::*; let conn = establish_connection(); let posts = PostView::list(&conn, PostListingType::Community, &SortType::New, - Some(community_id), + Some(*community_id), None, None, None, @@ -146,7 +146,7 @@ impl ChatServer { None, Some(9999))?; for post in posts { - self.send_room_message(post.id, message, skip_id); + self.send_room_message(&post.id, message, skip_id); } Ok(()) @@ -346,7 +346,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { @@ -361,16 +361,18 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { 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()?; let res_str = serde_json::to_string(&res)?; - chat.send_community_message(ban_from_community.community_id, &res_str, msg.id)?; + chat.send_community_message(&community_id, &res_str, msg.id)?; Ok(res_str) }, UserOperation::AddModToCommunity => { 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()?; let res_str = serde_json::to_string(&res)?; - chat.send_community_message(mod_add_to_community.community_id, &res_str, msg.id)?; + chat.send_community_message(&community_id, &res_str, msg.id)?; Ok(res_str) }, UserOperation::ListCategories => { @@ -407,7 +409,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { @@ -418,22 +420,24 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { chat.check_rate_limit(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()?; 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(create_comment.post_id, &comment_sent_str, msg.id); + chat.send_room_message(&post_id, &comment_sent_str, msg.id); Ok(serde_json::to_string(&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()?; 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(edit_comment.post_id, &comment_sent_str, msg.id); + chat.send_room_message(&post_id, &comment_sent_str, msg.id); Ok(serde_json::to_string(&res)?) }, UserOperation::SaveComment => { @@ -444,12 +448,13 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { chat.check_rate_limit(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()?; 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(create_comment_like.post_id, &comment_sent_str, msg.id); + chat.send_room_message(&post_id, &comment_sent_str, msg.id); Ok(serde_json::to_string(&res)?) }, UserOperation::GetModlog => { From f2bdc78d2a6056d0bb7c22d892227c28198b144c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 5 May 2019 10:00:17 -0700 Subject: [PATCH 5/5] Fixing docker file. --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e794b249b..f30d57bb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ COPY server/Cargo.toml server/Cargo.lock ./ # this build step will cache your dependencies RUN mkdir -p ./src/bin \ && echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs -RUN cargo build --release --bin lemmy +RUN cargo build --release RUN rm -r ./target/release/.fingerprint/lemmy_server-* # copy your source tree @@ -29,8 +29,8 @@ COPY server/src ./src/ COPY server/migrations ./migrations/ # build for release -RUN cargo build --frozen --release --bin lemmy -RUN mv /app/server/target/release/lemmy /app/lemmy +RUN cargo build --frozen --release +RUN mv /app/server/target/release/lemmy_server /app/lemmy # Get diesel-cli on there just in case # RUN cargo install diesel_cli --no-default-features --features postgres