diff --git a/Cargo.lock b/Cargo.lock index a6b5ec3..296d844 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -296,6 +296,7 @@ dependencies = [ "lum_libs", "lum_log", "reqwest", + "serde_yaml_ng", "thiserror", "tokio", ] @@ -1339,6 +1340,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml_ng" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1652,6 +1666,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 8dc3123..4cc7536 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,6 @@ lum_config = "0.2.3" lum_libs = { version = "0.2.4", features = ["serde"] } lum_log = "0.2.5" reqwest = "0.12.19" +serde_yaml_ng = "0.10.0" thiserror = "2.0.12" tokio = { version = "1.45.1", features = ["full"] } diff --git a/README.md b/README.md index ea865b8..72e7052 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,5 @@ If your provider is not supported, you can implement your own provider by defini ## Configuration Documentation coming soon™ + +See `docs/example-config.yaml` for configuration reference. *dnrs* creates this if no configuration is available at first start. diff --git a/docs/example-config.yaml b/docs/example-config.yaml new file mode 100644 index 0000000..93fe75c --- /dev/null +++ b/docs/example-config.yaml @@ -0,0 +1,54 @@ +resolver: + ipv4: + url: https://ip.cancom.io + type: Raw + ipv6: + url: https://ipv6.cancom.io + type: Raw +providers: +- Nitrado: + name: Nitrado1 + api_key: your_api_key + api_base_url: https://api.nitrado.net +dns: +- Nitrado: + provider_name: Nitrado1 + domains: + - domain: example.com + records: + - !Manual + domain: ipv4 + value: !A 127.0.0.1 + ttl: 3600 + - !Manual + domain: ipv6 + value: !AAAA ::1 + ttl: 3600 + - !Manual + domain: forward + value: !CNAME example.com + ttl: 3600 + - !Manual + domain: '@' + value: !MX + priority: 10 + target: mail.example.com + ttl: 3600 + - !Manual + domain: '@' + value: !TXT v=spf1 include:example.com ~all + ttl: 3600 + - !Manual + domain: '@' + value: !Custom + - RecordType + - Value + ttl: 3600 + - !Automatic + domain: auto-ipv4 + ttl: 300 + resolve_type: IPv4 + - !Automatic + domain: auto-ipv6 + ttl: 300 + resolve_type: IPv6 diff --git a/src/config.rs b/src/config.rs index 5829bb8..e8354ea 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,48 +5,27 @@ pub mod dns; pub mod provider; pub mod resolver; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(crate = "lum_libs::serde")] -#[serde(default)] -pub struct FileConfig { - resolver: resolver::FileConfig, - providers: Vec, - dns: Vec, -} - -impl Default for FileConfig { - fn default() -> Self { - FileConfig { - resolver: resolver::FileConfig::default(), - providers: vec![provider::FileConfig::default()], - dns: vec![dns::FileConfig::default()], - } - } -} - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(crate = "lum_libs::serde")] #[serde(default)] pub struct Config { - pub resolver: resolver::FileConfig, - pub providers: Vec, - pub dns: Vec, + pub resolver: resolver::Config, + pub providers: Vec, + pub dns: Vec, } impl Default for Config { fn default() -> Self { - let file_config = FileConfig::default(); - Config { - resolver: file_config.resolver, - providers: file_config.providers, - dns: file_config.dns, + resolver: resolver::Config::default(), + providers: vec![provider::Config::default()], + dns: vec![dns::Config::default()], } } } -impl MergeFrom for Config { - fn merge_from(self, other: FileConfig) -> Self { +impl MergeFrom for Config { + fn merge_from(self, other: Self) -> Self { Self { resolver: other.resolver, providers: other.providers, diff --git a/src/config/dns.rs b/src/config/dns.rs index 4e3e384..67bf83f 100644 --- a/src/config/dns.rs +++ b/src/config/dns.rs @@ -32,14 +32,14 @@ pub enum ResolveType { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(crate = "lum_libs::serde")] -pub struct FileConfig { +pub struct Config { #[serde(flatten)] pub dns: Type, } -impl Default for FileConfig { +impl Default for Config { fn default() -> Self { - FileConfig { + Config { dns: Type::Nitrado(nitrado::DnsConfig::default()), } } diff --git a/src/config/provider.rs b/src/config/provider.rs index f0757bf..402b7c3 100644 --- a/src/config/provider.rs +++ b/src/config/provider.rs @@ -10,14 +10,14 @@ pub enum Provider { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(crate = "lum_libs::serde")] -pub struct FileConfig { +pub struct Config { #[serde(flatten)] pub provider: Provider, } -impl Default for FileConfig { +impl Default for Config { fn default() -> Self { - FileConfig { + Config { provider: Provider::Nitrado(nitrado::ProviderConfig::default()), } } diff --git a/src/config/resolver.rs b/src/config/resolver.rs index 5615e18..c854025 100644 --- a/src/config/resolver.rs +++ b/src/config/resolver.rs @@ -18,14 +18,14 @@ pub struct IpResolver { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(crate = "lum_libs::serde")] -pub struct FileConfig { +pub struct Config { pub ipv4: IpResolver, pub ipv6: IpResolver, } -impl Default for FileConfig { +impl Default for Config { fn default() -> Self { - FileConfig { + Config { ipv4: IpResolver { url: "https://ip.cancom.io".to_string(), type_: IpResolverType::Raw, diff --git a/src/lib.rs b/src/lib.rs index 6dd9336..659d2b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ pub mod provider; pub mod resolver; pub mod types; -pub use config::{Config, FileConfig}; +pub use config::Config; pub use logger::setup_logger; pub const PROGRAM_NAME: &str = env!("CARGO_PKG_NAME"); diff --git a/src/main.rs b/src/main.rs index c4eec60..37d4310 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use std::fmt::{self, Debug}; +use std::fs::{self, File}; -use dnrs::{Config, FileConfig, RuntimeError, run, setup_logger}; -use lum_config::{ - ConfigPathError, EnvironmentConfigParseError, FileConfigParseError, FileHandler, merge, -}; +use dnrs::{Config, RuntimeError, run, setup_logger}; +use lum_config::{ConfigPathError, EnvironmentConfigParseError, FileConfigParseError, merge}; +use lum_log::info; use lum_log::{error, log::SetLoggerError}; use thiserror::Error; @@ -52,6 +52,15 @@ enum Error { #[error("Failed to load file config: {0}")] FileHandler(#[from] ConfigPathError), + #[error("YAML config error: {0}")] + YamlConfig(#[from] serde_yaml_ng::Error), + + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + + #[error("Unable to determine config directory")] + NoConfigDirectory, + #[error("Runtime error: {0}")] Runtime(#[from] RuntimeError), } @@ -67,10 +76,33 @@ impl Debug for Error { async fn main() -> Result<(), Error> { setup_logger()?; - let file_config: FileConfig = FileHandler::new(APP_NAME, None, None)?.load_config()?; + let config_path = dirs::config_dir() + .ok_or(Error::NoConfigDirectory)? + .join(APP_NAME) + .join("config.yaml"); + + let mut loaded_config: Option = None; + if config_path.exists() { + let file = File::open(&config_path)?; + loaded_config = Some(serde_yaml_ng::from_reader(file)?); + } + let config_existed = loaded_config.is_some(); let config = Config::default(); - let config = merge(config, file_config); + let config = match loaded_config { + Some(loaded_config) => merge(config, loaded_config), + None => config, + }; + + if let Some(parent) = config_path.parent() { + fs::create_dir_all(parent)?; + } + let file = File::create(&config_path)?; + serde_yaml_ng::to_writer(file, &config)?; + + if !config_existed { + info!("Created default config file at: {}", config_path.display()); + } run(config).await?; Ok(()) diff --git a/src/provider/nitrado.rs b/src/provider/nitrado.rs index 47a0e3b..bbfc5b0 100644 --- a/src/provider/nitrado.rs +++ b/src/provider/nitrado.rs @@ -15,7 +15,7 @@ use thiserror::Error; use crate::{ config::dns::{AutomaticRecordConfig, RecordConfig, ResolveType}, provider::{self, Feature, Provider}, - types::dns::{self, Record, RecordValue}, + types::dns::{self, MxRecord, Record, RecordValue}, }; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -72,6 +72,24 @@ impl Default for DnsConfig { value: RecordValue::CNAME("example.com".to_string()), ttl: Some(3600), }), + RecordConfig::Manual(dns::Record { + domain: "@".to_string(), + value: RecordValue::MX(MxRecord { + priority: 10, + target: "mail.example.com".to_string(), + }), + ttl: Some(3600), + }), + RecordConfig::Manual(dns::Record { + domain: "@".to_string(), + value: RecordValue::TXT("v=spf1 include:example.com ~all".to_string()), + ttl: Some(3600), + }), + RecordConfig::Manual(dns::Record { + domain: "@".to_string(), + value: RecordValue::Custom("RecordType".to_string(), "Value".to_string()), + ttl: Some(3600), + }), RecordConfig::Automatic(AutomaticRecordConfig { domain: "auto-ipv4".to_string(), ttl: Some(300),