From 2d7d9cf7d8b596d80b869e7b442d370d47da0498 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sat, 19 Dec 2020 20:10:47 -0500 Subject: [PATCH] Some API cleanup, adding site_id to site aggregates. --- lemmy_api/src/comment.rs | 102 ++++++------------ lemmy_api/src/community.rs | 38 ++++--- lemmy_api/src/lib.rs | 11 ++ lemmy_api/src/post.rs | 18 +--- lemmy_api/src/site.rs | 95 ++++++++-------- lemmy_api/src/user.rs | 72 +++++++------ lemmy_apub/src/activities/receive/comment.rs | 1 + .../src/activities/receive/private_message.rs | 24 +++-- lemmy_apub/src/fetcher.rs | 1 + lemmy_apub/src/http/post.rs | 2 +- lemmy_db/src/aggregates/site_aggregates.rs | 3 +- lemmy_db/src/schema.rs | 2 + lemmy_db/src/source/post.rs | 5 - lemmy_db/src/source/site.rs | 5 + lemmy_db/src/views/comment_view.rs | 9 ++ lemmy_db/src/views/site_view.rs | 21 +++- lemmy_structs/src/comment.rs | 7 +- lemmy_structs/src/community.rs | 2 +- lemmy_structs/src/lib.rs | 1 - lemmy_structs/src/post.rs | 3 +- lemmy_structs/src/site.rs | 7 +- lemmy_structs/src/user.rs | 16 +-- lemmy_structs/src/websocket.rs | 1 - .../down.sql | 2 + .../up.sql | 35 ++++-- 25 files changed, 247 insertions(+), 236 deletions(-) delete mode 100644 lemmy_structs/src/websocket.rs diff --git a/lemmy_api/src/comment.rs b/lemmy_api/src/comment.rs index 689fe4b8a..e5f079ad2 100644 --- a/lemmy_api/src/comment.rs +++ b/lemmy_api/src/comment.rs @@ -1,5 +1,6 @@ use crate::{ check_community_ban, + check_downvotes_enabled, collect_moderated_communities, get_post, get_user_from_jwt, @@ -11,16 +12,13 @@ use actix_web::web::Data; use lemmy_apub::{ApubLikeableType, ApubObjectType}; use lemmy_db::{ source::{ - comment::*, + comment::{Comment, CommentForm, CommentLike, CommentLikeForm, CommentSaved, CommentSavedForm}, comment_report::{CommentReport, CommentReportForm}, - moderator::*, - post::*, - user::*, + moderator::{ModRemoveComment, ModRemoveCommentForm}, }, views::{ comment_report_view::{CommentReportQueryBuilder, CommentReportView}, comment_view::{CommentQueryBuilder, CommentView}, - site_view::SiteView, }, Crud, Likeable, @@ -110,6 +108,7 @@ impl Perform for CreateComment { updated_comment.send_create(&user, context).await?; // Scan the comment for user mentions, add those rows + let post_id = post.id; let mentions = scrape_text_for_mentions(&comment_form.content); let recipient_ids = send_local_notifs( mentions, @@ -124,7 +123,7 @@ impl Perform for CreateComment { // You like your own comment by default let like_form = CommentLikeForm { comment_id: inserted_comment.id, - post_id: data.post_id, + post_id, user_id: user.id, score: 1, }; @@ -156,6 +155,7 @@ impl Perform for CreateComment { // strip out the recipient_ids, so that // users don't get double notifs + // TODO Do this in a different way res.recipient_ids = Vec::new(); Ok(res) @@ -203,16 +203,13 @@ impl Perform for EditComment { updated_comment.send_update(&user, context).await?; // Do the mentions / recipients - let post_id = orig_comment.post.id; - let post = get_post(post_id, context.pool()).await?; - let updated_comment_content = updated_comment.content.to_owned(); let mentions = scrape_text_for_mentions(&updated_comment_content); let recipient_ids = send_local_notifs( mentions, updated_comment, &user, - post, + orig_comment.post, context.pool(), false, ) @@ -239,6 +236,7 @@ impl Perform for EditComment { // strip out the recipient_ids, so that // users don't get double notifs + // TODO again res.recipient_ids = Vec::new(); Ok(res) @@ -297,14 +295,13 @@ impl Perform for DeleteComment { .await??; // Build the recipients - let post_id = comment_view.post.id; - let post = get_post(post_id, context.pool()).await?; + let comment_view_2 = comment_view.clone(); let mentions = vec![]; let recipient_ids = send_local_notifs( mentions, updated_comment, &user, - post, + comment_view_2.post, context.pool(), false, ) @@ -313,7 +310,7 @@ impl Perform for DeleteComment { let mut res = CommentResponse { comment_view, recipient_ids, - form_id: None, + form_id: None, // TODO a comment delete might clear forms? }; context.chat_server().do_send(SendComment { @@ -324,6 +321,7 @@ impl Perform for DeleteComment { // strip out the recipient_ids, so that // users don't get double notifs + // TODO again res.recipient_ids = Vec::new(); Ok(res) @@ -392,14 +390,14 @@ impl Perform for RemoveComment { .await??; // Build the recipients - let post_id = comment_view.post.id; - let post = get_post(post_id, context.pool()).await?; + let comment_view_2 = comment_view.clone(); + let mentions = vec![]; let recipient_ids = send_local_notifs( mentions, updated_comment, &user, - post, + comment_view_2.post, context.pool(), false, ) @@ -408,7 +406,7 @@ impl Perform for RemoveComment { let mut res = CommentResponse { comment_view, recipient_ids, - form_id: None, + form_id: None, // TODO maybe this might clear other forms }; context.chat_server().do_send(SendComment { @@ -419,6 +417,7 @@ impl Perform for RemoveComment { // strip out the recipient_ids, so that // users don't get double notifs + // TODO again res.recipient_ids = Vec::new(); Ok(res) @@ -437,41 +436,23 @@ impl Perform for MarkCommentAsRead { let data: &MarkCommentAsRead = &self; let user = get_user_from_jwt(&data.auth, context.pool()).await?; - let edit_id = data.edit_id; + let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { - CommentView::read(&conn, edit_id, None) + CommentView::read(&conn, comment_id, None) }) .await??; check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; // Verify that only the recipient can mark as read - // Needs to fetch the parent comment / post to get the recipient - let parent_id = orig_comment.comment.parent_id; - match parent_id { - Some(pid) => { - let parent_comment = blocking(context.pool(), move |conn| { - CommentView::read(&conn, pid, None) - }) - .await??; - if user.id != parent_comment.creator.id { - return Err(APIError::err("no_comment_edit_allowed").into()); - } - } - None => { - let parent_post_id = orig_comment.post.id; - let parent_post = - blocking(context.pool(), move |conn| Post::read(conn, parent_post_id)).await??; - if user.id != parent_post.creator_id { - return Err(APIError::err("no_comment_edit_allowed").into()); - } - } + if user.id != orig_comment.get_recipient_id() { + return Err(APIError::err("no_comment_edit_allowed").into()); } // Do the mark as read let read = data.read; match blocking(context.pool(), move |conn| { - Comment::update_read(conn, edit_id, read) + Comment::update_read(conn, comment_id, read) }) .await? { @@ -480,7 +461,7 @@ impl Perform for MarkCommentAsRead { }; // Refetch it - let edit_id = data.edit_id; + let edit_id = data.comment_id; let user_id = user.id; let comment_view = blocking(context.pool(), move |conn| { CommentView::read(conn, edit_id, Some(user_id)) @@ -556,12 +537,7 @@ impl Perform for CreateCommentLike { let mut recipient_ids = Vec::new(); // Don't do a downvote if site has downvotes disabled - if data.score == -1 { - let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??; - if !site_view.site.enable_downvotes { - return Err(APIError::err("downvotes_disabled").into()); - } - } + check_downvotes_enabled(data.score, context.pool()).await?; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { @@ -569,34 +545,14 @@ impl Perform for CreateCommentLike { }) .await??; - let post_id = orig_comment.post.id; - let post = get_post(post_id, context.pool()).await?; - check_community_ban(user.id, post.community_id, context.pool()).await?; + check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; - let comment_id = data.comment_id; - let comment = blocking(context.pool(), move |conn| Comment::read(conn, comment_id)).await??; - - // Add to recipient ids - match comment.parent_id { - Some(parent_id) => { - let parent_comment = - blocking(context.pool(), move |conn| Comment::read(conn, parent_id)).await??; - if parent_comment.creator_id != user.id { - let parent_user = blocking(context.pool(), move |conn| { - User_::read(conn, parent_comment.creator_id) - }) - .await??; - recipient_ids.push(parent_user.id); - } - } - None => { - recipient_ids.push(post.creator_id); - } - } + // Add parent user to recipients + recipient_ids.push(orig_comment.get_recipient_id()); let like_form = CommentLikeForm { comment_id: data.comment_id, - post_id, + post_id: orig_comment.post.id, user_id: user.id, score: data.score, }; @@ -609,6 +565,7 @@ impl Perform for CreateCommentLike { .await??; // Only add the like if the score isnt 0 + let comment = orig_comment.comment; let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); if do_add { let like_form2 = like_form.clone(); @@ -649,6 +606,7 @@ impl Perform for CreateCommentLike { // strip out the recipient_ids, so that // users don't get double notifs res.recipient_ids = Vec::new(); + // TODO why Ok(res) } diff --git a/lemmy_api/src/community.rs b/lemmy_api/src/community.rs index 6e20a30ba..c5ac40829 100644 --- a/lemmy_api/src/community.rs +++ b/lemmy_api/src/community.rs @@ -58,20 +58,22 @@ impl Perform for GetCommunity { let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let user_id = user.map(|u| u.id); - let name = data.name.to_owned().unwrap_or_else(|| "main".to_string()); - let community = match data.id { - Some(id) => blocking(context.pool(), move |conn| Community::read(conn, id)).await??, - None => match blocking(context.pool(), move |conn| { - Community::read_from_name(conn, &name) - }) - .await? - { - Ok(community) => community, - Err(_e) => return Err(APIError::err("couldnt_find_community").into()), - }, + let community_id = match data.id { + Some(id) => id, + None => { + let name = data.name.to_owned().unwrap_or_else(|| "main".to_string()); + match blocking(context.pool(), move |conn| { + Community::read_from_name(conn, &name) + }) + .await? + { + Ok(community) => community, + Err(_e) => return Err(APIError::err("couldnt_find_community").into()), + } + .id + } }; - let community_id = community.id; let community_view = match blocking(context.pool(), move |conn| { CommunityView::read(conn, community_id, user_id) }) @@ -81,7 +83,6 @@ impl Perform for GetCommunity { Err(_e) => return Err(APIError::err("couldnt_find_community").into()), }; - let community_id = community.id; let moderators: Vec = match blocking(context.pool(), move |conn| { CommunityModeratorView::for_community(conn, community_id) }) @@ -178,6 +179,7 @@ impl Perform for CreateCommunity { Err(_e) => return Err(APIError::err("community_already_exists").into()), }; + // The community creator becomes a moderator let community_moderator_form = CommunityModeratorForm { community_id: inserted_community.id, user_id: user.id, @@ -188,6 +190,7 @@ impl Perform for CreateCommunity { return Err(APIError::err("community_moderator_already_exists").into()); } + // Follow your own community let community_follower_form = CommunityFollowerForm { community_id: inserted_community.id, user_id: user.id, @@ -584,15 +587,15 @@ impl Perform for BanFromCommunity { } // Remove/Restore their data if that's desired - if let Some(remove_data) = data.remove_data { + if data.remove_data { // Posts blocking(context.pool(), move |conn: &'_ _| { - Post::update_removed_for_creator(conn, banned_user_id, Some(community_id), remove_data) + Post::update_removed_for_creator(conn, banned_user_id, Some(community_id), true) }) .await??; // Comments - // Diesel doesn't allow updates with joins, so this has to be a loop + // TODO Diesel doesn't allow updates with joins, so this has to be a loop let comments = blocking(context.pool(), move |conn| { CommentQueryBuilder::create(conn) .creator_id(banned_user_id) @@ -605,7 +608,7 @@ impl Perform for BanFromCommunity { for comment_view in &comments { let comment_id = comment_view.comment.id; blocking(context.pool(), move |conn: &'_ _| { - Comment::update_removed(conn, comment_id, remove_data) + Comment::update_removed(conn, comment_id, true) }) .await??; } @@ -743,6 +746,7 @@ impl Perform for TransferCommunity { let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; + // Making sure the creator, if an admin, is at the top let creator_index = admins .iter() .position(|r| r.user.id == site_creator_id) diff --git a/lemmy_api/src/lib.rs b/lemmy_api/src/lib.rs index ad7355e1c..eadb0d1ac 100644 --- a/lemmy_api/src/lib.rs +++ b/lemmy_api/src/lib.rs @@ -4,6 +4,7 @@ use lemmy_db::{ source::{ community::{Community, CommunityModerator}, post::Post, + site::Site, user::User_, }, views::community::community_user_ban_view::CommunityUserBanView, @@ -102,6 +103,16 @@ pub(crate) async fn check_community_ban( } } +pub(crate) async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> { + if score == -1 { + let site = blocking(pool, move |conn| Site::read_simple(conn)).await??; + if !site.enable_downvotes { + return Err(APIError::err("downvotes_disabled").into()); + } + } + Ok(()) +} + /// Returns a list of communities that the user moderates /// or if a community_id is supplied validates the user is a moderator /// of that community and returns the community id in a vec diff --git a/lemmy_api/src/post.rs b/lemmy_api/src/post.rs index 22f95877a..f021b0d7c 100644 --- a/lemmy_api/src/post.rs +++ b/lemmy_api/src/post.rs @@ -1,5 +1,6 @@ use crate::{ check_community_ban, + check_downvotes_enabled, check_optional_url, collect_moderated_communities, get_user_from_jwt, @@ -18,10 +19,9 @@ use lemmy_db::{ }, views::{ comment_view::CommentQueryBuilder, - community::{community_moderator_view::CommunityModeratorView, community_view::CommunityView}, + community::community_moderator_view::CommunityModeratorView, post_report_view::{PostReportQueryBuilder, PostReportView}, post_view::{PostQueryBuilder, PostView}, - site_view::SiteView, }, Crud, Likeable, @@ -192,12 +192,6 @@ impl Perform for GetPost { }) .await??; - let community_id = post_view.community.id; - let community = blocking(context.pool(), move |conn| { - CommunityView::read(conn, community_id, user_id) - }) - .await??; - let community_id = post_view.community.id; let moderators = blocking(context.pool(), move |conn| { CommunityModeratorView::for_community(conn, community_id) @@ -214,7 +208,6 @@ impl Perform for GetPost { Ok(GetPostResponse { post_view, comments, - community, moderators, online, }) @@ -285,12 +278,7 @@ impl Perform for CreatePostLike { let user = get_user_from_jwt(&data.auth, context.pool()).await?; // Don't do a downvote if site has downvotes disabled - if data.score == -1 { - let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??; - if !site_view.site.enable_downvotes { - return Err(APIError::err("downvotes_disabled").into()); - } - } + check_downvotes_enabled(data.score, context.pool()).await?; // Check for a community ban let post_id = data.post_id; diff --git a/lemmy_api/src/site.rs b/lemmy_api/src/site.rs index 138cc8751..16c6ecec9 100644 --- a/lemmy_api/src/site.rs +++ b/lemmy_api/src/site.rs @@ -10,7 +10,6 @@ use actix_web::web::Data; use anyhow::Context; use lemmy_apub::fetcher::search_by_apub_id; use lemmy_db::{ - aggregates::site_aggregates::SiteAggregates, diesel_option_overwrite, naive_now, source::{category::*, moderator::*, site::*}, @@ -156,7 +155,7 @@ impl Perform for CreateSite { ) -> Result { let data: &CreateSite = &self; - let read_site = move |conn: &'_ _| Site::read(conn, 1); + let read_site = move |conn: &'_ _| Site::read_simple(conn); if blocking(context.pool(), read_site).await?.is_ok() { return Err(APIError::err("site_already_exists").into()); }; @@ -188,7 +187,7 @@ impl Perform for CreateSite { let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??; - Ok(SiteResponse { site: site_view }) + Ok(SiteResponse { site_view }) } } @@ -209,7 +208,7 @@ impl Perform for EditSite { // Make sure user is an admin is_admin(context.pool(), user.id).await?; - let found_site = blocking(context.pool(), move |conn| Site::read(conn, 1)).await??; + let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??; let icon = diesel_option_overwrite(&data.icon); let banner = diesel_option_overwrite(&data.banner); @@ -233,7 +232,7 @@ impl Perform for EditSite { let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??; - let res = SiteResponse { site: site_view }; + let res = SiteResponse { site_view }; context.chat_server().do_send(SendAllMessage { op: UserOperation::EditSite, @@ -256,39 +255,41 @@ impl Perform for GetSite { ) -> Result { let data: &GetSite = &self; - // TODO refactor this a little - let res = blocking(context.pool(), move |conn| Site::read(conn, 1)).await?; - let site_view = if res.is_ok() { - Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??) - } else if let Some(setup) = Settings::get().setup.as_ref() { - let register = Register { - username: setup.admin_username.to_owned(), - email: setup.admin_email.to_owned(), - password: setup.admin_password.to_owned(), - password_verify: setup.admin_password.to_owned(), - admin: true, - show_nsfw: true, - captcha_uuid: None, - captcha_answer: None, - }; - let login_response = register.perform(context, websocket_id).await?; - info!("Admin {} created", setup.admin_username); + let site_view = match blocking(context.pool(), move |conn| SiteView::read(conn)).await? { + Ok(site_view) => Some(site_view), + // If the site isn't created yet, check the setup + Err(_) => { + if let Some(setup) = Settings::get().setup.as_ref() { + let register = Register { + username: setup.admin_username.to_owned(), + email: setup.admin_email.to_owned(), + password: setup.admin_password.to_owned(), + password_verify: setup.admin_password.to_owned(), + admin: true, + show_nsfw: true, + captcha_uuid: None, + captcha_answer: None, + }; + let login_response = register.perform(context, websocket_id).await?; + info!("Admin {} created", setup.admin_username); - let create_site = CreateSite { - name: setup.site_name.to_owned(), - description: None, - icon: None, - banner: None, - enable_downvotes: true, - open_registration: true, - enable_nsfw: true, - auth: login_response.jwt, - }; - create_site.perform(context, websocket_id).await?; - info!("Site {} created", setup.site_name); - Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??) - } else { - None + let create_site = CreateSite { + name: setup.site_name.to_owned(), + description: None, + icon: None, + banner: None, + enable_downvotes: true, + open_registration: true, + enable_nsfw: true, + auth: login_response.jwt, + }; + create_site.perform(context, websocket_id).await?; + info!("Site {} created", setup.site_name); + Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??) + } else { + None + } + } }; let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; @@ -321,17 +322,14 @@ impl Perform for GetSite { u }); - let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??; - Ok(GetSiteResponse { - site: site_view, + site_view, admins, banned, online, version: version::VERSION.to_string(), my_user, federated_instances: linked_instances(context.pool()).await?, - counts, }) } } @@ -521,7 +519,7 @@ impl Perform for TransferSite { user.private_key = None; user.public_key = None; - let read_site = blocking(context.pool(), move |conn| Site::read(conn, 1)).await??; + let read_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??; // Make sure user is the creator if read_site.creator_id != user.id { @@ -555,17 +553,14 @@ impl Perform for TransferSite { let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??; - let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??; - Ok(GetSiteResponse { - site: Some(site_view), + site_view: Some(site_view), admins, banned, online: 0, version: version::VERSION.to_string(), my_user: Some(user), federated_instances: linked_instances(context.pool()).await?, - counts, }) } } @@ -604,12 +599,8 @@ impl Perform for SaveSiteConfig { let user = get_user_from_jwt(&data.auth, context.pool()).await?; // Only let admins read this - let admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; - let admin_ids: Vec = admins.into_iter().map(|m| m.user.id).collect(); - - if !admin_ids.contains(&user.id) { - return Err(APIError::err("not_an_admin").into()); - } + let user_id = user.id; + is_admin(context.pool(), user_id).await?; // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem let config_hjson = match Settings::save_config_file(&data.config_hjson) { diff --git a/lemmy_api/src/user.rs b/lemmy_api/src/user.rs index f31e42e5a..cca8dffb8 100644 --- a/lemmy_api/src/user.rs +++ b/lemmy_api/src/user.rs @@ -38,7 +38,6 @@ use lemmy_db::{ post_report_view::PostReportView, post_view::PostQueryBuilder, private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView}, - site_view::SiteView, user_mention_view::{UserMentionQueryBuilder, UserMentionView}, user_view::{UserViewDangerous, UserViewSafe}, }, @@ -120,8 +119,8 @@ impl Perform for Register { let data: &Register = &self; // Make sure site has open registration - if let Ok(site_view) = blocking(context.pool(), move |conn| SiteView::read(conn)).await? { - if !site_view.site.open_registration { + if let Ok(site) = blocking(context.pool(), move |conn| Site::read_simple(conn)).await? { + if !site.open_registration { return Err(APIError::err("registration_closed").into()); } } @@ -347,9 +346,6 @@ impl Perform for SaveUserSettings { let data: &SaveUserSettings = &self; let user = get_user_from_jwt(&data.auth, context.pool()).await?; - let user_id = user.id; - let read_user = blocking(context.pool(), move |conn| User_::read(conn, user_id)).await??; - let avatar = diesel_option_overwrite(&data.avatar); let banner = diesel_option_overwrite(&data.banner); let email = diesel_option_overwrite(&data.email); @@ -373,6 +369,7 @@ impl Perform for SaveUserSettings { } } + let user_id = user.id; let password_encrypted = match &data.new_password { Some(new_password) => { match &data.new_password_verify { @@ -385,8 +382,7 @@ impl Perform for SaveUserSettings { // Check the old password match &data.old_password { Some(old_password) => { - let valid: bool = - verify(old_password, &read_user.password_encrypted).unwrap_or(false); + let valid: bool = verify(old_password, &user.password_encrypted).unwrap_or(false); if !valid { return Err(APIError::err("password_incorrect").into()); } @@ -403,33 +399,36 @@ impl Perform for SaveUserSettings { None => return Err(APIError::err("passwords_dont_match").into()), } } - None => read_user.password_encrypted, + None => user.password_encrypted, }; + let default_listing_type = ListingType::from_str(&data.default_listing_type)? as i16; + let default_sort_type = SortType::from_str(&data.default_sort_type)? as i16; + let user_form = UserForm { - name: read_user.name, + name: user.name, email, matrix_user_id, avatar, banner, password_encrypted, preferred_username, - published: Some(read_user.published), + published: Some(user.published), updated: Some(naive_now()), - admin: read_user.admin, - banned: Some(read_user.banned), + admin: user.admin, + banned: Some(user.banned), show_nsfw: data.show_nsfw, theme: data.theme.to_owned(), - default_sort_type: data.default_sort_type, - default_listing_type: data.default_listing_type, + default_sort_type, + default_listing_type, lang: data.lang.to_owned(), show_avatars: data.show_avatars, send_notifications_to_email: data.send_notifications_to_email, - actor_id: Some(read_user.actor_id), + actor_id: Some(user.actor_id), bio, - local: read_user.local, - private_key: read_user.private_key, - public_key: read_user.public_key, + local: user.local, + private_key: user.private_key, + public_key: user.public_key, last_refreshed_at: None, }; @@ -579,9 +578,8 @@ impl Perform for GetUserDetails { // Return the jwt Ok(GetUserDetailsResponse { - // TODO need to figure out dangerous user view here - user: user_view, - user_dangerous, + user_view, + user_view_dangerous: user_dangerous, follows, moderates, comments, @@ -669,22 +667,22 @@ impl Perform for BanUser { } // Remove their data if that's desired - if let Some(remove_data) = data.remove_data { + if data.remove_data { // Posts blocking(context.pool(), move |conn: &'_ _| { - Post::update_removed_for_creator(conn, banned_user_id, None, remove_data) + Post::update_removed_for_creator(conn, banned_user_id, None, true) }) .await??; // Communities blocking(context.pool(), move |conn: &'_ _| { - Community::update_removed_for_creator(conn, banned_user_id, remove_data) + Community::update_removed_for_creator(conn, banned_user_id, true) }) .await??; // Comments blocking(context.pool(), move |conn: &'_ _| { - Comment::update_removed_for_creator(conn, banned_user_id, remove_data) + Comment::update_removed_for_creator(conn, banned_user_id, true) }) .await??; } @@ -712,7 +710,7 @@ impl Perform for BanUser { .await??; let res = BanUserResponse { - user: user_view, + user_view, banned: data.ban, }; @@ -1091,7 +1089,9 @@ impl Perform for CreatePrivateMessage { }) .await??; - let res = PrivateMessageResponse { message }; + let res = PrivateMessageResponse { + private_message_view: message, + }; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::CreatePrivateMessage, @@ -1148,7 +1148,9 @@ impl Perform for EditPrivateMessage { .await??; let recipient_id = message.recipient.id; - let res = PrivateMessageResponse { message }; + let res = PrivateMessageResponse { + private_message_view: message, + }; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::EditPrivateMessage, @@ -1211,7 +1213,9 @@ impl Perform for DeletePrivateMessage { .await??; let recipient_id = message.recipient.id; - let res = PrivateMessageResponse { message }; + let res = PrivateMessageResponse { + private_message_view: message, + }; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::DeletePrivateMessage, @@ -1267,7 +1271,9 @@ impl Perform for MarkPrivateMessageAsRead { .await??; let recipient_id = message.recipient.id; - let res = PrivateMessageResponse { message }; + let res = PrivateMessageResponse { + private_message_view: message, + }; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::MarkPrivateMessageAsRead, @@ -1305,7 +1311,9 @@ impl Perform for GetPrivateMessages { }) .await??; - Ok(PrivateMessagesResponse { messages }) + Ok(PrivateMessagesResponse { + private_messages: messages, + }) } } diff --git a/lemmy_apub/src/activities/receive/comment.rs b/lemmy_apub/src/activities/receive/comment.rs index 700a2653c..32eb8c0ca 100644 --- a/lemmy_apub/src/activities/receive/comment.rs +++ b/lemmy_apub/src/activities/receive/comment.rs @@ -10,6 +10,7 @@ use lemmy_db::{ post::Post, }, views::comment_view::CommentView, + Crud, Likeable, }; use lemmy_structs::{blocking, comment::CommentResponse, send_local_notifs}; diff --git a/lemmy_apub/src/activities/receive/private_message.rs b/lemmy_apub/src/activities/receive/private_message.rs index 15cde53f2..8f7952b6b 100644 --- a/lemmy_apub/src/activities/receive/private_message.rs +++ b/lemmy_apub/src/activities/receive/private_message.rs @@ -47,9 +47,11 @@ pub(crate) async fn receive_create_private_message( }) .await??; - let res = PrivateMessageResponse { message }; + let res = PrivateMessageResponse { + private_message_view: message, + }; - let recipient_id = res.message.recipient.id; + let recipient_id = res.private_message_view.recipient.id; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::CreatePrivateMessage, @@ -85,9 +87,11 @@ pub(crate) async fn receive_update_private_message( }) .await??; - let res = PrivateMessageResponse { message }; + let res = PrivateMessageResponse { + private_message_view: message, + }; - let recipient_id = res.message.recipient.id; + let recipient_id = res.private_message_view.recipient.id; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::EditPrivateMessage, @@ -117,8 +121,10 @@ pub(crate) async fn receive_delete_private_message( }) .await??; - let res = PrivateMessageResponse { message }; - let recipient_id = res.message.recipient.id; + let res = PrivateMessageResponse { + private_message_view: message, + }; + let recipient_id = res.private_message_view.recipient.id; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::EditPrivateMessage, response: res, @@ -152,8 +158,10 @@ pub(crate) async fn receive_undo_delete_private_message( }) .await??; - let res = PrivateMessageResponse { message }; - let recipient_id = res.message.recipient.id; + let res = PrivateMessageResponse { + private_message_view: message, + }; + let recipient_id = res.private_message_view.recipient.id; context.chat_server().do_send(SendUserRoomMessage { op: UserOperation::EditPrivateMessage, response: res, diff --git a/lemmy_apub/src/fetcher.rs b/lemmy_apub/src/fetcher.rs index 61cdbd47a..39188f138 100644 --- a/lemmy_apub/src/fetcher.rs +++ b/lemmy_apub/src/fetcher.rs @@ -27,6 +27,7 @@ use lemmy_db::{ user_view::UserViewSafe, }, ApubObject, + Crud, Joinable, SearchType, }; diff --git a/lemmy_apub/src/http/post.rs b/lemmy_apub/src/http/post.rs index 563af8456..3d0c6ff8e 100644 --- a/lemmy_apub/src/http/post.rs +++ b/lemmy_apub/src/http/post.rs @@ -4,7 +4,7 @@ use crate::{ }; use actix_web::{body::Body, web, HttpResponse}; use diesel::result::Error::NotFound; -use lemmy_db::source::post::Post; +use lemmy_db::{source::post::Post, Crud}; use lemmy_structs::blocking; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; diff --git a/lemmy_db/src/aggregates/site_aggregates.rs b/lemmy_db/src/aggregates/site_aggregates.rs index 6856bfc9e..794dd5318 100644 --- a/lemmy_db/src/aggregates/site_aggregates.rs +++ b/lemmy_db/src/aggregates/site_aggregates.rs @@ -2,10 +2,11 @@ use crate::schema::site_aggregates; use diesel::{result::Error, *}; use serde::Serialize; -#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)] +#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Clone)] #[table_name = "site_aggregates"] pub struct SiteAggregates { pub id: i32, + pub site_id: i32, pub users: i64, pub posts: i64, pub comments: i64, diff --git a/lemmy_db/src/schema.rs b/lemmy_db/src/schema.rs index 33e2389fd..f0aca2db9 100644 --- a/lemmy_db/src/schema.rs +++ b/lemmy_db/src/schema.rs @@ -362,6 +362,7 @@ table! { table! { site_aggregates (id) { id -> Int4, + site_id -> Int4, users -> Int8, posts -> Int8, comments -> Int8, @@ -560,6 +561,7 @@ joinable!(post_report -> post (post_id)); joinable!(post_saved -> post (post_id)); joinable!(post_saved -> user_ (user_id)); joinable!(site -> user_ (creator_id)); +joinable!(site_aggregates -> site (site_id)); joinable!(user_aggregates -> user_ (user_id)); joinable!(user_ban -> user_ (user_id)); joinable!(user_mention -> comment (comment_id)); diff --git a/lemmy_db/src/source/post.rs b/lemmy_db/src/source/post.rs index 098ce8835..948791ea4 100644 --- a/lemmy_db/src/source/post.rs +++ b/lemmy_db/src/source/post.rs @@ -106,11 +106,6 @@ impl ApubObject for Post { } impl Post { - pub fn read(conn: &PgConnection, post_id: i32) -> Result { - use crate::schema::post::dsl::*; - post.filter(id.eq(post_id)).first::(conn) - } - pub fn list_for_community( conn: &PgConnection, the_community_id: i32, diff --git a/lemmy_db/src/source/site.rs b/lemmy_db/src/source/site.rs index 2f3fbcdff..1224e0ab7 100644 --- a/lemmy_db/src/source/site.rs +++ b/lemmy_db/src/source/site.rs @@ -59,4 +59,9 @@ impl Site { .set((creator_id.eq(new_creator_id), updated.eq(naive_now()))) .get_result::(conn) } + + pub fn read_simple(conn: &PgConnection) -> Result { + use crate::schema::site::dsl::*; + site.first::(conn) + } } diff --git a/lemmy_db/src/views/comment_view.rs b/lemmy_db/src/views/comment_view.rs index 35a9038de..8c7584471 100644 --- a/lemmy_db/src/views/comment_view.rs +++ b/lemmy_db/src/views/comment_view.rs @@ -145,6 +145,15 @@ impl CommentView { my_vote, }) } + + /// Gets the recipient user id. + /// If there is no parent comment, its the post creator + pub fn get_recipient_id(&self) -> i32 { + match &self.recipient { + Some(parent_commenter) => parent_commenter.id, + None => self.post.creator_id, + } + } } pub struct CommentQueryBuilder<'a> { diff --git a/lemmy_db/src/views/site_view.rs b/lemmy_db/src/views/site_view.rs index d10702fca..3c605277e 100644 --- a/lemmy_db/src/views/site_view.rs +++ b/lemmy_db/src/views/site_view.rs @@ -1,5 +1,6 @@ use crate::{ - schema::{site, user_}, + aggregates::site_aggregates::SiteAggregates, + schema::{site, site_aggregates, user_}, source::{ site::Site, user::{UserSafe, User_}, @@ -13,15 +14,25 @@ use serde::Serialize; pub struct SiteView { pub site: Site, pub creator: UserSafe, + pub counts: SiteAggregates, } impl SiteView { pub fn read(conn: &PgConnection) -> Result { - let (site, creator) = site::table + let (site, creator, counts) = site::table .inner_join(user_::table) - .select((site::all_columns, User_::safe_columns_tuple())) - .first::<(Site, UserSafe)>(conn)?; + .inner_join(site_aggregates::table) + .select(( + site::all_columns, + User_::safe_columns_tuple(), + site_aggregates::all_columns, + )) + .first::<(Site, UserSafe, SiteAggregates)>(conn)?; - Ok(SiteView { site, creator }) + Ok(SiteView { + site, + creator, + counts, + }) } } diff --git a/lemmy_structs/src/comment.rs b/lemmy_structs/src/comment.rs index be10906aa..fe65738d8 100644 --- a/lemmy_structs/src/comment.rs +++ b/lemmy_structs/src/comment.rs @@ -35,7 +35,7 @@ pub struct RemoveComment { #[derive(Deserialize)] pub struct MarkCommentAsRead { - pub edit_id: i32, + pub comment_id: i32, pub read: bool, pub auth: String, } @@ -50,8 +50,8 @@ pub struct SaveComment { #[derive(Serialize, Clone)] pub struct CommentResponse { pub comment_view: CommentView, - pub recipient_ids: Vec, - pub form_id: Option, + pub recipient_ids: Vec, // TODO another way to do this? Maybe a UserMention belongs to Comment + pub form_id: Option, // An optional front end ID, to tell which is coming back } #[derive(Deserialize)] @@ -98,6 +98,7 @@ pub struct ResolveCommentReport { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ResolveCommentReportResponse { + // TODO this should probably return the view pub report_id: i32, pub resolved: bool, } diff --git a/lemmy_structs/src/community.rs b/lemmy_structs/src/community.rs index ac7837c52..65ea0aaaf 100644 --- a/lemmy_structs/src/community.rs +++ b/lemmy_structs/src/community.rs @@ -57,7 +57,7 @@ pub struct BanFromCommunity { pub community_id: i32, pub user_id: i32, pub ban: bool, - pub remove_data: Option, + pub remove_data: bool, pub reason: Option, pub expires: Option, pub auth: String, diff --git a/lemmy_structs/src/lib.rs b/lemmy_structs/src/lib.rs index dc06a40cd..d19127384 100644 --- a/lemmy_structs/src/lib.rs +++ b/lemmy_structs/src/lib.rs @@ -3,7 +3,6 @@ pub mod community; pub mod post; pub mod site; pub mod user; -pub mod websocket; use diesel::PgConnection; use lemmy_db::{ diff --git a/lemmy_structs/src/post.rs b/lemmy_structs/src/post.rs index fe6a059e2..ac29d8f78 100644 --- a/lemmy_structs/src/post.rs +++ b/lemmy_structs/src/post.rs @@ -1,6 +1,6 @@ use lemmy_db::views::{ comment_view::CommentView, - community::{community_moderator_view::CommunityModeratorView, community_view::CommunityView}, + community::community_moderator_view::CommunityModeratorView, post_report_view::PostReportView, post_view::PostView, }; @@ -31,7 +31,6 @@ pub struct GetPost { pub struct GetPostResponse { pub post_view: PostView, pub comments: Vec, - pub community: CommunityView, pub moderators: Vec, pub online: usize, } diff --git a/lemmy_structs/src/site.rs b/lemmy_structs/src/site.rs index 9209a5420..ff6c8a391 100644 --- a/lemmy_structs/src/site.rs +++ b/lemmy_structs/src/site.rs @@ -1,5 +1,4 @@ use lemmy_db::{ - aggregates::site_aggregates::SiteAggregates, source::{category::*, user::*}, views::{ comment_view::CommentView, @@ -101,16 +100,14 @@ pub struct GetSite { pub auth: Option, } -// TODO combine siteresponse and getsiteresponse #[derive(Serialize, Clone)] pub struct SiteResponse { - pub site: SiteView, + pub site_view: SiteView, } #[derive(Serialize)] pub struct GetSiteResponse { - pub site: Option, // Because the site might not be set up yet - pub counts: SiteAggregates, + pub site_view: Option, // Because the site might not be set up yet pub admins: Vec, pub banned: Vec, pub online: usize, diff --git a/lemmy_structs/src/user.rs b/lemmy_structs/src/user.rs index f73d63f99..52871696c 100644 --- a/lemmy_structs/src/user.rs +++ b/lemmy_structs/src/user.rs @@ -48,8 +48,8 @@ pub struct CaptchaResponse { pub struct SaveUserSettings { pub show_nsfw: bool, pub theme: String, - pub default_sort_type: i16, - pub default_listing_type: i16, + pub default_sort_type: String, + pub default_listing_type: String, pub lang: String, pub avatar: Option, pub banner: Option, @@ -84,8 +84,8 @@ pub struct GetUserDetails { #[derive(Serialize)] pub struct GetUserDetailsResponse { - pub user: Option, - pub user_dangerous: Option, + pub user_view: Option, + pub user_view_dangerous: Option, pub follows: Vec, pub moderates: Vec, pub comments: Vec, @@ -123,7 +123,7 @@ pub struct AddAdminResponse { pub struct BanUser { pub user_id: i32, pub ban: bool, - pub remove_data: Option, + pub remove_data: bool, pub reason: Option, pub expires: Option, pub auth: String, @@ -131,7 +131,7 @@ pub struct BanUser { #[derive(Serialize, Clone)] pub struct BanUserResponse { - pub user: UserViewSafe, + pub user_view: UserViewSafe, pub banned: bool, } @@ -224,12 +224,12 @@ pub struct GetPrivateMessages { #[derive(Serialize, Clone)] pub struct PrivateMessagesResponse { - pub messages: Vec, + pub private_messages: Vec, } #[derive(Serialize, Clone)] pub struct PrivateMessageResponse { - pub message: PrivateMessageView, + pub private_message_view: PrivateMessageView, } #[derive(Deserialize, Debug)] diff --git a/lemmy_structs/src/websocket.rs b/lemmy_structs/src/websocket.rs deleted file mode 100644 index 8b1378917..000000000 --- a/lemmy_structs/src/websocket.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/migrations/2020-12-02-152437_create_site_aggregates/down.sql b/migrations/2020-12-02-152437_create_site_aggregates/down.sql index 4bbee7615..2a2aa97d5 100644 --- a/migrations/2020-12-02-152437_create_site_aggregates/down.sql +++ b/migrations/2020-12-02-152437_create_site_aggregates/down.sql @@ -1,10 +1,12 @@ -- Site aggregates drop table site_aggregates; +drop trigger site_aggregates_site on site; drop trigger site_aggregates_user on user_; drop trigger site_aggregates_post on post; drop trigger site_aggregates_comment on comment; drop trigger site_aggregates_community on community; drop function + site_aggregates_site, site_aggregates_user, site_aggregates_post, site_aggregates_comment, diff --git a/migrations/2020-12-02-152437_create_site_aggregates/up.sql b/migrations/2020-12-02-152437_create_site_aggregates/up.sql index b95723476..cc76a5c75 100644 --- a/migrations/2020-12-02-152437_create_site_aggregates/up.sql +++ b/migrations/2020-12-02-152437_create_site_aggregates/up.sql @@ -1,17 +1,38 @@ -- Add site aggregates create table site_aggregates ( id serial primary key, - users bigint not null, - posts bigint not null, - comments bigint not null, - communities bigint not null + site_id int references site on update cascade on delete cascade not null, + users bigint not null default 0, + posts bigint not null default 0, + comments bigint not null default 0, + communities bigint not null default 0 ); -insert into site_aggregates (users, posts, comments, communities) - select ( select coalesce(count(*), 0) from user_) as users, +insert into site_aggregates (site_id, users, posts, comments, communities) + select id as site_id, + ( select coalesce(count(*), 0) from user_) as users, ( select coalesce(count(*), 0) from post) as posts, ( select coalesce(count(*), 0) from comment) as comments, - ( select coalesce(count(*), 0) from community) as communities; + ( select coalesce(count(*), 0) from community) as communities + from site; + +-- initial site add +create function site_aggregates_site() +returns trigger language plpgsql +as $$ +begin + IF (TG_OP = 'INSERT') THEN + insert into site_aggregates (site_id) values (NEW.id); + ELSIF (TG_OP = 'DELETE') THEN + delete from site_aggregates where site_id = OLD.id; + END IF; + return null; +end $$; + +create trigger site_aggregates_site +after insert or delete on site +for each row +execute procedure site_aggregates_site(); -- Add site aggregate triggers -- user