diff --git a/src/service.rs b/src/service.rs index d9e97e9..d9c7c05 100644 --- a/src/service.rs +++ b/src/service.rs @@ -30,41 +30,73 @@ where } // TODO make this use an io::Write, rather than warp::Reply -fn render<'a, T>(handlebars: Handlebars<'a>, name: &'a str, value: T) -> impl warp::Reply +fn render<'a, T>(handlebars: Handlebars<'a>, name: &'a str, value: T) -> Box where T: Serialize, { - // TODO deal with 404 - let render = handlebars - .render(name, &value) - .unwrap_or_else(|err| err.to_string()); + 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(render); + let reply = warp::reply::html(rendered); - warp::reply::with_header(reply, "Content-Type", content_type) + Box::from(warp::reply::with_header( + reply, + "Content-Type", + content_type, + )) +} + +#[derive(Serialize)] +struct BasePresenter { + page_name: String, + query_args: HashMap, + data: 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, -) -> impl warp::Reply +) -> Box where T: Serialize, DM: domain::manager::Manager, { - #[derive(Serialize)] - struct Presenter { - page_name: String, - query_args: HashMap, - data: T, - } - - let presenter = Presenter { + let presenter = BasePresenter { page_name: name, query_args: render_ctx.query_args, data, @@ -137,18 +169,37 @@ where req: DomainGetNewRequest, domain_config: util::ConfigFromURL| { match render_ctx.domain_manager.get_config(&req.domain) { - Ok(_config) => panic!("TODO"), - Err(domain::manager::GetConfigError::NotFound) => render_page( - render_ctx, - String::from("/domain_get_new.html"), - DomainGetNewResponse { - domain: req.domain, - config: domain_config.try_into().expect("TODO"), - }, + Ok(_config) => render_error_page( + render_ctx.handlebars, + 500, + "TODO not yet implemented".to_string(), ), - Err(domain::manager::GetConfigError::Unexpected(e)) => { - panic!("{}", e) // TODO + 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, + 400, + format!("parsing domain configuration: {}", e), + ) + } + }; + + render_page( + render_ctx, + String::from("/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), + ), } }, ); @@ -170,7 +221,11 @@ where req: DomainPostRequest, domain_config: util::ConfigFromURL| { if req.passphrase != render_ctx.passphrase.as_str() { - panic!("TODO") + return render_error_page( + render_ctx.handlebars, + 401, + "Incorrect passphrase".to_string(), + ); } //if req.init { @@ -182,11 +237,35 @@ where challenge_token: String, } - let config: Option = - domain_config.try_into().expect("TODO"); - let config = config.expect("TODO"); + 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(), + ) + } + Err(e) => { + return render_error_page( + render_ctx.handlebars, + 400, + format!("invalid domain config: {e}"), + ) + } + }; + + let config_hash = match config.hash() { + Ok(hash) => hash, + Err(e) => { + return render_error_page( + render_ctx.handlebars, + 500, + format!("failed to hash domain config: {e}"), + ) + } + }; - let config_hash = config.hash().expect("TODO"); let target_cname = (*render_ctx.target_cname).clone(); return render_page( @@ -203,5 +282,16 @@ where }, ); - Ok(static_dir.or(index).or(domain_get).or(domain_post)) + 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()) + }); + + Ok(static_dir + .or(index) + .or(domain_get) + .or(domain_post) + .or(not_found)) } diff --git a/src/service/http_tpl/error.html b/src/service/http_tpl/error.html new file mode 100644 index 0000000..3c2f2d0 --- /dev/null +++ b/src/service/http_tpl/error.html @@ -0,0 +1 @@ +

{{ data.error_msg }}