diff options
author | Dessalines <happydooby@gmail.com> | 2019-05-05 10:00:57 -0700 |
---|---|---|
committer | Dessalines <happydooby@gmail.com> | 2019-05-05 10:00:57 -0700 |
commit | f34ba11cbb004d6f7da2e639d26183616d7bd9c7 (patch) | |
tree | a2b5207e6b8178359cdc74da842757b87f98951e | |
parent | ef169abbc53d74298f6e2ffbbbe60ba1e5d71b29 (diff) | |
parent | c05a301bd214acc3d384573835833ee3905c5503 (diff) |
Merge branch 'reorg' into dev
- Fixes #146
-rw-r--r-- | Dockerfile | 8 | ||||
-rw-r--r-- | server/Cargo.lock | 48 | ||||
-rw-r--r-- | server/Cargo.toml | 7 | ||||
-rw-r--r-- | server/src/actions/mod.rs | 11 | ||||
-rw-r--r-- | server/src/api/comment.rs | 316 | ||||
-rw-r--r-- | server/src/api/community.rs | 559 | ||||
-rw-r--r-- | server/src/api/mod.rs | 60 | ||||
-rw-r--r-- | server/src/api/post.rs | 469 | ||||
-rw-r--r-- | server/src/api/site.rs | 336 | ||||
-rw-r--r-- | server/src/api/user.rs | 503 | ||||
-rw-r--r-- | server/src/apub.rs | 2 | ||||
-rw-r--r-- | server/src/db/category.rs (renamed from server/src/actions/category.rs) | 14 | ||||
-rw-r--r-- | server/src/db/comment.rs (renamed from server/src/actions/comment.rs) | 16 | ||||
-rw-r--r-- | server/src/db/comment_view.rs (renamed from server/src/actions/comment_view.rs) | 23 | ||||
-rw-r--r-- | server/src/db/community.rs (renamed from server/src/actions/community.rs) | 10 | ||||
-rw-r--r-- | server/src/db/community_view.rs (renamed from server/src/actions/community_view.rs) | 26 | ||||
-rw-r--r-- | server/src/db/mod.rs | 92 | ||||
-rw-r--r-- | server/src/db/moderator.rs (renamed from server/src/actions/moderator.rs) | 15 | ||||
-rw-r--r-- | server/src/db/moderator_views.rs (renamed from server/src/actions/moderator_views.rs) | 22 | ||||
-rw-r--r-- | server/src/db/post.rs (renamed from server/src/actions/post.rs) | 12 | ||||
-rw-r--r-- | server/src/db/post_view.rs (renamed from server/src/actions/post_view.rs) | 18 | ||||
-rw-r--r-- | server/src/db/user.rs (renamed from server/src/actions/user.rs) | 11 | ||||
-rw-r--r-- | server/src/db/user_view.rs (renamed from server/src/actions/user_view.rs) | 11 | ||||
-rw-r--r-- | server/src/lib.rs | 93 | ||||
-rw-r--r-- | server/src/main.rs (renamed from server/src/bin/main.rs) | 90 | ||||
-rw-r--r-- | server/src/websocket/mod.rs (renamed from server/src/websocket_server/mod.rs) | 0 | ||||
-rw-r--r-- | server/src/websocket/server.rs | 486 | ||||
-rw-r--r-- | server/src/websocket_server/server.rs | 2890 |
28 files changed, 2932 insertions, 3216 deletions
@@ -20,8 +20,8 @@ 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 rm -r ./target/release/.fingerprint/server-* +RUN cargo build --release +RUN rm -r ./target/release/.fingerprint/lemmy_server-* # copy your source tree # RUN rm -rf ./src/ @@ -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 diff --git a/server/Cargo.lock b/server/Cargo.lock index 36d2a901..659bd235 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -753,6 +753,30 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1345,30 +1369,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/server/Cargo.toml b/server/Cargo.toml index 1daaca66..5ee8b8a6 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 <happydooby@gmail.com>"] -autobins = false - -[[bin]] -name = "lemmy" -path = "src/bin/main.rs" [dependencies] diesel = { version = "1.4.2", features = ["postgres","chrono"] } diff --git a/server/src/actions/mod.rs b/server/src/actions/mod.rs deleted file mode 100644 index ece1e885..00000000 --- a/server/src/actions/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod user; -pub mod community; -pub mod post; -pub mod comment; -pub mod post_view; -pub mod comment_view; -pub mod category; -pub mod community_view; -pub mod user_view; -pub mod moderator; -pub mod moderator_views; diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs new file mode 100644 index 00000000..65516aca --- /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<i32>, + edit_id: Option<i32>, + pub post_id: i32, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct EditComment { + content: String, + parent_id: Option<i32>, + edit_id: i32, + creator_id: i32, + pub post_id: i32, + removed: Option<bool>, + deleted: Option<bool>, + reason: Option<String>, + read: Option<bool>, + 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<CommentResponse> for Oper<CreateComment> { + fn perform(&self) -> Result<CommentResponse, Error> { + 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<CommentResponse> for Oper<EditComment> { + fn perform(&self) -> Result<CommentResponse, Error> { + 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<i32> = 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<CommentResponse> for Oper<SaveComment> { + fn perform(&self) -> Result<CommentResponse, Error> { + 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<CommentResponse> for Oper<CreateCommentLike> { + fn perform(&self) -> Result<CommentResponse, Error> { + 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 00000000..be4bb41a --- /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<i32>, + name: Option<String>, + auth: Option<String> +} + +#[derive(Serialize, Deserialize)] +pub struct GetCommunityResponse { + op: String, + community: CommunityView, + moderators: Vec<CommunityModeratorView>, + admins: Vec<UserView>, +} + + +#[derive(Serialize, Deserialize)] +pub struct CreateCommunity { + name: String, + title: String, + description: Option<String>, + 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<i64>, + limit: Option<i64>, + auth: Option<String> +} + +#[derive(Serialize, Deserialize)] +pub struct ListCommunitiesResponse { + op: String, + communities: Vec<CommunityView> +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct BanFromCommunity { + pub community_id: i32, + user_id: i32, + ban: bool, + reason: Option<String>, + expires: Option<i64>, + 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<CommunityModeratorView>, +} + +#[derive(Serialize, Deserialize)] +pub struct EditCommunity { + pub edit_id: i32, + name: String, + title: String, + description: Option<String>, + category_id: i32, + removed: Option<bool>, + deleted: Option<bool>, + reason: Option<String>, + expires: Option<i64>, + 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<CommunityFollowerView> +} + +impl Perform<GetCommunityResponse> for Oper<GetCommunity> { + fn perform(&self) -> Result<GetCommunityResponse, Error> { + let data: &GetCommunity = &self.data; + let conn = establish_connection(); + + let user_id: Option<i32> = 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<CommunityResponse> for Oper<CreateCommunity> { + fn perform(&self) -> Result<CommunityResponse, Error> { + 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<CommunityResponse> for Oper<EditCommunity> { + fn perform(&self) -> Result<CommunityResponse, Error> { + 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<i32> = 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<ListCommunitiesResponse> for Oper<ListCommunities> { + fn perform(&self) -> Result<ListCommunitiesResponse, Error> { + let data: &ListCommunities = &self.data; + let conn = establish_connection(); + + let user_id: Option<i32> = 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> = CommunityView::list(&conn, user_id, sort, data.page, data.limit)?; + + // Return the jwt + Ok( + ListCommunitiesResponse { + op: self.op.to_string(), + communities: communities + } + ) + } +} + + +impl Perform<CommunityResponse> for Oper<FollowCommunity> { + fn perform(&self) -> Result<CommunityResponse, Error> { + 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<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> { + fn perform(&self) -> Result<GetFollowedCommunitiesResponse, Error> { + let data: &GetFollowedCommunities = &self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, |