Rename domain::Config to domain::Domain, plus other moving
This commit is contained in:
parent
9c1bdc1e8a
commit
80e96c47fb
@ -1,80 +1,29 @@
|
||||
pub mod acme;
|
||||
pub mod checker;
|
||||
pub mod config;
|
||||
pub mod manager;
|
||||
mod name;
|
||||
pub mod store;
|
||||
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
pub use name::*;
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use trust_dns_client::rr as trust_dns_rr;
|
||||
use crate::error::unexpected::{self, Mappable};
|
||||
use crate::origin;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Validated representation of a domain name
|
||||
pub struct Name {
|
||||
inner: trust_dns_rr::Name,
|
||||
utf8_str: String,
|
||||
use hex::ToHex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Defines how a domain will behave when it is accessed. These are configured by the owner of the
|
||||
/// domain during setup.
|
||||
pub struct Domain {
|
||||
pub origin_descr: origin::Descr,
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.utf8_str.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.utf8_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Name {
|
||||
type Err = <trust_dns_rr::Name as FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut n = trust_dns_rr::Name::from_str(s)?;
|
||||
let utf8_str = n.clone().to_utf8();
|
||||
|
||||
n.set_fqdn(true);
|
||||
|
||||
Ok(Name { inner: n, utf8_str })
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Name {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct NameVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for NameVisitor {
|
||||
type Value = Name;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "a valid domain name")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
match Name::from_str(s) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(e) => Err(E::custom(format!("invalid domain name: {}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Name {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Name, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(NameVisitor)
|
||||
impl Domain {
|
||||
pub fn hash(&self) -> Result<String, unexpected::Error> {
|
||||
let mut h = Sha256::new();
|
||||
serde_json::to_writer(&mut h, self).or_unexpected()?;
|
||||
Ok(h.finalize().encode_hex::<String>())
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ impl DNSChecker {
|
||||
domain: &domain::Name,
|
||||
challenge_token: &str,
|
||||
) -> Result<(), CheckDomainError> {
|
||||
let domain = &domain.inner;
|
||||
let domain = domain.as_rr();
|
||||
|
||||
// check that the A is installed correctly on the domain
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::domain::{self, acme, checker, config};
|
||||
use crate::domain::{self, acme, checker, store};
|
||||
use crate::error::unexpected::{self, Mappable};
|
||||
use crate::origin;
|
||||
use crate::util;
|
||||
@ -15,11 +15,11 @@ pub enum GetConfigError {
|
||||
Unexpected(#[from] unexpected::Error),
|
||||
}
|
||||
|
||||
impl From<config::GetError> for GetConfigError {
|
||||
fn from(e: config::GetError) -> GetConfigError {
|
||||
impl From<store::GetError> for GetConfigError {
|
||||
fn from(e: store::GetError) -> GetConfigError {
|
||||
match e {
|
||||
config::GetError::NotFound => GetConfigError::NotFound,
|
||||
config::GetError::Unexpected(e) => GetConfigError::Unexpected(e),
|
||||
store::GetError::NotFound => GetConfigError::NotFound,
|
||||
store::GetError::Unexpected(e) => GetConfigError::Unexpected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,11 +36,11 @@ pub enum GetFileError {
|
||||
Unexpected(#[from] unexpected::Error),
|
||||
}
|
||||
|
||||
impl From<config::GetError> for GetFileError {
|
||||
fn from(e: config::GetError) -> Self {
|
||||
impl From<store::GetError> for GetFileError {
|
||||
fn from(e: store::GetError) -> Self {
|
||||
match e {
|
||||
config::GetError::NotFound => Self::DomainNotFound,
|
||||
config::GetError::Unexpected(e) => Self::Unexpected(e),
|
||||
store::GetError::NotFound => Self::DomainNotFound,
|
||||
store::GetError::Unexpected(e) => Self::Unexpected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,11 +69,11 @@ pub enum SyncError {
|
||||
Unexpected(#[from] unexpected::Error),
|
||||
}
|
||||
|
||||
impl From<config::GetError> for SyncError {
|
||||
fn from(e: config::GetError) -> SyncError {
|
||||
impl From<store::GetError> for SyncError {
|
||||
fn from(e: store::GetError) -> SyncError {
|
||||
match e {
|
||||
config::GetError::NotFound => SyncError::NotFound,
|
||||
config::GetError::Unexpected(e) => SyncError::Unexpected(e),
|
||||
store::GetError::NotFound => SyncError::NotFound,
|
||||
store::GetError::Unexpected(e) => SyncError::Unexpected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,10 +122,10 @@ impl From<checker::CheckDomainError> for SyncWithConfigError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<config::SetError> for SyncWithConfigError {
|
||||
fn from(e: config::SetError) -> SyncWithConfigError {
|
||||
impl From<store::SetError> for SyncWithConfigError {
|
||||
fn from(e: store::SetError) -> SyncWithConfigError {
|
||||
match e {
|
||||
config::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
|
||||
store::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyEr
|
||||
|
||||
//#[mockall::automock]
|
||||
pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
|
||||
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError>;
|
||||
fn get_config(&self, domain: &domain::Name) -> Result<domain::Domain, GetConfigError>;
|
||||
|
||||
fn get_file<'store>(
|
||||
&'store self,
|
||||
@ -150,7 +150,7 @@ pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
|
||||
fn sync_with_config<'mgr>(
|
||||
&'mgr self,
|
||||
domain: domain::Name,
|
||||
config: config::Config,
|
||||
config: domain::Domain,
|
||||
) -> util::BoxFuture<'mgr, Result<(), SyncWithConfigError>>;
|
||||
|
||||
fn get_acme_http01_challenge_key(
|
||||
@ -163,7 +163,7 @@ pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
|
||||
|
||||
pub struct ManagerImpl {
|
||||
origin_store: Box<dyn origin::Store + Send + Sync>,
|
||||
domain_config_store: Box<dyn config::Store + Send + Sync>,
|
||||
domain_config_store: Box<dyn store::Store + Send + Sync>,
|
||||
domain_checker: checker::DNSChecker,
|
||||
acme_manager: Option<Box<dyn acme::manager::Manager + Send + Sync>>,
|
||||
}
|
||||
@ -171,7 +171,7 @@ pub struct ManagerImpl {
|
||||
impl ManagerImpl {
|
||||
pub fn new<
|
||||
OriginStore: origin::Store + Send + Sync + 'static,
|
||||
DomainConfigStore: config::Store + Send + Sync + 'static,
|
||||
DomainConfigStore: store::Store + Send + Sync + 'static,
|
||||
AcmeManager: acme::manager::Manager + Send + Sync + 'static,
|
||||
>(
|
||||
task_stack: &mut util::TaskStack<unexpected::Error>,
|
||||
@ -224,7 +224,7 @@ impl ManagerImpl {
|
||||
}
|
||||
|
||||
impl Manager for ManagerImpl {
|
||||
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError> {
|
||||
fn get_config(&self, domain: &domain::Name) -> Result<domain::Domain, GetConfigError> {
|
||||
Ok(self.domain_config_store.get(domain)?)
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ impl Manager for ManagerImpl {
|
||||
fn sync_with_config<'mgr>(
|
||||
&'mgr self,
|
||||
domain: domain::Name,
|
||||
config: config::Config,
|
||||
config: domain::Domain,
|
||||
) -> util::BoxFuture<'mgr, Result<(), SyncWithConfigError>> {
|
||||
Box::pin(async move {
|
||||
let config_hash = config
|
||||
|
79
src/domain/name.rs
Normal file
79
src/domain/name.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use trust_dns_client::rr as trust_dns_rr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Validated representation of a domain name
|
||||
pub struct Name {
|
||||
inner: trust_dns_rr::Name,
|
||||
utf8_str: String,
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.utf8_str.as_str()
|
||||
}
|
||||
|
||||
pub fn as_rr(&self) -> &trust_dns_rr::Name {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.utf8_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Name {
|
||||
type Err = <trust_dns_rr::Name as FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut n = trust_dns_rr::Name::from_str(s)?;
|
||||
let utf8_str = n.clone().to_utf8();
|
||||
|
||||
n.set_fqdn(true);
|
||||
|
||||
Ok(Name { inner: n, utf8_str })
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Name {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct NameVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for NameVisitor {
|
||||
type Value = Name;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "a valid domain name")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
match Name::from_str(s) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(e) => Err(E::custom(format!("invalid domain name: {}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Name {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Name, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(NameVisitor)
|
||||
}
|
||||
}
|
@ -2,26 +2,8 @@ use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::{fs, io};
|
||||
|
||||
use crate::domain;
|
||||
use crate::error::unexpected::{self, Intoable, Mappable};
|
||||
use crate::{domain, origin};
|
||||
|
||||
use hex::ToHex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
/// Values which the owner of a domain can configure when they install a domain.
|
||||
pub struct Config {
|
||||
pub origin_descr: origin::Descr,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn hash(&self) -> Result<String, unexpected::Error> {
|
||||
let mut h = Sha256::new();
|
||||
serde_json::to_writer(&mut h, self).or_unexpected()?;
|
||||
Ok(h.finalize().encode_hex::<String>())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum GetError {
|
||||
@ -40,8 +22,8 @@ pub enum SetError {
|
||||
|
||||
#[mockall::automock]
|
||||
pub trait Store {
|
||||
fn get(&self, domain: &domain::Name) -> Result<Config, GetError>;
|
||||
fn set(&self, domain: &domain::Name, config: &Config) -> Result<(), SetError>;
|
||||
fn get(&self, domain: &domain::Name) -> Result<domain::Domain, GetError>;
|
||||
fn set(&self, domain: &domain::Name, config: &domain::Domain) -> Result<(), SetError>;
|
||||
fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error>;
|
||||
}
|
||||
|
||||
@ -67,7 +49,7 @@ impl FSStore {
|
||||
}
|
||||
|
||||
impl Store for FSStore {
|
||||
fn get(&self, domain: &domain::Name) -> Result<Config, GetError> {
|
||||
fn get(&self, domain: &domain::Name) -> Result<domain::Domain, GetError> {
|
||||
let path = self.config_file_path(domain);
|
||||
let config_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() {
|
||||
io::ErrorKind::NotFound => GetError::NotFound,
|
||||
@ -81,7 +63,7 @@ impl Store for FSStore {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn set(&self, domain: &domain::Name, config: &Config) -> Result<(), SetError> {
|
||||
fn set(&self, domain: &domain::Name, config: &domain::Domain) -> Result<(), SetError> {
|
||||
let dir_path = self.config_dir_path(domain);
|
||||
fs::create_dir_all(dir_path.as_path())
|
||||
.map_unexpected_while(|| format!("creating dir {}", dir_path.display()))?;
|
||||
@ -132,7 +114,7 @@ mod tests {
|
||||
|
||||
let domain = domain::Name::from_str("foo.com").expect("domain parsed");
|
||||
|
||||
let config = Config {
|
||||
let config = domain::Domain {
|
||||
origin_descr: Descr::Git {
|
||||
url: "bar".to_string(),
|
||||
branch_name: "baz".to_string(),
|
||||
@ -141,13 +123,13 @@ mod tests {
|
||||
|
||||
assert!(matches!(
|
||||
store.get(&domain),
|
||||
Err::<Config, GetError>(GetError::NotFound)
|
||||
Err::<domain::Domain, GetError>(GetError::NotFound)
|
||||
));
|
||||
|
||||
store.set(&domain, &config).expect("config set");
|
||||
assert_eq!(config, store.get(&domain).expect("config retrieved"));
|
||||
|
||||
let new_config = Config {
|
||||
let new_config = domain::Domain {
|
||||
origin_descr: Descr::Git {
|
||||
url: "BAR".to_string(),
|
||||
branch_name: "BAZ".to_string(),
|
@ -88,7 +88,7 @@ async fn main() {
|
||||
.expect("domain checker initialization failed");
|
||||
|
||||
let domain_config_store =
|
||||
domani::domain::config::FSStore::new(&config.domain_config_store_dir_path)
|
||||
domani::domain::store::FSStore::new(&config.domain_config_store_dir_path)
|
||||
.expect("domain config store initialization failed");
|
||||
|
||||
let domain_acme_manager = if config.https_listen_addr.is_some() {
|
||||
|
@ -192,7 +192,7 @@ impl<'svc> Service {
|
||||
#[derive(Serialize)]
|
||||
struct Response {
|
||||
domain: domain::Name,
|
||||
config: Option<domain::config::Config>,
|
||||
config: Option<domain::Domain>,
|
||||
}
|
||||
|
||||
let config = match self.domain_manager.get_config(&args.domain) {
|
||||
@ -231,7 +231,7 @@ impl<'svc> Service {
|
||||
challenge_token: String,
|
||||
}
|
||||
|
||||
let config: domain::config::Config = match domain_config.try_into() {
|
||||
let config: domain::Domain = match domain_config.try_into() {
|
||||
Ok(Some(config)) => config,
|
||||
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
||||
Err(e) => {
|
||||
@ -268,7 +268,7 @@ impl<'svc> Service {
|
||||
return self.render_error_page(401, "Incorrect passphrase");
|
||||
}
|
||||
|
||||
let config: domain::config::Config = match domain_config.try_into() {
|
||||
let config: domain::Domain = match domain_config.try_into() {
|
||||
Ok(Some(config)) => config,
|
||||
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
||||
Err(e) => {
|
||||
|
@ -11,7 +11,7 @@ pub struct FlatConfig {
|
||||
config_origin_descr_git_branch_name: Option<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<FlatConfig> for Option<domain::config::Config> {
|
||||
impl TryFrom<FlatConfig> for Option<domain::Domain> {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(v: FlatConfig) -> Result<Self, Self::Error> {
|
||||
@ -21,7 +21,7 @@ impl TryFrom<FlatConfig> for Option<domain::config::Config> {
|
||||
.as_str()
|
||||
{
|
||||
"" => Ok(None),
|
||||
"git" => Ok(Some(domain::config::Config {
|
||||
"git" => Ok(Some(domain::Domain {
|
||||
origin_descr: origin::Descr::Git {
|
||||
url: v
|
||||
.config_origin_descr_git_url
|
||||
@ -36,8 +36,8 @@ impl TryFrom<FlatConfig> for Option<domain::config::Config> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<domain::config::Config> for FlatConfig {
|
||||
fn from(v: domain::config::Config) -> Self {
|
||||
impl From<domain::Domain> for FlatConfig {
|
||||
fn from(v: domain::Domain) -> Self {
|
||||
match v.origin_descr {
|
||||
origin::Descr::Git { url, branch_name } => FlatConfig {
|
||||
config_origin_descr_kind: Some("git".to_string()),
|
||||
|
Loading…
Reference in New Issue
Block a user