diff --git a/apps/api/src/cmd/start_server.rs b/apps/api/src/cmd/start_server.rs index 7545156..c33cadf 100644 --- a/apps/api/src/cmd/start_server.rs +++ b/apps/api/src/cmd/start_server.rs @@ -147,6 +147,7 @@ fn get_app_state( ) -> AppState { AppState { database_connection: db_connection.clone(), + config: Arc::new(settings.clone()), service: Arc::new(AppService { server_state: Arc::new(ServerStateService::new(db_connection.clone())), settings: Arc::new(SettingsService::new(db_connection.clone())), diff --git a/apps/api/src/configs.rs b/apps/api/src/configs.rs index 6c274d6..68b46d0 100644 --- a/apps/api/src/configs.rs +++ b/apps/api/src/configs.rs @@ -11,6 +11,8 @@ use tracing::{debug, error}; pub trait FromConfig: Sized { fn from_config(config: &Config) -> Result; fn validate(&self) -> Result<(), String>; + #[cfg(test)] + fn mock() -> Self; } #[derive(Debug, Clone)] @@ -40,6 +42,16 @@ impl FromConfig for ProgramSettings { self.auth.validate()?; Ok(()) } + + #[cfg(test)] + fn mock() -> Self { + ProgramSettings { + logging: logging::LoggingSettings::mock(), + database: database::DatabaseSettings::mock(), + server: server::ServerSettings::mock(), + auth: auth::AuthSettings::mock(), + } + } } pub fn get_program_settings() -> ProgramSettings { diff --git a/apps/api/src/configs/auth.rs b/apps/api/src/configs/auth.rs index 4041092..c115241 100644 --- a/apps/api/src/configs/auth.rs +++ b/apps/api/src/configs/auth.rs @@ -48,4 +48,13 @@ impl FromConfig for AuthSettings { fn validate(&self) -> Result<(), String> { Ok(()) } + + #[cfg(test)] + fn mock() -> Self { + AuthSettings { + jwt_secret: Some("mock_jwt_secret".to_string()), + default_admin_username: Some("admin".to_string()), + default_admin_password: Some("password".to_string()), + } + } } diff --git a/apps/api/src/configs/database.rs b/apps/api/src/configs/database.rs index 25d9a78..596bfa3 100644 --- a/apps/api/src/configs/database.rs +++ b/apps/api/src/configs/database.rs @@ -50,4 +50,13 @@ impl FromConfig for DatabaseSettings { fn validate(&self) -> Result<(), String> { Ok(()) } + + #[cfg(test)] + fn mock() -> Self { + DatabaseSettings { + url: "sqlite::memory:".to_string(), + max_connections: 5, + migrate_on_startup: true, + } + } } diff --git a/apps/api/src/configs/key.rs b/apps/api/src/configs/key.rs index 204be32..dae73a4 100644 --- a/apps/api/src/configs/key.rs +++ b/apps/api/src/configs/key.rs @@ -5,6 +5,7 @@ pub(crate) const SERVER_ADDRESS_KEY: &str = "SERVER.ADDRESS"; pub(crate) const SERVER_PORT_KEY: &str = "SERVER.PORT"; pub(crate) const SERVER_SERVE_OPENAPI_KEY: &str = "SERVER.SERVE_OPENAPI"; pub(crate) const SERVER_CORS_ALLOWED_ORIGINS_KEY: &str = "SERVER.CORS.ALLOWED_ORIGINS"; +pub(crate) const SERVER_COOKIES_SECURE_KEY: &str = "SERVER.COOKIES.SECURE"; // pub(crate) const DATABASE_URL_KEY: &str = "DATABASE.URL"; pub(crate) const DATABASE_MAX_CONNECTIONS_KEY: &str = "DATABASE.MAX_CONNECTIONS"; diff --git a/apps/api/src/configs/logging.rs b/apps/api/src/configs/logging.rs index 1aa47b9..55bb578 100644 --- a/apps/api/src/configs/logging.rs +++ b/apps/api/src/configs/logging.rs @@ -49,4 +49,12 @@ impl FromConfig for LoggingSettings { fn validate(&self) -> Result<(), String> { Ok(()) } + + #[cfg(test)] + fn mock() -> Self { + LoggingSettings { + level: Level::INFO, + utc: false, + } + } } diff --git a/apps/api/src/configs/server.rs b/apps/api/src/configs/server.rs index 0ff7bae..3623f56 100644 --- a/apps/api/src/configs/server.rs +++ b/apps/api/src/configs/server.rs @@ -3,7 +3,7 @@ use std::net::IpAddr; use config::{Config, ConfigError}; use tracing::warn; -use crate::configs::key::{SERVER_CORS_ALLOWED_ORIGINS_KEY, SERVER_SERVE_OPENAPI_KEY}; +use crate::configs::key::{SERVER_COOKIES_SECURE_KEY, SERVER_CORS_ALLOWED_ORIGINS_KEY, SERVER_SERVE_OPENAPI_KEY}; use super::{ FromConfig, @@ -16,6 +16,7 @@ pub struct ServerSettings { pub port: u16, pub serve_openapi: bool, pub cors: CORSSettings, + pub cookies: CookiesSettings, } #[derive(Debug, Clone)] @@ -23,6 +24,11 @@ pub struct CORSSettings { pub allowed_origins: Vec, } +#[derive(Debug, Clone)] +pub struct CookiesSettings { + pub secure: bool, +} + impl FromConfig for ServerSettings { fn from_config(_config: &Config) -> Result { Ok(ServerSettings { @@ -81,6 +87,24 @@ impl FromConfig for ServerSettings { }) .collect(), }, + + cookies: CookiesSettings { + secure: _config + .get_bool(SERVER_COOKIES_SECURE_KEY) + .inspect(|is_secure| { + if !*is_secure { + warn!("Cookie 'secure' flag is disabled; this is not recommended in production environments."); + } + }) + .unwrap_or_else(|err| { + const DEFAULT_COOKIES_SECURE: bool = true; + warn!( + "{} not set or invalid in configuration, defaulting to {}. Error: {}", + SERVER_COOKIES_SECURE_KEY, DEFAULT_COOKIES_SECURE, err + ); + DEFAULT_COOKIES_SECURE + }), + }, }) } @@ -91,4 +115,19 @@ impl FromConfig for ServerSettings { } Ok(()) } -} + + #[cfg(test)] + fn mock() -> Self { + ServerSettings { + address: "0.0.0.0".parse().unwrap(), + port: 8080, + serve_openapi: false, + cors: CORSSettings { + allowed_origins: vec![], + }, + cookies: CookiesSettings { + secure: true, + }, + } + } +} \ No newline at end of file diff --git a/apps/api/src/routes.rs b/apps/api/src/routes.rs index 37d1cdd..cffc71b 100644 --- a/apps/api/src/routes.rs +++ b/apps/api/src/routes.rs @@ -9,7 +9,7 @@ use axum::{Extension, Router}; use migration::sea_orm::DatabaseConnection; use crate::{ - configs::server::CORSSettings, + configs::{ProgramSettings, server::CORSSettings}, middlewares, services::{ auth::{ @@ -23,10 +23,9 @@ use crate::{ #[derive(Clone)] pub struct AppState { - #[allow(dead_code)] pub database_connection: Arc, - #[allow(dead_code)] pub service: Arc, + pub config: Arc, } pub type ServiceState = Arc; diff --git a/apps/api/src/routes/api/auth/login.rs b/apps/api/src/routes/api/auth/login.rs index cab7cd5..1d4519f 100644 --- a/apps/api/src/routes/api/auth/login.rs +++ b/apps/api/src/routes/api/auth/login.rs @@ -84,10 +84,15 @@ pub async fn login(State(state): State>, Json(payload): Json