Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions crates/cli/src/commands/auth/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use anyhow::Result;
use console::style;

setup_command! {}

pub async fn run(_opts: Options) -> Result<()> {
let file = crate::config::read_file()?;
let active = crate::config::active_profile_name();

if file.profiles.is_empty() {
println!(
"\n {}\n",
style("No profiles configured. Run `edgee auth login` to get started.").dim()
);
return Ok(());
}

println!();
for (name, profile) in &file.profiles {
let marker = if *name == active {
style("*").green().bold().to_string()
} else {
style(" ").dim().to_string()
};
let email = profile.email.as_deref().unwrap_or("(not logged in)");
let org = profile.org_slug.as_deref().unwrap_or("(no org)");
println!(" {} {} — {} / {}", marker, style(name).bold(), email, org);
}
println!();

Ok(())
}
11 changes: 7 additions & 4 deletions crates/cli/src/commands/auth/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ pub struct Options {}

pub async fn run(_opts: Options) -> Result<()> {
let email = perform_login().await?;
let profile = crate::config::active_profile_name();

println!();
println!(
" {}",
style(format!("Logged in as {email}")).bold()
);
let line = if profile == "default" {
format!("Logged in as {email}")
} else {
format!("Logged in as {email} (profile: {profile})")
};
println!(" {}", style(line).bold());
println!();
Ok(())
}
Expand Down
8 changes: 8 additions & 0 deletions crates/cli/src/commands/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
pub mod list;
pub mod login;
pub mod status;
pub mod switch;

#[derive(Debug, clap::Subcommand)]
enum Command {
/// Log in to Edgee
Login(login::Options),
/// Show authentication status
Status(status::Options),
/// List all configured profiles
List(list::Options),
/// Switch the active profile globally
Switch(switch::Options),
}

#[derive(Debug, clap::Parser)]
Expand All @@ -19,5 +25,7 @@ pub async fn run(opts: Options) -> anyhow::Result<()> {
match opts.command {
Command::Login(o) => login::run(o).await,
Command::Status(o) => status::run(o).await,
Command::List(o) => list::run(o).await,
Command::Switch(o) => switch::run(o).await,
}
}
5 changes: 5 additions & 0 deletions crates/cli/src/commands/auth/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ pub async fn run(_opts: Options) -> Result<()> {
style("Config:").dim(),
style(crate::config::credentials_path().display()).dim()
);
println!(
" {} {}",
style("Profile:").dim(),
style(crate::config::active_profile_name()).bold()
);

match &creds.email {
Some(e) if !e.is_empty() => println!(
Expand Down
66 changes: 66 additions & 0 deletions crates/cli/src/commands/auth/switch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use anyhow::Result;
use console::style;
use dialoguer::{theme::ColorfulTheme, Select};

#[derive(Debug, clap::Parser)]
pub struct Options {
/// Name of the profile to switch to (interactive selector if omitted)
pub name: Option<String>,
}

pub async fn run(opts: Options) -> Result<()> {
let mut file = crate::config::read_file()?;

if file.profiles.is_empty() {
anyhow::bail!("No profiles configured. Run `edgee auth login` to get started.");
}

let name = match opts.name {
Some(n) => n,
None => {
let active = crate::config::active_profile_name();
let names: Vec<&String> = file.profiles.keys().collect();
let default_idx = names
.iter()
.position(|n| *n == &active)
.unwrap_or(0);

let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Select profile")
.items(&names)
.default(default_idx)
.interact()?;

names[selection].clone()
}
};

// Validate profile name: ascii alphanumeric, hyphens, underscores; max 64 chars.
if name.is_empty()
|| name.len() > 64
|| !name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
{
anyhow::bail!(
"Invalid profile name `{name}`. Use letters, digits, hyphens, or underscores (max 64 chars)."
);
}

if !file.profiles.contains_key(&name) {
anyhow::bail!(
"Profile `{name}` not found. Run `edgee auth list` to see available profiles,\nor `edgee auth login --profile {name}` to create it."
);
}

file.active_profile = Some(name.clone());
crate::config::write_file(&file)?;

println!(
"\n {} Now using profile {}.\n",
style("✓").green().bold(),
style(&name).bold()
);

Ok(())
}
Loading