systemprompt_cloud/
secrets_env.rs1use std::collections::HashMap;
11use std::path::Path;
12
13use base64::Engine;
14
15use crate::constants::env_vars;
16use crate::error::{CloudError, CloudResult};
17
18pub fn load_secrets_json(path: &Path) -> CloudResult<HashMap<String, String>> {
19 let content = std::fs::read_to_string(path)
20 .map_err(|e| CloudError::deploy_with(format!("Failed to read {}", path.display()), e))?;
21
22 let json: serde_json::Value = serde_json::from_str(&content)
25 .map_err(|e| CloudError::deploy_with("Failed to parse secrets.json", e))?;
26
27 let mut secrets = HashMap::new();
28
29 if let Some(obj) = json.as_object() {
30 for (key, value) in obj {
31 if let Some(s) = value.as_str()
32 && !s.is_empty()
33 {
34 secrets.insert(key.clone(), s.to_owned());
35 }
36 }
37 }
38
39 Ok(secrets)
40}
41
42#[must_use]
43pub fn map_secrets_to_env_vars<S: std::hash::BuildHasher>(
44 secrets: HashMap<String, String, S>,
45) -> HashMap<String, String> {
46 let has_internal = secrets.contains_key("internal_database_url");
47
48 let mut result: HashMap<String, String> = secrets
49 .into_iter()
50 .filter_map(|(k, v)| {
51 let env_key = to_env_var_name(&k, has_internal)?;
52 if env_vars::is_system_managed(&env_key) {
53 tracing::warn!(key = %env_key, "Skipping system-managed variable from secrets.json");
54 return None;
55 }
56 Some((env_key, v))
57 })
58 .collect();
59
60 let custom_keys: Vec<String> = result
61 .keys()
62 .filter(|k| !is_standard_env_var(k))
63 .cloned()
64 .collect();
65
66 if !custom_keys.is_empty() {
67 result.insert(env_vars::CUSTOM_SECRETS.to_owned(), custom_keys.join(","));
68 }
69
70 result
71}
72
73fn to_env_var_name(key: &str, has_internal_db_url: bool) -> Option<String> {
74 match key {
75 "gemini" => Some("GEMINI_API_KEY".to_owned()),
76 "anthropic" => Some("ANTHROPIC_API_KEY".to_owned()),
77 "openai" => Some("OPENAI_API_KEY".to_owned()),
78 "internal_database_url" => Some("DATABASE_URL".to_owned()),
79 "database_url" if has_internal_db_url => None,
80 _ => Some(key.to_uppercase()),
81 }
82}
83
84fn is_standard_env_var(key: &str) -> bool {
85 matches!(
86 key,
87 "OAUTH_AT_REST_PEPPER"
88 | "DATABASE_URL"
89 | "GEMINI_API_KEY"
90 | "ANTHROPIC_API_KEY"
91 | "OPENAI_API_KEY"
92 | "GITHUB_TOKEN"
93 )
94}
95
96pub fn read_signing_key_pem(path: &Path) -> CloudResult<Option<String>> {
97 if !path.exists() {
98 return Ok(None);
99 }
100 let pem = std::fs::read_to_string(path).map_err(|e| {
101 CloudError::deploy_with(format!("reading signing key {}", path.display()), e)
102 })?;
103 Ok(Some(
104 base64::engine::general_purpose::STANDARD.encode(pem.as_bytes()),
105 ))
106}