Added dynamic string formatting to interpolate {ip} tag in the data property from the JSON file.

This commit is contained in:
shad0wflame 2021-12-22 19:46:55 +01:00
parent c39b0e898a
commit fa814087bd
3 changed files with 41 additions and 57 deletions

7
Cargo.lock generated
View File

@ -181,6 +181,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"simple_logger", "simple_logger",
"strfmt",
"tokio", "tokio",
] ]
@ -791,6 +792,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "strfmt"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b278b244ef7aa5852b277f52dd0c6cac3a109919e1f6d699adde63251227a30f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.82" version = "1.0.82"

View File

@ -11,4 +11,5 @@ tokio = { version = "1", features = ["full"] }
serde = { version = "1.0.132", features = ["derive"]} serde = { version = "1.0.132", features = ["derive"]}
serde_json = "1.0" serde_json = "1.0"
log = { version = "0.4", features = ["std", "serde"] } log = { version = "0.4", features = ["std", "serde"] }
simple_logger = "1.16.0" simple_logger = "1.16.0"
strfmt = "0.1.6"

View File

@ -1,53 +1,17 @@
use std::collections::HashMap;
use std::fs::read_to_string; use std::fs::read_to_string;
use std::path::Path; use std::path::Path;
use log::{debug, info}; use log::{debug, info};
use strfmt::strfmt;
use serde::{Deserialize, Serialize}; use crate::go_daddy_ddns::dns_record::{DNSRecord, DNSRecordsHolder};
use crate::ip_handler::get_ip_to_publish; use crate::ip_handler::get_ip_to_publish;
mod dns_record;
const RECORDS_FILE_NAME: &'static str = "records.json"; const RECORDS_FILE_NAME: &'static str = "records.json";
#[derive(Debug, Deserialize)]
struct DNSRecordsHolder {
records: Vec<DNSRecord>,
}
#[derive(Debug, Serialize, Deserialize)]
struct DNSRecord {
name: String,
record_type: String,
data: Option<String>,
ttl: u32,
}
#[derive(Debug, Serialize, Deserialize)]
struct DNSRecordCreateTypeName {
data: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
port: Option<u16>, // SRV Only.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
priority: Option<u32>, // MX and SRV only.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
protocol: Option<String>, // SRV only.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
service: Option<String>, // SRV only.
ttl: u32,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
weight: Option<u32>, // SRV only.
}
/// Updates the DNS records if the IP has changed and returns the result of the execution. /// Updates the DNS records if the IP has changed and returns the result of the execution.
/// ///
/// # Arguments /// # Arguments
@ -62,7 +26,7 @@ pub async fn exec(domain: &str, key: &str, secret: &str) -> Result<(), Box<dyn s
let new_ip = get_ip_to_publish().await; let new_ip = get_ip_to_publish().await;
// There's no need to do anything here. So we stop the execution. // There's no need to do anything here. So we stop the execution.
if Option::is_none(&new_ip) { if new_ip.is_none() {
info!("The IP hasn't changed. Let's stop the execution here."); info!("The IP hasn't changed. Let's stop the execution here.");
return Ok(()); return Ok(());
} }
@ -70,7 +34,7 @@ pub async fn exec(domain: &str, key: &str, secret: &str) -> Result<(), Box<dyn s
info!("The IP has changed. Let's update the DNS records."); info!("The IP has changed. Let's update the DNS records.");
for record in get_records() { for record in get_records() {
debug!("{:?}", record); debug!("{:?}", record);
update_record(&record, &new_ip.clone().unwrap(), domain, key, secret).await; update_record(record, &new_ip.clone().unwrap(), domain, key, secret).await;
} }
Ok(()) Ok(())
@ -91,7 +55,7 @@ fn get_records() -> Vec<DNSRecord> {
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `record` - A &DNSRecord holding the record to update. /// * `record` - A DNSRecord holding the record to update.
/// ///
/// * `value` - A &str holding the current WAN ip. /// * `value` - A &str holding the current WAN ip.
/// ///
@ -101,7 +65,7 @@ fn get_records() -> Vec<DNSRecord> {
/// ///
/// * `secret` - A &str holding the GoDaddy developer secret. /// * `secret` - A &str holding the GoDaddy developer secret.
async fn update_record( async fn update_record(
record: &DNSRecord, record: DNSRecord,
value: &str, value: &str,
domain: &str, domain: &str,
key: &str, key: &str,
@ -110,23 +74,35 @@ async fn update_record(
let url = format!( let url = format!(
"https://api.godaddy.com/v1/domains/{domain}/records/{record_type}/{name}", "https://api.godaddy.com/v1/domains/{domain}/records/{record_type}/{name}",
domain = domain, domain = domain,
record_type = record.record_type, record_type = record.record_type.unwrap(),
name = record.name name = record.name.unwrap()
); );
let data = match &record.data { let data = match &record.data {
Some(x) => String::from(x), Some(x) => {
if record.interpolate.is_some() && record.interpolate.unwrap() == true {
let mut vars = HashMap::new();
vars.insert("ip".to_string(), value);
strfmt(x, &vars).expect("Error interpolating {ip} from data.")
} else {
String::from(x)
}
}
None => String::from(value), None => String::from(value),
}; };
let body = vec![DNSRecordCreateTypeName { let body = vec![DNSRecord {
data, name: None,
port: None, record_type: None,
priority: None, data: Some(data),
protocol: None, port: record.port,
service: None, priority: record.priority,
protocol: record.protocol,
service: record.service,
ttl: record.ttl, ttl: record.ttl,
weight: None, interpolate: None,
weight: record.weight,
}]; }];
let header = format!("sso-key {}:{}", key, secret); let header = format!("sso-key {}:{}", key, secret);