Skip to main content

systemprompt_cli/commands/cloud/profile/
create_setup.rs

1use anyhow::Result;
2use dialoguer::theme::ColorfulTheme;
3use dialoguer::Confirm;
4use std::path::Path;
5use std::process::Command;
6use systemprompt_cloud::ProjectContext;
7use systemprompt_logging::CliService;
8
9use super::templates::{run_migrations_cmd, validate_connection};
10use crate::cloud::tenant::wait_for_postgres_healthy;
11
12pub async fn handle_local_tenant_setup(
13    cloud_user: &crate::cloud::sync::admin_user::CloudUser,
14    db_url: &str,
15    tenant_name: &str,
16    profile_path: &Path,
17) -> Result<()> {
18    let spinner = CliService::spinner("Validating PostgreSQL connection...");
19    let mut connection_valid = validate_connection(db_url).await;
20    spinner.finish_and_clear();
21
22    if !connection_valid {
23        let ctx = ProjectContext::discover();
24        let compose_path = ctx.docker_dir().join(format!("{}.yaml", tenant_name));
25
26        if compose_path.exists() {
27            let start_docker = Confirm::with_theme(&ColorfulTheme::default())
28                .with_prompt("PostgreSQL not running. Start Docker container?")
29                .default(true)
30                .interact()?;
31
32            if start_docker {
33                connection_valid = start_postgres_container(&compose_path).await?;
34            }
35        } else {
36            CliService::warning("Could not connect to PostgreSQL.");
37            CliService::info("Ensure PostgreSQL is running before starting services.");
38        }
39    }
40
41    if connection_valid {
42        CliService::success("PostgreSQL connection verified");
43
44        let run_migrations = Confirm::with_theme(&ColorfulTheme::default())
45            .with_prompt("Run database migrations?")
46            .default(true)
47            .interact()?;
48
49        let migrations_succeeded = if run_migrations {
50            match run_migrations_cmd(profile_path).await {
51                Ok(()) => true,
52                Err(e) => {
53                    CliService::warning(&format!("Migration failed: {}", e));
54                    false
55                },
56            }
57        } else {
58            false
59        };
60
61        if migrations_succeeded {
62            let result = crate::cloud::sync::admin_user::sync_admin_to_database(
63                cloud_user,
64                db_url,
65                tenant_name,
66            )
67            .await;
68
69            match &result {
70                crate::cloud::sync::admin_user::SyncResult::Created { email, .. } => {
71                    CliService::success(&format!("Created admin user: {}", email));
72                },
73                crate::cloud::sync::admin_user::SyncResult::Promoted { email, .. } => {
74                    CliService::success(&format!("Promoted user to admin: {}", email));
75                },
76                crate::cloud::sync::admin_user::SyncResult::AlreadyAdmin { email, .. } => {
77                    CliService::info(&format!("User '{}' is already admin", email));
78                },
79                crate::cloud::sync::admin_user::SyncResult::ConnectionFailed { error, .. } => {
80                    CliService::warning(&format!("Could not sync admin user: {}", error));
81                },
82                crate::cloud::sync::admin_user::SyncResult::Failed { error, .. } => {
83                    CliService::warning(&format!("Admin user sync failed: {}", error));
84                },
85            }
86        }
87    }
88
89    Ok(())
90}
91
92pub fn get_cloud_user() -> Result<crate::cloud::sync::admin_user::CloudUser> {
93    crate::cloud::sync::admin_user::CloudUser::from_credentials()?.ok_or_else(|| {
94        anyhow::anyhow!("Cloud credentials required. Run 'systemprompt cloud login' first.")
95    })
96}
97
98async fn start_postgres_container(compose_path: &Path) -> Result<bool> {
99    CliService::info("Starting PostgreSQL container...");
100
101    let compose_path_str = compose_path
102        .to_str()
103        .ok_or_else(|| anyhow::anyhow!("Invalid compose path"))?;
104
105    let status = Command::new("docker")
106        .args(["compose", "-f", compose_path_str, "up", "-d"])
107        .status()
108        .map_err(|_| anyhow::anyhow!("Failed to execute docker compose. Is Docker running?"))?;
109
110    if !status.success() {
111        CliService::warning("Failed to start PostgreSQL container. Is Docker running?");
112        return Ok(false);
113    }
114
115    let spinner = CliService::spinner("Waiting for PostgreSQL to be ready...");
116    match wait_for_postgres_healthy(compose_path, 60).await {
117        Ok(()) => {
118            spinner.finish_and_clear();
119            Ok(true)
120        },
121        Err(e) => {
122            spinner.finish_and_clear();
123            CliService::warning(&format!("PostgreSQL failed to become healthy: {}", e));
124            Ok(false)
125        },
126    }
127}