Create template registry once and lazy.

Instead of creating a new Handlebars instance each call to send_email
it's now a rocket fairing that's initialized lazily.
This commit is contained in:
Kai Michaelis 2019-01-10 14:45:11 +01:00
parent b6dae24d66
commit 242c73290c
3 changed files with 45 additions and 35 deletions

View File

@ -14,19 +14,15 @@ pub struct Context{
pub domain: String,
}
fn send_mail<T>(to: &Email, subject: &str, template_dir: &str,
template_base: &str, from: &str, ctx: T)
fn send_mail<T>(to: &Email, subject: &str, mail_templates: &Handlebars,
template: &str, from: &str, ctx: T)
-> Result<()> where T: Serialize + Clone
{
// TODO: Should be done only on startup
let tmpl = format!("{}/{}", template_dir, template_base);
let mut handlebars = Handlebars::new();
handlebars.register_template_file("html", format!("{}-html.hbs", tmpl)).unwrap();
handlebars.register_template_file("txt", format!("{}-txt.hbs", tmpl)).unwrap();
let tmpl_html = format!("{}-html", template);
let tmpl_txt = format!("{}-txt", template);
let (html, txt) = {
if let (Ok(inner_html), Ok(inner_txt)) =
(handlebars.render("html", &ctx), handlebars.render("txt", &ctx)) {
(mail_templates.render(&tmpl_html, &ctx), mail_templates.render(&tmpl_txt, &ctx)) {
(Some(inner_html), Some(inner_txt))
} else {
(None, None)
@ -47,7 +43,7 @@ fn send_mail<T>(to: &Email, subject: &str, template_dir: &str,
Ok(())
}
pub fn send_verification_mail(userid: &Email, token: &str, template_dir: &str,
pub fn send_verification_mail(userid: &Email, token: &str, mail_templates: &Handlebars,
domain: &str, from: &str)
-> Result<()>
{
@ -57,11 +53,11 @@ pub fn send_verification_mail(userid: &Email, token: &str, template_dir: &str,
domain: domain.to_string(),
};
send_mail(userid, "Please verify your email address", template_dir,
"verify-email", from, ctx)
send_mail(userid, "Please verify your email address", mail_templates,
"verify", from, ctx)
}
pub fn send_confirmation_mail(userid: &Email, token: &str, template_dir: &str,
pub fn send_confirmation_mail(userid: &Email, token: &str, mail_templates: &Handlebars,
domain: &str, from: &str)
-> Result<()>
{
@ -71,6 +67,6 @@ pub fn send_confirmation_mail(userid: &Email, token: &str, template_dir: &str,
domain: domain.to_string(),
};
send_mail(userid, "Please confirm deletion of your key", template_dir,
"confirm-email", from, ctx)
send_mail(userid, "Please confirm deletion of your key", mail_templates,
"confirm", from, ctx)
}

View File

@ -5,8 +5,9 @@ use rocket::request::{self, Request, FromRequest};
use rocket::response::status::Custom;
use rocket::response::NamedFile;
use rocket::fairing::AdHoc;
use rocket_contrib::templates::Template;
use handlebars::Handlebars;
use std::path::{Path, PathBuf};
mod upload;
@ -50,9 +51,9 @@ mod templates {
}
struct StaticDir(String);
pub struct MailTemplateDir(String);
pub struct Domain(String);
pub struct From(String);
pub struct MailTemplates(Handlebars);
impl<'a, 'r> FromRequest<'a, 'r> for queries::Hkp {
type Error = ();
@ -190,7 +191,7 @@ fn verify(db: rocket::State<Polymorphic>, token: String)
#[get("/vks/delete/<fpr>")]
fn delete(db: rocket::State<Polymorphic>, fpr: String,
tmpl: State<MailTemplateDir>, domain: State<Domain>, from: State<From>)
tmpl: State<MailTemplates>, domain: State<Domain>, from: State<From>)
-> result::Result<Template, Custom<String>>
{
use mail::send_confirmation_mail;
@ -435,14 +436,6 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> {
Ok(rocket.manage(StaticDir(static_dir)))
}))
.attach(AdHoc::on_attach("template_dir", |rocket| {
let static_dir = rocket.config()
.get_str("template_dir")
.unwrap()
.to_string();
Ok(rocket.manage(MailTemplateDir(static_dir)))
}))
.attach(AdHoc::on_attach("domain", |rocket| {
let domain = rocket.config()
.get_str("domain")
@ -459,6 +452,25 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> {
Ok(rocket.manage(From(from)))
}))
.attach(AdHoc::on_attach("mail_templates", |rocket| {
let dir: PathBuf = rocket.config()
.get_str("template_dir")
.unwrap()
.to_string()
.into();
let confirm_html = dir.join("confirm-email-html.hbs");
let confirm_txt = dir.join("confirm-email-txt.hbs");
let verify_html = dir.join("verify-email-html.hbs");
let verify_txt = dir.join("verify-email-txt.hbs");
let mut handlebars = Handlebars::new();
handlebars.register_template_file("confirm-html", confirm_html).unwrap();
handlebars.register_template_file("confirm-txt", confirm_txt).unwrap();
handlebars.register_template_file("verify-html", verify_html).unwrap();
handlebars.register_template_file("verify-txt", verify_txt).unwrap();
Ok(rocket.manage(MailTemplates(handlebars)))
}))
.mount("/", routes)
.manage(db)

View File

@ -7,9 +7,11 @@ use rocket::http::{ContentType, Status};
use rocket::response::status::Custom;
use rocket_contrib::templates::Template;
use handlebars::Handlebars;
use types::Email;
use mail::send_verification_mail;
use web::{From, Domain, MailTemplateDir};
use web::{From, Domain, MailTemplates};
use database::{Database, Polymorphic};
use std::io::Read;
@ -31,7 +33,7 @@ mod template {
#[post("/pks/add", data = "<data>")]
// signature requires the request to have a `Content-Type`
pub fn multipart_upload(db: State<Polymorphic>, cont_type: &ContentType,
data: Data, tmpl: State<MailTemplateDir>,
data: Data, tmpl: State<MailTemplates>,
domain: State<Domain>, from: State<From>)
-> Result<Template, Custom<String>>
{
@ -79,7 +81,7 @@ pub fn multipart_upload(db: State<Polymorphic>, cont_type: &ContentType,
}
}
fn process_upload(boundary: &str, data: Data, db: &Polymorphic, tmpl: &str,
fn process_upload(boundary: &str, data: Data, db: &Polymorphic, mail_templates: &Handlebars,
domain: &str, from: &str)
-> Result<Template, Custom<String>>
{
@ -87,13 +89,13 @@ fn process_upload(boundary: &str, data: Data, db: &Polymorphic, tmpl: &str,
// Entries could implement FromData though that would give zero control over
// how the files are saved; Multipart would be a good impl candidate though
match Multipart::with_body(data.open(), boundary).save().temp() {
Full(entries) => process_multipart(entries, db, tmpl, domain, from),
Partial(partial, _) => process_multipart(partial.entries, db, tmpl, domain, from),
Full(entries) => process_multipart(entries, db, mail_templates, domain, from),
Partial(partial, _) => process_multipart(partial.entries, db, mail_templates, domain, from),
Error(err) => Err(Custom(Status::InternalServerError, err.to_string())),
}
}
fn process_multipart(entries: Entries, db: &Polymorphic, tmpl: &str,
fn process_multipart(entries: Entries, db: &Polymorphic, mail_templates: &Handlebars,
domain: &str, from: &str)
-> Result<Template, Custom<String>>
{
@ -103,14 +105,14 @@ fn process_multipart(entries: Entries, db: &Polymorphic, tmpl: &str,
Custom(Status::InternalServerError, err.to_string())
})?;
process_key(reader, db, tmpl, domain, from)
process_key(reader, db, mail_templates, domain, from)
}
Some(_) | None =>
Err(Custom(Status::BadRequest, "Not a PGP public key".into())),
}
}
fn process_key<R>(reader: R, db: &Polymorphic, tmpl: &str, domain: &str, from: &str)
fn process_key<R>(reader: R, db: &Polymorphic, mail_templates: &Handlebars, domain: &str, from: &str)
-> Result<Template, Custom<String>> where R: Read
{
use sequoia_openpgp::TPK;
@ -130,7 +132,7 @@ fn process_key<R>(reader: R, db: &Polymorphic, tmpl: &str, domain: &str, from: &
let &template::Token{ ref userid, ref token } = tok;
Email::from_str(userid).and_then(|email| {
send_verification_mail(&email, token, tmpl, domain, from)
send_verification_mail(&email, token, mail_templates, domain, from)
}).map_err(|err| {
Custom(Status::InternalServerError, format!("{:?}", err))
})?;