diff --git a/ee/tabby-webserver/email_templates/invitation.html b/ee/tabby-webserver/email_templates/invitation.html new file mode 100644 index 000000000000..267f92724aec --- /dev/null +++ b/ee/tabby-webserver/email_templates/invitation.html @@ -0,0 +1,4 @@ +You've been invited to join a Tabby workspace! +Welcome to Tabby! You have been invited to join a Tabby Server, where you can tap into AI-driven code completions and chat assistants. + +Go to {external_url}/auth/signup?invitationCode={code} to join! diff --git a/ee/tabby-webserver/src/service/email.rs b/ee/tabby-webserver/src/service/email/mod.rs similarity index 96% rename from ee/tabby-webserver/src/service/email.rs rename to ee/tabby-webserver/src/service/email/mod.rs index 7eddb79a4fb6..5a492adc9cdf 100644 --- a/ee/tabby-webserver/src/service/email.rs +++ b/ee/tabby-webserver/src/service/email/mod.rs @@ -14,6 +14,7 @@ use lettre::{ use tabby_db::{DbConn, DbEnum}; use tokio::{sync::RwLock, task::JoinHandle}; use tracing::warn; +mod templates; use crate::schema::{ email::{ @@ -223,11 +224,9 @@ impl EmailService for EmailServiceImpl { ) -> Result, SendEmailError> { let network_setting = self.db.read_network_setting().await?; let external_url = network_setting.external_url; - self.send_email_in_background( - email, - "You've been invited to join a Tabby workspace!".into(), - format!("Welcome to Tabby! You have been invited to join a Tabby Server, where you can tap into AI-driven code completions and chat assistants.\n\nGo to {external_url}/auth/signup?invitationCode={code} to join!"), - ).await + let contents = templates::invitation_email(&external_url, &code); + self.send_email_in_background(email, contents.subject, contents.body) + .await } } diff --git a/ee/tabby-webserver/src/service/email/templates.rs b/ee/tabby-webserver/src/service/email/templates.rs new file mode 100644 index 000000000000..d7aef2068c20 --- /dev/null +++ b/ee/tabby-webserver/src/service/email/templates.rs @@ -0,0 +1,26 @@ +pub struct EmailContents { + pub subject: String, + pub body: String, +} + +fn format_email(template: &'static str, replacements: &[(&str, &str)]) -> EmailContents { + let mut lines = template.lines(); + let mut subject = lines + .next() + .expect("Email must have subject line") + .to_string(); + let body: Vec<&str> = lines.collect(); + let mut body = body.join("\n"); + for (name, replacement) in replacements { + body = body.replace(name, replacement); + subject = subject.replace(name, replacement); + } + EmailContents { subject, body } +} + +pub fn invitation_email(external_url: &str, code: &str) -> EmailContents { + format_email( + include_str!("../../../email_templates/invitation.html"), + &[("{external_url}", external_url), ("{code}", code)], + ) +}