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 let path = format!("apps/{service}/.env");
230 debug!("Attempting to load env file: {}", path);
231 dotenv::from_filename(path).ok();
232
233 if Path::new(".env").exists() {
235 info!("loading env file: .env");
236 dotenv::from_filename(".env").ok();
237 }
238
239 if Path::new(".env.main").exists() {
241 info!("loading env file: .env.main");
242 dotenv::from_filename(".env.main").ok();
243 }
244
245 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}