systemprompt_cli/commands/cloud/tenant/create/
progress.rs1use std::sync::Mutex;
12
13use indicatif::ProgressBar;
14use systemprompt_cloud::tenants::{ProvisioningProgress, ProvisioningProgressEvent};
15use systemprompt_logging::CliService;
16use url::Url;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct DatabaseConnectionInfo {
20 pub host: String,
21 pub port: u16,
22 pub database: String,
23 pub username: String,
24 pub password: String,
25}
26
27impl DatabaseConnectionInfo {
28 #[must_use]
29 pub fn parse(url: &str) -> Option<Self> {
30 let parsed = Url::parse(url).ok()?;
31 Some(Self {
32 host: parsed.host_str().unwrap_or("unknown").to_owned(),
33 port: parsed.port().unwrap_or(5432),
34 database: parsed.path().trim_start_matches('/').to_owned(),
35 username: parsed.username().to_owned(),
36 password: parsed.password().unwrap_or("********").to_owned(),
37 })
38 }
39
40 #[must_use]
41 pub fn psql_command(&self) -> String {
42 format!(
43 "PGPASSWORD='{}' psql -h {} -p {} -U {} -d {}",
44 self.password, self.host, self.port, self.username, self.database
45 )
46 }
47}
48
49pub(super) struct CliProvisioningProgress {
50 spinner: Mutex<Option<ProgressBar>>,
51}
52
53impl CliProvisioningProgress {
54 pub(super) const fn new() -> Self {
55 Self {
56 spinner: Mutex::new(None),
57 }
58 }
59
60 fn start_spinner(&self, message: &str) {
61 if let Ok(mut slot) = self.spinner.lock() {
62 *slot = Some(CliService::spinner(message));
63 }
64 }
65
66 fn clear_spinner(&self) {
67 if let Ok(mut slot) = self.spinner.lock()
68 && let Some(spinner) = slot.take()
69 {
70 spinner.finish_and_clear();
71 }
72 }
73}
74
75impl ProvisioningProgress for CliProvisioningProgress {
76 fn event(&self, event: &ProvisioningProgressEvent<'_>) {
77 if !matches!(event, ProvisioningProgressEvent::ProvisioningUpdate { .. }) {
78 self.clear_spinner();
79 }
80 match event {
81 ProvisioningProgressEvent::CheckoutSessionStarted => {
82 self.start_spinner("Creating checkout session...");
83 },
84 ProvisioningProgressEvent::CheckoutComplete { tenant_id } => {
85 CliService::success(&format!(
86 "Checkout complete! Tenant ID: {}",
87 tenant_id.as_str()
88 ));
89 },
90 ProvisioningProgressEvent::ProvisioningStarted => {
91 self.start_spinner("Waiting for infrastructure provisioning...");
92 },
93 ProvisioningProgressEvent::ProvisioningUpdate { message } => {
94 CliService::info(message);
95 },
96 ProvisioningProgressEvent::Provisioned => {
97 CliService::success("Tenant provisioned successfully");
98 },
99 ProvisioningProgressEvent::CredentialsFetchStarted => {
100 self.start_spinner("Fetching database credentials...");
101 },
102 ProvisioningProgressEvent::CredentialsFetched => {
103 CliService::success("Database credentials retrieved");
104 },
105 ProvisioningProgressEvent::ExternalAccessStarted => {
106 self.start_spinner("Enabling external database access...");
107 },
108 ProvisioningProgressEvent::ExternalAccessEnabled { database_url } => {
109 CliService::success("External database access enabled");
110 print_database_connection_info(database_url);
111 },
112 ProvisioningProgressEvent::ExternalAccessFailed { error } => {
113 CliService::warning(&format!("Failed to enable external access: {}", error));
114 CliService::info("You can enable it later with 'systemprompt cloud tenant edit'");
115 },
116 ProvisioningProgressEvent::TenantSyncStarted => {
117 self.start_spinner("Syncing new tenant...");
118 },
119 ProvisioningProgressEvent::CheckoutSessionCreated
120 | ProvisioningProgressEvent::TenantSynced => {},
121 }
122 }
123}
124
125fn print_database_connection_info(url: &str) {
126 let Some(info) = DatabaseConnectionInfo::parse(url) else {
127 return;
128 };
129
130 CliService::section("Database Connection");
131 CliService::key_value("Host", &info.host);
132 CliService::key_value("Port", &info.port.to_string());
133 CliService::key_value("Database", &info.database);
134 CliService::key_value("User", &info.username);
135 CliService::key_value("Password", &info.password);
136 CliService::key_value("SSL", "required");
137 CliService::info("");
138 CliService::key_value("Connection URL", url);
139 CliService::info("");
140 CliService::info(&format!("Connect with psql:\n {}", info.psql_command()));
141}