diff --git a/src/email.rs b/src/email.rs
index 202118ef..b0386115 100644
--- a/src/email.rs
+++ b/src/email.rs
@@ -1,5 +1,6 @@
use reqwest::blocking::Client;
use std::collections::HashMap;
+use std::env;
pub fn send_email(target: String, subject: String, body: String, html: String) -> Result<(), ()> {
let mut map = HashMap::new();
@@ -16,3 +17,32 @@ pub fn send_email(target: String, subject: String, body: String, html: String) -
Err(_) => Err(())
}
}
+
+fn public_uri() -> String {
+ env::var("PUBLIC_URI").expect("PUBLIC_URI not in environment variables!")
+}
+
+pub fn send_verification_email(email: String, code: String) -> bool {
+ let url = format!("{}/api/account/verify/{}", public_uri(), code);
+ match send_email(
+ email,
+ "Verify your email!".to_string(),
+ format!("Verify your email here: {}", url).to_string(),
+ format!("Click to verify your email!", url).to_string()
+ ) {
+ Ok(_) => true,
+ Err(_) => false,
+ }
+}
+
+pub fn send_welcome_email(email: String, username: String) -> bool {
+ match send_email(
+ email,
+ "Welcome to REVOLT!".to_string(),
+ format!("Welcome, {}! You can now use REVOLT.", username.clone()).to_string(),
+ format!("Welcome, {}!
You can now use REVOLT.
Go to REVOLT", username.clone(), public_uri()).to_string()
+ ) {
+ Ok(_) => true,
+ Err(_) => false,
+ }
+}
diff --git a/src/routes/account.rs b/src/routes/account.rs
index 9a40c5b0..557704d8 100644
--- a/src/routes/account.rs
+++ b/src/routes/account.rs
@@ -78,23 +78,13 @@ pub fn create(info: Json) -> JsonValue {
"email_verification": {
"verified": false,
"target": info.email.clone(),
- "expiry": UtcDatetime(Utc::now() + chrono::Duration::seconds(1)),
+ "expiry": UtcDatetime(Utc::now() + chrono::Duration::days(1)),
"rate_limit": UtcDatetime(Utc::now() + chrono::Duration::minutes(1)),
"code": code.clone(),
}
}, None) {
Ok(_) => {
- let url = format!("http://192.168.0.10:5500/api/account/verify/{}", code);
- let sent =
- match email::send_email(
- info.email.clone(),
- "Verify your email!".to_string(),
- format!("Verify your email here: {}", url).to_string(),
- format!("Click to verify your email!", url).to_string()
- ) {
- Ok(_) => true,
- Err(_) => false,
- };
+ let sent = email::send_verification_email(info.email.clone(), code);
json!({
"success": true,
@@ -145,12 +135,17 @@ pub fn verify_email(code: String) -> JsonValue {
},
"$set": {
"email_verification.verified": true,
- "email": target,
+ "email": target.clone(),
},
},
None,
).expect("Failed to update user!");
+ email::send_welcome_email(
+ target.to_string(),
+ u.get_str("username").expect("Failed to retrieve username.").to_string()
+ );
+
json!({
"success": true
})
@@ -162,3 +157,65 @@ pub fn verify_email(code: String) -> JsonValue {
})
}
}
+
+#[derive(Serialize, Deserialize)]
+pub struct Resend {
+ email: String,
+}
+
+/// resend a verification email
+/// (1) check if verification is pending for x email
+/// (2) check for rate limit
+/// (3) resend the email
+#[post("/resend", data = "")]
+pub fn resend_email(info: Json) -> JsonValue {
+ let col = database::get_db().collection("users");
+
+ if let Some(u) =
+ col.find_one(doc! { "email_verification.target": info.email.clone() }, None).expect("Failed user lookup") {
+ let ev = u.get_document("email_verification").expect("Missing email_verification on user object!");
+ let rate_limit = ev.get_utc_datetime("rate_limit").expect("Missing rate_limit on email_verification!");
+
+ if Utc::now() < *rate_limit {
+ json!({
+ "success": false,
+ "error": "Hit rate limit! Please try again in a minute or so."
+ })
+ } else {
+ let code = rand::thread_rng()
+ .sample_iter(&Alphanumeric)
+ .take(48)
+ .collect::();
+
+ col.update_one(
+ doc! { "_id": u.get_str("_id").expect("Failed to retrieve user id.") },
+ doc! {
+ "$set": {
+ "email_verification.code": code.clone(),
+ "email_verification.expiry": UtcDatetime(Utc::now() + chrono::Duration::days(1)),
+ "email_verification.rate_limit": UtcDatetime(Utc::now() + chrono::Duration::minutes(1)),
+ },
+ },
+ None,
+ ).expect("Failed to update user!");
+
+ match email::send_verification_email(
+ info.email.to_string(),
+ code,
+ ) {
+ true => json!({
+ "success": true,
+ }),
+ false => json!({
+ "success": false,
+ "error": "Failed to send email! Likely an issue with the backend API."
+ })
+ }
+ }
+ } else {
+ json!({
+ "success": false,
+ "error": "Email not pending verification!",
+ })
+ }
+}
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index 4ae3e54b..3d871f3a 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -4,5 +4,5 @@ mod account;
pub fn mount(rocket: Rocket) -> Rocket {
rocket
- .mount("/api/account", routes![ account::root, account::create, account::verify_email ])
+ .mount("/api/account", routes![ account::root, account::create, account::verify_email, account::resend_email ])
}