Skip to content

Commit 8838932

Browse files
committed
Send email on crate owner invitation
If the recipient of a crate ownership invitation has a verified email address, they will be sent an email indicating that a specified user has invited them to become an owner of a named crate. Only sends the email on the first instance of an invitation.
1 parent 7f03d34 commit 8838932

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

src/email.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,23 @@ https://crates.io/confirm/{}",
8686
send_email(email, subject, &body)
8787
}
8888

89+
/// Attempts to send a crate owner invitation email. Swallows all errors.
90+
///
91+
/// Whether or not the email is sent, the invitation entry will be created in
92+
/// the database and the user will see the invitation when they visit
93+
/// https://crates.io/me/pending-invites/.
94+
pub fn send_owner_invite_email(email: &str, user_name: &str, crate_name: &str) {
95+
let subject = "Please confirm crate ownership";
96+
let body = format!(
97+
"{} has invited you to become an owner of the crate {}!\n
98+
Please visit https://crates.io/me/pending-invites to accept or reject
99+
this invitation.",
100+
user_name, crate_name
101+
);
102+
103+
let _ = send_email(email, subject, &body);
104+
}
105+
89106
fn send_email(recipient: &str, subject: &str, body: &str) -> CargoResult<()> {
90107
let mailgun_config = init_config_vars();
91108
let email = build_email(recipient, subject, body, &mailgun_config)?;

src/models/krate.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ use indexmap::IndexMap;
77
use url::Url;
88

99
use crate::app::App;
10+
use crate::email;
1011
use crate::util::{human, CargoResult};
1112

1213
use crate::models::{
13-
Badge, Category, CrateOwner, Keyword, NewCrateOwnerInvitation, Owner, OwnerKind,
14-
ReverseDependency, User, Version,
14+
Badge, Category, CrateOwner, CrateOwnerInvitation, Keyword, NewCrateOwnerInvitation, Owner,
15+
OwnerKind, ReverseDependency, User, Version,
1516
};
1617
use crate::views::{EncodableCrate, EncodableCrateLinks};
1718

@@ -427,14 +428,28 @@ impl Crate {
427428
match owner {
428429
// Users are invited and must accept before being added
429430
owner @ Owner::User(_) => {
430-
insert_into(crate_owner_invitations::table)
431+
let maybe_inserted = insert_into(crate_owner_invitations::table)
431432
.values(&NewCrateOwnerInvitation {
432433
invited_user_id: owner.id(),
433434
invited_by_user_id: req_user.id,
434435
crate_id: self.id,
435436
})
436437
.on_conflict_do_nothing()
437-
.execute(conn)?;
438+
.get_result::<CrateOwnerInvitation>(conn)
439+
.optional()?;
440+
441+
if maybe_inserted.is_some() {
442+
if let Owner::User(user) = &owner {
443+
if let Ok(Some(email)) = user.verified_email(&conn) {
444+
email::send_owner_invite_email(
445+
&email.as_str(),
446+
&req_user.gh_login.as_str(),
447+
&self.name.as_str(),
448+
);
449+
}
450+
}
451+
}
452+
438453
Ok(format!(
439454
"user {} has been invited to be an owner of crate {}",
440455
owner.login(),

0 commit comments

Comments
 (0)