Fix up init, implement sync (maybe)

This commit is contained in:
Brian Picciano 2023-05-13 16:34:51 +02:00
parent f3394acaf1
commit 6e88ab967f
7 changed files with 136 additions and 35 deletions

View File

@ -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)))?;

View File

@ -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<origin::store::SyncError> for SyncWithConfigError {
impl From<checker::CheckDomainError> 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

View File

@ -147,15 +147,15 @@ where
config: Option<domain::config::Config>,
}
let domain_get = warp::get()
let domain = warp::get()
.and(with_renderer.clone())
.and(warp::path!("domain.html"))
.and(warp::query::<DomainGetNewRequest>())
.and(warp::query::<util::ConfigFromURL>())
.and(warp::query::<util::FlatConfig>())
.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::<DomainPostRequest>())
.and(warp::query::<util::ConfigFromURL>())
.and(warp::path!("domain_init.html"))
.and(warp::query::<DomainInitRequest>())
.and(warp::query::<util::FlatConfig>())
.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::<DomainSyncRequest>())
.and(warp::query::<util::FlatConfig>())
.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<String>,
}
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))
}

View File

@ -10,8 +10,7 @@ automatically updated too!</p>
<p><em>In the future Cosmux will support more backends than just git
repos.</em></p>
<form method="POST" action="/domain.html">
<input name="init" type="hidden" value="true" />
<form method="GET" action="/domain_init.html">
<input name="domain" type="hidden" value="{{ data.domain }}" />
<input name="config_origin_descr_kind" type="hidden" value="git" />

View File

@ -0,0 +1,26 @@
<h2>Configure DNS</h2>
<p>Next you will need to configure your DNS server to point to Cosmux. There are
two entries you will need to add:</p>
<ul>
<li>
A <code>CNAME {{ data.domain }}</code> entry with the value
<code>{{ data.target_cname }}</code>
</li>
<li>
A <code>TXT _domiply_challenge.{{ data.domain }}</code> entry with the value
<code>{{ data.challenge_token }}</code>
</li>
</ul>
<p>Once complete, you can hit the following button to check your configuration
and set up your domain.</p>
<form method="GET" action="/domain_sync.html">
<input name="domain" type="hidden" value="{{ data.domain }}" />
{{ #each data.flat_config }}
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
{{ /each }}
<input type="submit" value="Next" />
</form>

View File

@ -0,0 +1,13 @@
{{#if data.error_msg}}
<p>Your domain is not yet set up.</p>
<p>{{ data.error_msg }}</p>
{{ else }}
<p>Congratulations! Your domain has been successfully configured. You can visit
it at:</p>
<p><a href="http://{{ data.domain }}">http://{{ data.domain }}</a></p>
{{/if}}

View File

@ -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<String>,
config_origin_descr_git_url: Option<String>,
config_origin_descr_git_branch_name: Option<String>,
}
impl TryFrom<ConfigFromURL> for Option<domain::config::Config> {
impl TryFrom<FlatConfig> for Option<domain::config::Config> {
type Error = String;
fn try_from(v: ConfigFromURL) -> Result<Self, Self::Error> {
fn try_from(v: FlatConfig) -> Result<Self, Self::Error> {
match v
.config_origin_descr_kind
.unwrap_or("".to_string())
@ -35,3 +35,15 @@ impl TryFrom<ConfigFromURL> for Option<domain::config::Config> {
}
}
}
impl From<domain::config::Config> 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),
},
}
}
}