diff --git a/services/tunnelbroker/src/config.rs b/services/tunnelbroker/src/config.rs index 2e666b1eb..2b77ffdea 100644 --- a/services/tunnelbroker/src/config.rs +++ b/services/tunnelbroker/src/config.rs @@ -1,78 +1,85 @@ use crate::constants; -use crate::constants::{ENV_APNS_CONFIG, ENV_FCM_CONFIG, ENV_WEB_PUSH_CONFIG}; +use crate::constants::{ + ENV_APNS_CONFIG, ENV_FCM_CONFIG, ENV_WEB_PUSH_CONFIG, ENV_WNS_CONFIG, +}; use crate::notifs::apns::config::APNsConfig; use crate::notifs::fcm::config::FCMConfig; use crate::notifs::web_push::config::WebPushConfig; +use crate::notifs::wns::config::WNSConfig; use anyhow::{ensure, Result}; use clap::Parser; use comm_lib::aws; use comm_lib::aws::config::BehaviorVersion; use once_cell::sync::Lazy; use tracing::info; #[derive(Parser)] #[command(version, about, long_about = None)] pub struct AppConfig { /// gRPC server listening port #[arg(long, default_value_t = constants::GRPC_SERVER_PORT)] pub grpc_port: u16, /// HTTP server listening port #[arg(long, default_value_t = 51001)] pub http_port: u16, /// AMQP server URI #[arg(env = "AMQP_URI")] #[arg(long, default_value = "amqp://comm:comm@localhost:5672")] pub amqp_uri: String, /// AWS Localstack service URL #[arg(env = "LOCALSTACK_ENDPOINT")] #[arg(long)] pub localstack_endpoint: Option, /// Comm Identity service URL #[arg(env = "COMM_TUNNELBROKER_IDENTITY_ENDPOINT")] #[arg(long, default_value = "http://localhost:50054")] pub identity_endpoint: String, /// APNs secrets #[arg(env = ENV_APNS_CONFIG)] #[arg(long)] pub apns_config: Option, /// FCM secrets #[arg(env = ENV_FCM_CONFIG)] #[arg(long)] pub fcm_config: Option, /// Web Push secrets #[arg(env = ENV_WEB_PUSH_CONFIG)] #[arg(long)] pub web_push_config: Option, + /// WNS secrets + #[arg(env = ENV_WNS_CONFIG)] + #[arg(long)] + pub wns_config: Option, } /// Stores configuration parsed from command-line arguments /// and environment variables pub static CONFIG: Lazy = Lazy::new(AppConfig::parse); /// Processes the command-line arguments and environment variables. /// Should be called at the beginning of the `main()` function. pub(super) fn parse_cmdline_args() -> Result<()> { // force evaluation of the lazy initialized config let cfg = Lazy::force(&CONFIG); // Perform some additional validation for CLI args ensure!( cfg.grpc_port != cfg.http_port, "gRPC and HTTP ports cannot be the same: {}", cfg.grpc_port ); Ok(()) } /// Provides region/credentials configuration for AWS SDKs pub async fn load_aws_config() -> aws::AwsConfig { let mut config_builder = aws::config::defaults(BehaviorVersion::v2024_03_28()); if let Some(endpoint) = &CONFIG.localstack_endpoint { info!("Using localstack URL: {}", endpoint); config_builder = config_builder.endpoint_url(endpoint); } config_builder.load().await } diff --git a/services/tunnelbroker/src/main.rs b/services/tunnelbroker/src/main.rs index 736b192a3..65b9ea99c 100644 --- a/services/tunnelbroker/src/main.rs +++ b/services/tunnelbroker/src/main.rs @@ -1,112 +1,116 @@ pub mod amqp; pub mod config; pub mod constants; pub mod database; pub mod error; pub mod grpc; pub mod identity; pub mod notifs; pub mod websockets; use crate::notifs::apns::APNsClient; use crate::notifs::fcm::FCMClient; use crate::notifs::web_push::WebPushClient; +use crate::notifs::wns::WNSClient; use crate::notifs::NotifClient; use anyhow::{anyhow, Result}; use config::CONFIG; use tracing::{self, error, info, Level}; use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> Result<()> { let filter = EnvFilter::builder() .with_default_directive(Level::INFO.into()) .with_env_var(constants::LOG_LEVEL_ENV_VAR) .from_env_lossy(); let subscriber = tracing_subscriber::fmt().with_env_filter(filter).finish(); tracing::subscriber::set_global_default(subscriber) .expect("Unable to configure tracing"); config::parse_cmdline_args()?; let aws_config = config::load_aws_config().await; let db_client = database::DatabaseClient::new(&aws_config); let amqp_connection = amqp::connect().await; let apns_config = CONFIG.apns_config.clone(); let apns = match apns_config { Some(config) => match APNsClient::new(&config) { Ok(apns_client) => { info!("APNs client created successfully"); Some(apns_client) } Err(err) => { error!("Error creating APNs client: {}", err); None } }, None => { error!("APNs config is missing"); None } }; let fcm_config = CONFIG.fcm_config.clone(); let fcm = match fcm_config { Some(config) => match FCMClient::new(&config) { Ok(fcm_client) => { info!("FCM client created successfully"); Some(fcm_client) } Err(err) => { error!("Error creating FCM client: {}", err); None } }, None => { error!("FCM config is missing"); None } }; let web_push_config = CONFIG.web_push_config.clone(); let web_push = match web_push_config { Some(config) => match WebPushClient::new(&config) { Ok(web_client) => { info!("Web Push client created successfully"); Some(web_client) } Err(err) => { error!("Error creating Web Push client: {}", err); None } }, None => { error!("Web Push config is missing"); None } }; + let wns_config = CONFIG.wns_config.clone(); + let notif_client = NotifClient { apns, fcm, web_push, + wns: None, }; let grpc_server = grpc::run_server(db_client.clone(), &amqp_connection); let websocket_server = websockets::run_server( db_client.clone(), &amqp_connection, notif_client.clone(), ); tokio::select! { Ok(_) = grpc_server => { Ok(()) }, Ok(_) = websocket_server => { Ok(()) }, else => { tracing::error!("A grpc or websocket server crashed."); Err(anyhow!("A grpc or websocket server crashed.")) } } } diff --git a/services/tunnelbroker/src/notifs/mod.rs b/services/tunnelbroker/src/notifs/mod.rs index fbfe56a9a..5ccc59ac4 100644 --- a/services/tunnelbroker/src/notifs/mod.rs +++ b/services/tunnelbroker/src/notifs/mod.rs @@ -1,36 +1,39 @@ use crate::notifs::apns::APNsClient; use crate::notifs::fcm::FCMClient; use crate::notifs::web_push::WebPushClient; +use crate::notifs::wns::WNSClient; use tunnelbroker_messages::Platform; pub mod apns; pub mod fcm; pub mod web_push; +pub mod wns; #[derive(PartialEq)] pub enum NotifClientType { APNs, FCM, WebPush, WNs, } impl NotifClientType { pub fn supported_platform(&self, platform: Platform) -> bool { match self { NotifClientType::APNs => { platform == Platform::IOS || platform == Platform::MacOS } NotifClientType::FCM => platform == Platform::Android, NotifClientType::WebPush => platform == Platform::Web, NotifClientType::WNs => platform == Platform::Windows, } } } #[derive(Clone)] pub struct NotifClient { pub(crate) apns: Option, pub(crate) fcm: Option, pub(crate) web_push: Option, + pub(crate) wns: Option, } diff --git a/services/tunnelbroker/src/notifs/wns/config.rs b/services/tunnelbroker/src/notifs/wns/config.rs new file mode 100644 index 000000000..f945afb69 --- /dev/null +++ b/services/tunnelbroker/src/notifs/wns/config.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +#[derive(clap::Args, Clone, Debug, Deserialize, Serialize)] +pub struct WNSConfig { + pub tenant_id: String, + pub app_id: String, + pub secret: String, +} + +impl FromStr for WNSConfig { + type Err = serde_json::Error; + fn from_str(s: &str) -> Result { + serde_json::from_str(s) + } +} diff --git a/services/tunnelbroker/src/notifs/wns/mod.rs b/services/tunnelbroker/src/notifs/wns/mod.rs new file mode 100644 index 000000000..89783bd31 --- /dev/null +++ b/services/tunnelbroker/src/notifs/wns/mod.rs @@ -0,0 +1,6 @@ +pub mod config; + +#[derive(Clone)] +pub struct WNSClient { + http_client: reqwest::Client, +}