config: simplify runtime environment model

This commit is contained in:
a.tolmachev
2026-04-11 12:40:00 +03:00
parent 9b6ce337e6
commit 51cf7691be
14 changed files with 127 additions and 66 deletions
+1 -1
View File
@@ -23,6 +23,7 @@ serde.workspace = true
serde_json.workspace = true
serde_yaml.workspace = true
sha2.workspace = true
sqlx.workspace = true
thiserror.workspace = true
time.workspace = true
tokio = { workspace = true, features = ["fs"] }
@@ -33,4 +34,3 @@ uuid.workspace = true
[dev-dependencies]
reqwest.workspace = true
serial_test = "3"
sqlx.workspace = true
+26 -5
View File
@@ -9,6 +9,7 @@ mod storage;
use std::{env, net::SocketAddr, path::PathBuf};
use crank_registry::PostgresRegistry;
use sqlx::postgres::PgConnectOptions;
use tokio::net::TcpListener;
use tracing::info;
@@ -28,15 +29,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
)
.init();
let database_url = env::var("CRANK_DATABASE_URL")?;
let storage_root = PathBuf::from(
env::var("CRANK_STORAGE_ROOT").unwrap_or_else(|_| "/var/lib/crank/storage".into()),
);
let bind_addr = env::var("CRANK_ADMIN_BIND").unwrap_or_else(|_| "0.0.0.0:3001".into());
let public_base_url =
env::var("CRANK_PUBLIC_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".into());
let base_url = env::var("CRANK_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".into());
let socket_addr: SocketAddr = bind_addr.parse()?;
let registry = PostgresRegistry::connect(&database_url).await?;
let registry = PostgresRegistry::connect_with_options(database_options_from_env()?).await?;
let auth_settings = AuthSettings {
session_secret: env::var("CRANK_SESSION_SECRET")?,
password_pepper: env::var("CRANK_PASSWORD_PEPPER")?,
@@ -44,7 +43,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.ok()
.and_then(|value| value.parse::<i64>().ok())
.unwrap_or(24),
cookie_secure: public_base_url.starts_with("https://"),
cookie_secure: base_url.starts_with("https://"),
bootstrap_admin: BootstrapAdminConfig {
email: env::var("CRANK_BOOTSTRAP_ADMIN_EMAIL")?,
password: env::var("CRANK_BOOTSTRAP_ADMIN_PASSWORD")?,
@@ -79,3 +78,25 @@ fn env_flag(name: &str) -> bool {
Some("1" | "true" | "yes" | "on")
)
}
fn database_options_from_env() -> Result<PgConnectOptions, Box<dyn std::error::Error>> {
if let Ok(database_url) = env::var("CRANK_DATABASE_URL") {
return Ok(database_url.parse::<PgConnectOptions>()?);
}
let host = env::var("POSTGRES_HOST").unwrap_or_else(|_| "postgres".into());
let port = env::var("POSTGRES_PORT")
.ok()
.and_then(|value| value.parse::<u16>().ok())
.unwrap_or(5432);
let database = env::var("POSTGRES_DB").unwrap_or_else(|_| "crank".into());
let username = env::var("POSTGRES_USER").unwrap_or_else(|_| "crank".into());
let password = env::var("POSTGRES_PASSWORD").unwrap_or_else(|_| "crank".into());
Ok(PgConnectOptions::new()
.host(&host)
.port(port)
.database(&database)
.username(&username)
.password(&password))
}
+1 -1
View File
@@ -16,6 +16,7 @@ futures-util = "0.3"
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
sqlx.workspace = true
time.workspace = true
tokio = { workspace = true, features = ["sync"] }
tracing.workspace = true
@@ -27,4 +28,3 @@ crank-adapter-grpc = { path = "../../crates/crank-adapter-grpc", features = ["te
crank-mapping = { path = "../../crates/crank-mapping" }
crank-schema = { path = "../../crates/crank-schema" }
reqwest.workspace = true
sqlx.workspace = true
+26 -4
View File
@@ -7,6 +7,7 @@ use std::{env, net::SocketAddr, time::Duration};
use crank_registry::PostgresRegistry;
use crank_runtime::SecretCrypto;
use sqlx::postgres::PgConnectOptions;
use tokio::net::TcpListener;
use tracing::info;
@@ -21,18 +22,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
)
.init();
let database_url = env::var("CRANK_DATABASE_URL")?;
let bind_addr = env::var("CRANK_MCP_BIND").unwrap_or_else(|_| "0.0.0.0:3002".into());
let public_base_url = env::var("CRANK_PUBLIC_BASE_URL").ok();
let base_url = env::var("CRANK_BASE_URL").ok();
let refresh_interval = env::var("CRANK_MCP_REFRESH_MS")
.ok()
.and_then(|value| value.parse::<u64>().ok())
.map(Duration::from_millis)
.unwrap_or_else(|| Duration::from_secs(5));
let socket_addr: SocketAddr = bind_addr.parse()?;
let registry = PostgresRegistry::connect(&database_url).await?;
let registry = PostgresRegistry::connect_with_options(database_options_from_env()?).await?;
let secret_crypto = SecretCrypto::new(&env::var("CRANK_MASTER_KEY")?)?;
let app = build_app(registry, refresh_interval, public_base_url, secret_crypto);
let app = build_app(registry, refresh_interval, base_url, secret_crypto);
let listener = TcpListener::bind(socket_addr).await?;
info!("mcp-server listening on {}", socket_addr);
@@ -42,6 +42,28 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
fn database_options_from_env() -> Result<PgConnectOptions, Box<dyn std::error::Error>> {
if let Ok(database_url) = env::var("CRANK_DATABASE_URL") {
return Ok(database_url.parse::<PgConnectOptions>()?);
}
let host = env::var("POSTGRES_HOST").unwrap_or_else(|_| "postgres".into());
let port = env::var("POSTGRES_PORT")
.ok()
.and_then(|value| value.parse::<u16>().ok())
.unwrap_or(5432);
let database = env::var("POSTGRES_DB").unwrap_or_else(|_| "crank".into());
let username = env::var("POSTGRES_USER").unwrap_or_else(|_| "crank".into());
let password = env::var("POSTGRES_PASSWORD").unwrap_or_else(|_| "crank".into());
Ok(PgConnectOptions::new()
.host(&host)
.port(port)
.database(&database)
.username(&username)
.password(&password))
}
#[cfg(test)]
mod tests {
use std::{
+6 -4
View File
@@ -91,13 +91,16 @@ until docker exec "$POSTGRES_CONTAINER" pg_isready -U "$POSTGRES_USER" -d "$POST
sleep 1
done
export CRANK_DATABASE_URL="postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@127.0.0.1:$POSTGRES_PORT/$POSTGRES_DB"
export POSTGRES_HOST="127.0.0.1"
export POSTGRES_PORT="$POSTGRES_PORT"
export POSTGRES_DB="$POSTGRES_DB"
export POSTGRES_USER="$POSTGRES_USER"
export POSTGRES_PASSWORD="$POSTGRES_PASSWORD"
export CRANK_STORAGE_ROOT="$TMP_DIR/storage"
export CRANK_ADMIN_BIND="0.0.0.0:$ADMIN_PORT"
export CRANK_MCP_BIND="0.0.0.0:$MCP_PORT"
export CRANK_MCP_REFRESH_MS="1000"
export CRANK_LOG_LEVEL="info"
export CRANK_SECRET_PROVIDER="env"
export CRANK_SESSION_SECRET="e2e-session-secret"
export CRANK_PASSWORD_PEPPER="e2e-password-pepper"
export CRANK_MASTER_KEY="0000000000000000000000000000000000000000000000000000000000000000"
@@ -106,8 +109,7 @@ export CRANK_BOOTSTRAP_ADMIN_EMAIL="$ADMIN_EMAIL"
export CRANK_BOOTSTRAP_ADMIN_PASSWORD="$ADMIN_PASSWORD"
export CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME="Crank E2E"
export CRANK_DEMO_SEED="true"
export CRANK_PUBLIC_BASE_URL="http://127.0.0.1:$UI_PORT"
export CRANK_MCP_PUBLIC_URL="http://127.0.0.1:$MCP_PORT"
export CRANK_BASE_URL="http://127.0.0.1:$UI_PORT"
export CRANK_E2E_STREAM_FIXTURE_PORT="$STREAM_FIXTURE_PORT"
export CRANK_E2E_GRPC_FIXTURE_BIND="$GRPC_FIXTURE_BIND"