feat(email): add email verification

This commit is contained in:
Andrea Toska 2025-04-26 13:38:11 +02:00
commit 18e74dd57e
No known key found for this signature in database
GPG key ID: 5B3C83807CCBE9A2
27 changed files with 1107 additions and 18 deletions

View file

@ -1,11 +0,0 @@
use rocket::{post, FromForm};
use rocket::form::Form;
#[derive(FromForm)]
struct ValidateEmailInput{
email: String,
}
#[post("/v1/api/support/validate/email", data="<data>")]
pub fn validate(data: Form<ValidateEmailInput>){
}

View file

@ -2,9 +2,9 @@ pub mod devices;
pub mod agreements;
pub mod timezones;
pub mod person_exists;
pub mod email;
pub mod oauth;
mod pid_distribution;
pub mod people;
pub mod provider;
pub mod mapped_ids;
pub mod support;

View file

@ -18,6 +18,8 @@ use crate::nnid::pid_distribution::next_pid;
use crate::nnid::timezones::{OFFSET_FROM_TIMEZONE, ZONE_TO_TIMEZONES};
use crate::Pool;
use crate::xml::{Xml, YesNoVal};
use crate::email::send_verification_email;
use rand::Rng;
static S3_URL_STRING: Lazy<Box<str>> = Lazy::new(||
env::var("S3_URL").expect("S3_URL not specified").into_boxed_str()
@ -114,6 +116,8 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
let pid = next_pid(database).await;
let verification_code: i32 = rand::thread_rng().gen_range(100_000..1_000_000);
let AccountCreationData {
user_id,
password,
@ -152,9 +156,10 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
off_device_allowed,
region,
gender,
mii_data
mii_data,
verification_code
) VALUES (
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14
)
",
pid,
@ -169,11 +174,16 @@ pub async fn create_account(database: &State<Pool>, data: Xml<AccountCreationDat
off_device_flag.0,
region,
gender.as_ref(),
data.as_ref()
data.as_ref(),
verification_code,
).execute(database).await.unwrap();
generate_s3_images(pid, &data).await;
if let Err(e) = send_verification_email(address.as_ref(), verification_code, user_id.as_ref()).await {
println!("Failed to send verification email: {e}");
}
Ok(
Xml(AccountCreationResponseData{
pid

65
src/nnid/support.rs Normal file
View file

@ -0,0 +1,65 @@
use rocket::{get, State, post, FromForm};
use rocket::http::Status;
use crate::Pool;
use rocket::form::Form;
use crate::email::send_verification_email;
use crate::error::{Error, Errors};
use chrono::Utc;
const BAD_CODE_ERROR: Errors = Errors{
error: &[
Error{
code: "0116",
message: "Missing or invalid verification code"
}
]
};
#[derive(FromForm)]
struct ValidateEmailInput{
email: String,
}
#[post("/v1/api/support/validate/email", data="<data>")]
pub async fn validate(data: Form<ValidateEmailInput>){
if let Err(e) = send_verification_email(&*data.email, 123456, "Andrea Test Username").await {
println!("Failed to send verification email: {e}");
}
}
#[get("/v1/api/support/email_confirmation/<pid>/<code>")]
pub async fn verify_email(database: &State<Pool>, pid: i32, code: i32) -> Result<(), Errors<'static>> {
let db = database.inner();
let result = sqlx::query!(
"SELECT verification_code FROM users WHERE pid = $1",
pid
)
.fetch_optional(db)
.await;
let Ok(Some(record)) = result else {
return Err(BAD_CODE_ERROR);
};
if let Some(stored_code) = record.verification_code {
if stored_code == code {
// Set email_verified_since to NOW
let now = Utc::now().naive_utc();
let update_result = sqlx::query!(
"UPDATE users SET email_verified_since = $1 WHERE pid = $2",
now,
pid
)
.execute(db)
.await;
if update_result.is_err() {
return Err(BAD_CODE_ERROR); // fallback in case the update fails
}
return Ok(()); // Success
}
}
Err(BAD_CODE_ERROR)
}