Some API cleanup, adding site_id to site aggregates.

pull/1335/head
Dessalines 2020-12-19 20:10:47 -05:00
parent 3f36730dba
commit 2d7d9cf7d8
25 changed files with 247 additions and 236 deletions

View File

@ -1,5 +1,6 @@
use crate::{ use crate::{
check_community_ban, check_community_ban,
check_downvotes_enabled,
collect_moderated_communities, collect_moderated_communities,
get_post, get_post,
get_user_from_jwt, get_user_from_jwt,
@ -11,16 +12,13 @@ use actix_web::web::Data;
use lemmy_apub::{ApubLikeableType, ApubObjectType}; use lemmy_apub::{ApubLikeableType, ApubObjectType};
use lemmy_db::{ use lemmy_db::{
source::{ source::{
comment::*, comment::{Comment, CommentForm, CommentLike, CommentLikeForm, CommentSaved, CommentSavedForm},
comment_report::{CommentReport, CommentReportForm}, comment_report::{CommentReport, CommentReportForm},
moderator::*, moderator::{ModRemoveComment, ModRemoveCommentForm},
post::*,
user::*,
}, },
views::{ views::{
comment_report_view::{CommentReportQueryBuilder, CommentReportView}, comment_report_view::{CommentReportQueryBuilder, CommentReportView},
comment_view::{CommentQueryBuilder, CommentView}, comment_view::{CommentQueryBuilder, CommentView},
site_view::SiteView,
}, },
Crud, Crud,
Likeable, Likeable,
@ -110,6 +108,7 @@ impl Perform for CreateComment {
updated_comment.send_create(&user, context).await?; updated_comment.send_create(&user, context).await?;
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
let post_id = post.id;
let mentions = scrape_text_for_mentions(&comment_form.content); let mentions = scrape_text_for_mentions(&comment_form.content);
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
@ -124,7 +123,7 @@ impl Perform for CreateComment {
// You like your own comment by default // You like your own comment by default
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
post_id: data.post_id, post_id,
user_id: user.id, user_id: user.id,
score: 1, score: 1,
}; };
@ -156,6 +155,7 @@ impl Perform for CreateComment {
// strip out the recipient_ids, so that // strip out the recipient_ids, so that
// users don't get double notifs // users don't get double notifs
// TODO Do this in a different way
res.recipient_ids = Vec::new(); res.recipient_ids = Vec::new();
Ok(res) Ok(res)
@ -203,16 +203,13 @@ impl Perform for EditComment {
updated_comment.send_update(&user, context).await?; updated_comment.send_update(&user, context).await?;
// Do the mentions / recipients // 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 updated_comment_content = updated_comment.content.to_owned();
let mentions = scrape_text_for_mentions(&updated_comment_content); let mentions = scrape_text_for_mentions(&updated_comment_content);
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, updated_comment,
&user, &user,
post, orig_comment.post,
context.pool(), context.pool(),
false, false,
) )
@ -239,6 +236,7 @@ impl Perform for EditComment {
// strip out the recipient_ids, so that // strip out the recipient_ids, so that
// users don't get double notifs // users don't get double notifs
// TODO again
res.recipient_ids = Vec::new(); res.recipient_ids = Vec::new();
Ok(res) Ok(res)
@ -297,14 +295,13 @@ impl Perform for DeleteComment {
.await??; .await??;
// Build the recipients // Build the recipients
let post_id = comment_view.post.id; let comment_view_2 = comment_view.clone();
let post = get_post(post_id, context.pool()).await?;
let mentions = vec![]; let mentions = vec![];
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, updated_comment,
&user, &user,
post, comment_view_2.post,
context.pool(), context.pool(),
false, false,
) )
@ -313,7 +310,7 @@ impl Perform for DeleteComment {
let mut res = CommentResponse { let mut res = CommentResponse {
comment_view, comment_view,
recipient_ids, recipient_ids,
form_id: None, form_id: None, // TODO a comment delete might clear forms?
}; };
context.chat_server().do_send(SendComment { context.chat_server().do_send(SendComment {
@ -324,6 +321,7 @@ impl Perform for DeleteComment {
// strip out the recipient_ids, so that // strip out the recipient_ids, so that
// users don't get double notifs // users don't get double notifs
// TODO again
res.recipient_ids = Vec::new(); res.recipient_ids = Vec::new();
Ok(res) Ok(res)
@ -392,14 +390,14 @@ impl Perform for RemoveComment {
.await??; .await??;
// Build the recipients // Build the recipients
let post_id = comment_view.post.id; let comment_view_2 = comment_view.clone();
let post = get_post(post_id, context.pool()).await?;
let mentions = vec![]; let mentions = vec![];
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, updated_comment,
&user, &user,
post, comment_view_2.post,
context.pool(), context.pool(),
false, false,
) )
@ -408,7 +406,7 @@ impl Perform for RemoveComment {
let mut res = CommentResponse { let mut res = CommentResponse {
comment_view, comment_view,
recipient_ids, recipient_ids,
form_id: None, form_id: None, // TODO maybe this might clear other forms
}; };
context.chat_server().do_send(SendComment { context.chat_server().do_send(SendComment {
@ -419,6 +417,7 @@ impl Perform for RemoveComment {
// strip out the recipient_ids, so that // strip out the recipient_ids, so that
// users don't get double notifs // users don't get double notifs
// TODO again
res.recipient_ids = Vec::new(); res.recipient_ids = Vec::new();
Ok(res) Ok(res)
@ -437,41 +436,23 @@ impl Perform for MarkCommentAsRead {
let data: &MarkCommentAsRead = &self; let data: &MarkCommentAsRead = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; 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| { let orig_comment = blocking(context.pool(), move |conn| {
CommentView::read(&conn, edit_id, None) CommentView::read(&conn, comment_id, None)
}) })
.await??; .await??;
check_community_ban(user.id, orig_comment.community.id, context.pool()).await?; check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
// Verify that only the recipient can mark as read // Verify that only the recipient can mark as read
// Needs to fetch the parent comment / post to get the recipient if user.id != orig_comment.get_recipient_id() {
let parent_id = orig_comment.comment.parent_id; return Err(APIError::err("no_comment_edit_allowed").into());
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());
}
}
} }
// Do the mark as read // Do the mark as read
let read = data.read; let read = data.read;
match blocking(context.pool(), move |conn| { match blocking(context.pool(), move |conn| {
Comment::update_read(conn, edit_id, read) Comment::update_read(conn, comment_id, read)
}) })
.await? .await?
{ {
@ -480,7 +461,7 @@ impl Perform for MarkCommentAsRead {
}; };
// Refetch it // Refetch it
let edit_id = data.edit_id; let edit_id = data.comment_id;
let user_id = user.id; let user_id = user.id;
let comment_view = blocking(context.pool(), move |conn| { let comment_view = blocking(context.pool(), move |conn| {
CommentView::read(conn, edit_id, Some(user_id)) CommentView::read(conn, edit_id, Some(user_id))
@ -556,12 +537,7 @@ impl Perform for CreateCommentLike {
let mut recipient_ids = Vec::new(); let mut recipient_ids = Vec::new();
// Don't do a downvote if site has downvotes disabled // Don't do a downvote if site has downvotes disabled
if data.score == -1 { check_downvotes_enabled(data.score, context.pool()).await?;
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());
}
}
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {
@ -569,34 +545,14 @@ impl Perform for CreateCommentLike {
}) })
.await??; .await??;
let post_id = orig_comment.post.id; check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
let post = get_post(post_id, context.pool()).await?;
check_community_ban(user.id, post.community_id, context.pool()).await?;
let comment_id = data.comment_id; // Add parent user to recipients
let comment = blocking(context.pool(), move |conn| Comment::read(conn, comment_id)).await??; recipient_ids.push(orig_comment.get_recipient_id());
// 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);
}
}
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id: data.comment_id, comment_id: data.comment_id,
post_id, post_id: orig_comment.post.id,
user_id: user.id, user_id: user.id,
score: data.score, score: data.score,
}; };
@ -609,6 +565,7 @@ impl Perform for CreateCommentLike {
.await??; .await??;
// Only add the like if the score isnt 0 // 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); let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add { if do_add {
let like_form2 = like_form.clone(); let like_form2 = like_form.clone();
@ -649,6 +606,7 @@ impl Perform for CreateCommentLike {
// strip out the recipient_ids, so that // strip out the recipient_ids, so that
// users don't get double notifs // users don't get double notifs
res.recipient_ids = Vec::new(); res.recipient_ids = Vec::new();
// TODO why
Ok(res) Ok(res)
} }

View File

@ -58,20 +58,22 @@ impl Perform for GetCommunity {
let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?; let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
let user_id = user.map(|u| u.id); let user_id = user.map(|u| u.id);
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string()); let community_id = match data.id {
let community = match data.id { Some(id) => id,
Some(id) => blocking(context.pool(), move |conn| Community::read(conn, id)).await??, None => {
None => match blocking(context.pool(), move |conn| { let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
Community::read_from_name(conn, &name) match blocking(context.pool(), move |conn| {
}) Community::read_from_name(conn, &name)
.await? })
{ .await?
Ok(community) => community, {
Err(_e) => return Err(APIError::err("couldnt_find_community").into()), 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| { let community_view = match blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, user_id) 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()), Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
}; };
let community_id = community.id;
let moderators: Vec<CommunityModeratorView> = match blocking(context.pool(), move |conn| { let moderators: Vec<CommunityModeratorView> = match blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id) CommunityModeratorView::for_community(conn, community_id)
}) })
@ -178,6 +179,7 @@ impl Perform for CreateCommunity {
Err(_e) => return Err(APIError::err("community_already_exists").into()), Err(_e) => return Err(APIError::err("community_already_exists").into()),
}; };
// The community creator becomes a moderator
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: inserted_community.id, community_id: inserted_community.id,
user_id: user.id, user_id: user.id,
@ -188,6 +190,7 @@ impl Perform for CreateCommunity {
return Err(APIError::err("community_moderator_already_exists").into()); return Err(APIError::err("community_moderator_already_exists").into());
} }
// Follow your own community
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: inserted_community.id, community_id: inserted_community.id,
user_id: user.id, user_id: user.id,
@ -584,15 +587,15 @@ impl Perform for BanFromCommunity {
} }
// Remove/Restore their data if that's desired // Remove/Restore their data if that's desired
if let Some(remove_data) = data.remove_data { if data.remove_data {
// Posts // Posts
blocking(context.pool(), move |conn: &'_ _| { 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??; .await??;
// Comments // 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| { let comments = blocking(context.pool(), move |conn| {
CommentQueryBuilder::create(conn) CommentQueryBuilder::create(conn)
.creator_id(banned_user_id) .creator_id(banned_user_id)
@ -605,7 +608,7 @@ impl Perform for BanFromCommunity {
for comment_view in &comments { for comment_view in &comments {
let comment_id = comment_view.comment.id; let comment_id = comment_view.comment.id;
blocking(context.pool(), move |conn: &'_ _| { blocking(context.pool(), move |conn: &'_ _| {
Comment::update_removed(conn, comment_id, remove_data) Comment::update_removed(conn, comment_id, true)
}) })
.await??; .await??;
} }
@ -743,6 +746,7 @@ impl Perform for TransferCommunity {
let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; 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 let creator_index = admins
.iter() .iter()
.position(|r| r.user.id == site_creator_id) .position(|r| r.user.id == site_creator_id)

View File

@ -4,6 +4,7 @@ use lemmy_db::{
source::{ source::{
community::{Community, CommunityModerator}, community::{Community, CommunityModerator},
post::Post, post::Post,
site::Site,
user::User_, user::User_,
}, },
views::community::community_user_ban_view::CommunityUserBanView, 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 /// Returns a list of communities that the user moderates
/// or if a community_id is supplied validates the user is a moderator /// or if a community_id is supplied validates the user is a moderator
/// of that community and returns the community id in a vec /// of that community and returns the community id in a vec

View File

@ -1,5 +1,6 @@
use crate::{ use crate::{
check_community_ban, check_community_ban,
check_downvotes_enabled,
check_optional_url, check_optional_url,
collect_moderated_communities, collect_moderated_communities,
get_user_from_jwt, get_user_from_jwt,
@ -18,10 +19,9 @@ use lemmy_db::{
}, },
views::{ views::{
comment_view::CommentQueryBuilder, comment_view::CommentQueryBuilder,
community::{community_moderator_view::CommunityModeratorView, community_view::CommunityView}, community::community_moderator_view::CommunityModeratorView,
post_report_view::{PostReportQueryBuilder, PostReportView}, post_report_view::{PostReportQueryBuilder, PostReportView},
post_view::{PostQueryBuilder, PostView}, post_view::{PostQueryBuilder, PostView},
site_view::SiteView,
}, },
Crud, Crud,
Likeable, Likeable,
@ -192,12 +192,6 @@ impl Perform for GetPost {
}) })
.await??; .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 community_id = post_view.community.id;
let moderators = blocking(context.pool(), move |conn| { let moderators = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id) CommunityModeratorView::for_community(conn, community_id)
@ -214,7 +208,6 @@ impl Perform for GetPost {
Ok(GetPostResponse { Ok(GetPostResponse {
post_view, post_view,
comments, comments,
community,
moderators, moderators,
online, online,
}) })
@ -285,12 +278,7 @@ impl Perform for CreatePostLike {
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
// Don't do a downvote if site has downvotes disabled // Don't do a downvote if site has downvotes disabled
if data.score == -1 { check_downvotes_enabled(data.score, context.pool()).await?;
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 for a community ban // Check for a community ban
let post_id = data.post_id; let post_id = data.post_id;

View File

@ -10,7 +10,6 @@ use actix_web::web::Data;
use anyhow::Context; use anyhow::Context;
use lemmy_apub::fetcher::search_by_apub_id; use lemmy_apub::fetcher::search_by_apub_id;
use lemmy_db::{ use lemmy_db::{
aggregates::site_aggregates::SiteAggregates,
diesel_option_overwrite, diesel_option_overwrite,
naive_now, naive_now,
source::{category::*, moderator::*, site::*}, source::{category::*, moderator::*, site::*},
@ -156,7 +155,7 @@ impl Perform for CreateSite {
) -> Result<SiteResponse, LemmyError> { ) -> Result<SiteResponse, LemmyError> {
let data: &CreateSite = &self; 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() { if blocking(context.pool(), read_site).await?.is_ok() {
return Err(APIError::err("site_already_exists").into()); 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??; 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 // Make sure user is an admin
is_admin(context.pool(), user.id).await?; 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 icon = diesel_option_overwrite(&data.icon);
let banner = diesel_option_overwrite(&data.banner); 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 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 { context.chat_server().do_send(SendAllMessage {
op: UserOperation::EditSite, op: UserOperation::EditSite,
@ -256,39 +255,41 @@ impl Perform for GetSite {
) -> Result<GetSiteResponse, LemmyError> { ) -> Result<GetSiteResponse, LemmyError> {
let data: &GetSite = &self; let data: &GetSite = &self;
// TODO refactor this a little let site_view = match blocking(context.pool(), move |conn| SiteView::read(conn)).await? {
let res = blocking(context.pool(), move |conn| Site::read(conn, 1)).await?; Ok(site_view) => Some(site_view),
let site_view = if res.is_ok() { // If the site isn't created yet, check the setup
Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??) Err(_) => {
} else if let Some(setup) = Settings::get().setup.as_ref() { if let Some(setup) = Settings::get().setup.as_ref() {
let register = Register { let register = Register {
username: setup.admin_username.to_owned(), username: setup.admin_username.to_owned(),
email: setup.admin_email.to_owned(), email: setup.admin_email.to_owned(),
password: setup.admin_password.to_owned(), password: setup.admin_password.to_owned(),
password_verify: setup.admin_password.to_owned(), password_verify: setup.admin_password.to_owned(),
admin: true, admin: true,
show_nsfw: true, show_nsfw: true,
captcha_uuid: None, captcha_uuid: None,
captcha_answer: None, captcha_answer: None,
}; };
let login_response = register.perform(context, websocket_id).await?; let login_response = register.perform(context, websocket_id).await?;
info!("Admin {} created", setup.admin_username); info!("Admin {} created", setup.admin_username);
let create_site = CreateSite { let create_site = CreateSite {
name: setup.site_name.to_owned(), name: setup.site_name.to_owned(),
description: None, description: None,
icon: None, icon: None,
banner: None, banner: None,
enable_downvotes: true, enable_downvotes: true,
open_registration: true, open_registration: true,
enable_nsfw: true, enable_nsfw: true,
auth: login_response.jwt, auth: login_response.jwt,
}; };
create_site.perform(context, websocket_id).await?; create_site.perform(context, websocket_id).await?;
info!("Site {} created", setup.site_name); info!("Site {} created", setup.site_name);
Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??) Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??)
} else { } else {
None None
}
}
}; };
let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
@ -321,17 +322,14 @@ impl Perform for GetSite {
u u
}); });
let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??;
Ok(GetSiteResponse { Ok(GetSiteResponse {
site: site_view, site_view,
admins, admins,
banned, banned,
online, online,
version: version::VERSION.to_string(), version: version::VERSION.to_string(),
my_user, my_user,
federated_instances: linked_instances(context.pool()).await?, federated_instances: linked_instances(context.pool()).await?,
counts,
}) })
} }
} }
@ -521,7 +519,7 @@ impl Perform for TransferSite {
user.private_key = None; user.private_key = None;
user.public_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 // Make sure user is the creator
if read_site.creator_id != user.id { 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 banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??;
Ok(GetSiteResponse { Ok(GetSiteResponse {
site: Some(site_view), site_view: Some(site_view),
admins, admins,
banned, banned,
online: 0, online: 0,
version: version::VERSION.to_string(), version: version::VERSION.to_string(),
my_user: Some(user), my_user: Some(user),
federated_instances: linked_instances(context.pool()).await?, 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?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
// Only let admins read this // Only let admins read this
let admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??; let user_id = user.id;
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.user.id).collect(); is_admin(context.pool(), user_id).await?;
if !admin_ids.contains(&user.id) {
return Err(APIError::err("not_an_admin").into());
}
// Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem // 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) { let config_hjson = match Settings::save_config_file(&data.config_hjson) {

View File

@ -38,7 +38,6 @@ use lemmy_db::{
post_report_view::PostReportView, post_report_view::PostReportView,
post_view::PostQueryBuilder, post_view::PostQueryBuilder,
private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView}, private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView},
site_view::SiteView,
user_mention_view::{UserMentionQueryBuilder, UserMentionView}, user_mention_view::{UserMentionQueryBuilder, UserMentionView},
user_view::{UserViewDangerous, UserViewSafe}, user_view::{UserViewDangerous, UserViewSafe},
}, },
@ -120,8 +119,8 @@ impl Perform for Register {
let data: &Register = &self; let data: &Register = &self;
// Make sure site has open registration // Make sure site has open registration
if let Ok(site_view) = blocking(context.pool(), move |conn| SiteView::read(conn)).await? { if let Ok(site) = blocking(context.pool(), move |conn| Site::read_simple(conn)).await? {
if !site_view.site.open_registration { if !site.open_registration {
return Err(APIError::err("registration_closed").into()); return Err(APIError::err("registration_closed").into());
} }
} }
@ -347,9 +346,6 @@ impl Perform for SaveUserSettings {
let data: &SaveUserSettings = &self; let data: &SaveUserSettings = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; 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 avatar = diesel_option_overwrite(&data.avatar);
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite(&data.banner);
let email = diesel_option_overwrite(&data.email); 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 { let password_encrypted = match &data.new_password {
Some(new_password) => { Some(new_password) => {
match &data.new_password_verify { match &data.new_password_verify {
@ -385,8 +382,7 @@ impl Perform for SaveUserSettings {
// Check the old password // Check the old password
match &data.old_password { match &data.old_password {
Some(old_password) => { Some(old_password) => {
let valid: bool = let valid: bool = verify(old_password, &user.password_encrypted).unwrap_or(false);
verify(old_password, &read_user.password_encrypted).unwrap_or(false);
if !valid { if !valid {
return Err(APIError::err("password_incorrect").into()); 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 => 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 { let user_form = UserForm {
name: read_user.name, name: user.name,
email, email,
matrix_user_id, matrix_user_id,
avatar, avatar,
banner, banner,
password_encrypted, password_encrypted,
preferred_username, preferred_username,
published: Some(read_user.published), published: Some(user.published),
updated: Some(naive_now()), updated: Some(naive_now()),
admin: read_user.admin, admin: user.admin,
banned: Some(read_user.banned), banned: Some(user.banned),
show_nsfw: data.show_nsfw, show_nsfw: data.show_nsfw,
theme: data.theme.to_owned(), theme: data.theme.to_owned(),
default_sort_type: data.default_sort_type, default_sort_type,
default_listing_type: data.default_listing_type, default_listing_type,
lang: data.lang.to_owned(), lang: data.lang.to_owned(),
show_avatars: data.show_avatars, show_avatars: data.show_avatars,
send_notifications_to_email: data.send_notifications_to_email, send_notifications_to_email: data.send_notifications_to_email,
actor_id: Some(read_user.actor_id), actor_id: Some(user.actor_id),
bio, bio,
local: read_user.local, local: user.local,
private_key: read_user.private_key, private_key: user.private_key,
public_key: read_user.public_key, public_key: user.public_key,
last_refreshed_at: None, last_refreshed_at: None,
}; };
@ -579,9 +578,8 @@ impl Perform for GetUserDetails {
// Return the jwt // Return the jwt
Ok(GetUserDetailsResponse { Ok(GetUserDetailsResponse {
// TODO need to figure out dangerous user view here user_view,
user: user_view, user_view_dangerous: user_dangerous,
user_dangerous,
follows, follows,
moderates, moderates,
comments, comments,
@ -669,22 +667,22 @@ impl Perform for BanUser {
} }
// Remove their data if that's desired // Remove their data if that's desired
if let Some(remove_data) = data.remove_data { if data.remove_data {
// Posts // Posts
blocking(context.pool(), move |conn: &'_ _| { 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??; .await??;
// Communities // Communities
blocking(context.pool(), move |conn: &'_ _| { 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??; .await??;
// Comments // Comments
blocking(context.pool(), move |conn: &'_ _| { 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??; .await??;
} }
@ -712,7 +710,7 @@ impl Perform for BanUser {
.await??; .await??;
let res = BanUserResponse { let res = BanUserResponse {
user: user_view, user_view,
banned: data.ban, banned: data.ban,
}; };
@ -1091,7 +1089,9 @@ impl Perform for CreatePrivateMessage {
}) })
.await??; .await??;
let res = PrivateMessageResponse { message }; let res = PrivateMessageResponse {
private_message_view: message,
};
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::CreatePrivateMessage, op: UserOperation::CreatePrivateMessage,
@ -1148,7 +1148,9 @@ impl Perform for EditPrivateMessage {
.await??; .await??;
let recipient_id = message.recipient.id; let recipient_id = message.recipient.id;
let res = PrivateMessageResponse { message }; let res = PrivateMessageResponse {
private_message_view: message,
};
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage, op: UserOperation::EditPrivateMessage,
@ -1211,7 +1213,9 @@ impl Perform for DeletePrivateMessage {
.await??; .await??;
let recipient_id = message.recipient.id; let recipient_id = message.recipient.id;
let res = PrivateMessageResponse { message }; let res = PrivateMessageResponse {
private_message_view: message,
};
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::DeletePrivateMessage, op: UserOperation::DeletePrivateMessage,
@ -1267,7 +1271,9 @@ impl Perform for MarkPrivateMessageAsRead {
.await??; .await??;
let recipient_id = message.recipient.id; let recipient_id = message.recipient.id;
let res = PrivateMessageResponse { message }; let res = PrivateMessageResponse {
private_message_view: message,
};
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::MarkPrivateMessageAsRead, op: UserOperation::MarkPrivateMessageAsRead,
@ -1305,7 +1311,9 @@ impl Perform for GetPrivateMessages {
}) })
.await??; .await??;
Ok(PrivateMessagesResponse { messages }) Ok(PrivateMessagesResponse {
private_messages: messages,
})
} }
} }

View File

@ -10,6 +10,7 @@ use lemmy_db::{
post::Post, post::Post,
}, },
views::comment_view::CommentView, views::comment_view::CommentView,
Crud,
Likeable, Likeable,
}; };
use lemmy_structs::{blocking, comment::CommentResponse, send_local_notifs}; use lemmy_structs::{blocking, comment::CommentResponse, send_local_notifs};

View File

@ -47,9 +47,11 @@ pub(crate) async fn receive_create_private_message(
}) })
.await??; .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 { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::CreatePrivateMessage, op: UserOperation::CreatePrivateMessage,
@ -85,9 +87,11 @@ pub(crate) async fn receive_update_private_message(
}) })
.await??; .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 { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage, op: UserOperation::EditPrivateMessage,
@ -117,8 +121,10 @@ pub(crate) async fn receive_delete_private_message(
}) })
.await??; .await??;
let res = PrivateMessageResponse { message }; let res = PrivateMessageResponse {
let recipient_id = res.message.recipient.id; private_message_view: message,
};
let recipient_id = res.private_message_view.recipient.id;
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage, op: UserOperation::EditPrivateMessage,
response: res, response: res,
@ -152,8 +158,10 @@ pub(crate) async fn receive_undo_delete_private_message(
}) })
.await??; .await??;
let res = PrivateMessageResponse { message }; let res = PrivateMessageResponse {
let recipient_id = res.message.recipient.id; private_message_view: message,
};
let recipient_id = res.private_message_view.recipient.id;
context.chat_server().do_send(SendUserRoomMessage { context.chat_server().do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage, op: UserOperation::EditPrivateMessage,
response: res, response: res,

View File

@ -27,6 +27,7 @@ use lemmy_db::{
user_view::UserViewSafe, user_view::UserViewSafe,
}, },
ApubObject, ApubObject,
Crud,
Joinable, Joinable,
SearchType, SearchType,
}; };

View File

@ -4,7 +4,7 @@ use crate::{
}; };
use actix_web::{body::Body, web, HttpResponse}; use actix_web::{body::Body, web, HttpResponse};
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_db::source::post::Post; use lemmy_db::{source::post::Post, Crud};
use lemmy_structs::blocking; use lemmy_structs::blocking;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;

View File

@ -2,10 +2,11 @@ use crate::schema::site_aggregates;
use diesel::{result::Error, *}; use diesel::{result::Error, *};
use serde::Serialize; use serde::Serialize;
#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)] #[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Clone)]
#[table_name = "site_aggregates"] #[table_name = "site_aggregates"]
pub struct SiteAggregates { pub struct SiteAggregates {
pub id: i32, pub id: i32,
pub site_id: i32,
pub users: i64, pub users: i64,
pub posts: i64, pub posts: i64,
pub comments: i64, pub comments: i64,

View File

@ -362,6 +362,7 @@ table! {
table! { table! {
site_aggregates (id) { site_aggregates (id) {
id -> Int4, id -> Int4,
site_id -> Int4,
users -> Int8, users -> Int8,
posts -> Int8, posts -> Int8,
comments -> Int8, comments -> Int8,
@ -560,6 +561,7 @@ joinable!(post_report -> post (post_id));
joinable!(post_saved -> post (post_id)); joinable!(post_saved -> post (post_id));
joinable!(post_saved -> user_ (user_id)); joinable!(post_saved -> user_ (user_id));
joinable!(site -> user_ (creator_id)); joinable!(site -> user_ (creator_id));
joinable!(site_aggregates -> site (site_id));
joinable!(user_aggregates -> user_ (user_id)); joinable!(user_aggregates -> user_ (user_id));
joinable!(user_ban -> user_ (user_id)); joinable!(user_ban -> user_ (user_id));
joinable!(user_mention -> comment (comment_id)); joinable!(user_mention -> comment (comment_id));

View File

@ -106,11 +106,6 @@ impl ApubObject<PostForm> for Post {
} }
impl Post { impl Post {
pub fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
post.filter(id.eq(post_id)).first::<Self>(conn)
}
pub fn list_for_community( pub fn list_for_community(
conn: &PgConnection, conn: &PgConnection,
the_community_id: i32, the_community_id: i32,

View File

@ -59,4 +59,9 @@ impl Site {
.set((creator_id.eq(new_creator_id), updated.eq(naive_now()))) .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn read_simple(conn: &PgConnection) -> Result<Self, Error> {
use crate::schema::site::dsl::*;
site.first::<Self>(conn)
}
} }

View File

@ -145,6 +145,15 @@ impl CommentView {
my_vote, 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> { pub struct CommentQueryBuilder<'a> {

View File

@ -1,5 +1,6 @@
use crate::{ use crate::{
schema::{site, user_}, aggregates::site_aggregates::SiteAggregates,
schema::{site, site_aggregates, user_},
source::{ source::{
site::Site, site::Site,
user::{UserSafe, User_}, user::{UserSafe, User_},
@ -13,15 +14,25 @@ use serde::Serialize;
pub struct SiteView { pub struct SiteView {
pub site: Site, pub site: Site,
pub creator: UserSafe, pub creator: UserSafe,
pub counts: SiteAggregates,
} }
impl SiteView { impl SiteView {
pub fn read(conn: &PgConnection) -> Result<Self, Error> { pub fn read(conn: &PgConnection) -> Result<Self, Error> {
let (site, creator) = site::table let (site, creator, counts) = site::table
.inner_join(user_::table) .inner_join(user_::table)
.select((site::all_columns, User_::safe_columns_tuple())) .inner_join(site_aggregates::table)
.first::<(Site, UserSafe)>(conn)?; .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,
})
} }
} }

View File

@ -35,7 +35,7 @@ pub struct RemoveComment {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct MarkCommentAsRead { pub struct MarkCommentAsRead {
pub edit_id: i32, pub comment_id: i32,
pub read: bool, pub read: bool,
pub auth: String, pub auth: String,
} }
@ -50,8 +50,8 @@ pub struct SaveComment {
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct CommentResponse { pub struct CommentResponse {
pub comment_view: CommentView, pub comment_view: CommentView,
pub recipient_ids: Vec<i32>, pub recipient_ids: Vec<i32>, // TODO another way to do this? Maybe a UserMention belongs to Comment
pub form_id: Option<String>, pub form_id: Option<String>, // An optional front end ID, to tell which is coming back
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -98,6 +98,7 @@ pub struct ResolveCommentReport {
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ResolveCommentReportResponse { pub struct ResolveCommentReportResponse {
// TODO this should probably return the view
pub report_id: i32, pub report_id: i32,
pub resolved: bool, pub resolved: bool,
} }

View File

@ -57,7 +57,7 @@ pub struct BanFromCommunity {
pub community_id: i32, pub community_id: i32,
pub user_id: i32, pub user_id: i32,
pub ban: bool, pub ban: bool,
pub remove_data: Option<bool>, pub remove_data: bool,
pub reason: Option<String>, pub reason: Option<String>,
pub expires: Option<i64>, pub expires: Option<i64>,
pub auth: String, pub auth: String,

View File

@ -3,7 +3,6 @@ pub mod community;
pub mod post; pub mod post;
pub mod site; pub mod site;
pub mod user; pub mod user;
pub mod websocket;
use diesel::PgConnection; use diesel::PgConnection;
use lemmy_db::{ use lemmy_db::{

View File

@ -1,6 +1,6 @@
use lemmy_db::views::{ use lemmy_db::views::{
comment_view::CommentView, comment_view::CommentView,
community::{community_moderator_view::CommunityModeratorView, community_view::CommunityView}, community::community_moderator_view::CommunityModeratorView,
post_report_view::PostReportView, post_report_view::PostReportView,
post_view::PostView, post_view::PostView,
}; };
@ -31,7 +31,6 @@ pub struct GetPost {
pub struct GetPostResponse { pub struct GetPostResponse {
pub post_view: PostView, pub post_view: PostView,
pub comments: Vec<CommentView>, pub comments: Vec<CommentView>,
pub community: CommunityView,
pub moderators: Vec<CommunityModeratorView>, pub moderators: Vec<CommunityModeratorView>,
pub online: usize, pub online: usize,
} }

View File

@ -1,5 +1,4 @@
use lemmy_db::{ use lemmy_db::{
aggregates::site_aggregates::SiteAggregates,
source::{category::*, user::*}, source::{category::*, user::*},
views::{ views::{
comment_view::CommentView, comment_view::CommentView,
@ -101,16 +100,14 @@ pub struct GetSite {
pub auth: Option<String>, pub auth: Option<String>,
} }
// TODO combine siteresponse and getsiteresponse
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct SiteResponse { pub struct SiteResponse {
pub site: SiteView, pub site_view: SiteView,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct GetSiteResponse { pub struct GetSiteResponse {
pub site: Option<SiteView>, // Because the site might not be set up yet pub site_view: Option<SiteView>, // Because the site might not be set up yet
pub counts: SiteAggregates,
pub admins: Vec<UserViewSafe>, pub admins: Vec<UserViewSafe>,
pub banned: Vec<UserViewSafe>, pub banned: Vec<UserViewSafe>,
pub online: usize, pub online: usize,

View File

@ -48,8 +48,8 @@ pub struct CaptchaResponse {
pub struct SaveUserSettings { pub struct SaveUserSettings {
pub show_nsfw: bool, pub show_nsfw: bool,
pub theme: String, pub theme: String,
pub default_sort_type: i16, pub default_sort_type: String,
pub default_listing_type: i16, pub default_listing_type: String,
pub lang: String, pub lang: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub banner: Option<String>, pub banner: Option<String>,
@ -84,8 +84,8 @@ pub struct GetUserDetails {
#[derive(Serialize)] #[derive(Serialize)]
pub struct GetUserDetailsResponse { pub struct GetUserDetailsResponse {
pub user: Option<UserViewSafe>, pub user_view: Option<UserViewSafe>,
pub user_dangerous: Option<UserViewDangerous>, pub user_view_dangerous: Option<UserViewDangerous>,
pub follows: Vec<CommunityFollowerView>, pub follows: Vec<CommunityFollowerView>,
pub moderates: Vec<CommunityModeratorView>, pub moderates: Vec<CommunityModeratorView>,
pub comments: Vec<CommentView>, pub comments: Vec<CommentView>,
@ -123,7 +123,7 @@ pub struct AddAdminResponse {
pub struct BanUser { pub struct BanUser {
pub user_id: i32, pub user_id: i32,
pub ban: bool, pub ban: bool,
pub remove_data: Option<bool>, pub remove_data: bool,
pub reason: Option<String>, pub reason: Option<String>,
pub expires: Option<i64>, pub expires: Option<i64>,
pub auth: String, pub auth: String,
@ -131,7 +131,7 @@ pub struct BanUser {
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct BanUserResponse { pub struct BanUserResponse {
pub user: UserViewSafe, pub user_view: UserViewSafe,
pub banned: bool, pub banned: bool,
} }
@ -224,12 +224,12 @@ pub struct GetPrivateMessages {
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct PrivateMessagesResponse { pub struct PrivateMessagesResponse {
pub messages: Vec<PrivateMessageView>, pub private_messages: Vec<PrivateMessageView>,
} }
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
pub struct PrivateMessageResponse { pub struct PrivateMessageResponse {
pub message: PrivateMessageView, pub private_message_view: PrivateMessageView,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@ -1 +0,0 @@

View File

@ -1,10 +1,12 @@
-- Site aggregates -- Site aggregates
drop table site_aggregates; drop table site_aggregates;
drop trigger site_aggregates_site on site;
drop trigger site_aggregates_user on user_; drop trigger site_aggregates_user on user_;
drop trigger site_aggregates_post on post; drop trigger site_aggregates_post on post;
drop trigger site_aggregates_comment on comment; drop trigger site_aggregates_comment on comment;
drop trigger site_aggregates_community on community; drop trigger site_aggregates_community on community;
drop function drop function
site_aggregates_site,
site_aggregates_user, site_aggregates_user,
site_aggregates_post, site_aggregates_post,
site_aggregates_comment, site_aggregates_comment,

View File

@ -1,17 +1,38 @@
-- Add site aggregates -- Add site aggregates
create table site_aggregates ( create table site_aggregates (
id serial primary key, id serial primary key,
users bigint not null, site_id int references site on update cascade on delete cascade not null,
posts bigint not null, users bigint not null default 0,
comments bigint not null, posts bigint not null default 0,
communities bigint not null comments bigint not null default 0,
communities bigint not null default 0
); );
insert into site_aggregates (users, posts, comments, communities) insert into site_aggregates (site_id, users, posts, comments, communities)
select ( select coalesce(count(*), 0) from user_) as users, 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 post) as posts,
( select coalesce(count(*), 0) from comment) as comments, ( 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 -- Add site aggregate triggers
-- user -- user