Skip to main content

systemprompt_cli/commands/cloud/tenant/docker/
config.rs

1use anyhow::{Context, Result};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::PathBuf;
6use systemprompt_cloud::ProjectContext;
7use systemprompt_identifiers::TenantId;
8
9pub(in crate::commands::cloud::tenant) const SHARED_CONTAINER_NAME: &str =
10    "systemprompt-postgres-shared";
11pub(in crate::commands::cloud) const SHARED_ADMIN_USER: &str = "systemprompt_admin";
12pub(in crate::commands::cloud) const SHARED_VOLUME_NAME: &str = "systemprompt-postgres-shared-data";
13pub(in crate::commands::cloud) const SHARED_PORT: u16 = 5432;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct SharedContainerConfig {
17    pub admin_password: String,
18    pub port: u16,
19    pub created_at: DateTime<Utc>,
20    pub tenant_databases: Vec<TenantDatabaseMapping>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct TenantDatabaseMapping {
25    pub tenant_id: TenantId,
26    pub database_name: String,
27}
28
29impl SharedContainerConfig {
30    pub fn new(admin_password: String, port: u16) -> Self {
31        Self {
32            admin_password,
33            port,
34            created_at: Utc::now(),
35            tenant_databases: Vec::new(),
36        }
37    }
38
39    pub(in crate::commands::cloud::tenant) fn add_tenant(
40        &mut self,
41        tenant: TenantId,
42        database_name: String,
43    ) {
44        self.tenant_databases.push(TenantDatabaseMapping {
45            tenant_id: tenant,
46            database_name,
47        });
48    }
49
50    pub(in crate::commands::cloud::tenant) fn remove_tenant(
51        &mut self,
52        tenant: &str,
53    ) -> Option<TenantDatabaseMapping> {
54        self.tenant_databases
55            .iter()
56            .position(|t| t.tenant_id == tenant)
57            .map(|pos| self.tenant_databases.remove(pos))
58    }
59}
60
61pub(in crate::commands::cloud::tenant) fn shared_config_path() -> PathBuf {
62    let ctx = ProjectContext::discover();
63    ctx.docker_dir().join("shared_config.json")
64}
65
66pub(in crate::commands::cloud) fn load_shared_config() -> Result<Option<SharedContainerConfig>> {
67    let path = shared_config_path();
68    if !path.exists() {
69        return Ok(None);
70    }
71    let content =
72        fs::read_to_string(&path).with_context(|| format!("Failed to read {}", path.display()))?;
73    let config: SharedContainerConfig = serde_json::from_str(&content)
74        .with_context(|| format!("Failed to parse {}", path.display()))?;
75    Ok(Some(config))
76}
77
78pub(in crate::commands::cloud) fn save_shared_config(config: &SharedContainerConfig) -> Result<()> {
79    let path = shared_config_path();
80    if let Some(parent) = path.parent() {
81        fs::create_dir_all(parent)?;
82    }
83    let content = serde_json::to_string_pretty(config)?;
84    fs::write(&path, content)?;
85
86    #[cfg(unix)]
87    {
88        use std::os::unix::fs::PermissionsExt;
89        let mut perms = fs::metadata(&path)?.permissions();
90        perms.set_mode(0o600);
91        fs::set_permissions(&path, perms)?;
92    }
93
94    Ok(())
95}