use std::path::Path;
use systemprompt_identifiers::{Email, TenantId};
use systemprompt_loader::ExtensionLoader;
use systemprompt_manifest::profile::{
ProfileResult, SecretsConfig, SecretsSource, SecretsValidationMode,
};
use systemprompt_manifest::services::SystemAdminConfig;
use systemprompt_manifest::{
CloudConfig, CloudValidationMode, ExtensionsConfig, HostingProvider, PathsConfig, Profile,
ProfileDatabaseConfig, ProfileType, RateLimitsConfig, ServerConfig, SiteConfig,
};
use super::{generate_display_name, local_runtime_config, security_config, webhook_governance};
use crate::constants::profile as consts;
use crate::paths::ProjectContext;
#[derive(Debug)]
pub struct LocalProfileBuilder {
name: String,
tenant_id: Option<TenantId>,
secrets_path: String,
services_path: String,
}
impl LocalProfileBuilder {
pub fn new(
name: impl Into<String>,
secrets_path: impl AsRef<Path>,
services_path: impl AsRef<Path>,
) -> Self {
Self {
name: name.into(),
tenant_id: None,
secrets_path: secrets_path.as_ref().to_string_lossy().to_string(),
services_path: services_path.as_ref().to_string_lossy().to_string(),
}
}
#[must_use]
pub fn with_tenant_id(mut self, tenant_id: TenantId) -> Self {
self.tenant_id = Some(tenant_id);
self
}
pub fn build(self) -> ProfileResult<Profile> {
let ctx = ProjectContext::discover();
let root = ctx.root();
let system_path = root.to_string_lossy().to_string();
let display_name = generate_display_name(&self.name);
let local_url = format!("http://localhost:{}", consts::DEFAULT_PORT);
let mut profile = Profile {
storage: systemprompt_manifest::profile::StorageConfig::default(),
observability: systemprompt_manifest::profile::ObservabilityConfig::default(),
config_planes: systemprompt_manifest::profile::ConfigPlanesConfig::default(),
licence: systemprompt_manifest::profile::LicenceConfig::default(),
notifications: systemprompt_manifest::profile::NotificationsProfile::default(),
brain: systemprompt_manifest::profile::BrainProfile::default(),
name: self.name,
display_name,
target: ProfileType::Local,
site: SiteConfig {
name: "systemprompt.io".to_owned(),
github_link: None,
},
database: ProfileDatabaseConfig {
db_type: consts::DEFAULT_DB_TYPE.to_owned(),
external_db_access: false,
migrate_on_boot: true,
pool: None,
},
security: security_config(Vec::new(), true),
server: local_server_config(local_url),
paths: PathsConfig {
root: None,
system: system_path,
services: self.services_path,
bin: ExtensionLoader::resolve_bin_directory(root, None)
.to_string_lossy()
.to_string(),
storage: Some(ctx.storage_dir().to_string_lossy().to_string()),
geoip_database: None,
data: None,
web_path: None,
shipped_files: None,
},
rate_limits: RateLimitsConfig {
disabled: true,
..Default::default()
},
runtime: local_runtime_config(),
cloud: Some(CloudConfig {
tenant_id: self.tenant_id,
provider: HostingProvider::DockerLocal,
validation: CloudValidationMode::Warn,
deploy: None,
operator: None,
image: None,
}),
secrets: Some(SecretsConfig {
secrets_path: Some(self.secrets_path),
validation: SecretsValidationMode::Warn,
source: SecretsSource::File,
vault: None,
external: None,
refresh_secs: None,
}),
extensions: ExtensionsConfig::default(),
governance: Some(webhook_governance()),
services: systemprompt_manifest::profile::ServicesProfileConfig::default(),
judge: systemprompt_manifest::profile::JudgeProfile::default(),
retention: systemprompt_manifest::profile::RetentionConfig::default(),
system_admin: SystemAdminConfig {
username: "admin".to_owned(),
email: Some(Email::local_admin()),
},
};
profile.derive_fields()?;
Ok(profile)
}
}
fn local_server_config(local_url: String) -> ServerConfig {
let mut server = ServerConfig::new(consts::LOCAL_HOST, consts::DEFAULT_PORT, local_url);
server.extra_cors_origins = vec!["http://localhost:5173".to_owned()];
server.role = systemprompt_manifest::profile::NodeRole::All;
server.trusted_proxies =
crate::trusted_proxies::default_trusted_proxies(HostingProvider::DockerLocal);
server
}