diff --git a/src/email.rs b/src/email.rs index 0962b60f95..e32ef01243 100644 --- a/src/email.rs +++ b/src/email.rs @@ -86,6 +86,23 @@ https://crates.io/confirm/{}", send_email(email, subject, &body) } +/// Attempts to send a crate owner invitation email. Swallows all errors. +/// +/// Whether or not the email is sent, the invitation entry will be created in +/// the database and the user will see the invitation when they visit +/// https://crates.io/me/pending-invites/. +pub fn send_owner_invite_email(email: &str, user_name: &str, crate_name: &str) { + let subject = "Crate ownership invitation"; + let body = format!( + "{} has invited you to become an owner of the crate {}!\n +Please visit https://crates.io/me/pending-invites to accept or reject +this invitation.", + user_name, crate_name + ); + + let _ = send_email(email, subject, &body); +} + fn send_email(recipient: &str, subject: &str, body: &str) -> CargoResult<()> { let mailgun_config = init_config_vars(); let email = build_email(recipient, subject, body, &mailgun_config)?; diff --git a/src/models/krate.rs b/src/models/krate.rs index ad56203dc5..e19cc21973 100644 --- a/src/models/krate.rs +++ b/src/models/krate.rs @@ -7,11 +7,12 @@ use indexmap::IndexMap; use url::Url; use crate::app::App; +use crate::email; use crate::util::{human, CargoResult}; use crate::models::{ - Badge, Category, CrateOwner, Keyword, NewCrateOwnerInvitation, Owner, OwnerKind, - ReverseDependency, User, Version, + Badge, Category, CrateOwner, CrateOwnerInvitation, Keyword, NewCrateOwnerInvitation, Owner, + OwnerKind, ReverseDependency, User, Version, }; use crate::views::{EncodableCrate, EncodableCrateLinks}; @@ -426,19 +427,30 @@ impl Crate { match owner { // Users are invited and must accept before being added - owner @ Owner::User(_) => { - insert_into(crate_owner_invitations::table) + Owner::User(user) => { + let maybe_inserted = insert_into(crate_owner_invitations::table) .values(&NewCrateOwnerInvitation { - invited_user_id: owner.id(), + invited_user_id: user.id, invited_by_user_id: req_user.id, crate_id: self.id, }) .on_conflict_do_nothing() - .execute(conn)?; + .get_result::(conn) + .optional()?; + + if maybe_inserted.is_some() { + if let Ok(Some(email)) = user.verified_email(&conn) { + email::send_owner_invite_email( + &email.as_str(), + &req_user.gh_login.as_str(), + &self.name.as_str(), + ); + } + } + Ok(format!( "user {} has been invited to be an owner of crate {}", - owner.login(), - self.name + user.gh_login, self.name )) } // Teams are added as owners immediately