naive_now,
post::Post,
site::*,
- user_view::*,
+ views::user_view::UserViewSafe,
Bannable,
Crud,
Followable,
let user_id = data.user_id;
let user_view = blocking(context.pool(), move |conn| {
- UserView::get_user_secure(conn, user_id)
+ UserViewSafe::read(conn, user_id)
})
.await??;
})
.await??;
- let mut admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
+ let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
let creator_index = admins
.iter()
- .position(|r| r.id == site_creator_id)
+ .position(|r| r.user.id == site_creator_id)
.context(location_info!())?;
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
// Make sure user is the creator, or an admin
- if user.id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user.id) {
+ if user.id != read_community.creator_id
+ && !admins.iter().map(|a| a.user.id).any(|x| x == user.id)
+ {
return Err(APIError::err("not_an_admin").into());
}
naive_now,
post_view::*,
site::*,
- user_view::*,
- views::site_view::SiteView,
+ views::{
+ site_view::SiteView,
+ user_view::{UserQueryBuilder, UserViewSafe},
+ },
Crud,
SearchType,
SortType,
None
};
- let mut admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
+ let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
// Make sure the site creator is the top admin
if let Some(site_view) = site_view.to_owned() {
let site_creator_id = site_view.creator.id;
// TODO investigate why this is sometimes coming back null
// Maybe user_.admin isn't being set to true?
- if let Some(creator_index) = admins.iter().position(|r| r.id == site_creator_id) {
+ if let Some(creator_index) = admins.iter().position(|r| r.user.id == site_creator_id) {
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
}
}
- let banned = blocking(context.pool(), move |conn| UserView::banned(conn)).await??;
+ let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
let online = context
.chat_server()
let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
- let mut admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
+ let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
let creator_index = admins
.iter()
- .position(|r| r.id == site_view.creator.id)
+ .position(|r| r.user.id == site_view.creator.id)
.context(location_info!())?;
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
- let banned = blocking(context.pool(), move |conn| UserView::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??;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
// Only let admins read this
- let admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
- let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
+ let admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
+ let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.user.id).collect();
if !admin_ids.contains(&user.id) {
return Err(APIError::err("not_an_admin").into());
user::*,
user_mention::*,
user_mention_view::*,
- user_view::*,
- views::site_view::SiteView,
+ views::{
+ site_view::SiteView,
+ user_view::{UserViewDangerous, UserViewSafe},
+ },
Crud,
Followable,
Joinable,
// Make sure there are no admins
let any_admins = blocking(context.pool(), move |conn| {
- UserView::admins(conn).map(|a| a.is_empty())
+ UserViewSafe::admins(conn).map(|a| a.is_empty())
})
.await??;
if data.admin && !any_admins {
};
let user_id = user.map(|u| u.id);
- let user_fun = move |conn: &'_ _| {
- match user_id {
- // if there's a logged in user and it's the same id as the user whose details are being
- // requested we need to use get_user_dangerous so it returns their email or other sensitive
- // data hidden when viewing users other than yourself
- Some(auth_user_id) => {
- if user_details_id == auth_user_id {
- UserView::get_user_dangerous(conn, auth_user_id)
- } else {
- UserView::get_user_secure(conn, user_details_id)
- }
- }
- None => UserView::get_user_secure(conn, user_details_id),
+
+ let (user_view, user_dangerous) = if let Some(auth_user_id) = user_id {
+ if user_details_id == auth_user_id {
+ (
+ None,
+ Some(
+ blocking(context.pool(), move |conn| {
+ UserViewDangerous::read(conn, auth_user_id)
+ })
+ .await??,
+ ),
+ )
+ } else {
+ (
+ Some(
+ blocking(context.pool(), move |conn| {
+ UserViewSafe::read(conn, user_details_id)
+ })
+ .await??,
+ ),
+ None,
+ )
}
+ } else {
+ (
+ Some(
+ blocking(context.pool(), move |conn| {
+ UserViewSafe::read(conn, user_details_id)
+ })
+ .await??,
+ ),
+ None,
+ )
};
- let user_view = blocking(context.pool(), user_fun).await??;
-
let page = data.page;
let limit = data.limit;
let saved_only = data.saved_only;
// Return the jwt
Ok(GetUserDetailsResponse {
+ // TODO need to figure out dangerous user view here
user: user_view,
+ user_dangerous,
follows,
moderates,
comments,
})
.await??;
- let mut admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
+ let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
let creator_index = admins
.iter()
- .position(|r| r.id == site_creator_id)
+ .position(|r| r.user.id == site_creator_id)
.context(location_info!())?;
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
let user_id = data.user_id;
let user_view = blocking(context.pool(), move |conn| {
- UserView::get_user_secure(conn, user_id)
+ UserViewSafe::read(conn, user_id)
})
.await??;
post::{Post, PostForm},
post_view::PostView,
user::{UserForm, User_},
- user_view::UserView,
+ views::user_view::UserViewSafe,
Crud,
Joinable,
SearchType,
response.users = vec![
blocking(context.pool(), move |conn| {
- UserView::get_user_secure(conn, user.id)
+ UserViewSafe::read(conn, user.id)
})
.await??,
];
}
fn community_mods_and_admins(conn: &PgConnection, community_id: i32) -> Result<Vec<i32>, Error> {
- use crate::{community_view::CommunityModeratorView, user_view::UserView};
+ use crate::{community_view::CommunityModeratorView, views::user_view::UserViewSafe};
let mut mods_and_admins: Vec<i32> = Vec::new();
mods_and_admins.append(
&mut CommunityModeratorView::for_community(conn, community_id)
.map(|v| v.into_iter().map(|m| m.user_id).collect())?,
);
mods_and_admins
- .append(&mut UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
+ .append(&mut UserViewSafe::admins(conn).map(|v| v.into_iter().map(|a| a.user.id).collect())?);
Ok(mods_and_admins)
}
pub mod user;
pub mod user_mention;
pub mod user_mention_view;
-pub mod user_view;
pub mod views;
pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
+++ /dev/null
-use super::user_view::user_fast::BoxedQuery;
-use crate::{fuzzy_search, limit_and_offset, MaybeOptional, SortType};
-use diesel::{dsl::*, pg::Pg, result::Error, *};
-use serde::Serialize;
-
-table! {
- user_view (id) {
- id -> Int4,
- actor_id -> Text,
- name -> Varchar,
- preferred_username -> Nullable<Varchar>,
- avatar -> Nullable<Text>,
- banner -> Nullable<Text>,
- email -> Nullable<Text>,
- matrix_user_id -> Nullable<Text>,
- bio -> Nullable<Text>,
- local -> Bool,
- admin -> Bool,
- banned -> Bool,
- show_avatars -> Bool,
- send_notifications_to_email -> Bool,
- published -> Timestamp,
- number_of_posts -> BigInt,
- post_score -> BigInt,
- number_of_comments -> BigInt,
- comment_score -> BigInt,
- }
-}
-
-table! {
- user_fast (id) {
- id -> Int4,
- actor_id -> Text,
- name -> Varchar,
- preferred_username -> Nullable<Varchar>,
- avatar -> Nullable<Text>,
- banner -> Nullable<Text>,
- email -> Nullable<Text>,
- matrix_user_id -> Nullable<Text>,
- bio -> Nullable<Text>,
- local -> Bool,
- admin -> Bool,
- banned -> Bool,
- show_avatars -> Bool,
- send_notifications_to_email -> Bool,
- published -> Timestamp,
- number_of_posts -> BigInt,
- post_score -> BigInt,
- number_of_comments -> BigInt,
- comment_score -> BigInt,
- }
-}
-
-#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, QueryableByName, Clone)]
-#[table_name = "user_fast"]
-pub struct UserView {
- pub id: i32,
- pub actor_id: String,
- pub name: String,
- pub preferred_username: Option<String>,
- pub avatar: Option<String>,
- pub banner: Option<String>,
- pub email: Option<String>, // TODO this shouldn't be in this view
- pub matrix_user_id: Option<String>,
- pub bio: Option<String>,
- pub local: bool,
- pub admin: bool,
- pub banned: bool,
- pub show_avatars: bool, // TODO this is a setting, probably doesn't need to be here
- pub send_notifications_to_email: bool, // TODO also never used
- pub published: chrono::NaiveDateTime,
- pub number_of_posts: i64,
- pub post_score: i64,
- pub number_of_comments: i64,
- pub comment_score: i64,
-}
-
-pub struct UserQueryBuilder<'a> {
- conn: &'a PgConnection,
- query: BoxedQuery<'a, Pg>,
- sort: &'a SortType,
- page: Option<i64>,
- limit: Option<i64>,
-}
-
-impl<'a> UserQueryBuilder<'a> {
- pub fn create(conn: &'a PgConnection) -> Self {
- use super::user_view::user_fast::dsl::*;
-
- let query = user_fast.into_boxed();
-
- UserQueryBuilder {
- conn,
- query,
- sort: &SortType::Hot,
- page: None,
- limit: None,
- }
- }
-
- pub fn sort(mut self, sort: &'a SortType) -> Self {
- self.sort = sort;
- self
- }
-
- pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
- use super::user_view::user_fast::dsl::*;
- if let Some(search_term) = search_term.get_optional() {
- self.query = self.query.filter(name.ilike(fuzzy_search(&search_term)));
- }
- self
- }
-
- pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
- self.page = page.get_optional();
- self
- }
-
- pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
- self.limit = limit.get_optional();
- self
- }
-
- pub fn list(self) -> Result<Vec<UserView>, Error> {
- use super::user_view::user_fast::dsl::*;
- use diesel::sql_types::{Nullable, Text};
-
- let mut query = self.query;
-
- query = match self.sort {
- SortType::Hot => query
- .order_by(comment_score.desc())
- .then_order_by(published.desc()),
- SortType::Active => query
- .order_by(comment_score.desc())
- .then_order_by(published.desc()),
- SortType::New => query.order_by(published.desc()),
- SortType::TopAll => query.order_by(comment_score.desc()),
- SortType::TopYear => query
- .filter(published.gt(now - 1.years()))
- .order_by(comment_score.desc()),
- SortType::TopMonth => query
- .filter(published.gt(now - 1.months()))
- .order_by(comment_score.desc()),
- SortType::TopWeek => query
- .filter(published.gt(now - 1.weeks()))
- .order_by(comment_score.desc()),
- SortType::TopDay => query
- .filter(published.gt(now - 1.days()))
- .order_by(comment_score.desc()),
- };
-
- let (limit, offset) = limit_and_offset(self.page, self.limit);
- query = query.limit(limit).offset(offset);
-
- // The select is necessary here to not get back emails
- query = query.select((
- id,
- actor_id,
- name,
- preferred_username,
- avatar,
- banner,
- "".into_sql::<Nullable<Text>>(),
- matrix_user_id,
- bio,
- local,
- admin,
- banned,
- show_avatars,
- send_notifications_to_email,
- published,
- number_of_posts,
- post_score,
- number_of_comments,
- comment_score,
- ));
- query.load::<UserView>(self.conn)
- }
-}
-
-impl UserView {
- pub fn admins(conn: &PgConnection) -> Result<Vec<Self>, Error> {
- use super::user_view::user_fast::dsl::*;
- use diesel::sql_types::{Nullable, Text};
- user_fast
- // The select is necessary here to not get back emails
- .select((
- id,
- actor_id,
- name,
- preferred_username,
- avatar,
- banner,
- "".into_sql::<Nullable<Text>>(),
- matrix_user_id,
- bio,
- local,
- admin,
- banned,
- show_avatars,
- send_notifications_to_email,
- published,
- number_of_posts,
- post_score,
- number_of_comments,
- comment_score,
- ))
- .filter(admin.eq(true))
- .order_by(published)
- .load::<Self>(conn)
- }
-
- pub fn banned(conn: &PgConnection) -> Result<Vec<Self>, Error> {
- use super::user_view::user_fast::dsl::*;
- use diesel::sql_types::{Nullable, Text};
- user_fast
- .select((
- id,
- actor_id,
- name,
- preferred_username,
- avatar,
- banner,
- "".into_sql::<Nullable<Text>>(),
- matrix_user_id,
- bio,
- local,
- admin,
- banned,
- show_avatars,
- send_notifications_to_email,
- published,
- number_of_posts,
- post_score,
- number_of_comments,
- comment_score,
- ))
- .filter(banned.eq(true))
- .load::<Self>(conn)
- }
-
- // WARNING!!! this method WILL return sensitive user information and should only be called
- // if the user requesting these details is also the authenticated user.
- // please use get_user_secure to obtain user rows in most cases.
- pub fn get_user_dangerous(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
- use super::user_view::user_fast::dsl::*;
- user_fast.find(user_id).first::<Self>(conn)
- }
-
- pub fn get_user_secure(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
- use super::user_view::user_fast::dsl::*;
- use diesel::sql_types::{Nullable, Text};
- user_fast
- .select((
- id,
- actor_id,
- name,
- preferred_username,
- avatar,
- banner,
- "".into_sql::<Nullable<Text>>(),
- matrix_user_id,
- bio,
- local,
- admin,
- banned,
- show_avatars,
- send_notifications_to_email,
- published,
- number_of_posts,
- post_score,
- number_of_comments,
- comment_score,
- ))
- .find(user_id)
- .first::<Self>(conn)
- }
-}
use crate::{
aggregates::user_aggregates::UserAggregates,
+ fuzzy_search,
+ limit_and_offset,
schema::{user_, user_aggregates},
user::{UserSafe, User_},
+ MaybeOptional,
+ SortType,
};
-use diesel::{result::Error, *};
+use diesel::{dsl::*, result::Error, *};
use serde::Serialize;
#[derive(Debug, Serialize, Clone)]
}
}
+mod join_types {
+ use crate::schema::{user_, user_aggregates};
+ use diesel::{
+ pg::Pg,
+ query_builder::BoxedSelectStatement,
+ query_source::joins::{Inner, Join, JoinOn},
+ sql_types::*,
+ };
+
+ /// TODO awful, but necessary because of the boxed join
+ pub(super) type BoxedUserJoin<'a> = BoxedSelectStatement<
+ 'a,
+ (
+ (
+ Integer,
+ Text,
+ Nullable<Text>,
+ Text,
+ Nullable<Text>,
+ Nullable<Text>,
+ diesel::sql_types::Bool,
+ Bool,
+ Timestamp,
+ Nullable<Timestamp>,
+ Bool,
+ Text,
+ SmallInt,
+ SmallInt,
+ Text,
+ Bool,
+ Bool,
+ Nullable<Text>,
+ Text,
+ Nullable<Text>,
+ Bool,
+ Nullable<Text>,
+ Nullable<Text>,
+ Timestamp,
+ Nullable<Text>,
+ Bool,
+ ),
+ (Integer, Integer, BigInt, BigInt, BigInt, BigInt),
+ ),
+ JoinOn<
+ Join<user_::table, user_aggregates::table, Inner>,
+ diesel::expression::operators::Eq<
+ diesel::expression::nullable::Nullable<user_aggregates::columns::user_id>,
+ diesel::expression::nullable::Nullable<user_::columns::id>,
+ >,
+ >,
+ Pg,
+ >;
+}
+
+pub struct UserQueryBuilder<'a> {
+ conn: &'a PgConnection,
+ query: join_types::BoxedUserJoin<'a>,
+ sort: &'a SortType,
+ page: Option<i64>,
+ limit: Option<i64>,
+}
+
+impl<'a> UserQueryBuilder<'a> {
+ pub fn create(conn: &'a PgConnection) -> Self {
+ let query = user_::table.inner_join(user_aggregates::table).into_boxed();
+
+ UserQueryBuilder {
+ conn,
+ query,
+ sort: &SortType::Hot,
+ page: None,
+ limit: None,
+ }
+ }
+
+ pub fn sort(mut self, sort: &'a SortType) -> Self {
+ self.sort = sort;
+ self
+ }
+
+ pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
+ if let Some(search_term) = search_term.get_optional() {
+ self.query = self
+ .query
+ .filter(user_::name.ilike(fuzzy_search(&search_term)));
+ }
+ self
+ }
+
+ pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
+ self.page = page.get_optional();
+ self
+ }
+
+ pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
+ self.limit = limit.get_optional();
+ self
+ }
+
+ pub fn list(self) -> Result<Vec<UserViewSafe>, Error> {
+ let mut query = self.query;
+
+ query = match self.sort {
+ SortType::Hot => query
+ .order_by(user_aggregates::comment_score.desc())
+ .then_order_by(user_::published.desc()),
+ SortType::Active => query
+ .order_by(user_aggregates::comment_score.desc())
+ .then_order_by(user_::published.desc()),
+ SortType::New => query.order_by(user_::published.desc()),
+ SortType::TopAll => query.order_by(user_aggregates::comment_score.desc()),
+ SortType::TopYear => query
+ .filter(user_::published.gt(now - 1.years()))
+ .order_by(user_aggregates::comment_score.desc()),
+ SortType::TopMonth => query
+ .filter(user_::published.gt(now - 1.months()))
+ .order_by(user_aggregates::comment_score.desc()),
+ SortType::TopWeek => query
+ .filter(user_::published.gt(now - 1.weeks()))
+ .order_by(user_aggregates::comment_score.desc()),
+ SortType::TopDay => query
+ .filter(user_::published.gt(now - 1.days()))
+ .order_by(user_aggregates::comment_score.desc()),
+ };
+
+ let (limit, offset) = limit_and_offset(self.page, self.limit);
+ query = query.limit(limit).offset(offset);
+
+ let res = query.load::<(User_, UserAggregates)>(self.conn)?;
+
+ Ok(vec_to_user_view_safe(res))
+ }
+}
+
fn vec_to_user_view_safe(users: Vec<(User_, UserAggregates)>) -> Vec<UserViewSafe> {
users
.iter()
use lemmy_db::{
community_view::{CommunityFollowerView, CommunityModeratorView, CommunityView},
- user_view::UserView,
+ views::user_view::UserViewSafe,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Clone)]
pub struct BanFromCommunityResponse {
- pub user: UserView,
+ pub user: UserViewSafe,
pub banned: bool,
}
moderator_views::*,
post_view::*,
user::*,
- user_view::*,
- views::site_view::SiteView,
+ views::{site_view::SiteView, user_view::UserViewSafe},
};
use serde::{Deserialize, Serialize};
pub comments: Vec<CommentView>,
pub posts: Vec<PostView>,
pub communities: Vec<CommunityView>,
- pub users: Vec<UserView>,
+ pub users: Vec<UserViewSafe>,
}
#[derive(Deserialize)]
pub struct GetSiteResponse {
pub site: Option<SiteView>, // Because the site might not be set up yet
pub counts: SiteAggregates,
- pub admins: Vec<UserView>,
- pub banned: Vec<UserView>,
+ pub admins: Vec<UserViewSafe>,
+ pub banned: Vec<UserViewSafe>,
pub online: usize,
pub version: String,
pub my_user: Option<User_>,
post_view::PostView,
private_message_view::PrivateMessageView,
user_mention_view::UserMentionView,
- user_view::UserView,
+ views::user_view::{UserViewDangerous, UserViewSafe},
};
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
pub struct GetUserDetailsResponse {
- pub user: UserView,
+ pub user: Option<UserViewSafe>,
+ pub user_dangerous: Option<UserViewDangerous>,
pub follows: Vec<CommunityFollowerView>,
pub moderates: Vec<CommunityModeratorView>,
pub comments: Vec<CommentView>,
#[derive(Serialize, Clone)]
pub struct AddAdminResponse {
- pub admins: Vec<UserView>,
+ pub admins: Vec<UserViewSafe>,
}
#[derive(Deserialize)]
#[derive(Serialize, Clone)]
pub struct BanUserResponse {
- pub user: UserView,
+ pub user: UserViewSafe,
pub banned: bool,
}