diff --git a/src/adapters/persistence/dbconfig/db.rs b/src/adapters/persistence/dbconfig/db.rs index aec72cf2..135818b7 100644 --- a/src/adapters/persistence/dbconfig/db.rs +++ b/src/adapters/persistence/dbconfig/db.rs @@ -63,9 +63,15 @@ fn init_postgres_db_pool(database_url: &str) -> Result::new(database_url); let pool = Pool::builder() .max_size(db_connections as u32) - .build(manager) - .expect("Failed to create pool."); - Ok(pool) + .build(manager); + + match pool { + Err(e) => { + log::error!("Failed to create postgres pool: {}", e); + exit(1); + } + Ok(pool) => Ok(pool), + } } fn init_sqlite_db_pool(database_url: &str) -> Result>, String> { @@ -77,7 +83,13 @@ fn init_sqlite_db_pool(database_url: &str) -> Result { + log::error!("Failed to create pool: {}", e); + exit(1); + } + Ok(pool) => Ok(pool), + } } diff --git a/src/command_line_runner.rs b/src/command_line_runner.rs index 1425385d..2d17343d 100644 --- a/src/command_line_runner.rs +++ b/src/command_line_runner.rs @@ -21,22 +21,37 @@ use std::io::{stdin, stdout, Error, ErrorKind, Write}; use std::process::exit; use std::str::FromStr; -pub async fn start_command_line(mut args: Args) { +pub async fn start_command_line(mut args: Args) -> Result<(), CustomError> { println!("Starting from command line"); // This needs to be nth(1) because the first argument is the binary name let conn = &mut get_connection(); - match args.nth(1).unwrap().as_str() { + let arg = match args.next() { + Some(arg) => arg, + None => { + println!("Please provide a command"); + exit(1); + } + }; + match arg.as_str() { "help" | "--help" => { println!( r" The following commands are available: users => Handles user management podcasts => Handles podcast management " - ) + ); + Ok(()) } "podcasts" => { println!("Podcast management"); - match args.next().unwrap().as_str() { + let podcast_args = match args.next() { + Some(arg) => arg, + None => { + println!("Please provide a command"); + exit(1); + } + }; + match podcast_args.as_str() { "refresh" => { let podcast_rss_feed = args.next(); @@ -50,9 +65,8 @@ pub async fn start_command_line(mut args: Args) { let podcast = Podcast::get_podcast_by_rss_feed(replaced_feed, conn) .expect("Error getting podcast"); - PodcastEpisodeService::insert_podcast_episodes(podcast.clone()) - .unwrap(); - PodcastService::schedule_episode_download(podcast, None).unwrap(); + PodcastEpisodeService::insert_podcast_episodes(podcast.clone())?; + PodcastService::schedule_episode_download(podcast, None) } None => { println!("Please provide a podcast rss feed url"); @@ -61,13 +75,14 @@ pub async fn start_command_line(mut args: Args) { } } "refresh-all" => { - let podcasts = Podcast::get_all_podcasts(); - for podcast in podcasts.unwrap() { + let podcasts = Podcast::get_all_podcasts()?; + for podcast in podcasts { println!("Refreshing podcast {}", podcast.name); - PodcastEpisodeService::insert_podcast_episodes(podcast.clone()).unwrap(); - PodcastService::schedule_episode_download(podcast, None).unwrap(); + PodcastEpisodeService::insert_podcast_episodes(podcast.clone())?; + PodcastService::schedule_episode_download(podcast, None)?; } + Ok(()) } "list" => { let podcasts = Podcast::get_all_podcasts(); @@ -77,9 +92,11 @@ pub async fn start_command_line(mut args: Args) { for podcast in podcasts { println!("{} - {} - {}", podcast.id, podcast.name, podcast.rssfeed); } + Ok(()) } Err(..) => { println!("Error getting podcasts"); + Ok(()) } } } @@ -90,39 +107,66 @@ pub async fn start_command_line(mut args: Args) { refresh-all => Refreshes all podcasts list => Lists all podcasts " - ) + ); + Ok(()) } _ => { println!("Unknown command"); + Err(CustomError::BadRequest("Unknown command".to_string())) } } } "users" => { println!("User management"); - match args.next().unwrap().as_str() { + let user_args = match args.next() { + Some(arg) => arg, + None => { + println!("Please provide a command"); + exit(1); + } + }; + match user_args.as_str() { "add" => { - let mut user = read_user_account().unwrap(); + let mut user = read_user_account()?; println!( "Should a user with the following settings be applied {:?}", user ); + if ask_for_confirmation().is_ok() { - user.password = Some(digest(user.password.unwrap())); + user.password = Some(digest(user.password.expect("Error digesting password"))); if User::insert_user(&mut user).is_ok() { println!("User succesfully created") } } + Ok(()) } - "generate" => match args.next().unwrap().as_str() { - "apiKey" => User::find_all_users(conn).iter().for_each(|u| { - log::info!("Updating api key of user {}", &u.username); - User::update_api_key_of_user(&u.username, uuid::Uuid::new_v4().to_string()) - .expect("Error updating api key"); - }), - _ => { - error!("Command not found") + "generate" => { + let arg = match args.next() { + Some(arg)=>{ + arg + }, + None=>{ + error!("Command not found"); + return Err(CustomError::BadRequest("Command not found".to_string())) + } + }; + + match arg.as_str() { + "apiKey" => { + User::find_all_users(conn).iter().for_each(|u| { + log::info!("Updating api key of user {}", &u.username); + User::update_api_key_of_user(&u.username, uuid::Uuid::new_v4() + .to_string()).expect("Error updating api key") + }); + Ok(()) + }, + _ => { + error!("Command not found"); + Err(CustomError::BadRequest("Command not found".to_string())) + } } }, "remove" => { @@ -153,10 +197,12 @@ pub async fn start_command_line(mut args: Args) { .expect("TODO: panic message"); User::delete_by_username(trim_string(&username), &mut get_connection()) .expect("Error deleting user"); - println!("User deleted") + println!("User deleted"); + Ok(()) } None => { - println!("Username not found") + println!("Username not found"); + Ok(()) } } } @@ -171,14 +217,16 @@ pub async fn start_command_line(mut args: Args) { ); username = trim_string(&username); println!(">{}<", username); - let user = User::find_by_username(username.as_str()).unwrap(); + let user = User::find_by_username(username.as_str())?; - do_user_update(user) + do_user_update(user); + Ok(()) } "list" => { // list users list_users(); + Ok(()) } "help" | "--help" => { println!( @@ -188,21 +236,26 @@ pub async fn start_command_line(mut args: Args) { update => Updates a user list => Lists all users " - ) + ); + Ok(()) } _ => { - error!("Command not found") + error!("Command not found"); + Ok(()) } } } "migration" => { - error!("Command not found") + error!("Command not found"); + Ok(()) } "debug" => { create_debug_message(); + Ok(()) } _ => { - error!("Command not found") + error!("Command not found"); + Ok(()) } } } @@ -252,7 +305,13 @@ pub fn read_user_account() -> Result { pub fn retry_read(prompt: &str, input: &mut String) { println!("{}", prompt); - stdin().read_line(input).unwrap(); + match stdin().read_line(input) { + Ok(..) => {} + Err(..) => { + println!("Error reading from terminal"); + exit(1); + } + } match !input.is_empty() { true => { if input.trim().is_empty() { @@ -267,8 +326,20 @@ pub fn retry_read(prompt: &str, input: &mut String) { pub fn retry_read_secret(prompt: &str) -> String { println!("{}", prompt); - stdout().flush().unwrap(); - let input = read_password().unwrap(); + match stdout().flush() { + Ok(..) => {} + Err(..) => { + println!("Error reading from terminal"); + exit(1); + } + } + let input = match read_password() { + Ok(input) => input, + Err(..) => { + println!("Error reading from terminal"); + exit(1); + } + }; match !input.is_empty() { true => { if input.trim().is_empty() { @@ -285,14 +356,20 @@ pub fn retry_read_secret(prompt: &str) -> String { pub fn retry_read_role(prompt: &str) -> Role { let mut input = String::new(); println!("{}", prompt); - stdin().read_line(&mut input).unwrap(); + match stdin().read_line(&mut input) { + Ok(..) => {} + Err(..) => { + println!("Error reading from terminal"); + exit(1); + } + } let res = Role::from_str(&trim_string(&input)); match res { Err(..) => { println!("Error setting role. Please choose one of the possible roles."); retry_read_role(prompt) } - Ok(..) => res.unwrap(), + Ok(e) => e, } } @@ -313,7 +390,7 @@ fn trim_string(string_to_trim: &str) -> String { .trim_end_matches('\n') .trim() .parse() - .unwrap() + .expect("Error parsing string") } fn do_user_update(mut user: User) { diff --git a/src/controllers/podcast_episode_controller.rs b/src/controllers/podcast_episode_controller.rs index d27ccafd..ecaf5d7e 100644 --- a/src/controllers/podcast_episode_controller.rs +++ b/src/controllers/podcast_episode_controller.rs @@ -195,7 +195,7 @@ pub async fn delete_podcast_episode_locally( PodcastEpisodeService::delete_podcast_episode_locally(&id.into_inner())?.into(); lobby .send_broadcast( - MAIN_ROOM.parse().unwrap(), + MAIN_ROOM.to_string(), serde_json::to_string(&BroadcastMessage { podcast_episode: Some(delted_podcast_episode), podcast_episodes: None, diff --git a/src/main.rs b/src/main.rs index 3ce0200e..f463b016 100644 --- a/src/main.rs +++ b/src/main.rs @@ -181,7 +181,10 @@ async fn main() -> std::io::Result<()> { EnvironmentService::print_banner(); ENVIRONMENT_SERVICE.get_environment(); if args().len() > 1 { - start_command_line(args()).await; + if let Err(e) = start_command_line(args()).await { + log::error!("Error in command line: {}", e); + exit(1); + } exit(0) } diff --git a/src/service/environment_service.rs b/src/service/environment_service.rs index 2c7f3377..611f8417 100644 --- a/src/service/environment_service.rs +++ b/src/service/environment_service.rs @@ -64,18 +64,24 @@ impl Default for EnvironmentService { } impl EnvironmentService { - pub fn new() -> EnvironmentService { - let mut option_oidc_config = None; + + fn handle_oidc() -> Option { let oidc_configured = is_env_var_present_and_true(OIDC_AUTH); if oidc_configured { - option_oidc_config = Some(OidcConfig { + Some(OidcConfig { redirect_uri: var(OIDC_REDIRECT_URI).expect("OIDC redirect uri not configured"), authority: var(OIDC_AUTHORITY).expect("OIDC authority not configured"), client_id: var(OIDC_CLIENT_ID).expect("OIDC client id not configured"), scope: var(OIDC_SCOPE).unwrap_or("openid profile email".to_string()), jwks_uri: var(OIDC_JWKS).unwrap(), - }); + }) + } else { + None } + } + + pub fn new() -> EnvironmentService { + let oidc_configured = Self::handle_oidc(); let mut server_url = var(SERVER_URL).unwrap_or("http://localhost:8000".to_string()); // Add trailing slash if not present if !server_url.ends_with('/') { @@ -126,16 +132,16 @@ impl EnvironmentService { EnvironmentService { server_url: server_url.clone(), polling_interval: var(POLLING_INTERVAL) - .unwrap_or(POLLING_INTERVAL_DEFAULT.to_string()) - .parse::() - .unwrap(), + .map(|v| v.parse::().map_err(|_|POLLING_INTERVAL_DEFAULT) + .unwrap_or(POLLING_INTERVAL_DEFAULT)) + .unwrap_or(POLLING_INTERVAL_DEFAULT), podindex_api_key: var(PODINDEX_API_KEY).unwrap_or("".to_string()), podindex_api_secret: var(PODINDEX_API_SECRET).unwrap_or("".to_string()), http_basic: is_env_var_present_and_true(BASIC_AUTH), username: username_send, password, - oidc_configured, - oidc_config: option_oidc_config, + oidc_configured: oidc_configured.is_some(), + oidc_config: oidc_configured, reverse_proxy_config, gpodder_integration_enabled: is_env_var_present_and_true(GPODDER_INTEGRATION_ENABLED), database_url: var(DATABASE_URL).unwrap_or(DATABASE_URL_DEFAULT_SQLITE.to_string()), @@ -156,21 +162,26 @@ impl EnvironmentService { fn handle_telegram_config() -> Option { if is_env_var_present_and_true(TELEGRAM_API_ENABLED) { - let telegram_bot_token = var(TELEGRAM_BOT_TOKEN); - - if telegram_bot_token.is_err() { - panic!("Telegram bot token not configured"); - } - - let telegram_bot_chat_id = var(TELEGRAM_BOT_CHAT_ID); - - if telegram_bot_chat_id.is_err() { - panic!("Telegram bot chat id not configured"); - } + let telegram_bot_token = match var(TELEGRAM_BOT_TOKEN) { + Ok(token) => Some(token), + Err(_) => None, + }.map_or_else(|| { + log::error!("Telegram bot token not configured"); + std::process::exit(1); + }, |v| v); + + + let telegram_chat_id = match var(TELEGRAM_BOT_CHAT_ID) { + Ok(chat_id) => Some(chat_id), + Err(_) => None, + }.map_or_else(|| { + log::error!("Telegram chat id not configured"); + std::process::exit(1); + }, |v| v); Some(TelegramConfig { - telegram_bot_token: telegram_bot_token.unwrap(), - telegram_chat_id: telegram_bot_chat_id.unwrap(), + telegram_bot_token, + telegram_chat_id, }) } else { None diff --git a/src/utils/append_to_header.rs b/src/utils/append_to_header.rs index 26475344..93be0226 100644 --- a/src/utils/append_to_header.rs +++ b/src/utils/append_to_header.rs @@ -1,14 +1,25 @@ +use std::process::exit; +use std::sync::LazyLock; use base64::engine::general_purpose; use base64::Engine; use regex::Regex; + + +static BASIC_AUTH_COND_REGEX: LazyLock = LazyLock::new(|| { + let com_regex = Regex::new(r".*//([^/?#\s]+)@.*"); + match com_regex { + Ok(re) => re, + Err(_) => exit(1), + } +}); + pub fn add_basic_auth_headers_conditionally( url: String, header_map: &mut reqwest::header::HeaderMap, ) { if url.contains('@') { - let re = Regex::new(r".*//([^/?#\s]+)@.*").unwrap(); - if let Some(captures) = re.captures(&url) { + if let Some(captures) = BASIC_AUTH_COND_REGEX.captures(&url) { if let Some(auth) = captures.get(1) { let b64_auth = general_purpose::STANDARD.encode(auth.as_str()); header_map.append( diff --git a/src/utils/file_name_replacement.rs b/src/utils/file_name_replacement.rs index 37c1f5f0..fb0e16bd 100644 --- a/src/utils/file_name_replacement.rs +++ b/src/utils/file_name_replacement.rs @@ -42,7 +42,7 @@ impl Sanitizer { let options_retrieved = option.unwrap_or_default(); Sanitizer { - illegal_re: Regex::new(r#"[/\?<>\\:\*\|":]"#).unwrap(), + illegal_re: Regex::new(r#"[/?<>\\:*|":]"#).unwrap(), control_re: Regex::new(r#"[\x00-\x1f\x80-\x9f]"#).unwrap(), reserved_re: Regex::new(r#"^\.+$"#).unwrap(), windows_reserved_re: RegexBuilder::new(