systemprompt_models/config/
mod.rs1use std::path::PathBuf;
13use std::sync::OnceLock;
14use systemprompt_traits::ConfigProvider;
15
16use crate::auth::JwtAudience;
17use crate::profile::{ContentNegotiationConfig, SecurityHeadersConfig, TrustedIssuer};
18
19mod environment;
20mod paths;
21mod rate_limits;
22mod validation;
23mod verbosity;
24
25pub use environment::Environment;
26pub use paths::PathNotConfiguredError;
27pub use rate_limits::RateLimitConfig;
28pub use validation::validate_postgres_url;
29pub use verbosity::VerbosityLevel;
30
31static CONFIG: OnceLock<Config> = OnceLock::new();
32
33pub const DEFAULT_MAX_CONCURRENT_STREAMS: usize = 256;
34
35#[must_use]
36pub fn stable_instance_id() -> Option<String> {
37 std::env::var("HOSTNAME")
38 .ok()
39 .map(|h| h.trim().to_owned())
40 .filter(|h| !h.is_empty())
41}
42
43#[must_use]
44pub fn random_instance_id() -> String {
45 format!("instance-{}", uuid::Uuid::new_v4().simple())
46}
47
48#[must_use]
49pub fn default_instance_id() -> String {
50 stable_instance_id().unwrap_or_else(random_instance_id)
51}
52
53#[derive(Debug, Clone)]
54pub struct Config {
55 pub instance_id: String,
56 pub metrics_port: Option<u16>,
57 pub max_concurrent_streams: usize,
58 pub sitename: String,
59 pub database_type: String,
60 pub database_url: String,
61 pub database_write_url: Option<String>,
62 pub github_link: String,
63 pub github_token: Option<String>,
64 pub system_path: String,
65 pub services_path: String,
66 pub bin_path: String,
67 pub skills_path: String,
68 pub settings_path: String,
69 pub content_config_path: String,
70 pub geoip_database_path: Option<String>,
71 pub web_path: String,
72 pub web_config_path: String,
73 pub web_metadata_path: String,
74 pub host: String,
75 pub port: u16,
76 pub api_server_url: String,
77 pub api_internal_url: String,
78 pub api_external_url: String,
79 pub jwt_issuer: String,
80 pub jwt_access_token_expiration: i64,
81 pub jwt_refresh_token_expiration: i64,
82 pub jwt_audiences: Vec<JwtAudience>,
83 pub allowed_resource_audiences: Vec<String>,
84 pub trusted_issuers: Vec<TrustedIssuer>,
85 pub id_jag_ttl_secs: i64,
86 pub signing_key_path: PathBuf,
87 pub use_https: bool,
88 pub rate_limits: RateLimitConfig,
89 pub cors_allowed_origins: Vec<String>,
90 pub trusted_proxies: Vec<ipnet::IpNet>,
91 pub is_cloud: bool,
92 pub content_negotiation: ContentNegotiationConfig,
93 pub security_headers: SecurityHeadersConfig,
94 pub allow_registration: bool,
95 pub login_page_url: Option<String>,
96 pub system_admin_username: String,
97 pub system_admin_email: Option<systemprompt_identifiers::Email>,
98}
99
100impl Config {
101 pub fn is_initialized() -> bool {
102 CONFIG.get().is_some()
103 }
104
105 pub fn get() -> Result<&'static Self, crate::errors::ConfigError> {
106 CONFIG
107 .get()
108 .ok_or(crate::errors::ConfigError::NotInitialized)
109 }
110
111 pub fn install(config: Self) -> Result<(), Box<Self>> {
112 CONFIG.set(config).map_err(Box::new)
113 }
114
115 pub fn logs_path(&self) -> String {
116 format!("{}/logs", self.system_path)
117 }
118}
119
120impl ConfigProvider for Config {
121 fn get(&self, key: &str) -> Option<String> {
122 match key {
123 "database_type" => Some(self.database_type.clone()),
124 "database_url" => Some(self.database_url.clone()),
125 "database_write_url" => self.database_write_url.clone(),
126 "host" => Some(self.host.clone()),
127 "port" => Some(self.port.to_string()),
128 "system_path" => Some(self.system_path.clone()),
129 "services_path" => Some(self.services_path.clone()),
130 "bin_path" => Some(self.bin_path.clone()),
131 "skills_path" => Some(self.skills_path.clone()),
132 "settings_path" => Some(self.settings_path.clone()),
133 "content_config_path" => Some(self.content_config_path.clone()),
134 "web_path" => Some(self.web_path.clone()),
135 "web_config_path" => Some(self.web_config_path.clone()),
136 "web_metadata_path" => Some(self.web_metadata_path.clone()),
137 "sitename" => Some(self.sitename.clone()),
138 "github_link" => Some(self.github_link.clone()),
139 "github_token" => self.github_token.clone(),
140 "api_server_url" => Some(self.api_server_url.clone()),
141 "api_external_url" => Some(self.api_external_url.clone()),
142 "jwt_issuer" => Some(self.jwt_issuer.clone()),
143 "is_cloud" => Some(self.is_cloud.to_string()),
144 "instance_id" => Some(self.instance_id.clone()),
145 "max_concurrent_streams" => Some(self.max_concurrent_streams.to_string()),
146 _ => None,
147 }
148 }
149
150 fn database_url(&self) -> &str {
151 &self.database_url
152 }
153
154 fn database_write_url(&self) -> Option<&str> {
155 self.database_write_url.as_deref()
156 }
157
158 fn system_path(&self) -> &str {
159 &self.system_path
160 }
161
162 fn api_port(&self) -> u16 {
163 self.port
164 }
165
166 fn as_any(&self) -> &dyn std::any::Any {
167 self
168 }
169}