email: premium HTML template with accent colour and copy affordance

Redesigns the mail template (branded header bar, hero code block, copy
button, muted footer) and adds a configurable accent colour alongside the
brand name.
This commit is contained in:
Jean Chevronnet 2026-07-12 16:07:36 +00:00
parent 06e9da4dc8
commit 98630c7d36
No known key found for this signature in database
7 changed files with 63 additions and 22 deletions

View file

@ -456,8 +456,9 @@ pub struct Db {
pub(crate) scram_iterations: u32,
// Whether outbound email is configured, so email features can gate themselves.
email_enabled: bool,
// Display name for email templates.
// Display name and accent colour for email templates.
email_brand: String,
email_accent: String,
// Node-local, non-persisted email codes: account -> (purpose, code, expiry).
codes: HashMap<String, (CodeKind, String, Instant)>,
}
@ -506,7 +507,7 @@ impl Db {
apply(&mut accounts, &mut channels, &mut grouped, event);
}
tracing::info!(accounts = accounts.len(), channels = channels.len(), "account store loaded");
Self { accounts, channels, grouped, log, scram_iterations: scram::DEFAULT_ITERATIONS, email_enabled: false, email_brand: "Network Services".to_string(), codes: HashMap::new() }
Self { accounts, channels, grouped, log, scram_iterations: scram::DEFAULT_ITERATIONS, email_enabled: false, email_brand: "Network Services".to_string(), email_accent: "#4f46e5".to_string(), codes: HashMap::new() }
}
/// Fold an entry authored by another node into the store — the services-side
@ -738,10 +739,19 @@ impl Db {
&self.email_brand
}
/// Accent colour used in email templates.
pub fn email_accent(&self) -> &str {
&self.email_accent
}
pub fn set_email_brand(&mut self, brand: &str) {
self.email_brand = brand.to_string();
}
pub fn set_email_accent(&mut self, accent: &str) {
self.email_accent = accent.to_string();
}
/// Issue a fresh emailed code for `account` and purpose, valid for 15 minutes.
pub fn issue_code(&mut self, account: &str, kind: CodeKind) -> String {
let code = gen_code();

View file

@ -548,7 +548,7 @@ impl Engine {
if ok && !self.db.is_verified(account) {
if let (Some(addr), RegReply::NickServ { agent, uid, .. }) = (addr, &reply) {
let code = self.db.issue_code(account, db::CodeKind::Confirm);
let mail = crate::email::confirm(self.db.email_brand(), account, &code);
let mail = crate::email::confirm(self.db.email_brand(), self.db.email_accent(), account, &code);
out.push(NetAction::SendEmail { to: addr, subject: mail.subject, text: mail.text, html: Some(mail.html) });
out.push(NetAction::Notice { from: agent.clone(), to: uid.clone(), text: "A confirmation code has been emailed to you. Confirm with \x02CONFIRM <code>\x02.".to_string() });
}