-use crate::{
- get_user_from_jwt,
- get_user_from_jwt_opt,
- get_user_safe_settings_from_jwt,
- get_user_safe_settings_from_jwt_opt,
+use crate::Perform;
+use actix_web::web::Data;
+use diesel::NotFound;
+use lemmy_api_common::{
+ blocking,
+ build_federated_instances,
+ check_private_instance,
+ get_local_user_view_from_jwt,
+ get_local_user_view_from_jwt_opt,
is_admin,
- linked_instances,
- version,
- Perform,
+ send_application_approved_email,
+ site::*,
};
-use actix_web::web::Data;
-use anyhow::Context;
-use lemmy_apub::fetcher::search::search_by_apub_id;
-use lemmy_db_queries::{
- diesel_option_overwrite,
- source::{category::Category_, site::Site_},
- Crud,
- SearchType,
- SortType,
+use lemmy_apub::{
+ fetcher::{
+ search::{search_by_apub_id, SearchableObjects},
+ webfinger::webfinger_resolve,
+ },
+ objects::community::ApubCommunity,
+ EndpointType,
};
use lemmy_db_schema::{
- naive_now,
+ diesel_option_overwrite,
+ from_opt_str_to_opt_enum,
+ newtypes::PersonId,
source::{
- category::Category,
+ local_user::{LocalUser, LocalUserForm},
moderator::*,
- site::{Site, *},
+ person::Person,
+ registration_application::{RegistrationApplication, RegistrationApplicationForm},
+ site::Site,
},
+ traits::{Crud, DeleteableOrRemoveable},
+ DbPool,
+ ListingType,
+ SearchType,
+ SortType,
};
use lemmy_db_views::{
- comment_view::CommentQueryBuilder,
- post_view::PostQueryBuilder,
+ comment_view::{CommentQueryBuilder, CommentView},
+ local_user_view::LocalUserView,
+ post_view::{PostQueryBuilder, PostView},
+ registration_application_view::{
+ RegistrationApplicationQueryBuilder,
+ RegistrationApplicationView,
+ },
site_view::SiteView,
};
use lemmy_db_views_actor::{
- community_view::CommunityQueryBuilder,
- user_view::{UserQueryBuilder, UserViewSafe},
+ community_view::{CommunityQueryBuilder, CommunityView},
+ person_view::{PersonQueryBuilder, PersonViewSafe},
};
use lemmy_db_views_moderator::{
mod_add_community_view::ModAddCommunityView,
mod_remove_community_view::ModRemoveCommunityView,
mod_remove_post_view::ModRemovePostView,
mod_sticky_post_view::ModStickyPostView,
+ mod_transfer_community_view::ModTransferCommunityView,
};
-use lemmy_structs::{blocking, site::*, user::Register};
-use lemmy_utils::{
- location_info,
- settings::Settings,
- utils::{check_slurs, check_slurs_opt},
- APIError,
- ConnectionId,
- LemmyError,
-};
-use lemmy_websocket::{
- messages::{GetUsersOnline, SendAllMessage},
- LemmyContext,
- UserOperation,
-};
-use log::{debug, info};
-use std::str::FromStr;
-
-#[async_trait::async_trait(?Send)]
-impl Perform for ListCategories {
- type Response = ListCategoriesResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ListCategoriesResponse, LemmyError> {
- let _data: &ListCategories = &self;
-
- let categories = blocking(context.pool(), move |conn| Category::list_all(conn)).await??;
-
- // Return the jwt
- Ok(ListCategoriesResponse { categories })
- }
-}
+use lemmy_utils::{settings::structs::Settings, version, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
impl Perform for GetModlog {
type Response = GetModlogResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<GetModlogResponse, LemmyError> {
- let data: &GetModlog = &self;
+ let data: &GetModlog = self;
+
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
let community_id = data.community_id;
- let mod_user_id = data.mod_user_id;
+ let mod_person_id = data.mod_person_id;
let page = data.page;
let limit = data.limit;
let removed_posts = blocking(context.pool(), move |conn| {
- ModRemovePostView::list(conn, community_id, mod_user_id, page, limit)
+ ModRemovePostView::list(conn, community_id, mod_person_id, page, limit)
})
.await??;
let locked_posts = blocking(context.pool(), move |conn| {
- ModLockPostView::list(conn, community_id, mod_user_id, page, limit)
+ ModLockPostView::list(conn, community_id, mod_person_id, page, limit)
})
.await??;
let stickied_posts = blocking(context.pool(), move |conn| {
- ModStickyPostView::list(conn, community_id, mod_user_id, page, limit)
+ ModStickyPostView::list(conn, community_id, mod_person_id, page, limit)
})
.await??;
let removed_comments = blocking(context.pool(), move |conn| {
- ModRemoveCommentView::list(conn, community_id, mod_user_id, page, limit)
+ ModRemoveCommentView::list(conn, community_id, mod_person_id, page, limit)
})
.await??;
let banned_from_community = blocking(context.pool(), move |conn| {
- ModBanFromCommunityView::list(conn, community_id, mod_user_id, page, limit)
+ ModBanFromCommunityView::list(conn, community_id, mod_person_id, page, limit)
})
.await??;
let added_to_community = blocking(context.pool(), move |conn| {
- ModAddCommunityView::list(conn, community_id, mod_user_id, page, limit)
+ ModAddCommunityView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let transferred_to_community = blocking(context.pool(), move |conn| {
+ ModTransferCommunityView::list(conn, community_id, mod_person_id, page, limit)
})
.await??;
let (removed_communities, banned, added) = if data.community_id.is_none() {
blocking(context.pool(), move |conn| {
Ok((
- ModRemoveCommunityView::list(conn, mod_user_id, page, limit)?,
- ModBanView::list(conn, mod_user_id, page, limit)?,
- ModAddView::list(conn, mod_user_id, page, limit)?,
+ ModRemoveCommunityView::list(conn, mod_person_id, page, limit)?,
+ ModBanView::list(conn, mod_person_id, page, limit)?,
+ ModAddView::list(conn, mod_person_id, page, limit)?,
)) as Result<_, LemmyError>
})
.await??
banned,
added_to_community,
added,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for CreateSite {
- type Response = SiteResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<SiteResponse, LemmyError> {
- let data: &CreateSite = &self;
-
- 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());
- };
-
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
-
- check_slurs(&data.name)?;
- check_slurs_opt(&data.description)?;
-
- // Make sure user is an admin
- is_admin(context.pool(), user.id).await?;
-
- let site_form = SiteForm {
- name: data.name.to_owned(),
- description: data.description.to_owned(),
- icon: Some(data.icon.to_owned()),
- banner: Some(data.banner.to_owned()),
- creator_id: user.id,
- enable_downvotes: data.enable_downvotes,
- open_registration: data.open_registration,
- enable_nsfw: data.enable_nsfw,
- updated: None,
- };
-
- let create_site = move |conn: &'_ _| Site::create(conn, &site_form);
- if blocking(context.pool(), create_site).await?.is_err() {
- return Err(APIError::err("site_already_exists").into());
- }
-
- let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
-
- Ok(SiteResponse { site_view })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for EditSite {
- type Response = SiteResponse;
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<SiteResponse, LemmyError> {
- let data: &EditSite = &self;
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
-
- check_slurs(&data.name)?;
- check_slurs_opt(&data.description)?;
-
- // Make sure user is an admin
- is_admin(context.pool(), user.id).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);
-
- let site_form = SiteForm {
- name: data.name.to_owned(),
- description: data.description.to_owned(),
- icon,
- banner,
- creator_id: found_site.creator_id,
- updated: Some(naive_now()),
- enable_downvotes: data.enable_downvotes,
- open_registration: data.open_registration,
- enable_nsfw: data.enable_nsfw,
- };
-
- let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
- if blocking(context.pool(), update_site).await?.is_err() {
- return Err(APIError::err("couldnt_update_site").into());
- }
-
- let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
-
- let res = SiteResponse { site_view };
-
- context.chat_server().do_send(SendAllMessage {
- op: UserOperation::EditSite,
- response: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetSite {
- type Response = GetSiteResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<GetSiteResponse, LemmyError> {
- let data: &GetSite = &self;
-
- 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(),
- 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 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.user.id == site_creator_id) {
- let creator_user = admins.remove(creator_index);
- admins.insert(0, creator_user);
- }
- }
-
- let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
-
- let online = context
- .chat_server()
- .send(GetUsersOnline)
- .await
- .unwrap_or(1);
-
- let my_user = get_user_safe_settings_from_jwt_opt(&data.auth, context.pool()).await?;
-
- Ok(GetSiteResponse {
- site_view,
- admins,
- banned,
- online,
- version: version::VERSION.to_string(),
- my_user,
- federated_instances: linked_instances(context.pool()).await?,
+ transferred_to_community,
})
}
}
impl Perform for Search {
type Response = SearchResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<SearchResponse, LemmyError> {
- let data: &Search = &self;
+ let data: &Search = self;
- match search_by_apub_id(&data.q, context).await {
- Ok(r) => return Ok(r),
- Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
- }
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
- let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
- let user_id = user.map(|u| u.id);
+ let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
+ let show_bot_accounts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_bot_accounts);
+ let show_read_posts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_read_posts);
- let type_ = SearchType::from_str(&data.type_)?;
+ let person_id = local_user_view.map(|u| u.person.id);
let mut posts = Vec::new();
let mut comments = Vec::new();
let q = data.q.to_owned();
let page = data.page;
let limit = data.limit;
- let sort = SortType::from_str(&data.sort)?;
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+ let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.listing_type);
+ let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
let community_id = data.community_id;
- let community_name = data.community_name.to_owned();
- match type_ {
+ let community_actor_id = if let Some(name) = &data.community_name {
+ webfinger_resolve::<ApubCommunity>(name, EndpointType::Community, context, &mut 0)
+ .await
+ .ok()
+ } else {
+ None
+ };
+ let creator_id = data.creator_id;
+ match search_type {
SearchType::Posts => {
posts = blocking(context.pool(), move |conn| {
PostQueryBuilder::create(conn)
- .sort(&sort)
- .show_nsfw(true)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .listing_type(listing_type)
.community_id(community_id)
- .community_name(community_name)
- .my_user_id(user_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
.search_term(q)
.page(page)
.limit(limit)
}
SearchType::Comments => {
comments = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(&conn)
- .sort(&sort)
+ CommentQueryBuilder::create(conn)
+ .sort(sort)
+ .listing_type(listing_type)
.search_term(q)
- .my_user_id(user_id)
+ .show_bot_accounts(show_bot_accounts)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
.page(page)
.limit(limit)
.list()
SearchType::Communities => {
communities = blocking(context.pool(), move |conn| {
CommunityQueryBuilder::create(conn)
- .sort(&sort)
+ .sort(sort)
+ .listing_type(listing_type)
.search_term(q)
+ .my_person_id(person_id)
.page(page)
.limit(limit)
.list()
}
SearchType::Users => {
users = blocking(context.pool(), move |conn| {
- UserQueryBuilder::create(conn)
- .sort(&sort)
+ PersonQueryBuilder::create(conn)
+ .sort(sort)
.search_term(q)
.page(page)
.limit(limit)
.await??;
}
SearchType::All => {
+ // If the community or creator is included, dont search communities or users
+ let community_or_creator_included =
+ data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
+ let community_actor_id_2 = community_actor_id.to_owned();
+
posts = blocking(context.pool(), move |conn| {
PostQueryBuilder::create(conn)
- .sort(&sort)
- .show_nsfw(true)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .listing_type(listing_type)
.community_id(community_id)
- .community_name(community_name)
- .my_user_id(user_id)
+ .community_actor_id(community_actor_id_2)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
.search_term(q)
.page(page)
.limit(limit)
.await??;
let q = data.q.to_owned();
- let sort = SortType::from_str(&data.sort)?;
+ let community_actor_id = community_actor_id.to_owned();
comments = blocking(context.pool(), move |conn| {
CommentQueryBuilder::create(conn)
- .sort(&sort)
+ .sort(sort)
+ .listing_type(listing_type)
.search_term(q)
- .my_user_id(user_id)
+ .show_bot_accounts(show_bot_accounts)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
.page(page)
.limit(limit)
.list()
.await??;
let q = data.q.to_owned();
- let sort = SortType::from_str(&data.sort)?;
- communities = blocking(context.pool(), move |conn| {
- CommunityQueryBuilder::create(conn)
- .sort(&sort)
- .search_term(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
+ communities = if community_or_creator_included {
+ vec![]
+ } else {
+ blocking(context.pool(), move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .sort(sort)
+ .listing_type(listing_type)
+ .search_term(q)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??
+ };
let q = data.q.to_owned();
- let sort = SortType::from_str(&data.sort)?;
- users = blocking(context.pool(), move |conn| {
- UserQueryBuilder::create(conn)
- .sort(&sort)
- .search_term(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
+ users = if community_or_creator_included {
+ vec![]
+ } else {
+ blocking(context.pool(), move |conn| {
+ PersonQueryBuilder::create(conn)
+ .sort(sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??
+ };
}
SearchType::Url => {
posts = blocking(context.pool(), move |conn| {
PostQueryBuilder::create(conn)
- .sort(&sort)
- .show_nsfw(true)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .listing_type(listing_type)
+ .my_person_id(person_id)
.community_id(community_id)
- .community_name(community_name)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
.url_search(q)
.page(page)
.limit(limit)
}
};
+ // Blank out deleted or removed info for non logged in users
+ if person_id.is_none() {
+ for cv in communities
+ .iter_mut()
+ .filter(|cv| cv.community.deleted || cv.community.removed)
+ {
+ cv.community = cv.to_owned().community.blank_out_deleted_or_removed_info();
+ }
+
+ for pv in posts
+ .iter_mut()
+ .filter(|p| p.post.deleted || p.post.removed)
+ {
+ pv.post = pv.to_owned().post.blank_out_deleted_or_removed_info();
+ }
+
+ for cv in comments
+ .iter_mut()
+ .filter(|cv| cv.comment.deleted || cv.comment.removed)
+ {
+ cv.comment = cv.to_owned().comment.blank_out_deleted_or_removed_info();
+ }
+ }
+
// Return the jwt
Ok(SearchResponse {
- type_: data.type_.to_owned(),
+ type_: search_type.to_string(),
comments,
posts,
communities,
}
#[async_trait::async_trait(?Send)]
-impl Perform for TransferSite {
+impl Perform for ResolveObject {
+ type Response = ResolveObjectResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<ResolveObjectResponse, LemmyError> {
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let res = search_by_apub_id(&self.q, context)
+ .await
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_object"))?;
+ convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
+ .await
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_find_object"))
+ }
+}
+
+async fn convert_response(
+ object: SearchableObjects,
+ user_id: Option<PersonId>,
+ pool: &DbPool,
+) -> Result<ResolveObjectResponse, LemmyError> {
+ let removed_or_deleted;
+ let mut res = ResolveObjectResponse {
+ comment: None,
+ post: None,
+ community: None,
+ person: None,
+ };
+ use SearchableObjects::*;
+ match object {
+ Person(p) => {
+ removed_or_deleted = p.deleted;
+ res.person = Some(blocking(pool, move |conn| PersonViewSafe::read(conn, p.id)).await??)
+ }
+ Community(c) => {
+ removed_or_deleted = c.deleted || c.removed;
+ res.community =
+ Some(blocking(pool, move |conn| CommunityView::read(conn, c.id, user_id)).await??)
+ }
+ Post(p) => {
+ removed_or_deleted = p.deleted || p.removed;
+ res.post = Some(blocking(pool, move |conn| PostView::read(conn, p.id, user_id)).await??)
+ }
+ Comment(c) => {
+ removed_or_deleted = c.deleted || c.removed;
+ res.comment = Some(blocking(pool, move |conn| CommentView::read(conn, c.id, user_id)).await??)
+ }
+ };
+ // if the object was deleted from database, dont return it
+ if removed_or_deleted {
+ return Err(NotFound {}.into());
+ }
+ Ok(res)
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for LeaveAdmin {
type Response = GetSiteResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<GetSiteResponse, LemmyError> {
- let data: &TransferSite = &self;
- let user = get_user_safe_settings_from_jwt(&data.auth, context.pool()).await?;
+ let data: &LeaveAdmin = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
- is_admin(context.pool(), user.id).await?;
+ is_admin(&local_user_view)?;
- 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 {
- return Err(APIError::err("not_an_admin").into());
+ // Make sure there isn't just one admin (so if one leaves, there will still be one left)
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
+ if admins.len() == 1 {
+ return Err(LemmyError::from_message("cannot_leave_admin"));
}
- let new_creator_id = data.user_id;
- let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
- if blocking(context.pool(), transfer_site).await?.is_err() {
- return Err(APIError::err("couldnt_update_site").into());
- };
+ let person_id = local_user_view.person.id;
+ blocking(context.pool(), move |conn| {
+ Person::leave_admin(conn, person_id)
+ })
+ .await??;
// Mod tables
let form = ModAddForm {
- mod_user_id: user.id,
- other_user_id: data.user_id,
- removed: Some(false),
+ mod_person_id: person_id,
+ other_person_id: person_id,
+ removed: Some(true),
};
blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
- let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
+ // Reread site and admins
+ let site_view = blocking(context.pool(), SiteView::read).await??;
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
- let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
- let creator_index = admins
- .iter()
- .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| UserViewSafe::banned(conn)).await??;
+ let federated_instances = build_federated_instances(
+ context.pool(),
+ &context.settings().federation,
+ &context.settings().hostname,
+ )
+ .await?;
Ok(GetSiteResponse {
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?,
+ my_user: None,
+ federated_instances,
})
}
}
impl Perform for GetSiteConfig {
type Response = GetSiteConfigResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<GetSiteConfigResponse, LemmyError> {
- let data: &GetSiteConfig = &self;
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
+ let data: &GetSiteConfig = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
// Only let admins read this
- is_admin(context.pool(), user.id).await?;
+ is_admin(&local_user_view)?;
let config_hjson = Settings::read_config_file()?;
impl Perform for SaveSiteConfig {
type Response = GetSiteConfigResponse;
+ #[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<GetSiteConfigResponse, LemmyError> {
- let data: &SaveSiteConfig = &self;
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
+ let data: &SaveSiteConfig = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
// Only let admins read this
- let user_id = user.id;
- is_admin(context.pool(), user_id).await?;
+ is_admin(&local_user_view)?;
// 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) {
- Ok(config_hjson) => config_hjson,
- Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
- };
+ let config_hjson = Settings::save_config_file(&data.config_hjson)
+ .map_err(LemmyError::from)
+ .map_err(|e| e.with_message("couldnt_update_site"))?;
Ok(GetSiteConfigResponse { config_hjson })
}
}
+
+/// Lists registration applications, filterable by undenied only.
+#[async_trait::async_trait(?Send)]
+impl Perform for ListRegistrationApplications {
+ type Response = ListRegistrationApplicationsResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Make sure user is an admin
+ is_admin(&local_user_view)?;
+
+ let unread_only = data.unread_only;
+ let verified_email_only = blocking(context.pool(), Site::read_simple)
+ .await??
+ .require_email_verification;
+
+ let page = data.page;
+ let limit = data.limit;
+ let registration_applications = blocking(context.pool(), move |conn| {
+ RegistrationApplicationQueryBuilder::create(conn)
+ .unread_only(unread_only)
+ .verified_email_only(verified_email_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let res = Self::Response {
+ registration_applications,
+ };
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for ApproveRegistrationApplication {
+ type Response = RegistrationApplicationResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let app_id = data.id;
+
+ // Only let admins do this
+ is_admin(&local_user_view)?;
+
+ // Update the registration with reason, admin_id
+ let deny_reason = diesel_option_overwrite(&data.deny_reason);
+ let app_form = RegistrationApplicationForm {
+ admin_id: Some(local_user_view.person.id),
+ deny_reason,
+ ..RegistrationApplicationForm::default()
+ };
+
+ let registration_application = blocking(context.pool(), move |conn| {
+ RegistrationApplication::update(conn, app_id, &app_form)
+ })
+ .await??;
+
+ // Update the local_user row
+ let local_user_form = LocalUserForm {
+ accepted_application: Some(data.approve),
+ ..LocalUserForm::default()
+ };
+
+ let approved_user_id = registration_application.local_user_id;
+ blocking(context.pool(), move |conn| {
+ LocalUser::update(conn, approved_user_id, &local_user_form)
+ })
+ .await??;
+
+ if data.approve {
+ let approved_local_user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::read(conn, approved_user_id)
+ })
+ .await??;
+
+ if approved_local_user_view.local_user.email.is_some() {
+ send_application_approved_email(&approved_local_user_view, &context.settings())?;
+ }
+ }
+
+ // Read the view
+ let registration_application = blocking(context.pool(), move |conn| {
+ RegistrationApplicationView::read(conn, app_id)
+ })
+ .await??;
+
+ Ok(Self::Response {
+ registration_application,
+ })
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetUnreadRegistrationApplicationCount {
+ type Response = GetUnreadRegistrationApplicationCountResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Only let admins do this
+ is_admin(&local_user_view)?;
+
+ let verified_email_only = blocking(context.pool(), Site::read_simple)
+ .await??
+ .require_email_verification;
+
+ let registration_applications = blocking(context.pool(), move |conn| {
+ RegistrationApplicationView::get_unread_count(conn, verified_email_only)
+ })
+ .await??;
+
+ Ok(Self::Response {
+ registration_applications,
+ })
+ }
+}