Add Debian packaging to the App #32
5 changed files with 128 additions and 40 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -118,7 +118,6 @@ version = "1.1.1-rc.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"confy",
|
"confy",
|
||||||
"dotenv",
|
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"log",
|
"log",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -178,12 +177,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dotenv"
|
|
||||||
version = "0.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.33"
|
||||||
|
|
|
@ -20,6 +20,5 @@ strum_macros = "^0.24.3"
|
||||||
log = "^0.4.20"
|
log = "^0.4.20"
|
||||||
systemd-journal-logger = "^2.1.1"
|
systemd-journal-logger = "^2.1.1"
|
||||||
confy = "^0.5.1"
|
confy = "^0.5.1"
|
||||||
dotenv = "^0.15.0"
|
|
||||||
ipnet = "^2.9.0"
|
ipnet = "^2.9.0"
|
||||||
url = "2.5.0"
|
url = "2.5.0"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
|
||||||
use std::env::VarError;
|
use std::env::VarError;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
@ -11,7 +10,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||||
use strum_macros::{Display, IntoStaticStr};
|
use strum_macros::{Display, IntoStaticStr};
|
||||||
use systemd_journal_logger::connected_to_journal;
|
use systemd_journal_logger::connected_to_journal;
|
||||||
use url::ParseError;
|
use url::ParseError;
|
||||||
use crate::config::{ZoneConfig, ZoneEntry};
|
use crate::config::{AppConfig, ZoneConfig, ZoneEntry};
|
||||||
|
|
||||||
const API_BASE: &str = "https://api.cloudflare.com/client/v4";
|
const API_BASE: &str = "https://api.cloudflare.com/client/v4";
|
||||||
|
|
||||||
|
@ -34,8 +33,8 @@ pub(crate) struct CloudflareZone {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CloudflareZone {
|
impl CloudflareZone {
|
||||||
pub(crate) fn new(zone: &ZoneConfig) -> Result<Self, VarError> {
|
pub(crate) fn new(zone: &ZoneConfig, config: &AppConfig) -> Result<Self, VarError> {
|
||||||
let key = env::var("CF_API_TOKEN")?;
|
let key = config.cloudflare_api_token.clone();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: zone.name.clone(),
|
name: zone.name.clone(),
|
||||||
email: zone.email.clone(),
|
email: zone.email.clone(),
|
||||||
|
|
|
@ -140,3 +140,47 @@ impl Default for ZoneConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) struct AppConfig {
|
||||||
|
#[serde(alias="cf_api_token")]
|
||||||
|
pub(crate) cloudflare_api_token: String,
|
||||||
|
pub(crate) check_interval_seconds: Option<u16>,
|
||||||
|
pub(crate) uptime_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppConfig {
|
||||||
|
pub(crate) fn load() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let cfg: Self = match confy::load(env!("CARGO_PKG_NAME"),"config") {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(e) => {
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => error!("[ERROR] {e}"),
|
||||||
|
false => eprintln!("[ERROR] {e}")
|
||||||
|
}
|
||||||
|
return Err(Box::new(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if cfg.cloudflare_api_token.is_empty() {
|
||||||
|
let err_msg = "Cloudflare api token not specified. The app cannot work without this";
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => error!("[ERROR] {err_msg}"),
|
||||||
|
false => eprintln!("[ERROR] {err_msg}")
|
||||||
|
}
|
||||||
|
panic!("{err_msg}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
cloudflare_api_token: "".to_owned(),
|
||||||
|
check_interval_seconds: None,
|
||||||
|
uptime_url: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
99
src/main.rs
99
src/main.rs
|
@ -1,16 +1,15 @@
|
||||||
/*use cloudflare_old::{Instance, CloudflareDnsType};*/
|
/*use cloudflare_old::{Instance, CloudflareDnsType};*/
|
||||||
use reqwest::blocking::get;
|
use reqwest::blocking::get;
|
||||||
use std::{env, thread::{sleep}};
|
use std::{thread::{sleep}};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use chrono::{Utc, Duration};
|
use chrono::{Utc, Duration};
|
||||||
use dotenv::dotenv;
|
|
||||||
use log::{info, warn, error, LevelFilter};
|
use log::{info, warn, error, LevelFilter};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use systemd_journal_logger::{connected_to_journal, JournalLog};
|
use systemd_journal_logger::{connected_to_journal, JournalLog};
|
||||||
use crate::cloudflare::{CloudflareZone, DnsRecordType};
|
use crate::cloudflare::{CloudflareZone, DnsRecordType};
|
||||||
use crate::config::{InterfaceConfig, ZoneConfig, ZoneEntry};
|
use crate::config::{AppConfig, InterfaceConfig, ZoneConfig, ZoneEntry};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod cloudflare;
|
mod cloudflare;
|
||||||
|
@ -233,10 +232,10 @@ fn compare_zones(old_zone: &ZoneConfig, new_zone: &ZoneConfig) -> Vec<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dotenv().ok();
|
|
||||||
JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error");
|
JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error");
|
||||||
log::set_max_level(LevelFilter::Info);
|
log::set_max_level(LevelFilter::Info);
|
||||||
|
|
||||||
|
let mut config = AppConfig::load().unwrap();
|
||||||
let mut ifaces = InterfaceConfig::load().unwrap();
|
let mut ifaces = InterfaceConfig::load().unwrap();
|
||||||
let mut zone_cfgs = ZoneConfig::load().unwrap();
|
let mut zone_cfgs = ZoneConfig::load().unwrap();
|
||||||
|
|
||||||
|
@ -248,32 +247,22 @@ fn main() {
|
||||||
Err(e) => panic!("{}", e)
|
Err(e) => panic!("{}", e)
|
||||||
};
|
};
|
||||||
|
|
||||||
let reload_interval = match env::var("CHECK_INTERVAL_SECONDS") {
|
let reload_interval = config.check_interval_seconds.unwrap_or_else(|| {
|
||||||
Ok(interval_string) => i64::from_str(&interval_string).unwrap_or_else(|e| {
|
let warn_msg = "Reload interval option not set, defaulting to 60";
|
||||||
let warn_msg = format!("Expected integer number, got '{interval_string}'. Defaulting to 60");
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => warn!("[WARN] {warn_msg}"),
|
|
||||||
false => println!("[WARN] {warn_msg}"),
|
|
||||||
};
|
|
||||||
60
|
|
||||||
}),
|
|
||||||
Err(_) => {
|
|
||||||
let warn_msg = "Reload interval env not set, defaulting to 60";
|
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => warn!("[WARN] {warn_msg}"),
|
true => warn!("[WARN] {warn_msg}"),
|
||||||
false => println!("[WARN] {warn_msg}"),
|
false => println!("[WARN] {warn_msg}"),
|
||||||
}
|
}
|
||||||
60
|
60
|
||||||
},
|
}) as i64;
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
now = Utc::now();
|
now = Utc::now();
|
||||||
if now >= start + Duration::seconds(reload_interval) {
|
if now >= start + Duration::seconds(reload_interval) {
|
||||||
start = now;
|
start = now;
|
||||||
|
|
||||||
if let Ok(uptime_url) = env::var("UPTIME_URL") {
|
if let Some(uptime_url) = &config.uptime_url {
|
||||||
get(uptime_url);
|
let _ = get(uptime_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
match InterfaceConfig::load() {
|
match InterfaceConfig::load() {
|
||||||
|
@ -353,10 +342,9 @@ fn main() {
|
||||||
|
|
||||||
ifaces = new_cfg
|
ifaces = new_cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to load ínterfaces.toml with error: {}", e);
|
let err_msg = format!("Unable to load ínterfaces.toml with error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -496,7 +484,72 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to load from zones.d with error: {}", e);
|
let err_msg = format!("Unable to load from zones.d with error: {e}");
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => error!("[ERROR] {err_msg}"),
|
||||||
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match AppConfig::load() {
|
||||||
|
Ok(new_cfg) => {
|
||||||
|
if config != new_cfg {
|
||||||
|
if config.cloudflare_api_token != new_cfg.cloudflare_api_token {
|
||||||
|
let info_msg = "API token in config.toml changed";
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => info!("[INFO] {info_msg}"),
|
||||||
|
false => println!("[INFO] {info_msg}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.check_interval_seconds != new_cfg.check_interval_seconds {
|
||||||
|
let info_msg = match config.check_interval_seconds {
|
||||||
|
Some(old_interval) => {
|
||||||
|
match new_cfg.check_interval_seconds {
|
||||||
|
Some(new_interval) => format!("Check interval in config.toml changed from {old_interval}s to {new_interval}s"),
|
||||||
|
None => format!("Check interval in config.toml changed from {old_interval}s to 60s"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
match new_cfg.check_interval_seconds {
|
||||||
|
Some(new_interval) => format!("Check interval in config.toml changed from 60s to {new_interval}s"),
|
||||||
|
None => "This is a unicorn error, congratulations.".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => info!("[INFO] {info_msg}"),
|
||||||
|
false => println!("[INFO] {info_msg}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.uptime_url != new_cfg.uptime_url {
|
||||||
|
let info_msg = match &config.uptime_url {
|
||||||
|
Some(old_url) => {
|
||||||
|
match &new_cfg.uptime_url {
|
||||||
|
Some(new_url) => format!("Uptime URL in config.toml changed from '{old_url}' to '{new_url}'"),
|
||||||
|
None => "Uptime URL in config.toml was removed".to_owned(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
match &new_cfg.uptime_url {
|
||||||
|
Some(new_url) => format!("Uptime URL '{new_url}' was added to config.toml"),
|
||||||
|
None => "This is a unicorn error, congratulations.".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => info!("[INFO] {info_msg}"),
|
||||||
|
false => println!("[INFO] {info_msg}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config = new_cfg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let err_msg = format!("Unable to load config.toml with error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -506,7 +559,7 @@ fn main() {
|
||||||
|
|
||||||
ips.update();
|
ips.update();
|
||||||
for zone in &zone_cfgs {
|
for zone in &zone_cfgs {
|
||||||
let cf_zone = match CloudflareZone::new(zone) {
|
let cf_zone = match CloudflareZone::new(zone, &config) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Cloudflare Token likely not set. Error: {}", e);
|
let err_msg = format!("Cloudflare Token likely not set. Error: {}", e);
|
||||||
|
|
Loading…
Reference in a new issue