domani/src/service.rs

208 lines
6.0 KiB
Rust
Raw Normal View History

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::sync;
use warp::Filter;
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>
where
DM: domain::manager::Manager,
{
domain_manager: sync::Arc<DM>,
target_cname: sync::Arc<domain::Name>,
passphrase: sync::Arc<String>,
handlebars: Handlebars<'a>,
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) -> impl warp::Reply
where
T: Serialize,
{
// TODO deal with 404
let render = handlebars
.render(name, &value)
.unwrap_or_else(|err| err.to_string());
let content_type = mime_guess::from_path(name)
.first_or_octet_stream()
.to_string();
let reply = warp::reply::html(render);
warp::reply::with_header(reply, "Content-Type", content_type)
}
fn render_page<'a, T, DM>(
render_ctx: RenderContext<'a, DM>,
name: String,
data: T,
) -> impl warp::Reply
where
T: Serialize,
DM: domain::manager::Manager,
{
#[derive(Serialize)]
struct Presenter<T> {
page_name: String,
query_args: HashMap<String, String>,
data: T,
}
let presenter = Presenter {
page_name: name,
query_args: render_ctx.query_args,
data,
};
render(render_ctx.handlebars, "/base.html", presenter)
}
pub fn new<DM>(
manager: DM,
target_cname: domain::Name,
passphrase: String,
) -> Result<
impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone + 'static,
Box<dyn Error>,
>
where
DM: domain::manager::Manager + 'static,
{
let manager = sync::Arc::new(manager);
let target_cname = sync::Arc::new(target_cname);
let passphrase = sync::Arc::new(passphrase);
let hbs = sync::Arc::new(self::http_tpl::get()?);
let with_render_ctx = warp::any()
.and(warp::query::<HashMap<String, String>>())
.map(move |query_args: HashMap<String, String>| RenderContext {
domain_manager: manager.clone(),
target_cname: target_cname.clone(),
passphrase: passphrase.clone(),
handlebars: hbs.clone(),
query_args,
});
let static_dir = warp::get()
.and(with_render_ctx.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(), ())
},
);
let index = warp::get()
.and(with_render_ctx.clone())
.and(warp::path::end())
.map(|render_ctx: RenderContext<'_, DM>| {
render_page(render_ctx, String::from("/index.html"), ())
});
#[derive(Deserialize)]
struct DomainGetNewRequest {
domain: domain::Name,
}
#[derive(Serialize)]
struct DomainGetNewResponse {
domain: domain::Name,
config: Option<domain::config::Config>,
}
let domain_get = warp::get()
.and(with_render_ctx.clone())
.and(warp::path!("domain.html"))
.and(warp::query::<DomainGetNewRequest>())
.and(warp::query::<util::ConfigFromURL>())
.map(
|render_ctx: RenderContext<'_, DM>,
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"),
},
),
Err(domain::manager::GetConfigError::Unexpected(e)) => {
panic!("{}", e) // TODO
}
}
},
);
#[derive(Deserialize)]
struct DomainPostRequest {
_init: bool,
domain: domain::Name,
passphrase: String,
}
let domain_post = warp::post()
.and(with_render_ctx.clone())
.and(warp::path!("domain.html"))
.and(warp::query::<DomainPostRequest>())
.and(warp::query::<util::ConfigFromURL>())
.map(
|render_ctx: RenderContext<'_, DM>,
req: DomainPostRequest,
domain_config: util::ConfigFromURL| {
if req.passphrase != render_ctx.passphrase.as_str() {
panic!("TODO")
}
//if req.init {
#[derive(Serialize)]
struct Response {
domain: domain::Name,
config: domain::config::Config,
target_cname: domain::Name,
challenge_token: String,
}
let config: Option<domain::config::Config> =
domain_config.try_into().expect("TODO");
let config = config.expect("TODO");
let config_hash = config.hash().expect("TODO");
let target_cname = (*render_ctx.target_cname).clone();
return render_page(
render_ctx,
String::from("/domain_post_init.html"),
Response {
domain: req.domain,
config: config,
target_cname: target_cname,
challenge_token: config_hash,
},
);
//}
},
);
Ok(static_dir.or(index).or(domain_get).or(domain_post))
}