systemprompt_cloud/profile_authoring/
local_builder.rs1use std::path::Path;
7
8use systemprompt_identifiers::{Email, TenantId};
9use systemprompt_loader::ExtensionLoader;
10use systemprompt_manifest::profile::{
11 ProfileResult, SecretsConfig, SecretsSource, SecretsValidationMode,
12};
13use systemprompt_manifest::services::SystemAdminConfig;
14use systemprompt_manifest::{
15 CloudConfig, CloudValidationMode, ExtensionsConfig, HostingProvider, PathsConfig, Profile,
16 ProfileDatabaseConfig, ProfileType, RateLimitsConfig, ServerConfig, SiteConfig,
17};
18
19use super::{generate_display_name, local_runtime_config, security_config, webhook_governance};
20use crate::constants::profile as consts;
21use crate::paths::ProjectContext;
22
23#[derive(Debug)]
24pub struct LocalProfileBuilder {
25 name: String,
26 tenant_id: Option<TenantId>,
27 secrets_path: String,
28 services_path: String,
29}
30
31impl LocalProfileBuilder {
32 pub fn new(
33 name: impl Into<String>,
34 secrets_path: impl AsRef<Path>,
35 services_path: impl AsRef<Path>,
36 ) -> Self {
37 Self {
38 name: name.into(),
39 tenant_id: None,
40 secrets_path: secrets_path.as_ref().to_string_lossy().to_string(),
41 services_path: services_path.as_ref().to_string_lossy().to_string(),
42 }
43 }
44
45 #[must_use]
46 pub fn with_tenant_id(mut self, tenant_id: TenantId) -> Self {
47 self.tenant_id = Some(tenant_id);
48 self
49 }
50
51 pub fn build(self) -> ProfileResult<Profile> {
52 let ctx = ProjectContext::discover();
53 let root = ctx.root();
54 let system_path = root.to_string_lossy().to_string();
55 let display_name = generate_display_name(&self.name);
56 let local_url = format!("http://localhost:{}", consts::DEFAULT_PORT);
57
58 let mut profile = Profile {
59 storage: systemprompt_manifest::profile::StorageConfig::default(),
60 observability: systemprompt_manifest::profile::ObservabilityConfig::default(),
61 config_planes: systemprompt_manifest::profile::ConfigPlanesConfig::default(),
62 licence: systemprompt_manifest::profile::LicenceConfig::default(),
63 notifications: systemprompt_manifest::profile::NotificationsProfile::default(),
64 brain: systemprompt_manifest::profile::BrainProfile::default(),
65 name: self.name,
66 display_name,
67 target: ProfileType::Local,
68 site: SiteConfig {
69 name: "systemprompt.io".to_owned(),
70 github_link: None,
71 },
72 database: ProfileDatabaseConfig {
73 db_type: consts::DEFAULT_DB_TYPE.to_owned(),
74 external_db_access: false,
75 migrate_on_boot: true,
76 pool: None,
77 },
78 security: security_config(Vec::new(), true),
79 server: local_server_config(local_url),
80 paths: PathsConfig {
81 root: None,
82 system: system_path,
83 services: self.services_path,
84 bin: ExtensionLoader::resolve_bin_directory(root, None)
85 .to_string_lossy()
86 .to_string(),
87 storage: Some(ctx.storage_dir().to_string_lossy().to_string()),
88 geoip_database: None,
89 data: None,
90 web_path: None,
91 shipped_files: None,
92 },
93 rate_limits: RateLimitsConfig {
94 disabled: true,
95 ..Default::default()
96 },
97 runtime: local_runtime_config(),
98 cloud: Some(CloudConfig {
99 tenant_id: self.tenant_id,
100 provider: HostingProvider::DockerLocal,
101 validation: CloudValidationMode::Warn,
102 deploy: None,
103 operator: None,
104 image: None,
105 }),
106 secrets: Some(SecretsConfig {
107 secrets_path: Some(self.secrets_path),
108 validation: SecretsValidationMode::Warn,
109 source: SecretsSource::File,
110 vault: None,
111 external: None,
112 refresh_secs: None,
113 }),
114 extensions: ExtensionsConfig::default(),
115 governance: Some(webhook_governance()),
116 services: systemprompt_manifest::profile::ServicesProfileConfig::default(),
117 judge: systemprompt_manifest::profile::JudgeProfile::default(),
118 retention: systemprompt_manifest::profile::RetentionConfig::default(),
119 system_admin: SystemAdminConfig {
120 username: "admin".to_owned(),
121 email: Some(Email::local_admin()),
122 },
123 };
124 profile.derive_fields()?;
125 Ok(profile)
126 }
127}
128
129fn local_server_config(local_url: String) -> ServerConfig {
130 let mut server = ServerConfig::new(consts::LOCAL_HOST, consts::DEFAULT_PORT, local_url);
131 server.extra_cors_origins = vec!["http://localhost:5173".to_owned()];
132 server.role = systemprompt_manifest::profile::NodeRole::All;
133 server.trusted_proxies =
134 crate::trusted_proxies::default_trusted_proxies(HostingProvider::DockerLocal);
135 server
136}