refactor renderer in service

main
Brian Picciano 1 year ago
parent 50d450eb2c
commit f3394acaf1
  1. 242
      src/service.rs

@ -9,15 +9,9 @@ use crate::domain;
pub mod http_tpl; pub mod http_tpl;
mod util; mod util;
/*
* POST /domain/config (domain, config, secret, init?) -> token?
* GET /domain/config (domain) -> config
* GET /domains
*/
type Handlebars<'a> = sync::Arc<handlebars::Handlebars<'a>>; type Handlebars<'a> = sync::Arc<handlebars::Handlebars<'a>>;
struct RenderContext<'a, DM> struct Renderer<'a, DM>
where where
DM: domain::manager::Manager, DM: domain::manager::Manager,
{ {
@ -29,80 +23,78 @@ where
query_args: HashMap<String, String>, query_args: HashMap<String, String>,
} }
// TODO make this use an io::Write, rather than warp::Reply
fn render<'a, T>(handlebars: Handlebars<'a>, name: &'a str, value: T) -> Box<dyn warp::Reply>
where
T: Serialize,
{
let rendered = match handlebars.render(name, &value) {
Ok(res) => res,
Err(handlebars::RenderError {
template_name: None,
..
}) => return render_error_page(handlebars, 404, "Static asset not found".to_string()),
Err(err) => return render_error_page(handlebars, 500, format!("template error: {err}")),
};
let content_type = mime_guess::from_path(name)
.first_or_octet_stream()
.to_string();
let reply = warp::reply::html(rendered);
Box::from(warp::reply::with_header(
reply,
"Content-Type",
content_type,
))
}
#[derive(Serialize)] #[derive(Serialize)]
struct BasePresenter<T> { struct BasePresenter<'a, T> {
page_name: String, page_name: &'a str,
query_args: HashMap<String, String>, query_args: &'a HashMap<String, String>,
data: T, data: &'a T,
} }
fn render_error_page<'a>( impl<'a, DM> Renderer<'a, DM>
handlebars: Handlebars<'a>,
status_code: u16,
e: String,
) -> Box<dyn warp::Reply> {
#[derive(Serialize)]
struct Response {
error_msg: String,
}
Box::from(warp::reply::with_status(
render(
handlebars,
"/base.html",
BasePresenter {
page_name: "/error.html".to_string(),
query_args: HashMap::default(),
data: Response { error_msg: e },
},
),
status_code.try_into().unwrap(),
))
}
fn render_page<'a, T, DM>(
render_ctx: RenderContext<'a, DM>,
name: String,
data: T,
) -> Box<dyn warp::Reply>
where where
T: Serialize,
DM: domain::manager::Manager, DM: domain::manager::Manager,
{ {
let presenter = BasePresenter { // TODO make this use an io::Write, rather than warp::Reply
page_name: name, fn render<T>(&self, name: &'_ str, value: &'_ T) -> Box<dyn warp::Reply>
query_args: render_ctx.query_args, where
data, T: Serialize,
}; {
let rendered = match self.handlebars.render(name, value) {
Ok(res) => res,
Err(handlebars::RenderError {
template_name: None,
..
}) => return self.render_error_page(404, "Static asset not found"),
Err(err) => {
return self.render_error_page(500, format!("template error: {err}").as_str())
}
};
let content_type = mime_guess::from_path(name)
.first_or_octet_stream()
.to_string();
let reply = warp::reply::html(rendered);
Box::from(warp::reply::with_header(
reply,
"Content-Type",
content_type,
))
}
fn render_error_page(&self, status_code: u16, e: &'_ str) -> Box<dyn warp::Reply> {
#[derive(Serialize)]
struct Response<'a> {
error_msg: &'a str,
}
Box::from(warp::reply::with_status(
self.render(
"/base.html",
&BasePresenter {
page_name: "/error.html",
query_args: &HashMap::default(),
data: &Response { error_msg: e },
},
),
status_code.try_into().unwrap(),
))
}
render(render_ctx.handlebars, "/base.html", presenter) fn render_page<T>(&self, name: &'_ str, data: &'_ T) -> Box<dyn warp::Reply>
where
T: Serialize,
{
self.render(
"/base.html",
&BasePresenter {
page_name: name,
query_args: &self.query_args,
data,
},
)
}
} }
pub fn new<DM>( pub fn new<DM>(
@ -121,9 +113,9 @@ where
let passphrase = sync::Arc::new(passphrase); let passphrase = sync::Arc::new(passphrase);
let hbs = sync::Arc::new(self::http_tpl::get()?); let hbs = sync::Arc::new(self::http_tpl::get()?);
let with_render_ctx = warp::any() let with_renderer = warp::any()
.and(warp::query::<HashMap<String, String>>()) .and(warp::query::<HashMap<String, String>>())
.map(move |query_args: HashMap<String, String>| RenderContext { .map(move |query_args: HashMap<String, String>| Renderer {
domain_manager: manager.clone(), domain_manager: manager.clone(),
target_cname: target_cname.clone(), target_cname: target_cname.clone(),
passphrase: passphrase.clone(), passphrase: passphrase.clone(),
@ -132,21 +124,17 @@ where
}); });
let static_dir = warp::get() let static_dir = warp::get()
.and(with_render_ctx.clone()) .and(with_renderer.clone())
.and(warp::path("static")) .and(warp::path("static"))
.and(warp::path::full()) .and(warp::path::full())
.map( .map(|renderer: Renderer<'_, DM>, full: warp::path::FullPath| {
|render_ctx: RenderContext<'_, DM>, full: warp::path::FullPath| { renderer.render(full.as_str(), &())
render(render_ctx.handlebars, full.as_str(), ()) });
},
);
let index = warp::get() let index = warp::get()
.and(with_render_ctx.clone()) .and(with_renderer.clone())
.and(warp::path::end()) .and(warp::path::end())
.map(|render_ctx: RenderContext<'_, DM>| { .map(|renderer: Renderer<'_, DM>| renderer.render_page("/index.html", &()));
render_page(render_ctx, String::from("/index.html"), ())
});
#[derive(Deserialize)] #[derive(Deserialize)]
struct DomainGetNewRequest { struct DomainGetNewRequest {
@ -160,46 +148,40 @@ where
} }
let domain_get = warp::get() let domain_get = warp::get()
.and(with_render_ctx.clone()) .and(with_renderer.clone())
.and(warp::path!("domain.html")) .and(warp::path!("domain.html"))
.and(warp::query::<DomainGetNewRequest>()) .and(warp::query::<DomainGetNewRequest>())
.and(warp::query::<util::ConfigFromURL>()) .and(warp::query::<util::ConfigFromURL>())
.map( .map(
|render_ctx: RenderContext<'_, DM>, |renderer: Renderer<'_, DM>,
req: DomainGetNewRequest, req: DomainGetNewRequest,
domain_config: util::ConfigFromURL| { domain_config: util::ConfigFromURL| {
match render_ctx.domain_manager.get_config(&req.domain) { match renderer.domain_manager.get_config(&req.domain) {
Ok(_config) => render_error_page( Ok(_config) => renderer.render_error_page(500, "TODO not yet implemented"),
render_ctx.handlebars,
500,
"TODO not yet implemented".to_string(),
),
Err(domain::manager::GetConfigError::NotFound) => { Err(domain::manager::GetConfigError::NotFound) => {
let domain_config = match domain_config.try_into() { let domain_config = match domain_config.try_into() {
Ok(domain_config) => domain_config, Ok(domain_config) => domain_config,
Err(e) => { Err(e) => {
return render_error_page( return renderer.render_error_page(
render_ctx.handlebars,
400, 400,
format!("parsing domain configuration: {}", e), format!("parsing domain configuration: {}", e).as_str(),
) )
} }
}; };
render_page( renderer.render_page(
render_ctx, "/domain_get_new.html",
String::from("/domain_get_new.html"), &DomainGetNewResponse {
DomainGetNewResponse {
domain: req.domain, domain: req.domain,
config: domain_config, config: domain_config,
}, },
) )
} }
Err(domain::manager::GetConfigError::Unexpected(e)) => render_error_page( Err(domain::manager::GetConfigError::Unexpected(e)) => renderer
render_ctx.handlebars, .render_error_page(
500, 500,
format!("retrieving configuration: {}", e), format!("retrieving configuration: {}", e).as_str(),
), ),
} }
}, },
); );
@ -212,20 +194,16 @@ where
} }
let domain_post = warp::post() let domain_post = warp::post()
.and(with_render_ctx.clone()) .and(with_renderer.clone())
.and(warp::path!("domain.html")) .and(warp::path!("domain.html"))
.and(warp::query::<DomainPostRequest>()) .and(warp::query::<DomainPostRequest>())
.and(warp::query::<util::ConfigFromURL>()) .and(warp::query::<util::ConfigFromURL>())
.map( .map(
|render_ctx: RenderContext<'_, DM>, |renderer: Renderer<'_, DM>,
req: DomainPostRequest, req: DomainPostRequest,
domain_config: util::ConfigFromURL| { domain_config: util::ConfigFromURL| {
if req.passphrase != render_ctx.passphrase.as_str() { if req.passphrase != renderer.passphrase.as_str() {
return render_error_page( return renderer.render_error_page(401, "Incorrect passphrase");
render_ctx.handlebars,
401,
"Incorrect passphrase".to_string(),
);
} }
//if req.init { //if req.init {
@ -240,38 +218,29 @@ where
let config: domain::config::Config = match domain_config.try_into() { let config: domain::config::Config = match domain_config.try_into() {
Ok(Some(config)) => config, Ok(Some(config)) => config,
Ok(None) => { Ok(None) => {
return render_error_page( return renderer.render_error_page(400, "domain config is required")
render_ctx.handlebars,
400,
"domain config is required".to_string(),
)
} }
Err(e) => { Err(e) => {
return render_error_page( return renderer
render_ctx.handlebars, .render_error_page(400, format!("invalid domain config: {e}").as_str())
400,
format!("invalid domain config: {e}"),
)
} }
}; };
let config_hash = match config.hash() { let config_hash = match config.hash() {
Ok(hash) => hash, Ok(hash) => hash,
Err(e) => { Err(e) => {
return render_error_page( return renderer.render_error_page(
render_ctx.handlebars,
500, 500,
format!("failed to hash domain config: {e}"), format!("failed to hash domain config: {e}").as_str(),
) )
} }
}; };
let target_cname = (*render_ctx.target_cname).clone(); let target_cname = (*renderer.target_cname).clone();
return render_page( return renderer.render_page(
render_ctx, "/domain_post_init.html",
String::from("/domain_post_init.html"), &Response {
Response {
domain: req.domain, domain: req.domain,
config: config, config: config,
target_cname: target_cname, target_cname: target_cname,
@ -282,12 +251,9 @@ where
}, },
); );
let not_found = let not_found = warp::any()
warp::any() .and(with_renderer.clone())
.and(with_render_ctx.clone()) .map(|renderer: Renderer<'_, DM>| renderer.render_error_page(404, "Page not found"));
.map(|render_ctx: RenderContext<'_, DM>| {
render_error_page(render_ctx.handlebars, 404, "Page not found".to_string())
});
Ok(static_dir Ok(static_dir
.or(index) .or(index)

Loading…
Cancel
Save