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