diff --git a/config/default.toml b/config/default.toml index 5c7e1d3..0778b60 100644 --- a/config/default.toml +++ b/config/default.toml @@ -79,5 +79,5 @@ tweets_req_interval_in_secs = 60 [alert] webhook_url = "https://www.webhook_url.com" -[feature_flags] -wallet_feature_flags_config_file = "../wallet_feature_flags/default_feature_flags.json" \ No newline at end of file +[remote_configs] +wallet_configs_file = "../wallet_configs/default_configs.json" diff --git a/config/example.toml b/config/example.toml index 580f6ba..a0884a3 100644 --- a/config/example.toml +++ b/config/example.toml @@ -89,8 +89,8 @@ tweets_req_interval_in_secs = 60 [alert] webhook_url = "https://www.webhook_url.com" -[feature_flags] -wallet_feature_flags_config_file = "../wallet_feature_flags/default_feature_flags.json" +[remote_configs] +wallet_configs_file = "../wallet_configs/default_configs.json" # Example environment variable overrides: # TASKMASTER_BLOCKCHAIN__NODE_URL="ws://remote-node:9944" diff --git a/config/test.toml b/config/test.toml index a5361d3..303f748 100644 --- a/config/test.toml +++ b/config/test.toml @@ -79,5 +79,5 @@ tweets_req_interval_in_secs = 1 [alert] webhook_url = "https://www.webhook_url.com" -[feature_flags] -wallet_feature_flags_config_file = "../wallet_feature_flags/test_feature_flags.json" \ No newline at end of file +[remote_configs] +wallet_configs_file = "../wallet_configs/test_configs.json" diff --git a/src/config.rs b/src/config.rs index cfb11e2..908f4d3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,12 +18,12 @@ pub struct Config { pub raid_leaderboard: RaidLeaderboardConfig, pub alert: AlertConfig, pub x_association: XAssociationConfig, - pub feature_flags: FeatureFlagsConfig, + pub remote_configs: RemoteConfigsConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FeatureFlagsConfig { - pub wallet_feature_flags_config_file: String, +pub struct RemoteConfigsConfig { + pub wallet_configs_file: String, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -160,13 +160,12 @@ impl Config { } fn resolve_relative_paths(&mut self, config_path: &str) { - let feature_flags_path = Path::new(&self.feature_flags.wallet_feature_flags_config_file); - if feature_flags_path.is_absolute() { + let wallet_configs_path = Path::new(&self.remote_configs.wallet_configs_file); + if wallet_configs_path.is_absolute() { return; } let base_dir = Path::new(config_path).parent().expect("Failed to get base directory"); - self.feature_flags.wallet_feature_flags_config_file = - base_dir.join(feature_flags_path).to_string_lossy().to_string(); + self.remote_configs.wallet_configs_file = base_dir.join(wallet_configs_path).to_string_lossy().to_string(); } } @@ -229,8 +228,8 @@ impl Default for Config { x_association: XAssociationConfig { keywords: "quantus".to_string(), }, - feature_flags: FeatureFlagsConfig { - wallet_feature_flags_config_file: "wallet_feature_flags/default_feature_flags.json".to_string(), + remote_configs: RemoteConfigsConfig { + wallet_configs_file: "wallet_configs/default_configs.json".to_string(), }, } } diff --git a/src/errors.rs b/src/errors.rs index 1c2740a..dae622d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -11,7 +11,7 @@ use crate::{ db_persistence::DbError, handlers::{address::AddressHandlerError, auth::AuthHandlerError, referral::ReferralHandlerError, HandlerError}, models::ModelError, - services::{graphql_client::GraphqlError, wallet_feature_flags_service::WalletFeatureFlagsError}, + services::{graphql_client::GraphqlError, wallet_config_service::WalletConfigsError}, }; #[derive(Debug, thiserror::Error)] @@ -27,7 +27,7 @@ pub enum AppError { #[error("Server error: {0}")] Server(String), #[error("Wallet feature flags error: {0}")] - WalletFeatureFlags(#[from] WalletFeatureFlagsError), + WalletConfigs(#[from] WalletConfigsError), #[error("Join error: {0}")] Join(#[from] tokio::task::JoinError), #[error("GraphQL error: {0}")] @@ -52,7 +52,7 @@ impl IntoResponse for AppError { ), // --- Wallet Feature Flags --- - AppError::WalletFeatureFlags(err) => map_wallet_feature_flags_error(err), + AppError::WalletConfigs(err) => map_wallet_configs_error(err), // --- Model --- AppError::Model(err) => (StatusCode::BAD_REQUEST, err.to_string()), @@ -172,6 +172,6 @@ fn map_db_error(err: DbError) -> (StatusCode, String) { } } -fn map_wallet_feature_flags_error(err: WalletFeatureFlagsError) -> (StatusCode, String) { +fn map_wallet_configs_error(err: WalletConfigsError) -> (StatusCode, String) { (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()) } diff --git a/src/handlers/wallet_feature_flags.rs b/src/handlers/config.rs similarity index 67% rename from src/handlers/wallet_feature_flags.rs rename to src/handlers/config.rs index d3f9258..c92a399 100644 --- a/src/handlers/wallet_feature_flags.rs +++ b/src/handlers/config.rs @@ -3,10 +3,10 @@ use serde_json::Value; use crate::{handlers::SuccessResponse, http_server::AppState, AppError}; -pub async fn handle_get_wallet_feature_flags( +pub async fn handle_get_wallet_configs( State(state): State, ) -> Result>, AppError> { - let flags = state.wallet_feature_flags_service.get_wallet_feature_flags()?; + let flags = state.wallet_config_service.get_wallet_configs()?; Ok(SuccessResponse::new(flags)) } diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 184d472..feee07e 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -9,11 +9,11 @@ use crate::{ pub mod address; pub mod auth; +pub mod config; pub mod raid_quest; pub mod referral; pub mod relevant_tweet; pub mod tweet_author; -pub mod wallet_feature_flags; #[derive(Debug, thiserror::Error)] pub enum HandlerError { diff --git a/src/http_server.rs b/src/http_server.rs index b186bdf..55df17b 100644 --- a/src/http_server.rs +++ b/src/http_server.rs @@ -13,7 +13,7 @@ use crate::{ db_persistence::DbPersistence, metrics::{metrics_handler, track_metrics, Metrics}, routes::api_routes, - services::wallet_feature_flags_service::WalletFeatureFlagsService, + services::wallet_config_service::WalletConfigService, Config, GraphqlClient, }; use chrono::{DateTime, Utc}; @@ -24,7 +24,7 @@ pub struct AppState { pub db: Arc, pub metrics: Arc, pub graphql_client: Arc, - pub wallet_feature_flags_service: Arc, + pub wallet_config_service: Arc, pub config: Arc, pub challenges: Arc>>, pub oauth_sessions: Arc>>, @@ -85,8 +85,8 @@ pub async fn start_server( db, metrics: Arc::new(Metrics::new()), graphql_client, - wallet_feature_flags_service: Arc::new(WalletFeatureFlagsService::new( - config.feature_flags.wallet_feature_flags_config_file.clone(), + wallet_config_service: Arc::new(WalletConfigService::new( + config.remote_configs.wallet_configs_file.clone(), )?), config, twitter_gateway, diff --git a/src/routes/config.rs b/src/routes/config.rs new file mode 100644 index 0000000..7c1d81f --- /dev/null +++ b/src/routes/config.rs @@ -0,0 +1,7 @@ +use axum::{routing::get, Router}; + +use crate::{handlers::config::handle_get_wallet_configs, http_server::AppState}; + +pub fn config_routes() -> Router { + Router::new().route("/configs/wallet", get(handle_get_wallet_configs)) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 8914884..acb6e1e 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,7 +1,7 @@ use auth::auth_routes; use axum::Router; +use config::config_routes; use referral::referral_routes; -use wallet_feature_flags::wallet_feature_flags_routes; use crate::{ http_server::AppState, @@ -13,11 +13,11 @@ use crate::{ pub mod address; pub mod auth; +pub mod config; pub mod raid_quest; pub mod referral; pub mod relevant_tweet; pub mod tweet_author; -pub mod wallet_feature_flags; pub fn api_routes(state: AppState) -> Router { Router::new() @@ -26,6 +26,6 @@ pub fn api_routes(state: AppState) -> Router { .merge(auth_routes(state.clone())) .merge(relevant_tweet_routes(state.clone())) .merge(tweet_author_routes(state.clone())) - .merge(wallet_feature_flags_routes()) + .merge(config_routes()) .merge(raid_quest_routes(state)) } diff --git a/src/routes/wallet_feature_flags.rs b/src/routes/wallet_feature_flags.rs deleted file mode 100644 index dceb6c4..0000000 --- a/src/routes/wallet_feature_flags.rs +++ /dev/null @@ -1,7 +0,0 @@ -use axum::{routing::get, Router}; - -use crate::{handlers::wallet_feature_flags::handle_get_wallet_feature_flags, http_server::AppState}; - -pub fn wallet_feature_flags_routes() -> Router { - Router::new().route("/feature-flags/wallet", get(handle_get_wallet_feature_flags)) -} diff --git a/src/services/mod.rs b/src/services/mod.rs index 9276193..7274e43 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -4,4 +4,4 @@ pub mod raid_leaderboard_service; pub mod signature_service; pub mod telegram_service; pub mod tweet_synchronizer_service; -pub mod wallet_feature_flags_service; +pub mod wallet_config_service; diff --git a/src/services/wallet_feature_flags_service.rs b/src/services/wallet_config_service.rs similarity index 73% rename from src/services/wallet_feature_flags_service.rs rename to src/services/wallet_config_service.rs index 095d7ac..a253d4a 100644 --- a/src/services/wallet_feature_flags_service.rs +++ b/src/services/wallet_config_service.rs @@ -8,27 +8,27 @@ use std::{ use tokio::{sync::mpsc, task::JoinHandle, time}; #[derive(Debug, thiserror::Error)] -pub enum WalletFeatureFlagsError { - #[error("Failed to read wallet feature flags file: {0}")] +pub enum WalletConfigsError { + #[error("Failed to read wallet configs file: {0}")] ReadFile(#[from] std::io::Error), - #[error("Failed to parse wallet feature flags JSON: {0}")] + #[error("Failed to parse wallet configs JSON: {0}")] ParseJson(#[from] serde_json::Error), #[error("Failed to initialize file watcher: {0}")] Watcher(#[from] notify::Error), - #[error("Failed to read wallet feature flags: {0}")] + #[error("Failed to read wallet configs: {0}")] ReadLock(String), #[error("Failed to get parent directory")] ParentDirectory, } #[derive(Debug)] -pub struct WalletFeatureFlagsService { - wallet_feature_flags: Arc>, +pub struct WalletConfigService { + wallet_configs: Arc>, _watcher: RecommendedWatcher, _watch_task: JoinHandle<()>, } -impl WalletFeatureFlagsService { +impl WalletConfigService { fn is_reload_event_kind(kind: &EventKind) -> bool { match kind { EventKind::Create(_) | EventKind::Modify(ModifyKind::Name(_)) => true, @@ -37,17 +37,17 @@ impl WalletFeatureFlagsService { } } - pub fn new(file_path: impl Into) -> Result { + pub fn new(file_path: impl Into) -> Result { let file_path = file_path.into(); - let flags = Self::read_flags_from_file_sync(&file_path)?; - let wallet_feature_flags = Arc::new(RwLock::new(flags)); + let configs = Self::read_flags_from_file_sync(&file_path)?; + let wallet_configs = Arc::new(RwLock::new(configs)); let (tx, mut rx) = mpsc::unbounded_channel(); let mut watcher = RecommendedWatcher::new( move |result| { if let Err(send_err) = tx.send(result) { - tracing::warn!("Wallet feature flags watcher channel closed: {}", send_err); + tracing::warn!("Wallet configs watcher channel closed: {}", send_err); } }, NotifyConfig::default(), @@ -55,10 +55,10 @@ impl WalletFeatureFlagsService { let parent_dir = Path::new(&file_path) .parent() - .ok_or(WalletFeatureFlagsError::ParentDirectory)?; + .ok_or(WalletConfigsError::ParentDirectory)?; watcher.watch(parent_dir, RecursiveMode::NonRecursive)?; - let wallet_feature_flags_clone = wallet_feature_flags.clone(); + let wallet_feature_flags_clone = wallet_configs.clone(); let watched_file_name = file_path.file_name().map(|n| n.to_os_string()); let debounce_duration = Duration::from_millis(250); @@ -96,7 +96,7 @@ impl WalletFeatureFlagsService { reload_sleep.as_mut().reset(time::Instant::now() + debounce_duration); } Err(err) => { - tracing::error!("Wallet feature flags watcher error: {}", err); + tracing::error!("Wallet configs watcher error: {}", err); } } } @@ -113,14 +113,14 @@ impl WalletFeatureFlagsService { *write_guard = updated_flags; tracing::info!( - "Wallet feature flags reloaded from {}", + "Wallet configs reloaded from {}", file_path.display() ); } } Err(err) => { tracing::warn!( - "Failed to reload wallet feature flags from {}: {}. Using last known good flags.", + "Failed to reload wallet configs from {}: {}. Using last known good configs.", file_path.display(), err ); @@ -132,38 +132,39 @@ impl WalletFeatureFlagsService { }); Ok(Self { - wallet_feature_flags, + wallet_configs, _watcher: watcher, _watch_task: watch_task, }) } - pub fn get_wallet_feature_flags(&self) -> Result { - let guard = self.wallet_feature_flags.read().map_err(|_| { - WalletFeatureFlagsError::ReadLock("Failed to read wallet feature flags from lock".to_string()) - })?; + pub fn get_wallet_configs(&self) -> Result { + let guard = self + .wallet_configs + .read() + .map_err(|_| WalletConfigsError::ReadLock("Failed to read wallet configs from lock".to_string()))?; Ok(guard.clone()) } // Synchronous read for initial startup - fn read_flags_from_file_sync(path: &Path) -> Result { + fn read_flags_from_file_sync(path: &Path) -> Result { let content = std::fs::read_to_string(path)?; - let flags = serde_json::from_str::(&content)?; - Ok(flags) + let configs = serde_json::from_str::(&content)?; + Ok(configs) } // Asynchronous read for the background watcher task - async fn read_flags_from_file_async(path: &Path) -> Result { + async fn read_flags_from_file_async(path: &Path) -> Result { let content = tokio::fs::read_to_string(path).await?; // For larger JSON payloads, you might want to wrap this next line in spawn_blocking, // but for a tiny struct of bools, inline is perfectly fine. - let flags = serde_json::from_str::(&content)?; - Ok(flags) + let configs = serde_json::from_str::(&content)?; + Ok(configs) } } -impl Drop for WalletFeatureFlagsService { +impl Drop for WalletConfigService { fn drop(&mut self) { self._watch_task.abort(); } @@ -176,11 +177,11 @@ mod tests { use uuid::Uuid; fn unique_temp_flags_path() -> PathBuf { - std::env::temp_dir().join(format!("wallet-feature-flags-{}.json", Uuid::new_v4())) + std::env::temp_dir().join(format!("wallet-configs-{}.json", Uuid::new_v4())) } fn write_flags_file(path: &Path, content: &str) { - std::fs::write(path, content).expect("failed to write flags file"); + std::fs::write(path, content).expect("failed to write configs file"); } async fn wait_until(timeout: Duration, mut predicate: F) @@ -215,14 +216,14 @@ mod tests { }"#, ); - let service = WalletFeatureFlagsService::new(path.clone()).expect("service should initialize"); - let flags = service.get_wallet_feature_flags().unwrap(); + let service = WalletConfigService::new(path.clone()).expect("service should initialize"); + let configs = service.get_wallet_configs().unwrap(); - assert!(!flags["enableTestButtons"].as_bool().unwrap()); - assert!(!flags["enableKeystoneHardwareWallet"].as_bool().unwrap()); - assert!(flags["enableHighSecurity"].as_bool().unwrap()); - assert!(flags["enableRemoteNotifications"].as_bool().unwrap()); - assert!(flags["enableSwap"].as_bool().unwrap()); + assert!(!configs["enableTestButtons"].as_bool().unwrap()); + assert!(!configs["enableKeystoneHardwareWallet"].as_bool().unwrap()); + assert!(configs["enableHighSecurity"].as_bool().unwrap()); + assert!(configs["enableRemoteNotifications"].as_bool().unwrap()); + assert!(configs["enableSwap"].as_bool().unwrap()); std::fs::remove_file(path).ok(); } @@ -241,7 +242,7 @@ mod tests { }"#, ); - let service = WalletFeatureFlagsService::new(path.clone()).expect("service should initialize"); + let service = WalletConfigService::new(path.clone()).expect("service should initialize"); write_flags_file( &path, @@ -255,12 +256,12 @@ mod tests { ); wait_until(Duration::from_secs(3), || { - let flags = service.get_wallet_feature_flags().unwrap(); - flags["enableTestButtons"].as_bool().unwrap() - && flags["enableKeystoneHardwareWallet"].as_bool().unwrap() - && !flags["enableHighSecurity"].as_bool().unwrap() - && !flags["enableRemoteNotifications"].as_bool().unwrap() - && !flags["enableSwap"].as_bool().unwrap() + let configs = service.get_wallet_configs().unwrap(); + configs["enableTestButtons"].as_bool().unwrap() + && configs["enableKeystoneHardwareWallet"].as_bool().unwrap() + && !configs["enableHighSecurity"].as_bool().unwrap() + && !configs["enableRemoteNotifications"].as_bool().unwrap() + && !configs["enableSwap"].as_bool().unwrap() }) .await; @@ -281,13 +282,13 @@ mod tests { }"#, ); - let service = WalletFeatureFlagsService::new(path.clone()).expect("service should initialize"); - let before = service.get_wallet_feature_flags().unwrap(); + let service = WalletConfigService::new(path.clone()).expect("service should initialize"); + let before = service.get_wallet_configs().unwrap(); write_flags_file(&path, r#"{ invalid json }"#); tokio::time::sleep(Duration::from_millis(300)).await; - let after = service.get_wallet_feature_flags().unwrap(); + let after = service.get_wallet_configs().unwrap(); assert_eq!( before["enableTestButtons"].as_bool().unwrap(), after["enableTestButtons"].as_bool().unwrap() diff --git a/src/utils/test_app_state.rs b/src/utils/test_app_state.rs index 90e912c..d269593 100644 --- a/src/utils/test_app_state.rs +++ b/src/utils/test_app_state.rs @@ -1,6 +1,6 @@ use crate::{ db_persistence::DbPersistence, http_server::AppState, metrics::Metrics, models::auth::TokenClaims, - services::wallet_feature_flags_service::WalletFeatureFlagsService, Config, GraphqlClient, + services::wallet_config_service::WalletConfigService, Config, GraphqlClient, }; use jsonwebtoken::{encode, EncodingKey, Header}; use rusx::RusxGateway; @@ -18,8 +18,8 @@ pub async fn create_test_app_state() -> AppState { db, metrics: Arc::new(Metrics::new()), graphql_client: Arc::new(graphql_client), - wallet_feature_flags_service: Arc::new( - WalletFeatureFlagsService::new(config.feature_flags.wallet_feature_flags_config_file.clone()).unwrap(), + wallet_config_service: Arc::new( + WalletConfigService::new(config.remote_configs.wallet_configs_file.clone()).unwrap(), ), config: Arc::new(config), twitter_gateway: Arc::new(twitter_gateway), diff --git a/wallet_feature_flags/default_feature_flags.json b/wallet_configs/default_configs.json similarity index 100% rename from wallet_feature_flags/default_feature_flags.json rename to wallet_configs/default_configs.json diff --git a/wallet_feature_flags/test_feature_flags.json b/wallet_configs/test_configs.json similarity index 100% rename from wallet_feature_flags/test_feature_flags.json rename to wallet_configs/test_configs.json