Skip to main content

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

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