BLOG-85 refactor: enhance configuration management with database, server, session, and storage modules
All checks were successful
Frontend CI / build (push) Successful in 1m8s
All checks were successful
Frontend CI / build (push) Successful in 1m8s
This commit is contained in:
parent
8bacf74c9e
commit
62803debee
@ -1,18 +1,33 @@
|
|||||||
use openidconnect::reqwest;
|
use openidconnect::reqwest;
|
||||||
|
|
||||||
use crate::configuration::oidc::OidcConfiguration;
|
use crate::configuration::{
|
||||||
|
db::DbConfiguration, oidc::OidcConfiguration, server::ServerConfiguration,
|
||||||
|
session::SessionConfiguration, storage::StorageConfiguration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod db;
|
||||||
pub mod oidc;
|
pub mod oidc;
|
||||||
|
pub mod server;
|
||||||
|
pub mod session;
|
||||||
|
pub mod storage;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub oidc_configuration: OidcConfiguration,
|
pub db: DbConfiguration,
|
||||||
|
pub oidc: OidcConfiguration,
|
||||||
|
pub server: ServerConfiguration,
|
||||||
|
pub session: SessionConfiguration,
|
||||||
|
pub storage: StorageConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
impl Configuration {
|
||||||
pub async fn new(http_client: reqwest::Client) -> Self {
|
pub async fn new(http_client: reqwest::Client) -> Self {
|
||||||
Self {
|
Self {
|
||||||
oidc_configuration: OidcConfiguration::new(http_client).await,
|
db: DbConfiguration::new(),
|
||||||
|
oidc: OidcConfiguration::new(http_client).await,
|
||||||
|
server: ServerConfiguration::new(),
|
||||||
|
session: SessionConfiguration::new(),
|
||||||
|
storage: StorageConfiguration::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
backend/server/src/configuration/db.rs
Normal file
44
backend/server/src/configuration/db.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
use sqlx::{Pool, Postgres, postgres::PgPoolOptions};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DbConfiguration {
|
||||||
|
pub database_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbConfiguration {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let host = env::var("DATABASE_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||||
|
let port = env::var("DATABASE_PORT").unwrap_or_else(|_| "5432".to_string());
|
||||||
|
let user = env::var("DATABASE_USER").unwrap_or_else(|_| "postgres".to_string());
|
||||||
|
let password = env::var("DATABASE_PASSWORD").unwrap_or_else(|_| "".to_string());
|
||||||
|
let dbname = env::var("DATABASE_NAME").unwrap_or_else(|_| "postgres".to_string());
|
||||||
|
|
||||||
|
let encoded_password =
|
||||||
|
percent_encoding::utf8_percent_encode(&password, percent_encoding::NON_ALPHANUMERIC)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let database_url = format!(
|
||||||
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
|
user, encoded_password, host, port, dbname
|
||||||
|
);
|
||||||
|
|
||||||
|
Self { database_url }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_connection(&self) -> Pool<Postgres> {
|
||||||
|
let db_pool = PgPoolOptions::new()
|
||||||
|
.max_connections(5)
|
||||||
|
.connect(&self.database_url)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create database connection pool");
|
||||||
|
|
||||||
|
sqlx::migrate!("../migrations")
|
||||||
|
.run(&db_pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to run database migrations");
|
||||||
|
|
||||||
|
db_pool
|
||||||
|
}
|
||||||
|
}
|
17
backend/server/src/configuration/server.rs
Normal file
17
backend/server/src/configuration/server.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ServerConfiguration {
|
||||||
|
pub host: String,
|
||||||
|
pub port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerConfiguration {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let host = std::env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||||
|
let port = std::env::var("PORT")
|
||||||
|
.unwrap_or_else(|_| "8080".to_string())
|
||||||
|
.parse::<u16>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Self { host, port }
|
||||||
|
}
|
||||||
|
}
|
36
backend/server/src/configuration/session.rs
Normal file
36
backend/server/src/configuration/session.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use actix_session::storage::RedisSessionStore;
|
||||||
|
use actix_web::cookie::Key;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SessionConfiguration {
|
||||||
|
pub session_key: Key,
|
||||||
|
pub redis_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionConfiguration {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let session_key_hex = std::env::var("SESSION_KEY").expect("SESSION_KEY must be set");
|
||||||
|
let session_key_bytes =
|
||||||
|
hex::decode(session_key_hex).expect("Invalid SESSION_KEY format, must be hex encoded");
|
||||||
|
|
||||||
|
if session_key_bytes.len() != 64 {
|
||||||
|
panic!("SESSION_KEY must be 64 bytes long");
|
||||||
|
}
|
||||||
|
|
||||||
|
let session_key = Key::from(&session_key_bytes);
|
||||||
|
|
||||||
|
let redis_url =
|
||||||
|
std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.1:6379".to_string());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
session_key,
|
||||||
|
redis_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_session_store(&self) -> RedisSessionStore {
|
||||||
|
RedisSessionStore::new(self.redis_url.clone())
|
||||||
|
.await
|
||||||
|
.expect("Failed to create Redis session store")
|
||||||
|
}
|
||||||
|
}
|
13
backend/server/src/configuration/storage.rs
Normal file
13
backend/server/src/configuration/storage.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StorageConfiguration {
|
||||||
|
pub storage_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageConfiguration {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let storage_path = env::var("STORAGE_PATH").unwrap_or_else(|_| "static".to_string());
|
||||||
|
Self { storage_path }
|
||||||
|
}
|
||||||
|
}
|
@ -49,11 +49,10 @@ pub struct Container {
|
|||||||
impl Container {
|
impl Container {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
db_pool: Pool<Postgres>,
|
db_pool: Pool<Postgres>,
|
||||||
storage_path: &str,
|
|
||||||
http_client: reqwest::Client,
|
http_client: reqwest::Client,
|
||||||
configuration: Configuration,
|
configuration: Configuration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let oidc_configuration = &configuration.oidc_configuration;
|
let oidc_configuration = &configuration.oidc;
|
||||||
let auth_oidc_service = Arc::new(AuthOidcServiceImpl::new(
|
let auth_oidc_service = Arc::new(AuthOidcServiceImpl::new(
|
||||||
oidc_configuration.provider_metadata.clone(),
|
oidc_configuration.provider_metadata.clone(),
|
||||||
&oidc_configuration.client_id,
|
&oidc_configuration.client_id,
|
||||||
@ -81,7 +80,7 @@ impl Container {
|
|||||||
));
|
));
|
||||||
|
|
||||||
let image_db_service = Arc::new(ImageDbServiceImpl::new(db_pool.clone()));
|
let image_db_service = Arc::new(ImageDbServiceImpl::new(db_pool.clone()));
|
||||||
let image_storage = Arc::new(ImageStorageImpl::new(storage_path.into()));
|
let image_storage = Arc::new(ImageStorageImpl::new(&configuration.storage.storage_path));
|
||||||
let image_repository = Arc::new(ImageRepositoryImpl::new(
|
let image_repository = Arc::new(ImageRepositoryImpl::new(
|
||||||
image_db_service.clone(),
|
image_db_service.clone(),
|
||||||
image_storage.clone(),
|
image_storage.clone(),
|
||||||
|
@ -4,7 +4,6 @@ use actix_session::{
|
|||||||
use actix_web::{
|
use actix_web::{
|
||||||
App, Error, HttpServer,
|
App, Error, HttpServer,
|
||||||
body::MessageBody,
|
body::MessageBody,
|
||||||
cookie,
|
|
||||||
dev::{ServiceFactory, ServiceRequest, ServiceResponse},
|
dev::{ServiceFactory, ServiceRequest, ServiceResponse},
|
||||||
web,
|
web,
|
||||||
};
|
};
|
||||||
@ -13,36 +12,30 @@ use image::framework::web::image_web_routes::configure_image_routes;
|
|||||||
use openidconnect::reqwest;
|
use openidconnect::reqwest;
|
||||||
use post::framework::web::post_web_routes::configure_post_routes;
|
use post::framework::web::post_web_routes::configure_post_routes;
|
||||||
use server::{configuration::Configuration, container::Container};
|
use server::{configuration::Configuration, container::Container};
|
||||||
use sqlx::{Pool, Postgres, postgres::PgPoolOptions};
|
use sqlx::{Pool, Postgres};
|
||||||
use std::env;
|
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let db_pool = init_database().await;
|
|
||||||
let storage_path = env::var("STORAGE_PATH").unwrap_or_else(|_| "static".to_string());
|
|
||||||
let http_client = reqwest::ClientBuilder::new()
|
let http_client = reqwest::ClientBuilder::new()
|
||||||
.redirect(reqwest::redirect::Policy::none())
|
.redirect(reqwest::redirect::Policy::none())
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to create HTTP client");
|
.expect("Failed to create HTTP client");
|
||||||
|
|
||||||
let session_store = init_session_store().await;
|
|
||||||
let session_key = init_session_key().await;
|
|
||||||
|
|
||||||
let host = env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
|
|
||||||
let port = env::var("PORT")
|
|
||||||
.unwrap_or_else(|_| "8080".to_string())
|
|
||||||
.parse::<u16>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let configuration = Configuration::new(http_client.clone()).await;
|
let configuration = Configuration::new(http_client.clone()).await;
|
||||||
|
|
||||||
|
let host = configuration.server.host.clone();
|
||||||
|
let port = configuration.server.port;
|
||||||
|
|
||||||
|
let db_pool = configuration.db.create_connection().await;
|
||||||
|
let session_key = configuration.session.session_key.clone();
|
||||||
|
let session_store = configuration.session.create_session_store().await;
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
create_app(
|
create_app(
|
||||||
db_pool.clone(),
|
db_pool.clone(),
|
||||||
storage_path.clone(),
|
|
||||||
http_client.clone(),
|
http_client.clone(),
|
||||||
SessionMiddleware::builder(session_store.clone(), session_key.clone()),
|
SessionMiddleware::builder(session_store.clone(), session_key.clone()),
|
||||||
configuration.clone(),
|
configuration.clone(),
|
||||||
@ -53,55 +46,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init_database() -> Pool<Postgres> {
|
|
||||||
let host = env::var("DATABASE_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
|
|
||||||
let port = env::var("DATABASE_PORT").unwrap_or_else(|_| "5432".to_string());
|
|
||||||
let user = env::var("DATABASE_USER").unwrap_or_else(|_| "postgres".to_string());
|
|
||||||
let password = env::var("DATABASE_PASSWORD").unwrap_or_else(|_| "".to_string());
|
|
||||||
let dbname = env::var("DATABASE_NAME").unwrap_or_else(|_| "postgres".to_string());
|
|
||||||
|
|
||||||
let encoded_password =
|
|
||||||
percent_encoding::utf8_percent_encode(&password, percent_encoding::NON_ALPHANUMERIC)
|
|
||||||
.to_string();
|
|
||||||
let database_url = format!(
|
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
|
||||||
user, encoded_password, host, port, dbname
|
|
||||||
);
|
|
||||||
|
|
||||||
let db_pool = PgPoolOptions::new()
|
|
||||||
.max_connections(5)
|
|
||||||
.connect(&database_url)
|
|
||||||
.await
|
|
||||||
.expect("Failed to create database connection pool");
|
|
||||||
|
|
||||||
sqlx::migrate!("../migrations")
|
|
||||||
.run(&db_pool)
|
|
||||||
.await
|
|
||||||
.expect("Failed to run database migrations");
|
|
||||||
|
|
||||||
db_pool
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn init_session_store() -> RedisSessionStore {
|
|
||||||
let redis_url = env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.1:6379".to_string());
|
|
||||||
RedisSessionStore::new(redis_url)
|
|
||||||
.await
|
|
||||||
.expect("Failed to create Redis session store")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn init_session_key() -> cookie::Key {
|
|
||||||
let session_key_hex = env::var("SESSION_KEY").expect("SESSION_KEY must be set");
|
|
||||||
let session_key_bytes =
|
|
||||||
hex::decode(session_key_hex).expect("Invalid SESSION_KEY format, must be hex encoded");
|
|
||||||
if session_key_bytes.len() != 64 {
|
|
||||||
panic!("SESSION_KEY must be 64 bytes long");
|
|
||||||
}
|
|
||||||
cookie::Key::from(&session_key_bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_app(
|
fn create_app(
|
||||||
db_pool: Pool<Postgres>,
|
db_pool: Pool<Postgres>,
|
||||||
storage_path: String,
|
|
||||||
http_client: reqwest::Client,
|
http_client: reqwest::Client,
|
||||||
session_middleware_builder: SessionMiddlewareBuilder<RedisSessionStore>,
|
session_middleware_builder: SessionMiddlewareBuilder<RedisSessionStore>,
|
||||||
configuration: Configuration,
|
configuration: Configuration,
|
||||||
@ -114,7 +60,7 @@ fn create_app(
|
|||||||
Error = Error,
|
Error = Error,
|
||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
let container = Container::new(db_pool, &storage_path, http_client, configuration);
|
let container = Container::new(db_pool, http_client, configuration);
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(session_middleware_builder.build())
|
.wrap(session_middleware_builder.build())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user