feat: Support for loading Servarr API tokens from a file

This commit is contained in:
2025-02-27 16:53:29 -07:00
parent 0167753cfe
commit 111485e7c4
6 changed files with 57 additions and 21 deletions
+3 -3
View File
@@ -319,13 +319,13 @@ sonarr:
uri: http://htpc.local/sonarr # Example of using the 'uri' key instead of 'host' and 'port' uri: http://htpc.local/sonarr # Example of using the 'uri' key instead of 'host' and 'port'
api_token: someApiToken1234567890 api_token: someApiToken1234567890
readarr: readarr:
host: 192.168.0.87 host: 192.168.0.87
port: 8787 port: 8787
api_token: someApiToken1234567890 api_token_file: /root/.config/readarr_api_token # Example of loading the API token from a file instead of hardcoding it in the configuration file
lidarr: lidarr:
host: 192.168.0.86 host: 192.168.0.86
port: 8686 port: 8686
api_token: someApiToken1234567890 api_token: ${MY_LIDARR_API_TOKEN} # Example of configuring using environment variables
whisparr: whisparr:
host: 192.168.0.69 host: 192.168.0.69
port: 6969 port: 6969
+6 -4
View File
@@ -348,7 +348,7 @@ mod tests {
assert_eq!(servarr_config.host, Some("localhost".to_string())); assert_eq!(servarr_config.host, Some("localhost".to_string()));
assert_eq!(servarr_config.port, None); assert_eq!(servarr_config.port, None);
assert_eq!(servarr_config.uri, None); assert_eq!(servarr_config.uri, None);
assert!(servarr_config.api_token.is_empty()); assert_eq!(servarr_config.api_token, Some(String::new()));
assert_eq!(servarr_config.ssl_cert_path, None); assert_eq!(servarr_config.ssl_cert_path, None);
} }
@@ -507,14 +507,16 @@ mod tests {
let port = 1234; let port = 1234;
let uri = "http://localhost:1234".to_owned(); let uri = "http://localhost:1234".to_owned();
let api_token = "thisisatest".to_owned(); let api_token = "thisisatest".to_owned();
let api_token_file = "/root/.config/api_token".to_owned();
let ssl_cert_path = "/some/path".to_owned(); let ssl_cert_path = "/some/path".to_owned();
let expected_str = format!("ServarrConfig {{ host: Some(\"{}\"), port: Some({}), uri: Some(\"{}\"), api_token: \"***********\", ssl_cert_path: Some(\"{}\") }}", let expected_str = format!("ServarrConfig {{ host: Some(\"{}\"), port: Some({}), uri: Some(\"{}\"), api_token: Some(\"***********\"), api_token_file: Some(\"{}\"), ssl_cert_path: Some(\"{}\") }}",
host, port, uri, ssl_cert_path); host, port, uri, api_token_file, ssl_cert_path);
let servarr_config = ServarrConfig { let servarr_config = ServarrConfig {
host: Some(host), host: Some(host),
port: Some(port), port: Some(port),
uri: Some(uri), uri: Some(uri),
api_token, api_token: Some(api_token),
api_token_file: Some(api_token_file),
ssl_cert_path: Some(ssl_cert_path), ssl_cert_path: Some(ssl_cert_path),
}; };
+38 -6
View File
@@ -1,6 +1,6 @@
use std::process; use std::{fs, process};
use std::path::PathBuf;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error, Result};
use colored::Colorize; use colored::Colorize;
use log::{debug, error}; use log::{debug, error};
use regex::Regex; use regex::Regex;
@@ -258,6 +258,28 @@ impl AppConfig {
_ => (), _ => (),
} }
} }
pub fn post_process_initialization(&mut self) {
let fetch_token = |config: &mut ServarrConfig, name: &'static str| {
if let Some(api_token_file) = config.api_token_file.as_ref() {
if !PathBuf::from(api_token_file).exists() {
log_and_print_error(format!("The specified {} API token file", name));
process::exit(1);
}
let api_token = fs::read_to_string(api_token_file).map_err(|e| anyhow!(e)).unwrap();
config.api_token = Some(api_token.trim().to_owned());
}
};
if let Some(radarr_config) = self.radarr.as_mut() {
fetch_token(radarr_config, "Radarr");
}
if let Some(sonarr_config) = self.sonarr.as_mut() {
fetch_token(sonarr_config, "Sonarr");
}
}
} }
#[derive(Redact, Deserialize, Serialize, Clone)] #[derive(Redact, Deserialize, Serialize, Clone)]
@@ -268,9 +290,11 @@ pub struct ServarrConfig {
pub port: Option<u16>, pub port: Option<u16>,
#[serde(default, deserialize_with = "deserialize_optional_env_var")] #[serde(default, deserialize_with = "deserialize_optional_env_var")]
pub uri: Option<String>, pub uri: Option<String>,
#[serde(default, deserialize_with = "deserialize_env_var")] #[serde(default, deserialize_with = "deserialize_optional_env_var")]
#[redact] #[redact]
pub api_token: String, pub api_token: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_env_var")]
pub api_token_file: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_env_var")] #[serde(default, deserialize_with = "deserialize_optional_env_var")]
pub ssl_cert_path: Option<String>, pub ssl_cert_path: Option<String>,
} }
@@ -281,6 +305,13 @@ impl ServarrConfig {
log_and_print_error("'host' or 'uri' is required for configuration".to_owned()); log_and_print_error("'host' or 'uri' is required for configuration".to_owned());
process::exit(1); process::exit(1);
} }
if self.api_token_file.is_none() && self.api_token.is_none() {
log_and_print_error(
"'api_token' or 'api_token_path' is required for configuration".to_owned(),
);
process::exit(1);
}
} }
} }
@@ -290,7 +321,8 @@ impl Default for ServarrConfig {
host: Some("localhost".to_string()), host: Some("localhost".to_string()),
port: None, port: None,
uri: None, uri: None,
api_token: "".to_string(), api_token: Some(String::new()),
api_token_file: None,
ssl_cert_path: None, ssl_cert_path: None,
} }
} }
+2 -1
View File
@@ -87,7 +87,7 @@ async fn main() -> Result<()> {
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let r = running.clone(); let r = running.clone();
let args = Cli::parse(); let args = Cli::parse();
let config = if let Some(ref config_file) = args.config { let mut config = if let Some(ref config_file) = args.config {
load_config(config_file.to_str().expect("Invalid config file specified"))? load_config(config_file.to_str().expect("Invalid config file specified"))?
} else { } else {
confy::load("managarr", "config")? confy::load("managarr", "config")?
@@ -95,6 +95,7 @@ async fn main() -> Result<()> {
let spinner_disabled = args.disable_spinner; let spinner_disabled = args.disable_spinner;
debug!("Managarr loaded using config: {config:?}"); debug!("Managarr loaded using config: {config:?}");
config.validate(); config.validate();
config.post_process_initialization();
let reqwest_client = build_network_client(&config); let reqwest_client = build_network_client(&config);
let (sync_network_tx, sync_network_rx) = mpsc::channel(500); let (sync_network_tx, sync_network_rx) = mpsc::channel(500);
let cancellation_token = CancellationToken::new(); let cancellation_token = CancellationToken::new();
+2 -1
View File
@@ -213,6 +213,7 @@ impl<'a, 'b> Network<'a, 'b> {
uri, uri,
api_token, api_token,
ssl_cert_path, ssl_cert_path,
..
}, },
default_port, default_port,
) = match network_event.into() { ) = match network_event.into() {
@@ -252,7 +253,7 @@ impl<'a, 'b> Network<'a, 'b> {
uri, uri,
method, method,
body, body,
api_token: api_token.to_owned(), api_token: api_token.as_ref().expect("API token not found").clone(),
ignore_status_code: false, ignore_status_code: false,
} }
} }
+6 -6
View File
@@ -38,7 +38,7 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let radarr_config = ServarrConfig { let radarr_config = ServarrConfig {
host, host,
api_token: String::new(), api_token: Some(String::new()),
port, port,
ssl_cert_path: None, ssl_cert_path: None,
..ServarrConfig::default() ..ServarrConfig::default()
@@ -472,7 +472,7 @@ mod tests {
let servarr_config = ServarrConfig { let servarr_config = ServarrConfig {
host: Some("192.168.0.123".to_owned()), host: Some("192.168.0.123".to_owned()),
port: Some(8080), port: Some(8080),
api_token: api_token.clone(), api_token: Some(api_token.clone()),
ssl_cert_path: Some("/test/cert.crt".to_owned()), ssl_cert_path: Some("/test/cert.crt".to_owned()),
..ServarrConfig::default() ..ServarrConfig::default()
}; };
@@ -507,7 +507,7 @@ mod tests {
let resource = network_event.resource(); let resource = network_event.resource();
let servarr_config = ServarrConfig { let servarr_config = ServarrConfig {
uri: Some("https://192.168.0.123:8080".to_owned()), uri: Some("https://192.168.0.123:8080".to_owned()),
api_token: api_token.clone(), api_token: Some(api_token.clone()),
..ServarrConfig::default() ..ServarrConfig::default()
}; };
{ {
@@ -577,7 +577,7 @@ mod tests {
let servarr_config = ServarrConfig { let servarr_config = ServarrConfig {
host: Some("192.168.0.123".to_owned()), host: Some("192.168.0.123".to_owned()),
port: Some(8080), port: Some(8080),
api_token: api_token.clone(), api_token: Some(api_token.clone()),
ssl_cert_path: Some("/test/cert.crt".to_owned()), ssl_cert_path: Some("/test/cert.crt".to_owned()),
..ServarrConfig::default() ..ServarrConfig::default()
}; };
@@ -618,7 +618,7 @@ mod tests {
let resource = network_event.resource(); let resource = network_event.resource();
let servarr_config = ServarrConfig { let servarr_config = ServarrConfig {
uri: Some("https://192.168.0.123:8080".to_owned()), uri: Some("https://192.168.0.123:8080".to_owned()),
api_token: api_token.clone(), api_token: Some(api_token.clone()),
..ServarrConfig::default() ..ServarrConfig::default()
}; };
{ {
@@ -738,7 +738,7 @@ pub(in crate::network) mod test_utils {
let servarr_config = ServarrConfig { let servarr_config = ServarrConfig {
host, host,
port, port,
api_token: "test1234".to_owned(), api_token: Some("test1234".to_owned()),
..ServarrConfig::default() ..ServarrConfig::default()
}; };