use crate::config::{self, TuiConfig};
use crate::saas_client::{SaasClient, TokenPollResult};
pub async fn run_login(config: &TuiConfig) -> Result<(), String> {
let client = SaasClient::new(&config.project_api_url)?;
let code = client.request_device_code().await?;
let display_url = rewrite_verification_url(&code.verification_uri, &config.project_api_url);
println!("\nTo login, visit this URL in your browser:\n");
println!(" {display_url}");
println!("\n Code: {}\n", code.user_code);
print!("Waiting for confirmation... (Ctrl+C to cancel)");
let max_wait = code.expires_in.min(900); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(max_wait);
let interval = std::time::Duration::from_secs(code.interval.max(5));
loop {
tokio::time::sleep(interval).await;
if std::time::Instant::now() > deadline {
println!();
return Err("Device code expired. Run `complior login` again.".into());
}
match client.poll_token(&code.device_code).await? {
TokenPollResult::Pending => {
print!(".");
use std::io::Write;
let _ = std::io::stdout().flush();
}
TokenPollResult::Success { access_token, refresh_token, expires_in, user_email, org_name } => {
let expires_at = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() + expires_in;
config::save_tokens(
&access_token,
&refresh_token,
expires_at,
user_email.as_deref(),
org_name.as_deref(),
)?;
config::save_project_api_url(&config.project_api_url).await;
println!();
let email_display = user_email.as_deref().unwrap_or("unknown");
let org_display = org_name.as_deref().unwrap_or("unknown");
println!("\u{2705} Authenticated as {email_display} ({org_display})");
println!(" Token stored in ~/.config/complior/credentials");
println!(" SaaS URL: {}", config.project_api_url);
return Ok(());
}
TokenPollResult::Expired => {
println!();
return Err("Device code expired. Run `complior login` again.".into());
}
}
}
}
fn rewrite_verification_url(verification_uri: &str, base_url: &str) -> String {
if let Some(pos) = verification_uri.find("://") {
let after_scheme = &verification_uri[pos + 3..];
if let Some(slash) = after_scheme.find('/') {
let path = &after_scheme[slash..]; return format!("{}{}", base_url.trim_end_matches('/'), path);
}
}
format!("{}/device", base_url.trim_end_matches('/'))
}
pub async fn run_logout(_config: &TuiConfig) -> Result<(), String> {
config::clear_tokens()?;
println!("\u{1f44b} Logged out. Tokens cleared from ~/.config/complior/credentials");
Ok(())
}