Removed the need for use_ssl to indicate SSL usage; instead just use the ssl_cert_path

Added the ability to specify either host/port, or uri for configuring Radarr
This commit is contained in:
2024-11-05 18:16:01 -07:00
parent 650c9783a6
commit 9936ce1ab5
7 changed files with 114 additions and 73 deletions
+11 -18
View File
@@ -111,7 +111,7 @@ To see all available commands, simply run `managarr --help`:
```shell ```shell
$ managarr --help $ managarr --help
managarr 0.1.5 managarr 0.2.1
Alex Clarke <alex.j.tusa@gmail.com> Alex Clarke <alex.j.tusa@gmail.com>
A TUI and CLI to manage your Servarrs A TUI and CLI to manage your Servarrs
@@ -199,45 +199,38 @@ managarr --config /path/to/config.yml
### Example Configuration: ### Example Configuration:
```yaml ```yaml
radarr: radarr:
host: 127.0.0.1 host: 192.168.0.78
port: 7878 port: 7878
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: true ssl_cert_path: /path/to/radarr.crt # Required to enable SSL
ssl_cert_path: /path/to/radarr.crt
sonarr: sonarr:
host: 127.0.0.1 uri: http://htpc.local/sonarr # Example of using the 'uri' key instead of 'host' and 'port'
port: 8989
api_token: someApiToken1234567890 api_token: someApiToken1234567890
readarr: readarr:
host: 127.0.0.1 host: 192.168.0.87
port: 8787 port: 8787
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: false
lidarr: lidarr:
host: 127.0.0.1 host: 192.168.0.86
port: 8686 port: 8686
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: false
whisparr: whisparr:
host: 127.0.0.1 host: 192.168.0.69
port: 6969 port: 6969
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: false ssl_cert_path: /path/to/whisparr.crt
bazarr: bazarr:
host: 127.0.0.1 host: 192.168.0.67
port: 6767 port: 6767
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: false
prowlarr: prowlarr:
host: 127.0.0.1 host: 192.168.0.96
port: 9696 port: 9696
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: false
tautulli: tautulli:
host: 127.0.0.1 host: 192.168.0.81
port: 8181 port: 8181
api_token: someApiToken1234567890 api_token: someApiToken1234567890
use_ssl: false
``` ```
## Environment Variables ## Environment Variables
+3 -3
View File
@@ -1,7 +1,7 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use anyhow::anyhow; use anyhow::anyhow;
use pretty_assertions::{assert_eq, assert_str_eq}; use pretty_assertions::assert_eq;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES}; use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES};
@@ -221,10 +221,10 @@ mod tests {
fn test_radarr_config_default() { fn test_radarr_config_default() {
let radarr_config = RadarrConfig::default(); let radarr_config = RadarrConfig::default();
assert_str_eq!(radarr_config.host, "localhost"); assert_eq!(radarr_config.host, Some("localhost".to_string()));
assert_eq!(radarr_config.port, Some(7878)); assert_eq!(radarr_config.port, Some(7878));
assert_eq!(radarr_config.uri, None);
assert!(radarr_config.api_token.is_empty()); assert!(radarr_config.api_token.is_empty());
assert!(!radarr_config.use_ssl);
assert_eq!(radarr_config.ssl_cert_path, None); assert_eq!(radarr_config.ssl_cert_path, None);
} }
} }
+31 -5
View File
@@ -1,4 +1,7 @@
use std::process;
use anyhow::anyhow; use anyhow::anyhow;
use colored::Colorize;
use log::{debug, error}; use log::{debug, error};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
@@ -166,29 +169,52 @@ pub struct Data<'a> {
pub radarr_data: RadarrData<'a>, pub radarr_data: RadarrData<'a>,
} }
pub trait ServarrConfig {
fn validate(&self);
}
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default)]
pub struct AppConfig { pub struct AppConfig {
pub radarr: RadarrConfig, pub radarr: RadarrConfig,
} }
impl ServarrConfig for AppConfig {
fn validate(&self) {
self.radarr.validate();
}
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct RadarrConfig { pub struct RadarrConfig {
pub host: String, pub host: Option<String>,
pub port: Option<u16>, pub port: Option<u16>,
pub uri: Option<String>,
pub api_token: String, pub api_token: String,
#[serde(default)]
pub use_ssl: bool,
pub ssl_cert_path: Option<String>, pub ssl_cert_path: Option<String>,
} }
impl ServarrConfig for RadarrConfig {
fn validate(&self) {
if self.host.is_none() && self.uri.is_none() {
log_and_print_error("'host' or 'uri' is required for Radarr configuration".to_owned());
process::exit(1);
}
}
}
impl Default for RadarrConfig { impl Default for RadarrConfig {
fn default() -> Self { fn default() -> Self {
RadarrConfig { RadarrConfig {
host: "localhost".to_string(), host: Some("localhost".to_string()),
port: Some(7878), port: Some(7878),
uri: None,
api_token: "".to_string(), api_token: "".to_string(),
use_ssl: false,
ssl_cert_path: None, ssl_cert_path: None,
} }
} }
} }
pub fn log_and_print_error(error: String) {
error!("{}", error);
eprintln!("error: {}", error.red());
}
+16 -22
View File
@@ -10,7 +10,7 @@ use std::{io, panic, process};
use anyhow::anyhow; use anyhow::anyhow;
use anyhow::Result; use anyhow::Result;
use app::AppConfig; use app::{log_and_print_error, AppConfig, ServarrConfig};
use clap::{ use clap::{
command, crate_authors, crate_description, crate_name, crate_version, CommandFactory, Parser, command, crate_authors, crate_description, crate_name, crate_version, CommandFactory, Parser,
}; };
@@ -88,6 +88,7 @@ async fn main() -> Result<()> {
} else { } else {
confy::load("managarr", "config")? confy::load("managarr", "config")?
}; };
config.validate();
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();
@@ -229,8 +230,8 @@ fn load_config(path: &str) -> Result<AppConfig> {
fn build_network_client(config: &AppConfig) -> Client { fn build_network_client(config: &AppConfig) -> Client {
let mut client_builder = Client::builder(); let mut client_builder = Client::builder();
if config.radarr.use_ssl { if let Some(ref cert_path) = config.radarr.ssl_cert_path {
let cert = create_cert(config.radarr.ssl_cert_path.clone(), "Radarr"); let cert = create_cert(cert_path, "Radarr");
client_builder = client_builder.add_root_certificate(cert); client_builder = client_builder.add_root_certificate(cert);
} }
@@ -244,32 +245,25 @@ fn build_network_client(config: &AppConfig) -> Client {
} }
} }
fn create_cert(cert_path: Option<String>, servarr_name: &str) -> Certificate { fn create_cert(cert_path: &String, servarr_name: &str) -> Certificate {
let err = |error: String| { match fs::read(cert_path) {
error!("{}", error);
eprintln!("error: {}", error.red());
process::exit(1);
};
if cert_path.is_none() {
err(format!(
"A {} cert path is required when 'use_ssl' is 'true'",
servarr_name
));
}
match fs::read(cert_path.unwrap()) {
Ok(cert) => match Certificate::from_pem(&cert) { Ok(cert) => match Certificate::from_pem(&cert) {
Ok(certificate) => certificate, Ok(certificate) => certificate,
Err(_) => err(format!( Err(_) => {
log_and_print_error(format!(
"Unable to read the specified {} SSL certificate", "Unable to read the specified {} SSL certificate",
servarr_name servarr_name
)), ));
process::exit(1);
}
}, },
Err(_) => err(format!( Err(_) => {
log_and_print_error(format!(
"Unable to open specified {} SSL certificate", "Unable to open specified {} SSL certificate",
servarr_name servarr_name
)), ));
process::exit(1);
}
} }
} }
+2 -2
View File
@@ -26,7 +26,7 @@ mod tests {
.with_body("{}") .with_body("{}")
.create_async() .create_async()
.await; .await;
let host = server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned(); let host = Some(server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned());
let port = Some( let port = Some(
server.host_with_port().split(':').collect::<Vec<&str>>()[1] server.host_with_port().split(':').collect::<Vec<&str>>()[1]
.parse() .parse()
@@ -38,8 +38,8 @@ mod tests {
host, host,
api_token: String::new(), api_token: String::new(),
port, port,
use_ssl: false,
ssl_cert_path: None, ssl_cert_path: None,
..RadarrConfig::default()
}; };
app.config.radarr = radarr_config; app.config.radarr = radarr_config;
let app_arc = Arc::new(Mutex::new(app)); let app_arc = Arc::new(Mutex::new(app));
+14 -5
View File
@@ -2261,15 +2261,24 @@ impl<'a, 'b> Network<'a, 'b> {
let RadarrConfig { let RadarrConfig {
host, host,
port, port,
uri,
api_token, api_token,
use_ssl, ssl_cert_path,
..
} = &app.config.radarr; } = &app.config.radarr;
let protocol = if *use_ssl { "https" } else { "http" }; let uri = if let Some(radarr_uri) = uri {
let uri = format!( format!("{radarr_uri}/api/v3{resource}")
} else {
let protocol = if ssl_cert_path.is_some() {
"https"
} else {
"http"
};
let host = host.as_ref().unwrap();
format!(
"{protocol}://{host}:{}/api/v3{resource}", "{protocol}://{host}:{}/api/v3{resource}",
port.unwrap_or(7878) port.unwrap_or(7878)
); )
};
RequestProps { RequestProps {
uri, uri,
+31 -12
View File
@@ -794,7 +794,7 @@ mod test {
.match_header("X-Api-Key", "test1234"); .match_header("X-Api-Key", "test1234");
async_server = async_server.expect_at_most(0).create_async().await; async_server = async_server.expect_at_most(0).create_async().await;
let host = server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned(); let host = Some(server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned());
let port = Some( let port = Some(
server.host_with_port().split(':').collect::<Vec<&str>>()[1] server.host_with_port().split(':').collect::<Vec<&str>>()[1]
.parse() .parse()
@@ -805,8 +805,7 @@ mod test {
host, host,
port, port,
api_token: "test1234".to_owned(), api_token: "test1234".to_owned(),
use_ssl: false, ..RadarrConfig::default()
ssl_cert_path: None,
}; };
app.config.radarr = radarr_config; app.config.radarr = radarr_config;
let app_arc = Arc::new(Mutex::new(app)); let app_arc = Arc::new(Mutex::new(app));
@@ -4876,11 +4875,10 @@ mod test {
assert!(request_props.api_token.is_empty()); assert!(request_props.api_token.is_empty());
app_arc.lock().await.config.radarr = RadarrConfig { app_arc.lock().await.config.radarr = RadarrConfig {
host: "192.168.0.123".to_owned(), host: Some("192.168.0.123".to_owned()),
port: Some(8080), port: Some(8080),
api_token: "testToken1234".to_owned(), api_token: "testToken1234".to_owned(),
use_ssl: false, ..RadarrConfig::default()
ssl_cert_path: None,
}; };
} }
@@ -4889,11 +4887,33 @@ mod test {
let api_token = "testToken1234".to_owned(); let api_token = "testToken1234".to_owned();
let app_arc = Arc::new(Mutex::new(App::default())); let app_arc = Arc::new(Mutex::new(App::default()));
app_arc.lock().await.config.radarr = RadarrConfig { app_arc.lock().await.config.radarr = RadarrConfig {
host: "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: api_token.clone(),
use_ssl: true, ssl_cert_path: Some("/test/cert.crt".to_owned()),
ssl_cert_path: None, ..RadarrConfig::default()
};
let network = Network::new(&app_arc, CancellationToken::new(), Client::new());
let request_props = network
.radarr_request_props_from("/test", RequestMethod::Get, None::<()>)
.await;
assert_str_eq!(request_props.uri, "https://192.168.0.123:8080/api/v3/test");
assert_eq!(request_props.method, RequestMethod::Get);
assert_eq!(request_props.body, None);
assert_str_eq!(request_props.api_token, api_token);
}
#[tokio::test]
async fn test_radarr_request_props_from_custom_radarr_config_using_uri_instead_of_host_and_port()
{
let api_token = "testToken1234".to_owned();
let app_arc = Arc::new(Mutex::new(App::default()));
app_arc.lock().await.config.radarr = RadarrConfig {
uri: Some("https://192.168.0.123:8080".to_owned()),
api_token: api_token.clone(),
..RadarrConfig::default()
}; };
let network = Network::new(&app_arc, CancellationToken::new(), Client::new()); let network = Network::new(&app_arc, CancellationToken::new(), Client::new());
@@ -4986,7 +5006,7 @@ mod test {
async_server = async_server.create_async().await; async_server = async_server.create_async().await;
let host = server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned(); let host = Some(server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned());
let port = Some( let port = Some(
server.host_with_port().split(':').collect::<Vec<&str>>()[1] server.host_with_port().split(':').collect::<Vec<&str>>()[1]
.parse() .parse()
@@ -4997,8 +5017,7 @@ mod test {
host, host,
port, port,
api_token: "test1234".to_owned(), api_token: "test1234".to_owned(),
use_ssl: false, ..RadarrConfig::default()
ssl_cert_path: None,
}; };
app.config.radarr = radarr_config; app.config.radarr = radarr_config;
let app_arc = Arc::new(Mutex::new(app)); let app_arc = Arc::new(Mutex::new(app));