|
|
|
@ -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<handlebars::Handlebars<'a>>; |
|
|
|
|
|
|
|
|
|
struct RenderContext<'a, DM> |
|
|
|
|
struct Renderer<'a, DM> |
|
|
|
|
where |
|
|
|
|
DM: domain::manager::Manager, |
|
|
|
|
{ |
|
|
|
@ -29,18 +23,31 @@ where |
|
|
|
|
query_args: HashMap<String, String>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Serialize)] |
|
|
|
|
struct BasePresenter<'a, T> { |
|
|
|
|
page_name: &'a str, |
|
|
|
|
query_args: &'a HashMap<String, String>, |
|
|
|
|
data: &'a T, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<'a, DM> Renderer<'a, DM> |
|
|
|
|
where |
|
|
|
|
DM: domain::manager::Manager, |
|
|
|
|
{ |
|
|
|
|
// 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> |
|
|
|
|
fn render<T>(&self, name: &'_ str, value: &'_ T) -> Box<dyn warp::Reply> |
|
|
|
|
where |
|
|
|
|
T: Serialize, |
|
|
|
|
{ |
|
|
|
|
let rendered = match handlebars.render(name, &value) { |
|
|
|
|
let rendered = match self.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}")), |
|
|
|
|
}) => 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) |
|
|
|
@ -56,53 +63,38 @@ where |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn render_error_page(&self, status_code: u16, e: &'_ str) -> Box<dyn warp::Reply> { |
|
|
|
|
#[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, |
|
|
|
|
struct Response<'a> { |
|
|
|
|
error_msg: &'a str, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Box::from(warp::reply::with_status( |
|
|
|
|
render( |
|
|
|
|
handlebars, |
|
|
|
|
self.render( |
|
|
|
|
"/base.html", |
|
|
|
|
BasePresenter { |
|
|
|
|
page_name: "/error.html".to_string(), |
|
|
|
|
query_args: HashMap::default(), |
|
|
|
|
data: Response { error_msg: e }, |
|
|
|
|
&BasePresenter { |
|
|
|
|
page_name: "/error.html", |
|
|
|
|
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> |
|
|
|
|
fn render_page<T>(&self, name: &'_ str, data: &'_ T) -> Box<dyn warp::Reply> |
|
|
|
|
where |
|
|
|
|
T: Serialize, |
|
|
|
|
DM: domain::manager::Manager, |
|
|
|
|
{ |
|
|
|
|
let presenter = BasePresenter { |
|
|
|
|
self.render( |
|
|
|
|
"/base.html", |
|
|
|
|
&BasePresenter { |
|
|
|
|
page_name: name, |
|
|
|
|
query_args: render_ctx.query_args, |
|
|
|
|
query_args: &self.query_args, |
|
|
|
|
data, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
render(render_ctx.handlebars, "/base.html", presenter) |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn new<DM>( |
|
|
|
@ -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::<HashMap<String, String>>()) |
|
|
|
|
.map(move |query_args: HashMap<String, String>| RenderContext { |
|
|
|
|
.map(move |query_args: HashMap<String, String>| 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,45 +148,39 @@ where |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let domain_get = warp::get() |
|
|
|
|
.and(with_render_ctx.clone()) |
|
|
|
|
.and(with_renderer.clone()) |
|
|
|
|
.and(warp::path!("domain.html")) |
|
|
|
|
.and(warp::query::<DomainGetNewRequest>()) |
|
|
|
|
.and(warp::query::<util::ConfigFromURL>()) |
|
|
|
|
.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, |
|
|
|
|
Err(domain::manager::GetConfigError::Unexpected(e)) => renderer |
|
|
|
|
.render_error_page( |
|
|
|
|
500, |
|
|
|
|
format!("retrieving configuration: {}", e), |
|
|
|
|
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::<DomainPostRequest>()) |
|
|
|
|
.and(warp::query::<util::ConfigFromURL>()) |
|
|
|
|
.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) |
|
|
|
|