Changed module name: "go_daddy_ddns -> records_handler" and added path and file handlers.

This commit is contained in:
shad0wflame 2021-12-26 01:07:01 +01:00
parent fa814087bd
commit 29bc00e0e8
8 changed files with 187 additions and 39 deletions

31
Cargo.lock generated
View File

@ -82,6 +82,26 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.30" version = "0.8.30"
@ -176,6 +196,7 @@ dependencies = [
name = "godaddy_ddns" name = "godaddy_ddns"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dirs",
"log", "log",
"reqwest", "reqwest",
"serde", "serde",
@ -616,6 +637,16 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall",
]
[[package]] [[package]]
name = "remove_dir_all" name = "remove_dir_all"
version = "0.5.3" version = "0.5.3"

View File

@ -13,3 +13,11 @@ 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" strfmt = "0.1.6"
dirs = "4.0.0"
[profile.release]
opt-level = 3
lto = true
debug = false
codegen-units = 1
panic = "abort"

80
src/file_handler/mod.rs Normal file
View File

@ -0,0 +1,80 @@
use std::fs::{create_dir, read_to_string, File};
use std::io::{Error, ErrorKind, Write};
use std::path::Path;
use crate::file_handler::path_handler::{
get_application_folder_path, get_ip_file_path, get_records_file_path,
};
pub mod path_handler;
/// Sets up the application folder.
pub fn application_folder_setup() -> std::io::Result<()> {
let app_folder_path = get_application_folder_path();
if app_folder_path.is_none() {
return Err(Error::from(ErrorKind::NotFound));
}
let path = app_folder_path.unwrap();
if path.exists() {
return Ok(());
}
create_dir(&path)
}
/// Gets the contents of the RECORDS_FILE.
pub fn get_records_file() -> String {
let path = get_records_file_path().expect("Couldn't get RECORDS_FILE path.");
read_file(&path).expect(&format!(
"{} does not exist. Please create it.",
path.display()
))
}
/// Gets the contents of the IP_FILE.
pub fn get_ip_file() -> Option<String> {
let path = get_ip_file_path().expect("Couldn't get IP_FILE path.");
read_file(&path).ok()
}
/// Stores the current IP value in the FILE_NAME.
///
/// # Arguments
///
/// * `content` - A &[u8] holding the current IP value.
pub fn set_ip_file(content: &[u8]) -> () {
let path = get_ip_file_path().expect("Couldn't get IP_FILE path.");
write_file(&path, content)
}
/// Reads a file and returns its contents as String if Ok().
///
/// # Arguments
///
/// * `path` - A &Path holding the path of the file to be read.
fn read_file(path: &Path) -> std::io::Result<String> {
read_to_string(path)
}
/// Writes a file.
///
/// # Arguments
///
/// * `path` - A &Path holding the path of the file to be written.
///
/// * `content` - A &[u8] holding the info that will be written to the file.
fn write_file(path: &Path, content: &[u8]) -> () {
let display = &path.display();
let mut file =
File::create(path).expect(&format!("Error opening or creating file: {}", display));
file.write_all(content)
.expect(&format!("Error writing file: {}", display))
}

View File

@ -0,0 +1,50 @@
use std::path::PathBuf;
const APP_DIR: &'static str = ".godaddy-ddns";
const IP_FILE_NAME: &'static str = "ddns_ip";
const RECORDS_FILE_NAME: &'static str = "records.json";
/// Gets the application folder path and returns it if found.
pub fn get_application_folder_path() -> Option<PathBuf> {
let home = dirs::home_dir();
if home.is_none() {
return None;
}
Some(PathBuf::from(format!(
"{}/{}",
home.unwrap().display(),
APP_DIR
)))
}
/// Gets the IP_FILE path and returns it if found.
pub fn get_ip_file_path() -> Option<PathBuf> {
let app_folder = get_application_folder_path();
if app_folder.is_none() {
return None;
}
Some(PathBuf::from(format!(
"{}/{}",
app_folder.unwrap().display(),
IP_FILE_NAME
)))
}
/// Gets the RECORDS_FILE path and returns it if found.
pub fn get_records_file_path() -> Option<PathBuf> {
let app_folder = get_application_folder_path();
if app_folder.is_none() {
return None;
}
Some(PathBuf::from(format!(
"{}/{}",
app_folder.unwrap().display(),
RECORDS_FILE_NAME
)))
}

View File

