2 api::{get_user_from_jwt, get_user_from_jwt_opt, is_admin, Perform},
3 apub::fetcher::search_by_apub_id,
6 messages::{GetUsersOnline, SendAllMessage},
11 use actix_web::web::Data;
13 use lemmy_api_structs::{blocking, site::*, user::Register};
18 diesel_option_overwrite,
33 utils::{check_slurs, check_slurs_opt},
38 use log::{debug, info};
39 use std::str::FromStr;
41 #[async_trait::async_trait(?Send)]
42 impl Perform for ListCategories {
43 type Response = ListCategoriesResponse;
47 context: &Data<LemmyContext>,
48 _websocket_id: Option<ConnectionId>,
49 ) -> Result<ListCategoriesResponse, LemmyError> {
50 let _data: &ListCategories = &self;
52 let categories = blocking(context.pool(), move |conn| Category::list_all(conn)).await??;
55 Ok(ListCategoriesResponse { categories })
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 user = get_user_from_jwt(&data.auth, context.pool()).await?;
146 check_slurs(&data.name)?;
147 check_slurs_opt(&data.description)?;
149 // Make sure user is an admin
150 is_admin(context.pool(), user.id).await?;
152 let site_form = SiteForm {
153 name: data.name.to_owned(),
154 description: data.description.to_owned(),
155 icon: Some(data.icon.to_owned()),
156 banner: Some(data.banner.to_owned()),
158 enable_downvotes: data.enable_downvotes,
159 open_registration: data.open_registration,
160 enable_nsfw: data.enable_nsfw,
164 let create_site = move |conn: &'_ _| Site::create(conn, &site_form);
165 if blocking(context.pool(), create_site).await?.is_err() {
166 return Err(APIError::err("site_already_exists").into());
169 let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
171 Ok(SiteResponse { site: site_view })
175 #[async_trait::async_trait(?Send)]
176 impl Perform for EditSite {
177 type Response = SiteResponse;
180 context: &Data<LemmyContext>,
181 websocket_id: Option<ConnectionId>,
182 ) -> Result<SiteResponse, LemmyError> {
183 let data: &EditSite = &self;
184 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
186 check_slurs(&data.name)?;
187 check_slurs_opt(&data.description)?;
189 // Make sure user is an admin
190 is_admin(context.pool(), user.id).await?;
192 let found_site = blocking(context.pool(), move |conn| Site::read(conn, 1)).await??;
194 let icon = diesel_option_overwrite(&data.icon);
195 let banner = diesel_option_overwrite(&data.banner);
197 let site_form = SiteForm {
198 name: data.name.to_owned(),
199 description: data.description.to_owned(),
202 creator_id: found_site.creator_id,
203 updated: Some(naive_now()),
204 enable_downvotes: data.enable_downvotes,
205 open_registration: data.open_registration,
206 enable_nsfw: data.enable_nsfw,
209 let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
210 if blocking(context.pool(), update_site).await?.is_err() {
211 return Err(APIError::err("couldnt_update_site").into());
214 let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
216 let res = SiteResponse { site: site_view };
218 context.chat_server().do_send(SendAllMessage {
219 op: UserOperation::EditSite,
220 response: res.clone(),
228 #[async_trait::async_trait(?Send)]
229 impl Perform for GetSite {
230 type Response = GetSiteResponse;
234 context: &Data<LemmyContext>,
235 websocket_id: Option<ConnectionId>,
236 ) -> Result<GetSiteResponse, LemmyError> {
237 let data: &GetSite = &self;
239 // TODO refactor this a little
240 let res = blocking(context.pool(), move |conn| Site::read(conn, 1)).await?;
241 let site_view = if res.is_ok() {
242 Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??)
243 } else if let Some(setup) = Settings::get().setup.as_ref() {
244 let register = Register {
245 username: setup.admin_username.to_owned(),
246 email: setup.admin_email.to_owned(),
247 password: setup.admin_password.to_owned(),
248 password_verify: setup.admin_password.to_owned(),
252 captcha_answer: None,
254 let login_response = register.perform(context, websocket_id).await?;
255 info!("Admin {} created", setup.admin_username);
257 let create_site = CreateSite {
258 name: setup.site_name.to_owned(),
262 enable_downvotes: true,
263 open_registration: true,
265 auth: login_response.jwt,
267 create_site.perform(context, websocket_id).await?;
268 info!("Site {} created", setup.site_name);
269 Some(blocking(context.pool(), move |conn| SiteView::read(conn)).await??)
274 let mut admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
276 // Make sure the site creator is the top admin
277 if let Some(site_view) = site_view.to_owned() {
278 let site_creator_id = site_view.creator_id;
279 // TODO investigate why this is sometimes coming back null
280 // Maybe user_.admin isn't being set to true?
281 if let Some(creator_index) = admins.iter().position(|r| r.id == site_creator_id) {
282 let creator_user = admins.remove(creator_index);
283 admins.insert(0, creator_user);
287 let banned = blocking(context.pool(), move |conn| UserView::banned(conn)).await??;
291 .send(GetUsersOnline)
295 let my_user = get_user_from_jwt_opt(&data.auth, context.pool())
298 u.password_encrypted = "".to_string();
299 u.private_key = None;
309 version: version::VERSION.to_string(),
311 federated_instances: Settings::get().get_allowed_instances(),
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;
329 match search_by_apub_id(&data.q, context).await {
330 Ok(r) => return Ok(r),
331 Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
334 let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
335 let user_id = user.map(|u| u.id);
337 let type_ = SearchType::from_str(&data.type_)?;
339 let mut posts = Vec::new();
340 let mut comments = Vec::new();
341 let mut communities = Vec::new();
342 let mut users = Vec::new();
344 // TODO no clean / non-nsfw searching rn
346 let q = data.q.to_owned();
347 let page = data.page;
348 let limit = data.limit;
349 let sort = SortType::from_str(&data.sort)?;
350 let community_id = data.community_id;
352 SearchType::Posts => {
353 posts = blocking(context.pool(), move |conn| {
354 PostQueryBuilder::create(conn)
357 .for_community_id(community_id)
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)
389 SearchType::Users => {
390 users = blocking(context.pool(), move |conn| {
391 UserQueryBuilder::create(conn)
401 posts = blocking(context.pool(), move |conn| {
402 PostQueryBuilder::create(conn)
405 .for_community_id(community_id)
414 let q = data.q.to_owned();
415 let sort = SortType::from_str(&data.sort)?;
417 comments = blocking(context.pool(), move |conn| {
418 CommentQueryBuilder::create(conn)
428 let q = data.q.to_owned();
429 let sort = SortType::from_str(&data.sort)?;
431 communities = blocking(context.pool(), move |conn| {
432 CommunityQueryBuilder::create(conn)
441 let q = data.q.to_owned();
442 let sort = SortType::from_str(&data.sort)?;
444 users = blocking(context.pool(), move |conn| {
445 UserQueryBuilder::create(conn)
455 posts = blocking(context.pool(), move |conn| {
456 PostQueryBuilder::create(conn)
459 .for_community_id(community_id)
471 type_: data.type_.to_owned(),
480 #[async_trait::async_trait(?Send)]
481 impl Perform for TransferSite {
482 type Response = GetSiteResponse;
486 context: &Data<LemmyContext>,
487 _websocket_id: Option<ConnectionId>,
488 ) -> Result<GetSiteResponse, LemmyError> {
489 let data: &TransferSite = &self;
490 let mut user = get_user_from_jwt(&data.auth, context.pool()).await?;
492 // TODO add a User_::read_safe() for this.
493 user.password_encrypted = "".to_string();
494 user.private_key = None;
495 user.public_key = None;
497 let read_site = blocking(context.pool(), move |conn| Site::read(conn, 1)).await??;
499 // Make sure user is the creator
500 if read_site.creator_id != user.id {
501 return Err(APIError::err("not_an_admin").into());
504 let new_creator_id = data.user_id;
505 let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
506 if blocking(context.pool(), transfer_site).await?.is_err() {
507 return Err(APIError::err("couldnt_update_site").into());
511 let form = ModAddForm {
512 mod_user_id: user.id,
513 other_user_id: data.user_id,
514 removed: Some(false),
517 blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
519 let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
521 let mut admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
522 let creator_index = admins
524 .position(|r| r.id == site_view.creator_id)
525 .context(location_info!())?;
526 let creator_user = admins.remove(creator_index);
527 admins.insert(0, creator_user);
529 let banned = blocking(context.pool(), move |conn| UserView::banned(conn)).await??;
532 site: Some(site_view),
536 version: version::VERSION.to_string(),
538 federated_instances: Settings::get().get_allowed_instances(),
543 #[async_trait::async_trait(?Send)]
544 impl Perform for GetSiteConfig {
545 type Response = GetSiteConfigResponse;
549 context: &Data<LemmyContext>,
550 _websocket_id: Option<ConnectionId>,
551 ) -> Result<GetSiteConfigResponse, LemmyError> {
552 let data: &GetSiteConfig = &self;
553 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
555 // Only let admins read this
556 is_admin(context.pool(), user.id).await?;
558 let config_hjson = Settings::read_config_file()?;
560 Ok(GetSiteConfigResponse { config_hjson })
564 #[async_trait::async_trait(?Send)]
565 impl Perform for SaveSiteConfig {
566 type Response = GetSiteConfigResponse;
570 context: &Data<LemmyContext>,
571 _websocket_id: Option<ConnectionId>,
572 ) -> Result<GetSiteConfigResponse, LemmyError> {
573 let data: &SaveSiteConfig = &self;
574 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
576 // Only let admins read this
577 let admins = blocking(context.pool(), move |conn| UserView::admins(conn)).await??;
578 let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
580 if !admin_ids.contains(&user.id) {
581 return Err(APIError::err("not_an_admin").into());
584 // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
585 let config_hjson = match Settings::save_config_file(&data.config_hjson) {
586 Ok(config_hjson) => config_hjson,
587 Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
590 Ok(GetSiteConfigResponse { config_hjson })