2 build_federated_instances,
5 get_user_safe_settings_from_jwt,
6 get_user_safe_settings_from_jwt_opt,
10 use actix_web::web::Data;
12 use lemmy_apub::fetcher::search::search_by_apub_id;
13 use lemmy_db_queries::{diesel_option_overwrite, source::site::Site_, Crud, SearchType, SortType};
14 use lemmy_db_schema::{
22 comment_view::CommentQueryBuilder,
23 post_view::PostQueryBuilder,
26 use lemmy_db_views_actor::{
27 community_view::CommunityQueryBuilder,
28 user_view::{UserQueryBuilder, UserViewSafe},
30 use lemmy_db_views_moderator::{
31 mod_add_community_view::ModAddCommunityView,
32 mod_add_view::ModAddView,
33 mod_ban_from_community_view::ModBanFromCommunityView,
34 mod_ban_view::ModBanView,
35 mod_lock_post_view::ModLockPostView,
36 mod_remove_comment_view::ModRemoveCommentView,
37 mod_remove_community_view::ModRemoveCommunityView,
38 mod_remove_post_view::ModRemovePostView,
39 mod_sticky_post_view::ModStickyPostView,
41 use lemmy_structs::{blocking, site::*, user::Register};
45 utils::{check_slurs, check_slurs_opt},
51 use lemmy_websocket::{
52 messages::{GetUsersOnline, SendAllMessage},
56 use log::{debug, info};
57 use std::str::FromStr;
59 #[async_trait::async_trait(?Send)]
60 impl Perform for GetModlog {
61 type Response = GetModlogResponse;
65 context: &Data<LemmyContext>,
66 _websocket_id: Option<ConnectionId>,
67 ) -> Result<GetModlogResponse, LemmyError> {
68 let data: &GetModlog = &self;
70 let community_id = data.community_id;
71 let mod_user_id = data.mod_user_id;
73 let limit = data.limit;
74 let removed_posts = blocking(context.pool(), move |conn| {
75 ModRemovePostView::list(conn, community_id, mod_user_id, page, limit)
79 let locked_posts = blocking(context.pool(), move |conn| {
80 ModLockPostView::list(conn, community_id, mod_user_id, page, limit)
84 let stickied_posts = blocking(context.pool(), move |conn| {
85 ModStickyPostView::list(conn, community_id, mod_user_id, page, limit)
89 let removed_comments = blocking(context.pool(), move |conn| {
90 ModRemoveCommentView::list(conn, community_id, mod_user_id, page, limit)
94 let banned_from_community = blocking(context.pool(), move |conn| {
95 ModBanFromCommunityView::list(conn, community_id, mod_user_id, page, limit)
99 let added_to_community = blocking(context.pool(), move |conn| {
100 ModAddCommunityView::list(conn, community_id, mod_user_id, page, limit)
104 // These arrays are only for the full modlog, when a community isn't given
105 let (removed_communities, banned, added) = if data.community_id.is_none() {
106 blocking(context.pool(), move |conn| {
108 ModRemoveCommunityView::list(conn, mod_user_id, page, limit)?,
109 ModBanView::list(conn, mod_user_id, page, limit)?,
110 ModAddView::list(conn, mod_user_id, page, limit)?,
111 )) as Result<_, LemmyError>
115 (Vec::new(), Vec::new(), Vec::new())
119 Ok(GetModlogResponse {
125 banned_from_community,
133 #[async_trait::async_trait(?Send)]
134 impl Perform for CreateSite {
135 type Response = SiteResponse;
139 context: &Data<LemmyContext>,
140 _websocket_id: Option<ConnectionId>,
141 ) -> Result<SiteResponse, LemmyError> {
142 let data: &CreateSite = &self;
144 let read_site = move |conn: &'_ _| Site::read_simple(conn);
145 if blocking(context.pool(), read_site).await?.is_ok() {
146 return Err(ApiError::err("site_already_exists").into());
149 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
151 check_slurs(&data.name)?;
152 check_slurs_opt(&data.description)?;
154 // Make sure user is an admin
155 is_admin(context.pool(), user.id).await?;
157 let site_form = SiteForm {
158 name: data.name.to_owned(),
159 description: data.description.to_owned(),
160 icon: Some(data.icon.to_owned()),
161 banner: Some(data.banner.to_owned()),
163 enable_downvotes: data.enable_downvotes,
164 open_registration: data.open_registration,
165 enable_nsfw: data.enable_nsfw,
169 let create_site = move |conn: &'_ _| Site::create(conn, &site_form);
170 if blocking(context.pool(), create_site).await?.is_err() {
171 return Err(ApiError::err("site_already_exists").into());
174 let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
176 Ok(SiteResponse { site_view })
180 #[async_trait::async_trait(?Send)]
181 impl Perform for EditSite {
182 type Response = SiteResponse;
185 context: &Data<LemmyContext>,
186 websocket_id: Option<ConnectionId>,
187 ) -> Result<SiteResponse, LemmyError> {
188 let data: &EditSite = &self;
189 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
191 check_slurs(&data.name)?;
192 check_slurs_opt(&data.description)?;
194 // Make sure user is an admin
195 is_admin(context.pool(), user.id).await?;
197 let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
199 let icon = diesel_option_overwrite(&data.icon);
200 let banner = diesel_option_overwrite(&data.banner);
202 let site_form = SiteForm {
203 name: data.name.to_owned(),
204 description: data.description.to_owned(),
207 creator_id: found_site.creator_id,
208 updated: Some(naive_now()),
209 enable_downvotes: data.enable_downvotes,
210 open_registration: data.open_registration,
211 enable_nsfw: data.enable_nsfw,
214 let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
215 if blocking(context.pool(), update_site).await?.is_err() {
216 return Err(ApiError::err("couldnt_update_site").into());
219 let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
221 let res = SiteResponse { site_view };
223 context.chat_server().do_send(SendAllMessage {
224 op: UserOperation::EditSite,
225 response: res.clone(),
233 #[async_trait::async_trait(?Send)]
234 impl Perform for GetSite {
235 type Response = GetSiteResponse;
239 context: &Data<LemmyContext>,
240 websocket_id: Option<ConnectionId>,
241 ) -> Result<GetSiteResponse, LemmyError> {
242 let data: &GetSite = &self;
244 let site_view = match blocking(context.pool(), move |conn| SiteView::read(conn)).await? {
245 Ok(site_view) => Some(site_view),
246 // If the site isn't created yet, check the setup
248 if let Some(setup) = Settings::get().setup.as_ref() {
249 let register = Register {
250 username: setup.admin_username.to_owned(),
251 email: setup.admin_email.to_owned(),
252 password: setup.admin_password.to_owned(),
253 password_verify: setup.admin_password.to_owned(),
256 captcha_answer: None,
258 let login_response = register.perform(context, websocket_id).await?;
259 info!("Admin {} created", setup.admin_username);
261 let create_site = CreateSite {
262 name: setup.site_name.to_owned(),
266 enable_downvotes: true,
267 open_registration: true,
269 auth: login_response.jwt,
271 create_site.perform(context, websocket_id).await?;
272 info!("Site {} created", setup.site_name);
273 Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??)
280 let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
282 // Make sure the site creator is the top admin
283 if let Some(site_view) = site_view.to_owned() {
284 let site_creator_id = site_view.creator.id;
285 // TODO investigate why this is sometimes coming back null
286 // Maybe user_.admin isn't being set to true?
287 if let Some(creator_index) = admins.iter().position(|r| r.user.id == site_creator_id) {
288 let creator_user = admins.remove(creator_index);
289 admins.insert(0, creator_user);
293 let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
297 .send(GetUsersOnline)
301 let my_user = get_user_safe_settings_from_jwt_opt(&data.auth, context.pool()).await?;
302 let federated_instances = build_federated_instances(context.pool()).await?;
309 version: version::VERSION.to_string(),
316 #[async_trait::async_trait(?Send)]
317 impl Perform for Search {
318 type Response = SearchResponse;
322 context: &Data<LemmyContext>,
323 _websocket_id: Option<ConnectionId>,
324 ) -> Result<SearchResponse, LemmyError> {
325 let data: &Search = &self;
327 match search_by_apub_id(&data.q, context).await {
328 Ok(r) => return Ok(r),
329 Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
332 let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
333 let user_id = user.map(|u| u.id);
335 let type_ = SearchType::from_str(&data.type_)?;
337 let mut posts = Vec::new();
338 let mut comments = Vec::new();
339 let mut communities = Vec::new();
340 let mut users = Vec::new();
342 // TODO no clean / non-nsfw searching rn
344 let q = data.q.to_owned();
345 let page = data.page;
346 let limit = data.limit;
347 let sort = SortType::from_str(&data.sort)?;
348 let community_id = data.community_id;
349 let community_name = data.community_name.to_owned();
351 SearchType::Posts => {
352 posts = blocking(context.pool(), move |conn| {
353 PostQueryBuilder::create(conn)
356 .community_id(community_id)
357 .community_name(community_name)
366 SearchType::Comments => {
367 comments = blocking(context.pool(), move |conn| {
368 CommentQueryBuilder::create(&conn)
378 SearchType::Communities => {
379 communities = blocking(context.pool(), move |conn| {
380 CommunityQueryBuilder::create(conn)
390 SearchType::Users => {
391 users = blocking(context.pool(), move |conn| {
392 UserQueryBuilder::create(conn)
402 posts = blocking(context.pool(), move |conn| {
403 PostQueryBuilder::create(conn)
406 .community_id(community_id)
407 .community_name(community_name)
416 let q = data.q.to_owned();
417 let sort = SortType::from_str(&data.sort)?;
419 comments = blocking(context.pool(), move |conn| {
420 CommentQueryBuilder::create(conn)
430 let q = data.q.to_owned();
431 let sort = SortType::from_str(&data.sort)?;
433 communities = blocking(context.pool(), move |conn| {
434 CommunityQueryBuilder::create(conn)
444 let q = data.q.to_owned();
445 let sort = SortType::from_str(&data.sort)?;
447 users = blocking(context.pool(), move |conn| {
448 UserQueryBuilder::create(conn)
458 posts = blocking(context.pool(), move |conn| {
459 PostQueryBuilder::create(conn)
463 .community_id(community_id)
464 .community_name(community_name)
476 type_: data.type_.to_owned(),
485 #[async_trait::async_trait(?Send)]
486 impl Perform for TransferSite {
487 type Response = GetSiteResponse;
491 context: &Data<LemmyContext>,
492 _websocket_id: Option<ConnectionId>,
493 ) -> Result<GetSiteResponse, LemmyError> {
494 let data: &TransferSite = &self;
495 let user = get_user_safe_settings_from_jwt(&data.auth, context.pool()).await?;
497 is_admin(context.pool(), user.id).await?;
499 let read_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
501 // Make sure user is the creator
502 if read_site.creator_id != user.id {
503 return Err(ApiError::err("not_an_admin").into());
506 let new_creator_id = data.user_id;
507 let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
508 if blocking(context.pool(), transfer_site).await?.is_err() {
509 return Err(ApiError::err("couldnt_update_site").into());
513 let form = ModAddForm {
514 mod_person_id: user.id,
515 other_person_id: data.user_id,
516 removed: Some(false),
519 blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
521 let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
523 let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
524 let creator_index = admins
526 .position(|r| r.user.id == site_view.creator.id)
527 .context(location_info!())?;
528 let creator_user = admins.remove(creator_index);
529 admins.insert(0, creator_user);
531 let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
532 let federated_instances = build_federated_instances(context.pool()).await?;
535 site_view: Some(site_view),
539 version: version::VERSION.to_string(),
546 #[async_trait::async_trait(?Send)]
547 impl Perform for GetSiteConfig {
548 type Response = GetSiteConfigResponse;
552 context: &Data<LemmyContext>,
553 _websocket_id: Option<ConnectionId>,
554 ) -> Result<GetSiteConfigResponse, LemmyError> {
555 let data: &GetSiteConfig = &self;
556 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
558 // Only let admins read this
559 is_admin(context.pool(), user.id).await?;
561 let config_hjson = Settings::read_config_file()?;
563 Ok(GetSiteConfigResponse { config_hjson })
567 #[async_trait::async_trait(?Send)]
568 impl Perform for SaveSiteConfig {
569 type Response = GetSiteConfigResponse;
573 context: &Data<LemmyContext>,
574 _websocket_id: Option<ConnectionId>,
575 ) -> Result<GetSiteConfigResponse, LemmyError> {
576 let data: &SaveSiteConfig = &self;
577 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
579 // Only let admins read this
580 let user_id = user.id;
581 is_admin(context.pool(), user_id).await?;
583 // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
584 let config_hjson = match Settings::save_config_file(&data.config_hjson) {
585 Ok(config_hjson) => config_hjson,
586 Err(_e) => return Err(ApiError::err("couldnt_update_site").into()),
589 Ok(GetSiteConfigResponse { config_hjson })