@ -1,14 +1,10 @@
use std::fs::{File, read_to_string};
use std::io::Write;
use std::path::Path;
use log::debug; use log::debug;
use crate::file_handler::{get_ip_file, set_ip_file};
use crate::ip_handler::ip::IP; use crate::ip_handler::ip::IP;
mod ip; mod ip;
const IP_FILE_NAME: &'static str = "ddns_ip";
const WEBSITE_URL: &'static str = "https://httpbin.org/ip"; const WEBSITE_URL: &'static str = "https://httpbin.org/ip";
/// Returns an Option holding the current IP address if the value has changed, otherwise returns None. /// Returns an Option holding the current IP address if the value has changed, otherwise returns None.
@ -37,31 +33,12 @@ pub async fn get_ip_to_publish() -> Option<String> {
async fn check_current_ip() -> Result<String, Box<dyn std::error::Error>> { async fn check_current_ip() -> Result<String, Box<dyn std::error::Error>> {
let resp = reqwest::get(WEBSITE_URL).await?.json::<IP>().await?; let resp = reqwest::get(WEBSITE_URL).await?.json::<IP>().await?;
record_current_ip(&resp.origin); set_ip_file((&resp.origin).as_ref());
Ok(resp.origin) Ok(resp.origin)
} }
/// Stores the current IP value in the FILE_NAME.
///
/// # Arguments
///
/// * `current_ip` - A &str holding the current IP value.
fn record_current_ip(current_ip: &str) -> () {
let mut file = File::create(IP_FILE_NAME).expect("Error creating file.");
file.write_all(current_ip.as_ref())
.expect("Error writing file.");
}
/// Reads the current IP value from the FILE_NAME and returns it. /// Reads the current IP value from the FILE_NAME and returns it.
fn check_previous_ip() -> Option<String> { fn check_previous_ip() -> Option<String> {
let path = Path::new(IP_FILE_NAME); get_ip_file()
if !path.exists() {
return None;
}
let contents = read_to_string(path).expect("Error reading file.");
Some(contents)
} }

View File

@ -1,13 +1,15 @@
use std::env; use std::env;
use std::error::Error;
use log::LevelFilter; use log::LevelFilter;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
mod go_daddy_ddns; mod file_handler;
mod records_handler;
mod ip_handler; mod ip_handler;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn Error>> {
SimpleLogger::new() SimpleLogger::new()
.with_colors(true) .with_colors(true)
.with_utc_timestamps() .with_utc_timestamps()
@ -15,9 +17,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.init() .init()
.unwrap(); .unwrap();
file_handler::application_folder_setup().expect("Error setting up application folder.");
let domain = env::var("DOMAIN").expect("You need to set DOMAIN env variable first."); let domain = env::var("DOMAIN").expect("You need to set DOMAIN env variable first.");
let key = env::var("KEY").expect("You need to set KEY env variable first."); let key = env::var("KEY").expect("You need to set KEY env variable first.");
let secret = env::var("SECRET").expect("You need to set SECRET env variable first."); let secret = env::var("SECRET").expect("You need to set SECRET env variable first.");
go_daddy_ddns::exec(&domain, &key, &secret).await records_handler::update(&domain, &key, &secret).await
} }

View File

@ -1,17 +1,14 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::read_to_string;
use std::path::Path;
use log::{debug, info}; use log::{debug, info};
use strfmt::strfmt; use strfmt::strfmt;
use crate::go_daddy_ddns::dns_record::{DNSRecord, DNSRecordsHolder}; use crate::file_handler::get_records_file;
use crate::records_handler::dns_record::{DNSRecord, DNSRecordsHolder};
use crate::ip_handler::get_ip_to_publish; use crate::ip_handler::get_ip_to_publish;
mod dns_record; mod dns_record;
const RECORDS_FILE_NAME: &'static str = "records.json";
/// 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
@ -21,7 +18,9 @@ const RECORDS_FILE_NAME: &'static str = "records.json";
/// * `key` - A &str holding the GoDaddy developer key. /// * `key` - A &str holding the GoDaddy developer key.
/// ///
/// * `secret` - A &str holding the GoDaddy developer secret. /// * `secret` - A &str holding the GoDaddy developer secret.
pub async fn exec(domain: &str, key: &str, secret: &str) -> Result<(), Box<dyn std::error::Error>> { pub async fn update(domain: &str, key: &str, secret: &str) -> Result<(), Box<dyn std::error::Error>> {
let records = get_records();
info!("Checking if the IP has changed."); info!("Checking if the IP has changed.");
let new_ip = get_ip_to_publish().await; let new_ip = get_ip_to_publish().await;
@ -32,7 +31,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 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;
} }
@ -42,8 +41,7 @@ pub async fn exec(domain: &str, key: &str, secret: &str) -> Result<(), Box<dyn s
/// Gets a vector of DNSRecord from RECORDS_FILE_NAME and returns it. /// Gets a vector of DNSRecord from RECORDS_FILE_NAME and returns it.
fn get_records() -> Vec<DNSRecord> { fn get_records() -> Vec<DNSRecord> {
let path = Path::new(RECORDS_FILE_NAME); let content = get_records_file();
let content = read_to_string(path).unwrap();
let base: DNSRecordsHolder = let base: DNSRecordsHolder =
serde_json::from_str(&content).expect("Failed to deserialize JSON"); serde_json::from_str(&content).expect("Failed to deserialize JSON");