diff --git a/lemmy_api/src/site.rs b/lemmy_api/src/site.rs index e8d0df04b..2cd97f7f6 100644 --- a/lemmy_api/src/site.rs +++ b/lemmy_api/src/site.rs @@ -19,6 +19,7 @@ use lemmy_db::{ naive_now, post_view::*, site::*, + site_aggregates::SiteAggregates, user_view::*, views::site_view::SiteView, Crud, @@ -310,6 +311,8 @@ impl Perform for GetSite { u }); + let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??; + Ok(GetSiteResponse { site: site_view, admins, @@ -318,11 +321,7 @@ impl Perform for GetSite { version: version::VERSION.to_string(), my_user, federated_instances: linked_instances(context.pool()).await?, - // TODO - number_of_users: 0, - number_of_posts: 0, - number_of_comments: 0, - number_of_communities: 0, + counts, }) } } @@ -546,6 +545,8 @@ impl Perform for TransferSite { let banned = blocking(context.pool(), move |conn| UserView::banned(conn)).await??; + let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??; + Ok(GetSiteResponse { site: Some(site_view), admins, @@ -554,11 +555,7 @@ impl Perform for TransferSite { version: version::VERSION.to_string(), my_user: Some(user), federated_instances: linked_instances(context.pool()).await?, - // TODO - number_of_users: 0, - number_of_posts: 0, - number_of_comments: 0, - number_of_communities: 0, + counts, }) } } diff --git a/lemmy_apub/src/activities/receive/private_message.rs b/lemmy_apub/src/activities/receive/private_message.rs index 8f1c95b9d..07913226c 100644 --- a/lemmy_apub/src/activities/receive/private_message.rs +++ b/lemmy_apub/src/activities/receive/private_message.rs @@ -194,7 +194,7 @@ async fn check_private_message_activity_valid( where T: AsBase + AsObject + ActorAndObjectRefExt, { - let to_and_cc = get_activity_to_and_cc(activity)?; + let to_and_cc = get_activity_to_and_cc(activity); if to_and_cc.len() != 1 { return Err(anyhow!("Private message can only be addressed to one user").into()); } diff --git a/lemmy_apub/src/inbox/community_inbox.rs b/lemmy_apub/src/inbox/community_inbox.rs index 7c144a00d..14878cfe5 100644 --- a/lemmy_apub/src/inbox/community_inbox.rs +++ b/lemmy_apub/src/inbox/community_inbox.rs @@ -81,7 +81,7 @@ pub async fn community_inbox( Community::read_from_name(&conn, &path) }) .await??; - let to_and_cc = get_activity_to_and_cc(&activity)?; + let to_and_cc = get_activity_to_and_cc(&activity); if !to_and_cc.contains(&&community.actor_id()?) { return Err(anyhow!("Activity delivered to wrong community").into()); } diff --git a/lemmy_apub/src/inbox/mod.rs b/lemmy_apub/src/inbox/mod.rs index ce6c7eded..f8dd8bfeb 100644 --- a/lemmy_apub/src/inbox/mod.rs +++ b/lemmy_apub/src/inbox/mod.rs @@ -50,7 +50,7 @@ pub(crate) async fn is_activity_already_known( } } -pub(crate) fn get_activity_to_and_cc(activity: &T) -> Result, LemmyError> +pub(crate) fn get_activity_to_and_cc(activity: &T) -> Vec where T: AsBase + AsObject + ActorAndObjectRefExt, { @@ -75,14 +75,14 @@ where .collect(); to_and_cc.append(&mut cc); } - Ok(to_and_cc) + to_and_cc } pub(crate) fn is_addressed_to_public(activity: &T) -> Result<(), LemmyError> where T: AsBase + AsObject + ActorAndObjectRefExt, { - let to_and_cc = get_activity_to_and_cc(activity)?; + let to_and_cc = get_activity_to_and_cc(activity); if to_and_cc.contains(&public()) { Ok(()) } else { diff --git a/lemmy_apub/src/inbox/shared_inbox.rs b/lemmy_apub/src/inbox/shared_inbox.rs index 2875696e2..e9a81ab36 100644 --- a/lemmy_apub/src/inbox/shared_inbox.rs +++ b/lemmy_apub/src/inbox/shared_inbox.rs @@ -66,7 +66,7 @@ pub async fn shared_inbox( let activity_any_base = activity.clone().into_any_base()?; let mut res: Option = None; - let to_and_cc = get_activity_to_and_cc(&activity)?; + let to_and_cc = get_activity_to_and_cc(&activity); // Handle community first, so in case the sender is banned by the community, it will error out. // If we handled the user receive first, the activity would be inserted to the database before the // community could check for bans. diff --git a/lemmy_apub/src/inbox/user_inbox.rs b/lemmy_apub/src/inbox/user_inbox.rs index 2f847a5cd..cc1c0661b 100644 --- a/lemmy_apub/src/inbox/user_inbox.rs +++ b/lemmy_apub/src/inbox/user_inbox.rs @@ -101,7 +101,7 @@ pub async fn user_inbox( User_::read_from_name(&conn, &username) }) .await??; - let to_and_cc = get_activity_to_and_cc(&activity)?; + let to_and_cc = get_activity_to_and_cc(&activity); // TODO: we should also accept activities that are sent to community followers if !to_and_cc.contains(&&user.actor_id()?) { return Err(anyhow!("Activity delivered to wrong user").into()); @@ -172,7 +172,7 @@ async fn is_for_user_inbox( context: &LemmyContext, activity: &UserAcceptedActivities, ) -> Result<(), LemmyError> { - let to_and_cc = get_activity_to_and_cc(activity)?; + let to_and_cc = get_activity_to_and_cc(activity); // Check if it is addressed directly to any local user if is_addressed_to_local_user(&to_and_cc, context.pool()).await? { return Ok(()); diff --git a/lemmy_db/src/lib.rs b/lemmy_db/src/lib.rs index bf291db3f..c7f4585fd 100644 --- a/lemmy_db/src/lib.rs +++ b/lemmy_db/src/lib.rs @@ -28,7 +28,7 @@ pub mod private_message; pub mod private_message_view; pub mod schema; pub mod site; -pub mod site_view; +pub mod site_aggregates; pub mod user; pub mod user_mention; pub mod user_mention_view; diff --git a/lemmy_db/src/schema.rs b/lemmy_db/src/schema.rs index 49bbc46fb..ce84c7d57 100644 --- a/lemmy_db/src/schema.rs +++ b/lemmy_db/src/schema.rs @@ -440,6 +440,16 @@ table! { } } +table! { + site_aggregates (id) { + id -> Int4, + users -> Int8, + posts -> Int8, + comments -> Int8, + communities -> Int8, + } +} + table! { user_ (id) { id -> Int4, @@ -587,6 +597,7 @@ allow_tables_to_appear_in_same_query!( post_saved, private_message, site, + site_aggregates, user_, user_ban, user_fast, diff --git a/lemmy_db/src/site_aggregates.rs b/lemmy_db/src/site_aggregates.rs new file mode 100644 index 000000000..488046ac3 --- /dev/null +++ b/lemmy_db/src/site_aggregates.rs @@ -0,0 +1,19 @@ +use crate::schema::site_aggregates; +use diesel::{result::Error, *}; +use serde::Serialize; + +#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)] +#[table_name = "site_aggregates"] +pub struct SiteAggregates { + pub id: i32, + pub users: i64, + pub posts: i64, + pub comments: i64, + pub communities: i64, +} + +impl SiteAggregates { + pub fn read(conn: &PgConnection) -> Result { + site_aggregates::table.first::(conn) + } +} diff --git a/lemmy_db/src/site_view.rs b/lemmy_db/src/site_view.rs deleted file mode 100644 index fd15ac778..000000000 --- a/lemmy_db/src/site_view.rs +++ /dev/null @@ -1,55 +0,0 @@ -use diesel::{result::Error, *}; -use serde::Serialize; - -table! { - site_view (id) { - id -> Int4, - name -> Varchar, - description -> Nullable, - creator_id -> Int4, - published -> Timestamp, - updated -> Nullable, - enable_downvotes -> Bool, - open_registration -> Bool, - enable_nsfw -> Bool, - icon -> Nullable, - banner -> Nullable, - creator_name -> Varchar, - creator_preferred_username -> Nullable, - creator_avatar -> Nullable, - number_of_users -> BigInt, - number_of_posts -> BigInt, - number_of_comments -> BigInt, - number_of_communities -> BigInt, - } -} - -#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, QueryableByName, Clone)] -#[table_name = "site_view"] -pub struct SiteView { - pub id: i32, - pub name: String, - pub description: Option, - pub creator_id: i32, - pub published: chrono::NaiveDateTime, - pub updated: Option, - pub enable_downvotes: bool, - pub open_registration: bool, - pub enable_nsfw: bool, - pub icon: Option, - pub banner: Option, - pub creator_name: String, - pub creator_preferred_username: Option, - pub creator_avatar: Option, - pub number_of_users: i64, - pub number_of_posts: i64, - pub number_of_comments: i64, - pub number_of_communities: i64, -} - -impl SiteView { - pub fn read(conn: &PgConnection) -> Result { - use super::site_view::site_view::dsl::*; - site_view.first::(conn) - } -} diff --git a/lemmy_db/src/views/mod.rs b/lemmy_db/src/views/mod.rs index 41fabde82..cd68cfe0a 100644 --- a/lemmy_db/src/views/mod.rs +++ b/lemmy_db/src/views/mod.rs @@ -1 +1,2 @@ pub mod site_view; +pub mod user_view; diff --git a/lemmy_db/src/views/user_view.rs b/lemmy_db/src/views/user_view.rs new file mode 100644 index 000000000..eb18afbc1 --- /dev/null +++ b/lemmy_db/src/views/user_view.rs @@ -0,0 +1,65 @@ +use crate::{ + schema::user_, + user::{UserSafe, User_}, +}; +use diesel::{result::Error, *}; +use serde::Serialize; + +#[derive(Debug, Serialize, Clone)] +pub struct UserViewSafe { + pub user: UserSafe, + // TODO + // pub number_of_posts: i64, + // pub post_score: i64, + // pub number_of_comments: i64, + // pub comment_score: i64, +} + +pub struct UserViewDangerous { + pub user: User_, + // TODO + // pub number_of_posts: i64, + // pub post_score: i64, + // pub number_of_comments: i64, + // pub comment_score: i64, +} + +impl UserViewDangerous { + pub fn read(conn: &PgConnection, id: i32) -> Result { + let user = user_::table.find(id).first::(conn)?; + Ok(Self { user }) + } +} + +impl UserViewSafe { + pub fn read(conn: &PgConnection, id: i32) -> Result { + let user = user_::table.find(id).first::(conn)?.to_safe(); + Ok(Self { user }) + } + + pub fn admins(conn: &PgConnection) -> Result, Error> { + let admins = user_::table + // TODO do joins here + .filter(user_::admin.eq(true)) + .order_by(user_::published) + .load::(conn)?; + + Ok(vec_to_user_view_safe(admins)) + } + + pub fn banned(conn: &PgConnection) -> Result, Error> { + let banned = user_::table + // TODO do joins here + .filter(user_::banned.eq(true)) + .load::(conn)?; + + Ok(vec_to_user_view_safe(banned)) + } +} + +fn vec_to_user_view_safe(users: Vec) -> Vec { + users + .iter() + .map(|a| UserViewSafe { user: a.to_safe() }) + .collect::>() +} diff --git a/lemmy_structs/src/site.rs b/lemmy_structs/src/site.rs index 2192c4fd7..12fda258b 100644 --- a/lemmy_structs/src/site.rs +++ b/lemmy_structs/src/site.rs @@ -4,6 +4,7 @@ use lemmy_db::{ community_view::*, moderator_views::*, post_view::*, + site_aggregates::SiteAggregates, user::*, user_view::*, views::site_view::SiteView, @@ -98,10 +99,7 @@ pub struct SiteResponse { #[derive(Serialize)] pub struct GetSiteResponse { pub site: Option, // Because the site might not be set up yet - pub number_of_users: i64, - pub number_of_posts: i64, - pub number_of_comments: i64, - pub number_of_communities: i64, + pub counts: SiteAggregates, pub admins: Vec, pub banned: Vec, pub online: usize, diff --git a/migrations/2020-12-02-152437_create_site_aggregates/down.sql b/migrations/2020-12-02-152437_create_site_aggregates/down.sql new file mode 100644 index 000000000..bd90603dd --- /dev/null +++ b/migrations/2020-12-02-152437_create_site_aggregates/down.sql @@ -0,0 +1,19 @@ +-- Site aggregates +drop table site_aggregates; +drop trigger site_aggregates_insert_user on user_; +drop trigger site_aggregates_delete_user on user_; +drop trigger site_aggregates_insert_post on post; +drop trigger site_aggregates_delete_post on post; +drop trigger site_aggregates_insert_comment on comment; +drop trigger site_aggregates_delete_comment on comment; +drop trigger site_aggregates_insert_community on community; +drop trigger site_aggregates_delete_community on community; +drop function + site_aggregates_user_increment, + site_aggregates_user_decrement, + site_aggregates_post_increment, + site_aggregates_post_decrement, + site_aggregates_comment_increment, + site_aggregates_comment_decrement, + site_aggregates_community_increment, + site_aggregates_community_decrement; diff --git a/migrations/2020-12-02-152437_create_site_aggregates/up.sql b/migrations/2020-12-02-152437_create_site_aggregates/up.sql new file mode 100644 index 000000000..7f4822685 --- /dev/null +++ b/migrations/2020-12-02-152437_create_site_aggregates/up.sql @@ -0,0 +1,124 @@ +-- 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 +); + +insert into site_aggregates (users, posts, comments, communities) + select ( 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; + +-- Add site aggregate triggers +-- user +create function site_aggregates_user_increment() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set users = users + 1; + return null; +end $$; + +create trigger site_aggregates_insert_user +after insert on user_ +execute procedure site_aggregates_user_increment(); + +create function site_aggregates_user_decrement() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set users = users - 1; + return null; +end $$; + +create trigger site_aggregates_delete_user +after delete on user_ +execute procedure site_aggregates_user_decrement(); + +-- post +create function site_aggregates_post_increment() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set posts = posts + 1; + return null; +end $$; + +create trigger site_aggregates_insert_post +after insert on post +execute procedure site_aggregates_post_increment(); + +create function site_aggregates_post_decrement() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set posts = posts - 1; + return null; +end $$; + +create trigger site_aggregates_delete_post +after delete on post +execute procedure site_aggregates_post_decrement(); + +-- comment +create function site_aggregates_comment_increment() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set comments = comments + 1; + return null; +end $$; + +create trigger site_aggregates_insert_comment +after insert on comment +execute procedure site_aggregates_comment_increment(); + +create function site_aggregates_comment_decrement() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set comments = comments - 1; + return null; +end $$; + +create trigger site_aggregates_delete_comment +after delete on comment +execute procedure site_aggregates_comment_decrement(); + +-- community +create function site_aggregates_community_increment() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set communities = communities + 1; + return null; +end $$; + +create trigger site_aggregates_insert_community +after insert on community +execute procedure site_aggregates_community_increment(); + +create function site_aggregates_community_decrement() +returns trigger language plpgsql +as $$ +begin + update site_aggregates + set communities = communities - 1; + return null; +end $$; + +create trigger site_aggregates_delete_community +after delete on community +execute procedure site_aggregates_community_decrement(); + diff --git a/migrations/2020-12-02-152437_remove_views/down.sql b/migrations/2020-12-02-152437_remove_views/down.sql deleted file mode 100644 index 291a97c5c..000000000 --- a/migrations/2020-12-02-152437_remove_views/down.sql +++ /dev/null @@ -1 +0,0 @@ --- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/migrations/2020-12-02-152437_remove_views/up.sql b/migrations/2020-12-02-152437_remove_views/up.sql deleted file mode 100644 index 33cf74b57..000000000 --- a/migrations/2020-12-02-152437_remove_views/up.sql +++ /dev/null @@ -1 +0,0 @@ --- Your SQL goes here \ No newline at end of file