1use anyhow::Result;
2use serde::Deserialize;
3
4#[derive(Debug, Clone, Deserialize, PartialEq)]
5pub enum DeploymentType {
6 OSS,
7 Private,
8}
9
10impl std::str::FromStr for DeploymentType {
11 type Err = anyhow::Error;
12
13 fn from_str(s: &str) -> Result<Self> {
14 match s.to_lowercase().as_str() {
15 "oss" => Ok(DeploymentType::OSS),
16 "private" => Ok(DeploymentType::Private),
17 _ => Err(anyhow::anyhow!("Invalid deployment type: {}", s)),
18 }
19 }
20}
21
22#[derive(Debug, Clone, Deserialize)]
23pub struct Config {
24 pub port: u16,
25 pub base_url: String,
26 pub fips_mode: bool,
27 pub fips_validation: bool,
28 pub deployment_type: DeploymentType,
29 pub license_signing_key: String,
30}
31
32impl Default for Config {
33 fn default() -> Self {
34 Self {
35 port: 8080,
36 base_url: "http://localhost:8080".to_string(),
37 fips_mode: false,
38 fips_validation: false,
39 deployment_type: DeploymentType::OSS,
40 license_signing_key: "t6eLp6y0Ly8BZJIVv_wK71WyBtJ1zY2Pxz2M_0z5t8Q".to_string(),
41 }
42 }
43}
44
45impl Config {
46 pub fn from_env() -> Result<Self> {
47 dotenvy::dotenv().ok();
48
49 let deployment_type = std::env::var("NABLA_DEPLOYMENT")
50 .unwrap_or_else(|_| "oss".to_string())
51 .parse()?;
52
53 let license_signing_key = Self::get_license_signing_key(&deployment_type)?;
54
55 let config = Config {
56 port: std::env::var("PORT")
57 .unwrap_or_else(|_| "8080".to_string())
58 .parse()?,
59 base_url: std::env::var("BASE_URL")
60 .unwrap_or_else(|_| "http://localhost:8080".to_string()),
61 fips_mode: std::env::var("FIPS_MODE")
62 .unwrap_or_else(|_| "false".to_string())
63 .parse()
64 .unwrap_or(false),
65 fips_validation: std::env::var("FIPS_VALIDATION")
66 .unwrap_or_else(|_| "false".to_string())
67 .parse()
68 .unwrap_or(false),
69 deployment_type,
70 license_signing_key,
71 };
72
73 Ok(config)
74 }
75
76 fn get_license_signing_key(deployment_type: &DeploymentType) -> Result<String> {
77 if let Ok(key) = std::env::var("LICENSE_SIGNING_KEY") {
79 return Ok(key);
80 }
81
82 if let (Ok(project), Ok(config_name)) = (
84 std::env::var("DOPPLER_PROJECT"),
85 std::env::var("DOPPLER_CONFIG")
86 ) {
87 let doppler_token = if config_name.contains("prd") {
89 std::env::var("DOPPLER_TOKEN_PRD")
90 .or_else(|_| std::env::var("DOPPLER_TOKEN"))
91 } else if config_name.contains("oss") {
92 std::env::var("DOPPLER_TOKEN_OSS")
93 .or_else(|_| std::env::var("DOPPLER_TOKEN"))
94 } else {
95 std::env::var("DOPPLER_TOKEN")
96 };
97
98 if let Ok(token) = doppler_token {
99
100 let url = format!("https://api.doppler.com/v3/configs/config/secret?project={}&config={}&name=LICENSE_SIGNING_KEY",
102 project, config_name);
103
104 match ureq::get(&url)
105 .set("Authorization", &format!("Bearer {}", token))
106 .call()
107 {
108 Ok(response) => {
109 match response.into_json::<serde_json::Value>() {
110 Ok(json) => {
111 if let Some(value) = json.get("value")
112 .and_then(|v| v.get("computed"))
113 .and_then(|c| c.as_str())
114 {
115 return Ok(value.to_string());
116 }
117 }
118 Err(_) => {}
119 }
120 }
121 Err(_) => {}
122 }
123 }
124 }
125
126 match deployment_type {
128 DeploymentType::OSS => {
129 Ok("t6eLp6y0Ly8BZJIVv_wK71WyBtJ1zY2Pxz2M_0z5t8Q".to_string())
131 }
132 DeploymentType::Private => {
133 Err(anyhow::anyhow!("LICENSE_SIGNING_KEY required for private deployment (try Doppler or env var)"))
134 }
135 }
136 }
137}