From 6e88ab967fa6952a12c12524ac6a25c46ccf4661 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sat, 13 May 2023 16:34:51 +0200 Subject: [PATCH] Fix up init, implement sync (maybe) --- src/domain/checker.rs | 5 +- src/domain/manager.rs | 4 - src/service.rs | 96 +++++++++++++++---- .../{domain_get_new.html => domain.html} | 3 +- src/service/http_tpl/domain_init.html | 26 +++++ src/service/http_tpl/domain_sync.html | 13 +++ src/service/util.rs | 24 +++-- 7 files changed, 136 insertions(+), 35 deletions(-) rename src/service/http_tpl/{domain_get_new.html => domain.html} (93%) create mode 100644 src/service/http_tpl/domain_init.html create mode 100644 src/service/http_tpl/domain_sync.html diff --git a/src/domain/checker.rs b/src/domain/checker.rs index 46a0802..d89559f 100644 --- a/src/domain/checker.rs +++ b/src/domain/checker.rs @@ -22,9 +22,6 @@ pub enum NewDNSCheckerError { #[derive(thiserror::Error, Debug)] pub enum CheckDomainError { - #[error("invalid domain name")] - InvalidDomainName, - #[error("target CNAME not set")] TargetCNAMENotSet, @@ -99,7 +96,7 @@ impl Checker for DNSChecker { // check that the TXT record with the challenge token is correctly installed on the domain { - let domain = Name::from_str("_gateway") + let domain = Name::from_str("_domiply_challenge") .map_err(|e| CheckDomainError::Unexpected(Box::from(e)))? .append_domain(domain) .map_err(|e| CheckDomainError::Unexpected(Box::from(e)))?; diff --git a/src/domain/manager.rs b/src/domain/manager.rs index 81f78e3..f636b95 100644 --- a/src/domain/manager.rs +++ b/src/domain/manager.rs @@ -67,9 +67,6 @@ pub enum SyncWithConfigError { #[error("invalid branch name")] InvalidBranchName, - #[error("invalid domain name")] - InvalidDomainName, - #[error("already in progress")] AlreadyInProgress, @@ -97,7 +94,6 @@ impl From for SyncWithConfigError { impl From for SyncWithConfigError { fn from(e: checker::CheckDomainError) -> SyncWithConfigError { match e { - checker::CheckDomainError::InvalidDomainName => SyncWithConfigError::InvalidDomainName, checker::CheckDomainError::TargetCNAMENotSet => SyncWithConfigError::TargetCNAMENotSet, checker::CheckDomainError::ChallengeTokenNotSet => { SyncWithConfigError::ChallengeTokenNotSet diff --git a/src/service.rs b/src/service.rs index 5eae619..f5c8ed1 100644 --- a/src/service.rs +++ b/src/service.rs @@ -147,15 +147,15 @@ where config: Option, } - let domain_get = warp::get() + let domain = warp::get() .and(with_renderer.clone()) .and(warp::path!("domain.html")) .and(warp::query::()) - .and(warp::query::()) + .and(warp::query::()) .map( |renderer: Renderer<'_, DM>, req: DomainGetNewRequest, - domain_config: util::ConfigFromURL| { + domain_config: util::FlatConfig| { match renderer.domain_manager.get_config(&req.domain) { Ok(_config) => renderer.render_error_page(500, "TODO not yet implemented"), Err(domain::manager::GetConfigError::NotFound) => { @@ -170,7 +170,7 @@ where }; renderer.render_page( - "/domain_get_new.html", + "/domain.html", &DomainGetNewResponse { domain: req.domain, config: domain_config, @@ -187,30 +187,28 @@ where ); #[derive(Deserialize)] - struct DomainPostRequest { - _init: bool, + struct DomainInitRequest { domain: domain::Name, passphrase: String, } - let domain_post = warp::post() + let domain_init = warp::get() .and(with_renderer.clone()) - .and(warp::path!("domain.html")) - .and(warp::query::()) - .and(warp::query::()) + .and(warp::path!("domain_init.html")) + .and(warp::query::()) + .and(warp::query::()) .map( |renderer: Renderer<'_, DM>, - req: DomainPostRequest, - domain_config: util::ConfigFromURL| { + req: DomainInitRequest, + domain_config: util::FlatConfig| { if req.passphrase != renderer.passphrase.as_str() { return renderer.render_error_page(401, "Incorrect passphrase"); } - //if req.init { #[derive(Serialize)] struct Response { domain: domain::Name, - config: domain::config::Config, + flat_config: util::FlatConfig, target_cname: domain::Name, challenge_token: String, } @@ -239,15 +237,74 @@ where let target_cname = (*renderer.target_cname).clone(); return renderer.render_page( - "/domain_post_init.html", + "/domain_init.html", &Response { domain: req.domain, - config: config, + flat_config: config.into(), target_cname: target_cname, challenge_token: config_hash, }, ); - //} + }, + ); + + #[derive(Deserialize)] + struct DomainSyncRequest { + domain: domain::Name, + } + + let domain_sync = warp::get() + .and(with_renderer.clone()) + .and(warp::path!("domain_sync.html")) + .and(warp::query::()) + .and(warp::query::()) + .map( + |renderer: Renderer<'_, DM>, + req: DomainSyncRequest, + domain_config: util::FlatConfig| { + let config: domain::config::Config = match domain_config.try_into() { + Ok(Some(config)) => config, + Ok(None) => { + return renderer.render_error_page(400, "domain config is required") + } + Err(e) => { + return renderer + .render_error_page(400, format!("invalid domain config: {e}").as_str()) + } + }; + + let sync_result = renderer + .domain_manager + .sync_with_config(&req.domain, &config); + + #[derive(Serialize)] + struct Response { + domain: domain::Name, + flat_config: util::FlatConfig, + error_msg: Option, + } + + let mut response = Response { + domain: req.domain, + flat_config: config.into(), + error_msg: None, + }; + + response.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::TargetCNAMENotSet) => Some("The CNAME 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}")), + }; + + return renderer.render_page( + "/domain_sync.html", + &response, + ) }, ); @@ -257,7 +314,8 @@ where Ok(static_dir .or(index) - .or(domain_get) - .or(domain_post) + .or(domain) + .or(domain_init) + .or(domain_sync) .or(not_found)) } diff --git a/src/service/http_tpl/domain_get_new.html b/src/service/http_tpl/domain.html similarity index 93% rename from src/service/http_tpl/domain_get_new.html rename to src/service/http_tpl/domain.html index 12a8a32..b15af4d 100644 --- a/src/service/http_tpl/domain_get_new.html +++ b/src/service/http_tpl/domain.html @@ -10,8 +10,7 @@ automatically updated too!

In the future Cosmux will support more backends than just git repos.

-
- + diff --git a/src/service/http_tpl/domain_init.html b/src/service/http_tpl/domain_init.html new file mode 100644 index 0000000..f6bd5ed --- /dev/null +++ b/src/service/http_tpl/domain_init.html @@ -0,0 +1,26 @@ +

Configure DNS

+ +

Next you will need to configure your DNS server to point to Cosmux. There are +two entries you will need to add:

+ +
    +
  • + A CNAME {{ data.domain }} entry with the value + {{ data.target_cname }} +
  • +
  • + A TXT _domiply_challenge.{{ data.domain }} entry with the value + {{ data.challenge_token }} +
  • +
+ +

Once complete, you can hit the following button to check your configuration +and set up your domain.

+ + + + {{ #each data.flat_config }} + + {{ /each }} + +
diff --git a/src/service/http_tpl/domain_sync.html b/src/service/http_tpl/domain_sync.html new file mode 100644 index 0000000..2d9aef1 --- /dev/null +++ b/src/service/http_tpl/domain_sync.html @@ -0,0 +1,13 @@ +{{#if data.error_msg}} + +

Your domain is not yet set up.

+

{{ data.error_msg }}

+ +{{ else }} + +

Congratulations! Your domain has been successfully configured. You can visit +it at:

+ +

http://{{ data.domain }}

+ +{{/if}} diff --git a/src/service/util.rs b/src/service/util.rs index a93ea25..ebb9b90 100644 --- a/src/service/util.rs +++ b/src/service/util.rs @@ -1,20 +1,20 @@ -use std::convert::TryFrom; +use std::convert::{From, TryFrom}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use crate::{domain, origin}; -#[derive(Deserialize)] -pub struct ConfigFromURL { +#[derive(Serialize, Deserialize)] +pub struct FlatConfig { config_origin_descr_kind: Option, config_origin_descr_git_url: Option, config_origin_descr_git_branch_name: Option, } -impl TryFrom for Option { +impl TryFrom for Option { type Error = String; - fn try_from(v: ConfigFromURL) -> Result { + fn try_from(v: FlatConfig) -> Result { match v .config_origin_descr_kind .unwrap_or("".to_string()) @@ -35,3 +35,15 @@ impl TryFrom for Option { } } } + +impl From for FlatConfig { + fn from(v: domain::config::Config) -> Self { + match v.origin_descr { + origin::Descr::Git { url, branch_name } => FlatConfig { + config_origin_descr_kind: Some("git".to_string()), + config_origin_descr_git_url: Some(url), + config_origin_descr_git_branch_name: Some(branch_name), + }, + } + } +}