From 1f9ae0038fbff7830c1d2f3fa54f5aaa21455f97 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sun, 18 Jun 2023 14:53:25 +0200 Subject: [PATCH] restructure service module --- src/main.rs | 14 +- src/service.rs | 395 +---------------- src/service/http.rs | 398 ++++++++++++++++++ src/service/{http_tpl.rs => http/tpl.rs} | 2 +- src/service/{http_tpl => http/tpl}/base.html | 0 .../{http_tpl => http/tpl}/domain.html | 0 .../{http_tpl => http/tpl}/domain_init.html | 0 .../{http_tpl => http/tpl}/domain_sync.html | 0 .../{http_tpl => http/tpl}/domains.html | 0 src/service/{http_tpl => http/tpl}/error.html | 0 src/service/{http_tpl => http/tpl}/index.html | 0 .../{http_tpl => http/tpl}/static/bamboo.css | 0 12 files changed, 407 insertions(+), 402 deletions(-) create mode 100644 src/service/http.rs rename src/service/{http_tpl.rs => http/tpl.rs} (88%) rename src/service/{http_tpl => http/tpl}/base.html (100%) rename src/service/{http_tpl => http/tpl}/domain.html (100%) rename src/service/{http_tpl => http/tpl}/domain_init.html (100%) rename src/service/{http_tpl => http/tpl}/domain_sync.html (100%) rename src/service/{http_tpl => http/tpl}/domains.html (100%) rename src/service/{http_tpl => http/tpl}/error.html (100%) rename src/service/{http_tpl => http/tpl}/index.html (100%) rename src/service/{http_tpl => http/tpl}/static/bamboo.css (100%) diff --git a/src/main.rs b/src/main.rs index 4a4a995..8d724a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,26 +156,26 @@ async fn main() { let domain_manager = sync::Arc::new(domain_manager); - let service = domiply::service::new( + let http_service = domiply::service::http::new( domain_manager.clone(), config.domain_checker_target_a, config.passphrase, config.http_domain.clone(), ); - let service = sync::Arc::new(service); + let http_service = sync::Arc::new(http_service); wait_group.push({ let http_domain = config.http_domain.clone(); let canceller = canceller.clone(); - let service = service.clone(); + let service = http_service.clone(); let make_service = hyper::service::make_service_fn(move |_| { let service = service.clone(); // Create a `Service` for responding to the request. let service = hyper::service::service_fn(move |req| { - domiply::service::handle_request(service.clone(), req) + domiply::service::http::handle_request(service.clone(), req) }); // Return the service to hyper. @@ -254,14 +254,14 @@ async fn main() { wait_group.push({ let http_domain = config.http_domain.clone(); let canceller = canceller.clone(); - let service = service.clone(); + let service = http_service.clone(); let make_service = hyper::service::make_service_fn(move |_| { let service = service.clone(); // Create a `Service` for responding to the request. let service = hyper::service::service_fn(move |req| { - domiply::service::handle_request(service.clone(), req) + domiply::service::http::handle_request(service.clone(), req) }); // Return the service to hyper. @@ -321,7 +321,7 @@ async fn main() { // TODO this is currently required so that we can be sure domain_manager is no longer used by // anything else, and the into_inner below works. It would be great if service could accept a // ref to domain_manager instead, and then maybe this wouldn't be needed? - drop(service); + drop(http_service); sync::Arc::into_inner(domain_manager) .unwrap() diff --git a/src/service.rs b/src/service.rs index 820119b..9a4a545 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,395 +1,2 @@ -use hyper::{Body, Method, Request, Response}; -use serde::{Deserialize, Serialize}; - -use std::convert::Infallible; -use std::future::Future; -use std::net; -use std::str::FromStr; -use std::sync; - -use crate::{domain, origin}; - -pub mod http_tpl; +pub mod http; mod util; - -type SvcResponse = Result, String>; - -#[derive(Clone)] -pub struct Service<'svc> { - domain_manager: sync::Arc, - target_a: net::Ipv4Addr, - passphrase: String, - http_domain: domain::Name, - handlebars: handlebars::Handlebars<'svc>, -} - -pub fn new<'svc>( - domain_manager: sync::Arc, - target_a: net::Ipv4Addr, - passphrase: String, - http_domain: domain::Name, -) -> Service<'svc> { - Service { - domain_manager, - target_a, - passphrase, - http_domain, - handlebars: self::http_tpl::get(), - } -} - -#[derive(Serialize)] -struct BasePresenter<'a, T> { - page_name: &'a str, - data: T, -} - -#[derive(Deserialize)] -struct DomainGetArgs { - domain: domain::Name, -} - -#[derive(Deserialize)] -struct DomainInitArgs { - domain: domain::Name, -} - -#[derive(Deserialize)] -struct DomainSyncArgs { - domain: domain::Name, - passphrase: String, -} - -impl<'svc> Service<'svc> { - fn serve_string(&self, status_code: u16, path: &'_ str, body: Vec) -> SvcResponse { - let content_type = mime_guess::from_path(path) - .first_or_octet_stream() - .to_string(); - - match Response::builder() - .status(status_code) - .header("Content-Type", content_type) - .body(body.into()) - { - Ok(res) => Ok(res), - Err(err) => Err(format!("failed to build {}: {}", path, err)), - } - } - - //// TODO make this use an io::Write, rather than SvcResponse - fn render(&self, status_code: u16, name: &'_ str, value: T) -> SvcResponse - 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()) - } - }; - - self.serve_string(status_code, name, rendered.into()) - } - - fn render_error_page(&'svc self, status_code: u16, e: &'_ str) -> SvcResponse { - #[derive(Serialize)] - struct Response<'a> { - error_msg: &'a str, - } - - self.render( - status_code, - "/base.html", - BasePresenter { - page_name: "/error.html", - data: &Response { error_msg: e }, - }, - ) - } - - fn render_page(&self, name: &'_ str, data: T) -> SvcResponse - where - T: Serialize, - { - self.render( - 200, - "/base.html", - BasePresenter { - page_name: name, - data, - }, - ) - } - - fn serve_origin(&self, domain: domain::Name, path: &'_ str) -> SvcResponse { - let mut path_owned; - - let path = match path.ends_with('/') { - true => { - path_owned = String::from(path); - path_owned.push_str("index.html"); - path_owned.as_str() - } - false => path, - }; - - let origin = match self.domain_manager.get_origin(&domain) { - Ok(o) => o, - Err(domain::manager::GetOriginError::NotFound) => { - return self.render_error_page(404, "Domain not found") - } - Err(domain::manager::GetOriginError::Unexpected(e)) => { - return self.render_error_page(500, format!("failed to fetch origin: {e}").as_str()) - } - }; - - let mut buf = Vec::::new(); - match origin.read_file_into(path, &mut buf) { - Ok(_) => self.serve_string(200, path, buf), - Err(origin::ReadFileIntoError::FileNotFound) => { - self.render_error_page(404, "File not found") - } - Err(origin::ReadFileIntoError::Unexpected(e)) => { - self.render_error_page(500, format!("failed to fetch file {path}: {e}").as_str()) - } - } - } - - async fn with_query_req<'a, F, In, Out>(&self, req: &'a Request, f: F) -> SvcResponse - where - In: Deserialize<'a>, - F: FnOnce(In) -> Out, - Out: Future, - { - let query = req.uri().query().unwrap_or(""); - match serde_urlencoded::from_str::(query) { - Ok(args) => f(args).await, - Err(err) => Err(format!("failed to parse query args: {}", err)), - } - } - - fn domain_get(&self, args: DomainGetArgs) -> SvcResponse { - #[derive(Serialize)] - struct Response { - domain: domain::Name, - config: Option, - } - - let config = match self.domain_manager.get_config(&args.domain) { - Ok(config) => Some(config), - Err(domain::manager::GetConfigError::NotFound) => None, - Err(domain::manager::GetConfigError::Unexpected(e)) => { - return self - .render_error_page(500, format!("retrieving configuration: {}", e).as_str()); - } - }; - - self.render_page( - "/domain.html", - Response { - domain: args.domain, - config, - }, - ) - } - - fn domain_init(&self, args: DomainInitArgs, domain_config: util::FlatConfig) -> SvcResponse { - #[derive(Serialize)] - struct Response { - domain: domain::Name, - flat_config: util::FlatConfig, - target_a: net::Ipv4Addr, - challenge_token: String, - } - - let config: domain::config::Config = match domain_config.try_into() { - Ok(Some(config)) => config, - Ok(None) => return self.render_error_page(400, "domain config is required"), - Err(e) => { - return self.render_error_page(400, format!("invalid domain config: {e}").as_str()) - } - }; - - let config_hash = match config.hash() { - Ok(hash) => hash, - Err(e) => { - return self - .render_error_page(500, format!("failed to hash domain config: {e}").as_str()) - } - }; - - self.render_page( - "/domain_init.html", - Response { - domain: args.domain, - flat_config: config.into(), - target_a: self.target_a, - challenge_token: config_hash, - }, - ) - } - - async fn domain_sync( - &self, - args: DomainSyncArgs, - domain_config: util::FlatConfig, - ) -> SvcResponse { - if args.passphrase != self.passphrase.as_str() { - return self.render_error_page(401, "Incorrect passphrase"); - } - - let config: domain::config::Config = match domain_config.try_into() { - Ok(Some(config)) => config, - Ok(None) => return self.render_error_page(400, "domain config is required"), - Err(e) => { - return self.render_error_page(400, format!("invalid domain config: {e}").as_str()) - } - }; - - let sync_result = self - .domain_manager - .sync_with_config(args.domain.clone(), config) - .await; - - #[derive(Serialize)] - struct Response { - domain: domain::Name, - error_msg: Option, - } - - let error_msg = match sync_result { - Ok(_) => None, - Err(domain::manager::SyncWithConfigError::InvalidURL) => Some("Fetching the git repository failed, please double check that you input the correct URL.".to_string()), - Err(domain::manager::SyncWithConfigError::InvalidBranchName) => Some("The git repository does not have a branch of the given name, please double check that you input the correct name.".to_string()), - Err(domain::manager::SyncWithConfigError::AlreadyInProgress) => Some("The configuration of your domain is still in progress, please refresh in a few minutes.".to_string()), - Err(domain::manager::SyncWithConfigError::TargetANotSet) => Some("The A record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()), - Err(domain::manager::SyncWithConfigError::ChallengeTokenNotSet) => Some("The TXT record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()), - Err(domain::manager::SyncWithConfigError::Unexpected(e)) => Some(format!("An unexpected error occurred: {e}")), - }; - - let response = Response { - domain: args.domain, - error_msg, - }; - - self.render_page("/domain_sync.html", response) - } - - pub fn domains(&self) -> SvcResponse { - #[derive(Serialize)] - struct Response { - domains: Vec, - } - - let domains = match self.domain_manager.all_domains() { - Ok(domains) => domains, - Err(e) => { - return self.render_error_page(500, format!("failed get all domains: {e}").as_str()) - } - }; - - let mut domains: Vec = domains - .into_iter() - .map(|domain| domain.as_str().to_string()) - .collect(); - - domains.sort(); - - self.render_page("/domains.html", Response { domains }) - } -} - -pub async fn handle_request( - svc: sync::Arc>, - req: Request, -) -> Result, Infallible> { - match handle_request_inner(svc, req).await { - Ok(res) => Ok(res), - Err(err) => panic!("unexpected error {err}"), - } -} - -fn strip_port(host: &str) -> &str { - match host.rfind(':') { - None => host, - Some(i) => &host[..i], - } -} - -pub async fn handle_request_inner(svc: sync::Arc>, req: Request) -> SvcResponse { - let maybe_host = match ( - req.headers() - .get("Host") - .and_then(|v| v.to_str().ok()) - .map(strip_port), - req.uri().host().map(strip_port), - ) { - (Some(h), _) if h != svc.http_domain.as_str() => Some(h), - (_, Some(h)) if h != svc.http_domain.as_str() => Some(h), - _ => None, - } - .and_then(|h| domain::Name::from_str(h).ok()); - - let method = req.method(); - let path = req.uri().path(); - - // Serving acme challenges always takes priority. We serve them from the same store no matter - // the domain, presumably they are cryptographically random enough that it doesn't matter. - if method == Method::GET && path.starts_with("/.well-known/acme-challenge/") { - let token = path.trim_start_matches("/.well-known/acme-challenge/"); - - if let Ok(key) = svc.domain_manager.get_acme_http01_challenge_key(token) { - let body: hyper::Body = key.into(); - return match Response::builder().status(200).body(body) { - Ok(res) => Ok(res), - Err(err) => Err(format!( - "failed to write acme http-01 challenge key: {}", - err - )), - }; - } - } - - // If a managed domain was given then serve that from its origin - if let Some(domain) = maybe_host { - return svc.serve_origin(domain, req.uri().path()); - } - - // Serve main domiply site - - if method == Method::GET && path.starts_with("/static/") { - return svc.render(200, path, ()); - } - - match (method, path) { - (&Method::GET, "/") | (&Method::GET, "/index.html") => svc.render_page("/index.html", ()), - (&Method::GET, "/domain.html") => { - svc.with_query_req(&req, |args: DomainGetArgs| async { svc.domain_get(args) }) - .await - } - (&Method::GET, "/domain_init.html") => { - svc.with_query_req(&req, |args: DomainInitArgs| async { - svc.with_query_req(&req, |config: util::FlatConfig| async { - svc.domain_init(args, config) - }) - .await - }) - .await - } - (&Method::GET, "/domain_sync.html") => { - svc.with_query_req(&req, |args: DomainSyncArgs| async { - svc.with_query_req(&req, |config: util::FlatConfig| async { - svc.domain_sync(args, config).await - }) - .await - }) - .await - } - (&Method::GET, "/domains.html") => svc.domains(), - _ => svc.render_error_page(404, "Page not found!"), - } -} diff --git a/src/service/http.rs b/src/service/http.rs new file mode 100644 index 0000000..a7bbab0 --- /dev/null +++ b/src/service/http.rs @@ -0,0 +1,398 @@ +mod tpl; + +use hyper::{Body, Method, Request, Response}; +use serde::{Deserialize, Serialize}; + +use std::convert::Infallible; +use std::future::Future; +use std::net; +use std::str::FromStr; +use std::sync; + +use crate::{domain, origin, service}; + +type SvcResponse = Result, String>; + +#[derive(Clone)] +pub struct Service<'svc> { + domain_manager: sync::Arc, + target_a: net::Ipv4Addr, + passphrase: String, + http_domain: domain::Name, + handlebars: handlebars::Handlebars<'svc>, +} + +pub fn new<'svc>( + domain_manager: sync::Arc, + target_a: net::Ipv4Addr, + passphrase: String, + http_domain: domain::Name, +) -> Service<'svc> { + Service { + domain_manager, + target_a, + passphrase, + http_domain, + handlebars: tpl::get(), + } +} + +#[derive(Serialize)] +struct BasePresenter<'a, T> { + page_name: &'a str, + data: T, +} + +#[derive(Deserialize)] +struct DomainGetArgs { + domain: domain::Name, +} + +#[derive(Deserialize)] +struct DomainInitArgs { + domain: domain::Name, +} + +#[derive(Deserialize)] +struct DomainSyncArgs { + domain: domain::Name, + passphrase: String, +} + +impl<'svc> Service<'svc> { + fn serve_string(&self, status_code: u16, path: &'_ str, body: Vec) -> SvcResponse { + let content_type = mime_guess::from_path(path) + .first_or_octet_stream() + .to_string(); + + match Response::builder() + .status(status_code) + .header("Content-Type", content_type) + .body(body.into()) + { + Ok(res) => Ok(res), + Err(err) => Err(format!("failed to build {}: {}", path, err)), + } + } + + //// TODO make this use an io::Write, rather than SvcResponse + fn render(&self, status_code: u16, name: &'_ str, value: T) -> SvcResponse + 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()) + } + }; + + self.serve_string(status_code, name, rendered.into()) + } + + fn render_error_page(&'svc self, status_code: u16, e: &'_ str) -> SvcResponse { + #[derive(Serialize)] + struct Response<'a> { + error_msg: &'a str, + } + + self.render( + status_code, + "/base.html", + BasePresenter { + page_name: "/error.html", + data: &Response { error_msg: e }, + }, + ) + } + + fn render_page(&self, name: &'_ str, data: T) -> SvcResponse + where + T: Serialize, + { + self.render( + 200, + "/base.html", + BasePresenter { + page_name: name, + data, + }, + ) + } + + fn serve_origin(&self, domain: domain::Name, path: &'_ str) -> SvcResponse { + let mut path_owned; + + let path = match path.ends_with('/') { + true => { + path_owned = String::from(path); + path_owned.push_str("index.html"); + path_owned.as_str() + } + false => path, + }; + + let origin = match self.domain_manager.get_origin(&domain) { + Ok(o) => o, + Err(domain::manager::GetOriginError::NotFound) => { + return self.render_error_page(404, "Domain not found") + } + Err(domain::manager::GetOriginError::Unexpected(e)) => { + return self.render_error_page(500, format!("failed to fetch origin: {e}").as_str()) + } + }; + + let mut buf = Vec::::new(); + match origin.read_file_into(path, &mut buf) { + Ok(_) => self.serve_string(200, path, buf), + Err(origin::ReadFileIntoError::FileNotFound) => { + self.render_error_page(404, "File not found") + } + Err(origin::ReadFileIntoError::Unexpected(e)) => { + self.render_error_page(500, format!("failed to fetch file {path}: {e}").as_str()) + } + } + } + + async fn with_query_req<'a, F, In, Out>(&self, req: &'a Request, f: F) -> SvcResponse + where + In: Deserialize<'a>, + F: FnOnce(In) -> Out, + Out: Future, + { + let query = req.uri().query().unwrap_or(""); + match serde_urlencoded::from_str::(query) { + Ok(args) => f(args).await, + Err(err) => Err(format!("failed to parse query args: {}", err)), + } + } + + fn domain_get(&self, args: DomainGetArgs) -> SvcResponse { + #[derive(Serialize)] + struct Response { + domain: domain::Name, + config: Option, + } + + let config = match self.domain_manager.get_config(&args.domain) { + Ok(config) => Some(config), + Err(domain::manager::GetConfigError::NotFound) => None, + Err(domain::manager::GetConfigError::Unexpected(e)) => { + return self + .render_error_page(500, format!("retrieving configuration: {}", e).as_str()); + } + }; + + self.render_page( + "/domain.html", + Response { + domain: args.domain, + config, + }, + ) + } + + fn domain_init( + &self, + args: DomainInitArgs, + domain_config: service::util::FlatConfig, + ) -> SvcResponse { + #[derive(Serialize)] + struct Response { + domain: domain::Name, + flat_config: service::util::FlatConfig, + target_a: net::Ipv4Addr, + challenge_token: String, + } + + let config: domain::config::Config = match domain_config.try_into() { + Ok(Some(config)) => config, + Ok(None) => return self.render_error_page(400, "domain config is required"), + Err(e) => { + return self.render_error_page(400, format!("invalid domain config: {e}").as_str()) + } + }; + + let config_hash = match config.hash() { + Ok(hash) => hash, + Err(e) => { + return self + .render_error_page(500, format!("failed to hash domain config: {e}").as_str()) + } + }; + + self.render_page( + "/domain_init.html", + Response { + domain: args.domain, + flat_config: config.into(), + target_a: self.target_a, + challenge_token: config_hash, + }, + ) + } + + async fn domain_sync( + &self, + args: DomainSyncArgs, + domain_config: service::util::FlatConfig, + ) -> SvcResponse { + if args.passphrase != self.passphrase.as_str() { + return self.render_error_page(401, "Incorrect passphrase"); + } + + let config: domain::config::Config = match domain_config.try_into() { + Ok(Some(config)) => config, + Ok(None) => return self.render_error_page(400, "domain config is required"), + Err(e) => { + return self.render_error_page(400, format!("invalid domain config: {e}").as_str()) + } + }; + + let sync_result = self + .domain_manager + .sync_with_config(args.domain.clone(), config) + .await; + + #[derive(Serialize)] + struct Response { + domain: domain::Name, + error_msg: Option, + } + + let error_msg = match sync_result { + Ok(_) => None, + Err(domain::manager::SyncWithConfigError::InvalidURL) => Some("Fetching the git repository failed, please double check that you input the correct URL.".to_string()), + Err(domain::manager::SyncWithConfigError::InvalidBranchName) => Some("The git repository does not have a branch of the given name, please double check that you input the correct name.".to_string()), + Err(domain::manager::SyncWithConfigError::AlreadyInProgress) => Some("The configuration of your domain is still in progress, please refresh in a few minutes.".to_string()), + Err(domain::manager::SyncWithConfigError::TargetANotSet) => Some("The A record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()), + Err(domain::manager::SyncWithConfigError::ChallengeTokenNotSet) => Some("The TXT record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()), + Err(domain::manager::SyncWithConfigError::Unexpected(e)) => Some(format!("An unexpected error occurred: {e}")), + }; + + let response = Response { + domain: args.domain, + error_msg, + }; + + self.render_page("/domain_sync.html", response) + } + + pub fn domains(&self) -> SvcResponse { + #[derive(Serialize)] + struct Response { + domains: Vec, + } + + let domains = match self.domain_manager.all_domains() { + Ok(domains) => domains, + Err(e) => { + return self.render_error_page(500, format!("failed get all domains: {e}").as_str()) + } + }; + + let mut domains: Vec = domains + .into_iter() + .map(|domain| domain.as_str().to_string()) + .collect(); + + domains.sort(); + + self.render_page("/domains.html", Response { domains }) + } +} + +pub async fn handle_request( + svc: sync::Arc>, + req: Request, +) -> Result, Infallible> { + match handle_request_inner(svc, req).await { + Ok(res) => Ok(res), + Err(err) => panic!("unexpected error {err}"), + } +} + +fn strip_port(host: &str) -> &str { + match host.rfind(':') { + None => host, + Some(i) => &host[..i], + } +} + +pub async fn handle_request_inner(svc: sync::Arc>, req: Request) -> SvcResponse { + let maybe_host = match ( + req.headers() + .get("Host") + .and_then(|v| v.to_str().ok()) + .map(strip_port), + req.uri().host().map(strip_port), + ) { + (Some(h), _) if h != svc.http_domain.as_str() => Some(h), + (_, Some(h)) if h != svc.http_domain.as_str() => Some(h), + _ => None, + } + .and_then(|h| domain::Name::from_str(h).ok()); + + let method = req.method(); + let path = req.uri().path(); + + // Serving acme challenges always takes priority. We serve them from the same store no matter + // the domain, presumably they are cryptographically random enough that it doesn't matter. + if method == Method::GET && path.starts_with("/.well-known/acme-challenge/") { + let token = path.trim_start_matches("/.well-known/acme-challenge/"); + + if let Ok(key) = svc.domain_manager.get_acme_http01_challenge_key(token) { + let body: hyper::Body = key.into(); + return match Response::builder().status(200).body(body) { + Ok(res) => Ok(res), + Err(err) => Err(format!( + "failed to write acme http-01 challenge key: {}", + err + )), + }; + } + } + + // If a managed domain was given then serve that from its origin + if let Some(domain) = maybe_host { + return svc.serve_origin(domain, req.uri().path()); + } + + // Serve main domiply site + + if method == Method::GET && path.starts_with("/static/") { + return svc.render(200, path, ()); + } + + match (method, path) { + (&Method::GET, "/") | (&Method::GET, "/index.html") => svc.render_page("/index.html", ()), + (&Method::GET, "/domain.html") => { + svc.with_query_req(&req, |args: DomainGetArgs| async { svc.domain_get(args) }) + .await + } + (&Method::GET, "/domain_init.html") => { + svc.with_query_req(&req, |args: DomainInitArgs| async { + svc.with_query_req(&req, |config: service::util::FlatConfig| async { + svc.domain_init(args, config) + }) + .await + }) + .await + } + (&Method::GET, "/domain_sync.html") => { + svc.with_query_req(&req, |args: DomainSyncArgs| async { + svc.with_query_req(&req, |config: service::util::FlatConfig| async { + svc.domain_sync(args, config).await + }) + .await + }) + .await + } + (&Method::GET, "/domains.html") => svc.domains(), + _ => svc.render_error_page(404, "Page not found!"), + } +} diff --git a/src/service/http_tpl.rs b/src/service/http/tpl.rs similarity index 88% rename from src/service/http_tpl.rs rename to src/service/http/tpl.rs index a9d3ffd..e25422e 100644 --- a/src/service/http_tpl.rs +++ b/src/service/http/tpl.rs @@ -1,7 +1,7 @@ use handlebars::Handlebars; #[derive(rust_embed::RustEmbed)] -#[folder = "src/service/http_tpl"] +#[folder = "src/service/http/tpl"] #[prefix = "/"] struct Dir; diff --git a/src/service/http_tpl/base.html b/src/service/http/tpl/base.html similarity index 100% rename from src/service/http_tpl/base.html rename to src/service/http/tpl/base.html diff --git a/src/service/http_tpl/domain.html b/src/service/http/tpl/domain.html similarity index 100% rename from src/service/http_tpl/domain.html rename to src/service/http/tpl/domain.html diff --git a/src/service/http_tpl/domain_init.html b/src/service/http/tpl/domain_init.html similarity index 100% rename from src/service/http_tpl/domain_init.html rename to src/service/http/tpl/domain_init.html diff --git a/src/service/http_tpl/domain_sync.html b/src/service/http/tpl/domain_sync.html similarity index 100% rename from src/service/http_tpl/domain_sync.html rename to src/service/http/tpl/domain_sync.html diff --git a/src/service/http_tpl/domains.html b/src/service/http/tpl/domains.html similarity index 100% rename from src/service/http_tpl/domains.html rename to src/service/http/tpl/domains.html diff --git a/src/service/http_tpl/error.html b/src/service/http/tpl/error.html similarity index 100% rename from src/service/http_tpl/error.html rename to src/service/http/tpl/error.html diff --git a/src/service/http_tpl/index.html b/src/service/http/tpl/index.html similarity index 100% rename from src/service/http_tpl/index.html rename to src/service/http/tpl/index.html diff --git a/src/service/http_tpl/static/bamboo.css b/src/service/http/tpl/static/bamboo.css similarity index 100% rename from src/service/http_tpl/static/bamboo.css rename to src/service/http/tpl/static/bamboo.css