]> Untitled Git - lemmy.git/blobdiff - crates/api_common/src/utils.rs
Implement separate mod activities for feature, lock post (#2716)
[lemmy.git] / crates / api_common / src / utils.rs
index 1e863e855c4ec266ddae4865df5ef640ea7ed545..919027636e264f68def860b8468a27b6e67a5db1 100644 (file)
@@ -1,14 +1,15 @@
 use crate::{request::purge_image_from_pictrs, sensitive::Sensitive, site::FederatedInstances};
+use anyhow::Context;
 use chrono::NaiveDateTime;
 use lemmy_db_schema::{
   impls::person::is_banned,
-  newtypes::{CommunityId, LocalUserId, PersonId, PostId},
+  newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
   source::{
     comment::{Comment, CommentUpdateForm},
     community::{Community, CommunityUpdateForm},
     email_verification::{EmailVerification, EmailVerificationForm},
     instance::Instance,
-    local_site::LocalSite,
+    local_site::{LocalSite, RegistrationMode},
     local_site_rate_limit::LocalSiteRateLimit,
     password_reset_request::PasswordResetRequest,
     person::{Person, PersonUpdateForm},
@@ -29,20 +30,23 @@ use lemmy_db_views_actor::structs::{
   CommunityModeratorView,
   CommunityPersonBanView,
   CommunityView,
+  PersonViewSafe,
 };
 use lemmy_utils::{
   claims::Claims,
   email::{send_email, translations::Lang},
   error::LemmyError,
+  location_info,
   rate_limit::RateLimitConfig,
   settings::structs::Settings,
-  utils::{build_slur_regex, generate_random_string},
+  utils::slurs::build_slur_regex,
 };
 use regex::Regex;
 use reqwest_middleware::ClientWithMiddleware;
 use rosetta_i18n::{Language, LanguageId};
 use std::str::FromStr;
 use tracing::warn;
+use url::{ParseError, Url};
 
 #[tracing::instrument(skip_all)]
 pub async fn is_mod_or_admin(
@@ -57,6 +61,18 @@ pub async fn is_mod_or_admin(
   Ok(())
 }
 
+pub async fn is_top_admin(pool: &DbPool, person_id: PersonId) -> Result<(), LemmyError> {
+  let admins = PersonViewSafe::admins(pool).await?;
+  let top_admin = admins
+    .get(0)
+    .ok_or_else(|| LemmyError::from_message("no admins"))?;
+
+  if top_admin.person.id != person_id {
+    return Err(LemmyError::from_message("not_top_admin"));
+  }
+  Ok(())
+}
+
 pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
   if !local_user_view.person.admin {
     return Err(LemmyError::from_message("not_an_admin"));
@@ -344,7 +360,7 @@ pub async fn send_password_reset_email(
   settings: &Settings,
 ) -> Result<(), LemmyError> {
   // Generate a random token
-  let token = generate_random_string();
+  let token = uuid::Uuid::new_v4().to_string();
 
   // Insert the row
   let token2 = token.clone();
@@ -370,7 +386,7 @@ pub async fn send_verification_email(
   let form = EmailVerificationForm {
     local_user_id: user.local_user.id,
     email: new_email.to_string(),
-    verification_token: generate_random_string(),
+    verification_token: uuid::Uuid::new_v4().to_string(),
   };
   let verify_link = format!(
     "{}/verify_email/{}",
@@ -473,19 +489,41 @@ pub async fn send_new_applicant_email_to_admins(
   for admin in &admins {
     let email = &admin.local_user.email.clone().expect("email");
     let lang = get_interface_language_from_settings(admin);
-    let subject = lang.new_application_subject(applicant_username, &settings.hostname);
+    let subject = lang.new_application_subject(&settings.hostname, applicant_username);
     let body = lang.new_application_body(applications_link);
     send_email(&subject, email, &admin.person.name, &body, settings)?;
   }
   Ok(())
 }
 
+/// Send a report to all admins
+pub async fn send_new_report_email_to_admins(
+  reporter_username: &str,
+  reported_username: &str,
+  pool: &DbPool,
+  settings: &Settings,
+) -> Result<(), LemmyError> {
+  // Collect the admins with emails
+  let admins = LocalUserSettingsView::list_admins_with_emails(pool).await?;
+
+  let reports_link = &format!("{}/reports", settings.get_protocol_and_hostname(),);
+
+  for admin in &admins {
+    let email = &admin.local_user.email.clone().expect("email");
+    let lang = get_interface_language_from_settings(admin);
+    let subject = lang.new_report_subject(&settings.hostname, reporter_username, reported_username);
+    let body = lang.new_report_body(reports_link);
+    send_email(&subject, email, &admin.person.name, &body, settings)?;
+  }
+  Ok(())
+}
+
 pub async fn check_registration_application(
   local_user_view: &LocalUserView,
   local_site: &LocalSite,
   pool: &DbPool,
 ) -> Result<(), LemmyError> {
-  if local_site.require_application
+  if local_site.registration_mode == RegistrationMode::RequireApplication
     && !local_user_view.local_user.accepted_application
     && !local_user_view.person.admin
   {
@@ -743,3 +781,69 @@ mod tests {
     assert!(honeypot_check(&Some("message".to_string())).is_err());
   }
 }
+
+pub enum EndpointType {
+  Community,
+  Person,
+  Post,
+  Comment,
+  PrivateMessage,
+}
+
+/// Generates an apub endpoint for a given domain, IE xyz.tld
+pub fn generate_local_apub_endpoint(
+  endpoint_type: EndpointType,
+  name: &str,
+  domain: &str,
+) -> Result<DbUrl, ParseError> {
+  let point = match endpoint_type {
+    EndpointType::Community => "c",
+    EndpointType::Person => "u",
+    EndpointType::Post => "post",
+    EndpointType::Comment => "comment",
+    EndpointType::PrivateMessage => "private_message",
+  };
+
+  Ok(Url::parse(&format!("{domain}/{point}/{name}"))?.into())
+}
+
+pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{actor_id}/followers"))?.into())
+}
+
+pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{actor_id}/inbox"))?.into())
+}
+
+pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  let mut actor_id: Url = actor_id.clone().into();
+  actor_id.set_path("site_inbox");
+  Ok(actor_id.into())
+}
+
+pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
+  let actor_id: Url = actor_id.clone().into();
+  let url = format!(
+    "{}://{}{}/inbox",
+    &actor_id.scheme(),
+    &actor_id.host_str().context(location_info!())?,
+    if let Some(port) = actor_id.port() {
+      format!(":{port}")
+    } else {
+      String::new()
+    },
+  );
+  Ok(Url::parse(&url)?.into())
+}
+
+pub fn generate_outbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{actor_id}/outbox"))?.into())
+}
+
+pub fn generate_featured_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{actor_id}/featured"))?.into())
+}
+
+pub fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
+  Ok(Url::parse(&format!("{community_id}/moderators"))?.into())
+}