Skip to main content

systemprompt_cli/commands/cloud/profile/
create_setup.rs

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