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        if run_migrations {
50            run_migrations_cmd(profile_path).await?;
51        }
52    }
53
54    let result =
55        crate::cloud::sync::admin_user::sync_admin_to_database(cloud_user, db_url, tenant_name)
56            .await;
57
58    match &result {
59        crate::cloud::sync::admin_user::SyncResult::Created { email, .. } => {
60            CliService::success(&format!("Created admin user: {}", email));
61        },
62        crate::cloud::sync::admin_user::SyncResult::Promoted { email, .. } => {
63            CliService::success(&format!("Promoted user to admin: {}", email));
64        },
65        crate::cloud::sync::admin_user::SyncResult::AlreadyAdmin { email, .. } => {
66            CliService::info(&format!("User '{}' is already admin", email));
67        },
68        crate::cloud::sync::admin_user::SyncResult::ConnectionFailed { error, .. } => {
69            CliService::warning(&format!("Could not sync admin user: {}", error));
70        },
71        crate::cloud::sync::admin_user::SyncResult::Failed { error, .. } => {
72            CliService::warning(&format!("Admin user sync failed: {}", error));
73        },
74    }
75
76    Ok(())
77}
78
79pub fn get_cloud_user() -> Result<crate::cloud::sync::admin_user::CloudUser> {
80    crate::cloud::sync::admin_user::CloudUser::from_credentials()?.ok_or_else(|| {
81        anyhow::anyhow!("Cloud credentials required. Run 'systemprompt cloud login' first.")
82    })
83}
84
85async fn start_postgres_container(compose_path: &Path) -> Result<bool> {
86    CliService::info("Starting PostgreSQL container...");
87
88    let compose_path_str = compose_path
89        .to_str()
90        .ok_or_else(|| anyhow::anyhow!("Invalid compose path"))?;
91
92    let status = Command::new("docker")
93        .args(["compose", "-f", compose_path_str, "up", "-d"])
94        .status()
95        .map_err(|_| anyhow::anyhow!("Failed to execute docker compose. Is Docker running?"))?;
96
97    if !status.success() {
98        CliService::warning("Failed to start PostgreSQL container. Is Docker running?");
99        return Ok(false);
100    }
101
102    let spinner = CliService::spinner("Waiting for PostgreSQL to be ready...");
103    match wait_for_postgres_healthy(compose_path, 60).await {
104        Ok(()) => {
105            spinner.finish_and_clear();
106            Ok(true)
107        },
108        Err(e) => {
109            spinner.finish_and_clear();
110            CliService::warning(&format!("PostgreSQL failed to become healthy: {}", e));
111            Ok(false)
112        },
113    }
114}