1use anyhow::Result;
2use serde::Deserialize;
3
4#[derive(Debug, Clone, Deserialize, PartialEq)]
5pub enum DeploymentType {
6 OSS,
7 NablaSecure,
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 "nablasecure" | "nabla-secure" | "secure" => Ok(DeploymentType::NablaSecure),
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 deployment_type: DeploymentType,
27 pub enterprise_features: bool,
28 pub license_signing_key: String,
29}
30
31impl Default for Config {
32 fn default() -> Self {
33 Self {
34 port: 8080,
35 base_url: "http://localhost:8080".to_string(),
36 deployment_type: DeploymentType::OSS,
37 enterprise_features: false, license_signing_key: "t6eLp6y0Ly8BZJIVv_wK71WyBtJ1zY2Pxz2M_0z5t8Q".to_string(),
39 }
40 }
41}
42
43impl Config {
44 pub fn from_env() -> Result<Self> {
45 dotenvy::dotenv().ok();
46
47 let deployment_type = std::env::var("NABLA_DEPLOYMENT")
48 .unwrap_or_else(|_| "oss".to_string())
49 .parse()?;
50
51 let enterprise_features = std::env::var("NABLA_ENTERPRISE_FEATURES")
52 .unwrap_or_else(|_| match deployment_type {
53 DeploymentType::NablaSecure => "true".to_string(),
54 DeploymentType::OSS => "false".to_string(),
55 })
56 .parse()
57 .unwrap_or(false);
58
59 let license_signing_key = Self::get_license_signing_key(&deployment_type)?;
60
61 let config = Config {
62 port: std::env::var("PORT")
63 .unwrap_or_else(|_| "8080".to_string())
64 .parse()?,
65 base_url: std::env::var("BASE_URL")
66 .unwrap_or_else(|_| "http://localhost:8080".to_string()),
67 deployment_type,
68 enterprise_features,
69 license_signing_key,
70 };
71
72 Ok(config)
73 }
74
75 fn get_license_signing_key(deployment_type: &DeploymentType) -> Result<String> {
76 if let Ok(key) = std::env::var("LICENSE_SIGNING_KEY") {
78 return Ok(key);
79 }
80
81 if let (Ok(project), Ok(config_name)) = (
83 std::env::var("DOPPLER_PROJECT"),
84 std::env::var("DOPPLER_CONFIG"),
85 ) {
86 let doppler_token = if config_name.contains("prd") {
88 std::env::var("DOPPLER_TOKEN_PRD").or_else(|_| std::env::var("DOPPLER_TOKEN"))
89 } else if config_name.contains("oss") {
90 std::env::var("DOPPLER_TOKEN_OSS").or_else(|_| std::env::var("DOPPLER_TOKEN"))
91 } else {
92 std::env::var("DOPPLER_TOKEN")
93 };
94
95 if let Ok(token) = doppler_token {
96 let url = format!(
98 "https://api.doppler.com/v3/configs/config/secret?project={}&config={}&name=LICENSE_SIGNING_KEY",
99 project, config_name
100 );
101
102 match ureq::get(&url)
103 .set("Authorization", &format!("Bearer {}", token))
104 .call()
105 {
106 Ok(response) => match response.into_json::<serde_json::Value>() {
107 Ok(json) => {
108 if let Some(value) = json
109 .get("value")
110 .and_then(|v| v.get("computed"))
111 .and_then(|c| c.as_str())
112 {
113 return Ok(value.to_string());
114 }
115 }
116 Err(_) => {}
117 },
118 Err(_) => {}
119 }
120 }
121 }
122
123 match deployment_type {
125 DeploymentType::OSS => {
126 Ok("t6eLp6y0Ly8BZJIVv_wK71WyBtJ1zY2Pxz2M_0z5t8Q".to_string())
128 }
129 DeploymentType::NablaSecure => Err(anyhow::anyhow!(
130 "LICENSE_SIGNING_KEY required for NablaSecure deployment (try Doppler or env var)"
131 )),
132 }
133 }
134}