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