nabla_cli/
config.rs

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, // OSS defaults to no enterprise features
38            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        // Try environment variable first (fastest)
77        if let Ok(key) = std::env::var("LICENSE_SIGNING_KEY") {
78            return Ok(key);
79        }
80
81        // Try Doppler API via HTTP for both OSS and NablaSecure deployments
82        if let (Ok(project), Ok(config_name)) = (
83            std::env::var("DOPPLER_PROJECT"),
84            std::env::var("DOPPLER_CONFIG"),
85        ) {
86            // Try deployment-specific token first, then fall back to general token
87            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                // Use ureq for sync HTTP requests (no runtime conflicts)
97                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        // Final fallback based on deployment type
124        match deployment_type {
125            DeploymentType::OSS => {
126                // Hardcoded public key for OSS deployments as last resort
127                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}