feat(db): Added database setup and basic types (#5)
Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com> Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
253
src/infrastructure/config.rs
Normal file
253
src/infrastructure/config.rs
Normal file
@@ -0,0 +1,253 @@
|
||||
//! Configuration structs loaded once at startup from environment variables.
|
||||
//!
|
||||
//! Each struct provides a `from_env()` constructor that reads `std::env::var`
|
||||
//! values. Required variables cause an `Error::ConfigError` on failure;
|
||||
//! optional ones default to an empty string.
|
||||
|
||||
use secrecy::SecretString;
|
||||
|
||||
use super::Error;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Read a required environment variable or return `Error::ConfigError`.
|
||||
fn required_env(name: &str) -> Result<String, Error> {
|
||||
std::env::var(name).map_err(|_| Error::ConfigError(format!("{name} is required but not set")))
|
||||
}
|
||||
|
||||
/// Read an optional environment variable, defaulting to an empty string.
|
||||
fn optional_env(name: &str) -> String {
|
||||
std::env::var(name).unwrap_or_default()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KeycloakConfig
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Keycloak OpenID Connect settings for the public (frontend) client.
|
||||
///
|
||||
/// Also carries the admin service-account credentials used for
|
||||
/// server-to-server calls (e.g. user management APIs).
|
||||
#[derive(Debug)]
|
||||
pub struct KeycloakConfig {
|
||||
/// Base URL of the Keycloak instance (e.g. `http://localhost:8080`).
|
||||
pub url: String,
|
||||
/// Keycloak realm name.
|
||||
pub realm: String,
|
||||
/// Public client ID used by the dashboard frontend.
|
||||
pub client_id: String,
|
||||
/// OAuth redirect URI registered in Keycloak.
|
||||
pub redirect_uri: String,
|
||||
/// Root URL of this application (used for post-logout redirect).
|
||||
pub app_url: String,
|
||||
/// Confidential client ID for admin/server-to-server calls.
|
||||
pub admin_client_id: String,
|
||||
/// Confidential client secret (wrapped for debug safety).
|
||||
pub admin_client_secret: SecretString,
|
||||
}
|
||||
|
||||
impl KeycloakConfig {
|
||||
/// Load Keycloak configuration from environment variables.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Error::ConfigError` if a required variable is missing.
|
||||
pub fn from_env() -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
url: required_env("KEYCLOAK_URL")?,
|
||||
realm: required_env("KEYCLOAK_REALM")?,
|
||||
client_id: required_env("KEYCLOAK_CLIENT_ID")?,
|
||||
redirect_uri: required_env("REDIRECT_URI")?,
|
||||
app_url: required_env("APP_URL")?,
|
||||
admin_client_id: optional_env("KEYCLOAK_ADMIN_CLIENT_ID"),
|
||||
admin_client_secret: SecretString::from(optional_env("KEYCLOAK_ADMIN_CLIENT_SECRET")),
|
||||
})
|
||||
}
|
||||
|
||||
/// OpenID Connect authorization endpoint URL.
|
||||
pub fn auth_endpoint(&self) -> String {
|
||||
format!(
|
||||
"{}/realms/{}/protocol/openid-connect/auth",
|
||||
self.url, self.realm
|
||||
)
|
||||
}
|
||||
|
||||
/// OpenID Connect token endpoint URL.
|
||||
pub fn token_endpoint(&self) -> String {
|
||||
format!(
|
||||
"{}/realms/{}/protocol/openid-connect/token",
|
||||
self.url, self.realm
|
||||
)
|
||||
}
|
||||
|
||||
/// OpenID Connect userinfo endpoint URL.
|
||||
pub fn userinfo_endpoint(&self) -> String {
|
||||
format!(
|
||||
"{}/realms/{}/protocol/openid-connect/userinfo",
|
||||
self.url, self.realm
|
||||
)
|
||||
}
|
||||
|
||||
/// OpenID Connect end-session (logout) endpoint URL.
|
||||
pub fn logout_endpoint(&self) -> String {
|
||||
format!(
|
||||
"{}/realms/{}/protocol/openid-connect/logout",
|
||||
self.url, self.realm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SmtpConfig
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// SMTP mail settings for transactional emails (invites, alerts, etc.).
|
||||
#[derive(Debug)]
|
||||
pub struct SmtpConfig {
|
||||
/// SMTP server hostname.
|
||||
pub host: String,
|
||||
/// SMTP server port (as string for flexibility, e.g. "587").
|
||||
pub port: String,
|
||||
/// SMTP username.
|
||||
pub username: String,
|
||||
/// SMTP password (wrapped for debug safety).
|
||||
pub password: SecretString,
|
||||
/// Sender address shown in the `From:` header.
|
||||
pub from_address: String,
|
||||
}
|
||||
|
||||
impl SmtpConfig {
|
||||
/// Load SMTP configuration from environment variables.
|
||||
///
|
||||
/// All fields are optional; defaults to empty strings when absent.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Currently infallible but returns `Result` for consistency.
|
||||
pub fn from_env() -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
host: optional_env("SMTP_HOST"),
|
||||
port: optional_env("SMTP_PORT"),
|
||||
username: optional_env("SMTP_USERNAME"),
|
||||
password: SecretString::from(optional_env("SMTP_PASSWORD")),
|
||||
from_address: optional_env("SMTP_FROM_ADDRESS"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ServiceUrls
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// URLs and credentials for external services (Ollama, SearXNG, S3, etc.).
|
||||
#[derive(Debug)]
|
||||
pub struct ServiceUrls {
|
||||
/// Ollama LLM instance base URL.
|
||||
pub ollama_url: String,
|
||||
/// Default Ollama model to use.
|
||||
pub ollama_model: String,
|
||||
/// SearXNG meta-search engine base URL.
|
||||
pub searxng_url: String,
|
||||
/// LangChain service URL.
|
||||
pub langchain_url: String,
|
||||
/// LangGraph service URL.
|
||||
pub langgraph_url: String,
|
||||
/// Langfuse observability URL.
|
||||
pub langfuse_url: String,
|
||||
/// Vector database URL.
|
||||
pub vectordb_url: String,
|
||||
/// S3-compatible object storage URL.
|
||||
pub s3_url: String,
|
||||
/// S3 access key.
|
||||
pub s3_access_key: String,
|
||||
/// S3 secret key (wrapped for debug safety).
|
||||
pub s3_secret_key: SecretString,
|
||||
}
|
||||
|
||||
impl ServiceUrls {
|
||||
/// Load service URLs from environment variables.
|
||||
///
|
||||
/// All fields are optional with sensible defaults where applicable.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Currently infallible but returns `Result` for consistency.
|
||||
pub fn from_env() -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
ollama_url: std::env::var("OLLAMA_URL")
|
||||
.unwrap_or_else(|_| "http://localhost:11434".into()),
|
||||
ollama_model: std::env::var("OLLAMA_MODEL").unwrap_or_else(|_| "llama3.1:8b".into()),
|
||||
searxng_url: std::env::var("SEARXNG_URL")
|
||||
.unwrap_or_else(|_| "http://localhost:8888".into()),
|
||||
langchain_url: optional_env("LANGCHAIN_URL"),
|
||||
langgraph_url: optional_env("LANGGRAPH_URL"),
|
||||
langfuse_url: optional_env("LANGFUSE_URL"),
|
||||
vectordb_url: optional_env("VECTORDB_URL"),
|
||||
s3_url: optional_env("S3_URL"),
|
||||
s3_access_key: optional_env("S3_ACCESS_KEY"),
|
||||
s3_secret_key: SecretString::from(optional_env("S3_SECRET_KEY")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// StripeConfig
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Stripe billing configuration.
|
||||
#[derive(Debug)]
|
||||
pub struct StripeConfig {
|
||||
/// Stripe secret API key (wrapped for debug safety).
|
||||
pub secret_key: SecretString,
|
||||
/// Stripe webhook signing secret (wrapped for debug safety).
|
||||
pub webhook_secret: SecretString,
|
||||
/// Stripe publishable key (safe to expose to the frontend).
|
||||
pub publishable_key: String,
|
||||
}
|
||||
|
||||
impl StripeConfig {
|
||||
/// Load Stripe configuration from environment variables.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Currently infallible but returns `Result` for consistency.
|
||||
pub fn from_env() -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
secret_key: SecretString::from(optional_env("STRIPE_SECRET_KEY")),
|
||||
webhook_secret: SecretString::from(optional_env("STRIPE_WEBHOOK_SECRET")),
|
||||
publishable_key: optional_env("STRIPE_PUBLISHABLE_KEY"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LlmProvidersConfig
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Comma-separated list of enabled LLM provider identifiers.
|
||||
///
|
||||
/// For example: `LLM_PROVIDERS=ollama,openai,anthropic`
|
||||
#[derive(Debug)]
|
||||
pub struct LlmProvidersConfig {
|
||||
/// Parsed provider names.
|
||||
pub providers: Vec<String>,
|
||||
}
|
||||
|
||||
impl LlmProvidersConfig {
|
||||
/// Load the provider list from `LLM_PROVIDERS`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Currently infallible but returns `Result` for consistency.
|
||||
pub fn from_env() -> Result<Self, Error> {
|
||||
let raw = optional_env("LLM_PROVIDERS");
|
||||
let providers: Vec<String> = raw
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
Ok(Self { providers })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user