Skip to main content

foxtive/setup/
mod.rs

1#[cfg(feature = "cache")]
2#[allow(unused_imports)]
3use crate::cache::{Cache, contract::CacheDriverContract};
4#[cfg(feature = "database")]
5use crate::database::create_db_pool;
6#[cfg(feature = "jwt")]
7use crate::helpers::jwt::Jwt;
8#[cfg(feature = "crypto")]
9use crate::helpers::password::Password;
10#[cfg(feature = "rabbitmq")]
11use crate::prelude::RabbitMQ;
12#[cfg(feature = "redis")]
13use crate::prelude::Redis;
14#[cfg(feature = "rabbitmq")]
15use crate::rabbitmq::conn::create_rmq_conn_pool;
16#[cfg(feature = "redis")]
17use crate::redis::conn::create_redis_conn_pool;
18use crate::results::AppResult;
19use crate::setup::state::{FoxtiveHelpers, FoxtiveState};
20use crate::{Environment, internal_server_error};
21use std::path::Path;
22#[allow(unused_imports)]
23use std::sync::Arc;
24#[cfg(feature = "templating")]
25use tera::Tera;
26use tracing::{debug, info};
27
28pub(crate) mod state;
29pub mod trace;
30mod trace_layers;
31
32#[cfg(feature = "cache")]
33pub enum CacheDriverSetup {
34    #[cfg(feature = "cache-redis")]
35    Redis(fn(Arc<Redis>) -> Arc<dyn CacheDriverContract>),
36    #[cfg(feature = "cache-filesystem")]
37    Filesystem(fn() -> Arc<dyn CacheDriverContract>),
38    #[cfg(feature = "cache-in-memory")]
39    InMemory(fn() -> Arc<dyn CacheDriverContract>),
40}
41
42pub struct FoxtiveSetup {
43    pub env_prefix: String,
44    pub private_key: String,
45    pub public_key: String,
46    pub app_key: String,
47    pub app_code: String,
48    pub app_name: String,
49
50    pub env: Environment,
51
52    #[cfg(feature = "jwt")]
53    pub jwt_iss_public_key: String,
54    #[cfg(feature = "jwt")]
55    pub jwt_token_lifetime: i64,
56
57    #[cfg(feature = "templating")]
58    pub template_directory: String,
59
60    #[cfg(feature = "database")]
61    pub db_config: crate::database::DbConfig,
62
63    #[cfg(feature = "rabbitmq")]
64    pub rmq_config: crate::rabbitmq::config::RabbitmqConfig,
65
66    #[cfg(feature = "redis")]
67    pub redis_config: crate::redis::config::RedisConfig,
68
69    #[cfg(feature = "cache")]
70    pub cache_driver_setup: CacheDriverSetup,
71}
72
73pub async fn make_state(setup: FoxtiveSetup) -> AppResult<FoxtiveState> {
74    debug!("Initializing Foxtive state for app: {}", setup.app_name);
75    let foxtive = create_state(setup).await?;
76
77    crate::FOXTIVE
78        .set(foxtive.clone())
79        .map_err(|_| internal_server_error!("Failed to set global Foxtive state"))?;
80
81    debug!("Foxtive state initialized successfully");
82
83    Ok(foxtive)
84}
85
86async fn create_state(setup: FoxtiveSetup) -> AppResult<FoxtiveState> {
87    debug!("Creating helpers for app: {}", setup.app_code);
88    let helpers = make_helpers(&setup);
89
90    let env_prefix = setup.env_prefix;
91
92    #[cfg(feature = "database")]
93    let database_pool = {
94        debug!("Initializing database pool");
95        create_db_pool(setup.db_config)
96    }?;
97
98    #[cfg(feature = "redis")]
99    let (redis, redis_pool) = {
100        debug!("Initializing Redis connection pool");
101
102        let redis_pool = create_redis_conn_pool(setup.redis_config)?;
103        let redis = Arc::new(Redis::new(redis_pool.clone()));
104
105        (redis, redis_pool)
106    };
107
108    #[cfg(feature = "rabbitmq")]
109    let (rabbitmq, rabbitmq_pool) = {
110        debug!("Initializing RabbitMQ connection pool");
111        let rabbitmq_pool = create_rmq_conn_pool(setup.rmq_config).await?;
112
113        let rmq = Arc::new(tokio::sync::Mutex::new(
114            RabbitMQ::new(rabbitmq_pool.clone()).await?,
115        ));
116
117        (rmq, rabbitmq_pool)
118    };
119
120    #[cfg(feature = "templating")]
121    let tera_templating = {
122        debug!(
123            "Initializing Tera templating engine from: {}",
124            setup.template_directory
125        );
126
127        Tera::new(&setup.template_directory)
128    }?;
129
130    #[cfg(feature = "cache")]
131    let cache_driver = {
132        debug!("Setting up cache driver");
133        match setup.cache_driver_setup {
134            #[cfg(feature = "cache-redis")]
135            CacheDriverSetup::Redis(setup_fn) => {
136                debug!("Using Redis cache driver");
137                setup_fn(redis.clone())
138            }
139            #[cfg(feature = "cache-filesystem")]
140            CacheDriverSetup::Filesystem(setup_fn) => {
141                debug!("Using Filesystem cache driver");
142                setup_fn()
143            }
144            #[cfg(feature = "cache-in-memory")]
145            CacheDriverSetup::InMemory(setup_fn) => {
146                debug!("Using In-Memory cache driver");
147                setup_fn()
148            }
149        }
150    };
151
152    debug!("All components initialized, creating final state");
153
154    Ok(FoxtiveState {
155        helpers,
156
157        env: setup.env,
158
159        app_env_prefix: env_prefix.clone(),
160
161        app_code: setup.app_code,
162        app_name: setup.app_name,
163
164        app_key: setup.app_key,
165        app_public_key: setup.public_key,
166        app_private_key: setup.private_key,
167
168        #[cfg(feature = "redis")]
169        redis_pool,
170        #[cfg(feature = "redis")]
171        redis,
172        #[cfg(feature = "database")]
173        database: database_pool,
174        #[cfg(feature = "rabbitmq")]
175        rabbitmq_pool,
176        #[cfg(feature = "rabbitmq")]
177        rabbitmq,
178        #[cfg(feature = "templating")]
179        tera: tera_templating,
180
181        #[cfg(feature = "jwt")]
182        jwt_iss_public_key: setup.jwt_iss_public_key,
183
184        #[cfg(feature = "jwt")]
185        jwt_token_lifetime: setup.jwt_token_lifetime,
186
187        #[cfg(feature = "cache")]
188        cache: Arc::from(Cache::new(cache_driver)),
189    })
190}
191
192#[allow(unused_variables)]
193fn make_helpers(setup: &FoxtiveSetup) -> FoxtiveHelpers {
194    #[cfg(feature = "crypto")]
195    let pwd_helper = {
196        debug!("Creating password helper");
197        Password::new(setup.app_key.clone())
198    };
199
200    #[cfg(feature = "jwt")]
201    let jwt_helper = {
202        debug!(
203            "Creating JWT helper with {}s token lifetime",
204            setup.jwt_token_lifetime
205        );
206        Jwt::new(
207            setup.jwt_iss_public_key.clone(),
208            setup.private_key.clone(),
209            setup.jwt_token_lifetime,
210        )
211    };
212
213    FoxtiveHelpers {
214        #[cfg(feature = "jwt")]
215        jwt: Arc::new(jwt_helper),
216        #[cfg(feature = "crypto")]
217        password: Arc::new(pwd_helper),
218    }
219}
220
221pub fn load_environment_variables(service: &str) {
222    info!(
223        "log level: {:?}",
224        std::env::var("RUST_LOG").unwrap_or(String::from("info"))
225    );
226    info!("root directory: {service:?}");
227
228    // load project level .env
229    let path = format!("apps/{service}/.env");
230    debug!("Attempting to load env file: {}", path);
231    dotenv::from_filename(path).ok();
232
233    // load project level .env
234    if Path::new(".env").exists() {
235        info!("loading env file: .env");
236        dotenv::from_filename(".env").ok();
237    }
238
239    // load project level .env.main
240    if Path::new(".env.main").exists() {
241        info!("loading env file: .env.main");
242        dotenv::from_filename(".env.main").ok();
243    }
244
245    // load service-specific env
246    let filename = format!(".env.{service}");
247    if Path::new(filename.as_str()).exists() {
248        info!("loading env file: {filename}");
249        dotenv::from_filename(filename).ok();
250    }
251}