Skip to main content

systemprompt_cli/commands/cloud/profile/
create_setup.rs

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