systemprompt_cloud/profile_authoring/
local_builder.rs1use std::path::Path;
7
8use systemprompt_identifiers::TenantId;
9use systemprompt_loader::ExtensionLoader;
10use systemprompt_models::profile::{
11 ProviderRegistry, SecretsConfig, SecretsSource, SecretsValidationMode,
12};
13use systemprompt_models::services::SystemAdminConfig;
14use systemprompt_models::{
15 CloudConfig, CloudValidationMode, ContentNegotiationConfig, ExtensionsConfig, PathsConfig,
16 Profile, ProfileDatabaseConfig, ProfileType, RateLimitsConfig, SecurityHeadersConfig,
17 ServerConfig, SiteConfig,
18};
19
20use super::{generate_display_name, local_runtime_config, security_config, webhook_governance};
21use crate::constants::profile as consts;
22use crate::paths::ProjectContext;
23
24#[derive(Debug)]
25pub struct LocalProfileBuilder {
26 name: String,
27 tenant_id: Option<TenantId>,
28 secrets_path: String,
29 services_path: String,
30}
31
32impl LocalProfileBuilder {
33 pub fn new(
34 name: impl Into<String>,
35 secrets_path: impl AsRef<Path>,
36 services_path: impl AsRef<Path>,
37 ) -> Self {
38 Self {
39 name: name.into(),
40 tenant_id: None,
41 secrets_path: secrets_path.as_ref().to_string_lossy().to_string(),
42 services_path: services_path.as_ref().to_string_lossy().to_string(),
43 }
44 }
45
46 #[must_use]
47 pub fn with_tenant_id(mut self, tenant_id: TenantId) -> Self {
48 self.tenant_id = Some(tenant_id);
49 self
50 }
51
52 #[must_use]
53 pub fn build(self) -> Profile {
54 let ctx = ProjectContext::discover();
55 let root = ctx.root();
56 let system_path = root.to_string_lossy().to_string();
57 let display_name = generate_display_name(&self.name);
58 let local_url = format!("http://localhost:{}", consts::DEFAULT_PORT);
59 let internal_url = local_url.clone();
60
61 Profile {
62 name: self.name,
63 display_name,
64 target: ProfileType::Local,
65 site: SiteConfig {
66 name: "systemprompt.io".to_owned(),
67 github_link: None,
68 },
69 database: ProfileDatabaseConfig {
70 db_type: consts::DEFAULT_DB_TYPE.to_owned(),
71 external_db_access: false,
72 pool: None,
73 },
74 server: ServerConfig {
75 host: consts::LOCAL_HOST.to_owned(),
76 port: consts::DEFAULT_PORT,
77 api_server_url: local_url.clone(),
78 api_internal_url: local_url.clone(),
79 api_external_url: local_url.clone(),
80 use_https: false,
81 cors_allowed_origins: vec![local_url, "http://localhost:5173".to_owned()],
82 content_negotiation: ContentNegotiationConfig::default(),
83 security_headers: SecurityHeadersConfig::default(),
84 instance_id: None,
85 max_concurrent_streams: systemprompt_models::config::DEFAULT_MAX_CONCURRENT_STREAMS,
86 trusted_proxies: Vec::new(),
87 },
88 paths: PathsConfig {
89 system: system_path,
90 services: self.services_path,
91 bin: ExtensionLoader::resolve_bin_directory(root, None)
92 .to_string_lossy()
93 .to_string(),
94 storage: Some(ctx.storage_dir().to_string_lossy().to_string()),
95 geoip_database: None,
96 web_path: None,
97 },
98 security: security_config(consts::LOCAL_ISSUER, Vec::new()),
99 rate_limits: RateLimitsConfig {
100 disabled: true,
101 ..Default::default()
102 },
103 runtime: local_runtime_config(),
104 cloud: Some(CloudConfig {
105 tenant_id: self.tenant_id,
106 validation: CloudValidationMode::Warn,
107 }),
108 secrets: Some(SecretsConfig {
109 secrets_path: self.secrets_path,
110 validation: SecretsValidationMode::Warn,
111 source: SecretsSource::File,
112 }),
113 extensions: ExtensionsConfig::default(),
114 providers: ProviderRegistry::default(),
115 gateway: None,
116 governance: Some(webhook_governance(&internal_url)),
117 system_admin: SystemAdminConfig {
118 username: "admin".to_owned(),
119 },
120 }
121 }
122}