implement error pages

This commit is contained in:
Brian Picciano 2023-05-13 15:22:47 +02:00
parent 130581d61e
commit 50d450eb2c
2 changed files with 123 additions and 32 deletions

View File

@ -30,41 +30,73 @@ where
} }
// TODO make this use an io::Write, rather than warp::Reply // 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<dyn warp::Reply>
where where
T: Serialize, T: Serialize,
{ {
// TODO deal with 404 let rendered = match handlebars.render(name, &value) {
let render = handlebars Ok(res) => res,
.render(name, &value) Err(handlebars::RenderError {
.unwrap_or_else(|err| err.to_string()); 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) let content_type = mime_guess::from_path(name)
.first_or_octet_stream() .first_or_octet_stream()
.to_string(); .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<T> {
page_name: String,
query_args: HashMap<String, String>,
data: T,
}
fn render_error_page<'a>(
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>( fn render_page<'a, T, DM>(
render_ctx: RenderContext<'a, DM>, render_ctx: RenderContext<'a, DM>,
name: String, name: String,
data: T, data: T,
) -> impl warp::Reply ) -> Box<dyn warp::Reply>
where where
T: Serialize, T: Serialize,
DM: domain::manager::Manager, DM: domain::manager::Manager,
{ {
#[derive(Serialize)] let presenter = BasePresenter {
struct Presenter<T> {
page_name: String,
query_args: HashMap<String, String>,
data: T,
}
let presenter = Presenter {
page_name: name, page_name: name,
query_args: render_ctx.query_args, query_args: render_ctx.query_args,
data, data,
@ -137,18 +169,37 @@ where
req: DomainGetNewRequest, req: DomainGetNewRequest,
domain_config: util::ConfigFromURL| { domain_config: util::ConfigFromURL| {
match render_ctx.domain_manager.get_config(&req.domain) { match render_ctx.domain_manager.get_config(&req.domain) {
Ok(_config) => panic!("TODO"), Ok(_config) => render_error_page(
Err(domain::manager::GetConfigError::NotFound) => render_page( render_ctx.handlebars,
render_ctx, 500,
String::from("/domain_get_new.html"), "TODO not yet implemented".to_string(),
DomainGetNewResponse {
domain: req.domain,
config: domain_config.try_into().expect("TODO"),
},
), ),
Err(domain::manager::GetConfigError::Unexpected(e)) => { Err(domain::manager::GetConfigError::NotFound) => {
panic!("{}", e) // TODO 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, req: DomainPostRequest,
domain_config: util::ConfigFromURL| { domain_config: util::ConfigFromURL| {
if req.passphrase != render_ctx.passphrase.as_str() { 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 { //if req.init {
@ -182,11 +237,35 @@ where
challenge_token: String, challenge_token: String,
} }
let config: Option<domain::config::Config> = let config: domain::config::Config = match domain_config.try_into() {
domain_config.try_into().expect("TODO"); Ok(Some(config)) => config,
let config = config.expect("TODO"); 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(); let target_cname = (*render_ctx.target_cname).clone();
return render_page( 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))
} }

View File

@ -0,0 +1 @@
<p style="color: red">{{ data.error_msg }}</p>