From d0fb652079a8c0e0ba92ae9d871867affd03a3ba Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Fri, 2 Feb 2024 13:17:34 +0530 Subject: [PATCH 1/6] feat(auth): read email template from fastn package reads from path /email/confirmation-mail.html --- fastn-core/src/auth/email_password.rs | 19 ++++++++++++++----- fastn-core/src/auth/routes.rs | 2 +- .../src/library2022/processor/user_details.rs | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fastn-core/src/auth/email_password.rs b/fastn-core/src/auth/email_password.rs index e3e9fce2a7..d5dcf05dbe 100644 --- a/fastn-core/src/auth/email_password.rs +++ b/fastn-core/src/auth/email_password.rs @@ -155,7 +155,7 @@ pub(crate) async fn create_user( tracing::info!("fastn_user email inserted"); let conf_link = - create_and_send_confirmation_email(email.0.to_string(), db_pool, req, next).await?; + create_and_send_confirmation_email(email.0.to_string(), db_pool, req, config, next).await?; let resp_body = serde_json::json!({ "user": user, @@ -424,6 +424,7 @@ pub(crate) async fn confirm_email( pub(crate) async fn resend_email( req: &fastn_core::http::Request, + config: &fastn_core::Config, db_pool: &fastn_core::db::PgPool, next: String, ) -> fastn_core::Result { @@ -447,7 +448,7 @@ pub(crate) async fn resend_email( } }; - create_and_send_confirmation_email(email, db_pool, req, next.clone()).await?; + create_and_send_confirmation_email(email, db_pool, req, config, next.clone()).await?; // TODO: there's no GET /-/auth/login/ yet // the client will have to create one for now @@ -462,6 +463,7 @@ async fn create_and_send_confirmation_email( email: String, db_pool: &fastn_core::db::PgPool, req: &fastn_core::http::Request, + config: &fastn_core::Config, next: String, ) -> fastn_core::Result { use diesel::prelude::*; @@ -540,13 +542,17 @@ async fn create_and_send_confirmation_email( .first(&mut conn) .await?; + let file_path = fastn_ds::Path::new("email/confirmation-mail.html"); + + let html_file = config.ds.read_to_string(&file_path).await?; + mailer .send_raw( format!("{} <{}>", name, email) .parse::() .unwrap(), "Verify your email", - confirmation_mail_body(&confirmation_link), + confirmation_mail_body(html_file, &confirmation_link), ) .await .map_err(|e| fastn_core::Error::generic(format!("failed to send email: {e}")))?; @@ -568,8 +574,11 @@ fn key_expired(sent_at: chrono::DateTime) -> bool { <= chrono::offset::Utc::now() } -fn confirmation_mail_body(link: &str) -> String { - format!("Use this link to verify your email: {link}") +fn confirmation_mail_body(content: String, link: &str) -> String { + // content will have a placeholder for the link + let content = content.replace("{{link}}", link); + + format!("{}", content) } fn generate_key(length: usize) -> String { diff --git a/fastn-core/src/auth/routes.rs b/fastn-core/src/auth/routes.rs index 2c48137889..729ea1ffa4 100644 --- a/fastn-core/src/auth/routes.rs +++ b/fastn-core/src/auth/routes.rs @@ -97,7 +97,7 @@ pub async fn handle_auth( fastn_core::auth::email_password::confirm_email(&req, pool, next).await } "/-/auth/resend-confirmation-email/" => { - fastn_core::auth::email_password::resend_email(&req, pool, next).await + fastn_core::auth::email_password::resend_email(&req, config, pool, next).await } "/-/auth/onboarding/" => { fastn_core::auth::email_password::onboarding(&req, req_config, config, next).await diff --git a/fastn-core/src/library2022/processor/user_details.rs b/fastn-core/src/library2022/processor/user_details.rs index c397bb904d..9320698b4c 100644 --- a/fastn-core/src/library2022/processor/user_details.rs +++ b/fastn-core/src/library2022/processor/user_details.rs @@ -1,4 +1,4 @@ -/// currently returns the github user details +/// returns details of the logged in user pub async fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, From ca08d1e73943b42f5704e483ae49da503faf7a0a Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 7 Feb 2024 00:06:21 +0530 Subject: [PATCH 2/6] WIP: fails to get -- string from ftd doc --- fastn-core/src/auth/email_password.rs | 51 ++++++++++++++++++++++----- fastn-core/src/auth/routes.rs | 2 +- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/fastn-core/src/auth/email_password.rs b/fastn-core/src/auth/email_password.rs index 8858dbb455..a67a5662ee 100644 --- a/fastn-core/src/auth/email_password.rs +++ b/fastn-core/src/auth/email_password.rs @@ -158,7 +158,7 @@ pub(crate) async fn create_user( email.0.to_string(), db_pool, req, - &req_config.config.ds, + req_config, next, ) .await?; @@ -433,7 +433,7 @@ pub(crate) async fn confirm_email( pub(crate) async fn resend_email( req: &fastn_core::http::Request, - ds: &fastn_ds::DocumentStore, + req_config: &mut fastn_core::RequestConfig, db_pool: &fastn_core::db::PgPool, next: String, ) -> fastn_core::Result { @@ -457,7 +457,7 @@ pub(crate) async fn resend_email( } }; - create_and_send_confirmation_email(email, db_pool, req, ds, next.clone()).await?; + create_and_send_confirmation_email(email, db_pool, req, req_config, next.clone()).await?; // TODO: there's no GET /-/auth/login/ yet // the client will have to create one for now @@ -472,7 +472,7 @@ async fn create_and_send_confirmation_email( email: String, db_pool: &fastn_core::db::PgPool, req: &fastn_core::http::Request, - ds: &fastn_ds::DocumentStore, + req_config: &mut fastn_core::RequestConfig, next: String, ) -> fastn_core::Result { use diesel::prelude::*; @@ -524,7 +524,7 @@ async fn create_and_send_confirmation_email( let confirmation_link = confirmation_link(req, stored_key, next); - let mailer = fastn_core::mail::Mailer::from_env(ds).await; + let mailer = fastn_core::mail::Mailer::from_env(&req_config.config.ds).await; if mailer.is_err() { return Err(fastn_core::Error::generic( @@ -539,7 +539,7 @@ async fn create_and_send_confirmation_email( let mut mailer = mailer.unwrap(); - if let Ok(debug_mode) = ds.env("DEBUG").await { + if let Ok(debug_mode) = req_config.config.ds.env("DEBUG").await { if debug_mode == "true" { mailer.mock(); } @@ -551,9 +551,42 @@ async fn create_and_send_confirmation_email( .first(&mut conn) .await?; - let file_path = fastn_ds::Path::new("email/confirmation-mail.html"); + let path = req_config.config.package.eval_auto_import("auth").unwrap(); + let path = path.strip_prefix(format!("{}/", req_config.config.package.name).as_str()).unwrap(); + + dbg!(&path); + + let content = req_config.config.ds.read_to_string(&fastn_ds::Path::new(format!("{}.ftd", path))).await?; + + dbg!(&content); + + let auth_doc = fastn_core::Document { + package_name: req_config.config.package.name.clone(), + id: "auth".to_string(), + content, + parent_path: fastn_ds::Path::new("/"), + }; + + let main_ftd_doc = fastn_core::doc::interpret_helper( + auth_doc.id_with_package().as_str(), + auth_doc.content.as_str(), + req_config, + "/", + false, + 0, + ) + .await?; + + let html: String = main_ftd_doc.get("confirmation-mail-html").unwrap(); + + // let html = match var.value { + // ftd::interpreter::prelude::PropertyValue::Value{value, ..} => { + // value.string("auth", 0) + // }, + // t => ftd::interpreter::utils::e2(format!("Expected confirmation-mail-html to be a string, found {:?}", t), "auth", 0), + // }.unwrap(); - let html_file = config.ds.read_to_string(&file_path).await?; + dbg!(&html); mailer .send_raw( @@ -561,7 +594,7 @@ async fn create_and_send_confirmation_email( .parse::() .unwrap(), "Verify your email", - confirmation_mail_body(html_file, &confirmation_link), + confirmation_mail_body(html, &confirmation_link), ) .await .map_err(|e| fastn_core::Error::generic(format!("failed to send email: {e}")))?; diff --git a/fastn-core/src/auth/routes.rs b/fastn-core/src/auth/routes.rs index 952311536b..44b2d1e734 100644 --- a/fastn-core/src/auth/routes.rs +++ b/fastn-core/src/auth/routes.rs @@ -103,7 +103,7 @@ pub async fn handle_auth( .await } "/-/auth/resend-confirmation-email/" => { - fastn_core::auth::email_password::resend_email(&req, &req_config.config.ds, pool, next) + fastn_core::auth::email_password::resend_email(&req, req_config, pool, next) .await } "/-/auth/onboarding/" => { From 0f2fce42a9b58e564811dce9336c6cab87fcd9df Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 7 Feb 2024 18:54:03 +0530 Subject: [PATCH 3/6] WIP trying parse_ftd --- fastn-core/src/auth/email_password.rs | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/fastn-core/src/auth/email_password.rs b/fastn-core/src/auth/email_password.rs index a67a5662ee..9b2be49723 100644 --- a/fastn-core/src/auth/email_password.rs +++ b/fastn-core/src/auth/email_password.rs @@ -567,26 +567,9 @@ async fn create_and_send_confirmation_email( parent_path: fastn_ds::Path::new("/"), }; - let main_ftd_doc = fastn_core::doc::interpret_helper( - auth_doc.id_with_package().as_str(), - auth_doc.content.as_str(), - req_config, - "/", - false, - 0, - ) - .await?; - - let html: String = main_ftd_doc.get("confirmation-mail-html").unwrap(); - - // let html = match var.value { - // ftd::interpreter::prelude::PropertyValue::Value{value, ..} => { - // value.string("auth", 0) - // }, - // t => ftd::interpreter::utils::e2(format!("Expected confirmation-mail-html to be a string, found {:?}", t), "auth", 0), - // }.unwrap(); + let main_ftd_doc = fastn_core::doc::parse_ftd(&auth_doc.id, &auth_doc.content, &Default::default())?; - dbg!(&html); + let html: String = main_ftd_doc.get("confirmation-mail-html").unwrap(); mailer .send_raw( From f4bbbcef39c4d70e09f6b1e22799bd2f3c0fc7b6 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 7 Feb 2024 20:22:46 +0530 Subject: [PATCH 4/6] fetch html email template from ftd file the ftd file will have to define a string variable: ```ftd -- string confirmation-mail-html: $always-include$: true ;; this is required Click this to confirm your email ``` --- fastn-core/src/auth/email_password.rs | 51 ++++++++++++++++++--------- fastn-core/src/auth/routes.rs | 3 +- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/fastn-core/src/auth/email_password.rs b/fastn-core/src/auth/email_password.rs index 9b2be49723..27c4bbcaba 100644 --- a/fastn-core/src/auth/email_password.rs +++ b/fastn-core/src/auth/email_password.rs @@ -154,14 +154,9 @@ pub(crate) async fn create_user( tracing::info!("fastn_user email inserted"); - let conf_link = create_and_send_confirmation_email( - email.0.to_string(), - db_pool, - req, - req_config, - next, - ) - .await?; + let conf_link = + create_and_send_confirmation_email(email.0.to_string(), db_pool, req, req_config, next) + .await?; let resp_body = serde_json::json!({ "user": user, @@ -551,25 +546,47 @@ async fn create_and_send_confirmation_email( .first(&mut conn) .await?; - let path = req_config.config.package.eval_auto_import("auth").unwrap(); - let path = path.strip_prefix(format!("{}/", req_config.config.package.name).as_str()).unwrap(); - - dbg!(&path); + // To use auth. The package has to have auto import with alias `auth` setup + let path = req_config + .config + .package + .eval_auto_import("auth") + .unwrap() + .to_owned(); - let content = req_config.config.ds.read_to_string(&fastn_ds::Path::new(format!("{}.ftd", path))).await?; + let path = path + .strip_prefix(format!("{}/", req_config.config.package.name).as_str()) + .unwrap(); - dbg!(&content); + let content = req_config + .config + .ds + .read_to_string(&fastn_ds::Path::new(format!("{}.ftd", path))) + .await?; let auth_doc = fastn_core::Document { package_name: req_config.config.package.name.clone(), - id: "auth".to_string(), + id: path.to_string(), content, parent_path: fastn_ds::Path::new("/"), }; - let main_ftd_doc = fastn_core::doc::parse_ftd(&auth_doc.id, &auth_doc.content, &Default::default())?; + let main_ftd_doc = fastn_core::doc::interpret_helper( + auth_doc.id_with_package().as_str(), + auth_doc.content.as_str(), + req_config, + "/", + false, + 0, + ) + .await?; + + let html_email_templ = format!( + "{}/{}#confirmation-mail-html", + req_config.config.package.name, path + ); - let html: String = main_ftd_doc.get("confirmation-mail-html").unwrap(); + let html: String = main_ftd_doc.get(&html_email_templ).unwrap(); mailer .send_raw( diff --git a/fastn-core/src/auth/routes.rs b/fastn-core/src/auth/routes.rs index 44b2d1e734..55d4ef4dc7 100644 --- a/fastn-core/src/auth/routes.rs +++ b/fastn-core/src/auth/routes.rs @@ -103,8 +103,7 @@ pub async fn handle_auth( .await } "/-/auth/resend-confirmation-email/" => { - fastn_core::auth::email_password::resend_email(&req, req_config, pool, next) - .await + fastn_core::auth::email_password::resend_email(&req, req_config, pool, next).await } "/-/auth/onboarding/" => { fastn_core::auth::email_password::onboarding(&req, req_config, config, next).await From d5fdf23e0ce2557ae8695f51f59e956c69b0560a Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 7 Feb 2024 20:27:03 +0530 Subject: [PATCH 5/6] clippy fix --- fastn-core/src/auth/email_password.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastn-core/src/auth/email_password.rs b/fastn-core/src/auth/email_password.rs index 27c4bbcaba..5ea5027814 100644 --- a/fastn-core/src/auth/email_password.rs +++ b/fastn-core/src/auth/email_password.rs @@ -622,7 +622,7 @@ fn confirmation_mail_body(content: String, link: &str) -> String { // content will have a placeholder for the link let content = content.replace("{{link}}", link); - format!("{}", content) + content.to_string() } fn generate_key(length: usize) -> String { From 87452ae2834c445f254a80b13bf46a09e6e7aed7 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 7 Feb 2024 22:21:09 +0530 Subject: [PATCH 6/6] fix: update integration tests --- integration-tests/FASTN.ftd | 2 ++ integration-tests/authy.ftd | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 integration-tests/authy.ftd diff --git a/integration-tests/FASTN.ftd b/integration-tests/FASTN.ftd index 365555f38b..f7cdeb8e22 100644 --- a/integration-tests/FASTN.ftd +++ b/integration-tests/FASTN.ftd @@ -2,6 +2,8 @@ -- fastn.package: integration-tests +-- fastn.auto-import: integration-tests/authy as auth + -- fastn.url-mappings: /ftd/* -> http+proxy://fastn.com/ftd/* diff --git a/integration-tests/authy.ftd b/integration-tests/authy.ftd new file mode 100644 index 0000000000..4fdd021277 --- /dev/null +++ b/integration-tests/authy.ftd @@ -0,0 +1,45 @@ +-- string test: fsdkjflk + +-- string confirmation-mail-html: +$always-include$: true + + + + + + Confirm your email + + +

Hello

+ Click on this text to confirm your email + + + + +-- component email-confirmation-request-sent: + +-- ftd.column: + +-- ftd.text: We have sent you an email. Please verify. + +-- end: ftd.column + +-- end: email-confirmation-request-sent + + +-- component onboarding: + +-- ftd.column: +-- ftd.text: welcome aboard, meine freundin +color: green + +-- ftd.text: glad you're here, the next time you refresh this page you won't be here + +-- ftd.text: + +This is useful for doing onboarding stuff. collecting extra information about the user. Use sql processor or endpoint for now to do this stuff + +-- end: ftd.column + +-- end: onboarding +