systemprompt_cli/commands/cloud/tenant/docker/
config.rs1use 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}