Skip to main content

systemprompt_cloud/profile_authoring/
cloud_builder.rs

1//! Builder for the cloud-deployment [`Profile`].
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use systemprompt_identifiers::TenantId;
7use systemprompt_models::profile::{
8    ProviderRegistry, SecretsConfig, SecretsSource, SecretsValidationMode, TrustedIssuer,
9};
10use systemprompt_models::services::SystemAdminConfig;
11use systemprompt_models::{
12    CloudConfig, CloudValidationMode, ContentNegotiationConfig, ExtensionsConfig, PathsConfig,
13    Profile, ProfileDatabaseConfig, ProfileType, RateLimitsConfig, SecurityHeadersConfig,
14    ServerConfig, SiteConfig,
15};
16
17use super::{cloud_runtime_config, generate_display_name, security_config, webhook_governance};
18use crate::constants::{container, profile as consts};
19
20#[derive(Debug)]
21pub struct CloudProfileBuilder {
22    name: String,
23    tenant_id: Option<TenantId>,
24    external_url: Option<String>,
25    external_db_access: bool,
26    secrets_path: Option<String>,
27    trusted_issuers: Vec<TrustedIssuer>,
28}
29
30impl CloudProfileBuilder {
31    pub fn new(name: impl Into<String>) -> Self {
32        Self {
33            name: name.into(),
34            tenant_id: None,
35            external_url: None,
36            external_db_access: false,
37            secrets_path: None,
38            trusted_issuers: Vec::new(),
39        }
40    }
41
42    #[must_use]
43    pub fn with_trusted_issuer(mut self, issuer: TrustedIssuer) -> Self {
44        self.trusted_issuers.push(issuer);
45        self
46    }
47
48    #[must_use]
49    pub fn with_tenant_id(mut self, tenant_id: TenantId) -> Self {
50        self.tenant_id = Some(tenant_id);
51        self
52    }
53
54    #[must_use]
55    pub fn with_external_url(mut self, url: impl Into<String>) -> Self {
56        self.external_url = Some(url.into());
57        self
58    }
59
60    #[must_use]
61    pub const fn with_external_db_access(mut self, enabled: bool) -> Self {
62        self.external_db_access = enabled;
63        self
64    }
65
66    #[must_use]
67    pub fn with_secrets_path(mut self, path: impl Into<String>) -> Self {
68        self.secrets_path = Some(path.into());
69        self
70    }
71
72    #[must_use]
73    pub fn build(self) -> Profile {
74        let display_name = generate_display_name(&self.name);
75        let external = self
76            .external_url
77            .unwrap_or_else(|| consts::DEFAULT_CLOUD_URL.to_owned());
78        let internal_url = format!("http://localhost:{}", consts::DEFAULT_PORT);
79
80        Profile {
81            name: self.name,
82            display_name,
83            target: ProfileType::Cloud,
84            site: SiteConfig {
85                name: "systemprompt.io".to_owned(),
86                github_link: None,
87            },
88            database: ProfileDatabaseConfig {
89                db_type: consts::DEFAULT_DB_TYPE.to_owned(),
90                external_db_access: self.external_db_access,
91                pool: None,
92            },
93            server: ServerConfig {
94                host: consts::CLOUD_HOST.to_owned(),
95                port: consts::DEFAULT_PORT,
96                api_server_url: external.clone(),
97                api_internal_url: internal_url.clone(),
98                api_external_url: external.clone(),
99                use_https: true,
100                cors_allowed_origins: vec![external],
101                content_negotiation: ContentNegotiationConfig::default(),
102                security_headers: SecurityHeadersConfig::default(),
103                instance_id: None,
104                max_concurrent_streams: systemprompt_models::config::DEFAULT_MAX_CONCURRENT_STREAMS,
105                trusted_proxies: Vec::new(),
106            },
107            paths: PathsConfig {
108                system: container::APP.to_owned(),
109                services: container::SERVICES.to_owned(),
110                bin: container::BIN.to_owned(),
111                storage: Some(container::STORAGE.to_owned()),
112                geoip_database: None,
113                web_path: Some(container::WEB.to_owned()),
114            },
115            security: security_config(consts::CLOUD_ISSUER, self.trusted_issuers),
116            rate_limits: RateLimitsConfig::default(),
117            runtime: cloud_runtime_config(),
118            cloud: Some(CloudConfig {
119                tenant_id: self.tenant_id,
120                validation: CloudValidationMode::Strict,
121            }),
122            secrets: Some(SecretsConfig {
123                secrets_path: self.secrets_path.unwrap_or_else(String::new),
124                validation: SecretsValidationMode::Strict,
125                source: SecretsSource::Env,
126            }),
127            extensions: ExtensionsConfig::default(),
128            providers: ProviderRegistry::default(),
129            gateway: None,
130            governance: Some(webhook_governance(&internal_url)),
131            system_admin: SystemAdminConfig {
132                username: "admin".to_owned(),
133            },
134        }
135    }
136}