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