use crate::auth::{self, SecureString};
use crate::cli::args::ServerCommands;
use crate::cli::UI;
use crate::platform;
use crate::platform::server_registry::{ServerConfig, ServerPlatform, ServerRegistry};
use anyhow::{bail, Result};
pub async fn execute(action: ServerCommands, ui: &UI) -> Result<()> {
match action {
ServerCommands::Add {
name,
platform,
api_url,
token,
no_push,
} => add(name, platform, api_url, token, !no_push, ui).await,
ServerCommands::Remove { name } => remove(name, ui),
ServerCommands::List => list(ui),
ServerCommands::Status => status(ui).await,
ServerCommands::CreateRepo {
name,
server,
namespace,
private,
description,
} => create_repo(name, server, namespace, private, description, ui).await,
}
}
async fn add(
name: String,
platform_str: String,
api_url: String,
token: Option<String>,
push_enabled: bool,
ui: &UI,
) -> Result<()> {
let platform = match platform_str.to_lowercase().as_str() {
"github" | "gh" => ServerPlatform::GitHub,
"gitlab" | "gl" => ServerPlatform::GitLab,
other => bail!("Unknown platform '{}'. Use 'github' or 'gitlab'.", other),
};
url::Url::parse(&api_url)?;
ui.header("Register Server");
ui.blank();
ui.field("Name", &name);
ui.field("Platform", platform.to_string());
ui.field("API URL", &api_url);
ui.field("Push", if push_enabled { "enabled" } else { "disabled" });
ui.blank();
let token_str = if let Some(t) = token {
t
} else {
let input = dialoguer::Password::new().with_prompt("Token").interact()?;
input
};
let secure_token = SecureString::from_string(token_str);
let store_key = format!("server:{}", name);
auth::store::store_token(&store_key, &secure_token)?;
let spinner = ui.spinner("Validating token...");
let server_config = ServerConfig {
name: name.clone(),
platform: platform.clone(),
api_url: api_url.clone(),
web_url: None,
push_enabled,
};
let temp_client =
platform::create_client_for_server(&server_config, secure_token, "validation", "check");
let username = match temp_client.get_authenticated_user().await {
Ok(user) => {
ui.finish_progress(&spinner, "Token validated");
user
}
Err(e) => {
ui.finish_progress(&spinner, "Validation failed");
let _ = auth::store::delete_token(&store_key);
bail!("Token validation failed: {}", e);
}
};
let mut registry = ServerRegistry::load()?;
registry.add(server_config)?;
registry.save()?;
ui.blank();
ui.success(format!("Server '{}' registered", name));
ui.field("Authenticated as", &username);
ui.blank();
Ok(())
}
fn remove(name: String, ui: &UI) -> Result<()> {
let mut registry = ServerRegistry::load()?;
registry.remove(&name)?;
registry.save()?;
let store_key = format!("server:{}", name);
let _ = auth::store::delete_token(&store_key);
ui.success(format!("Server '{}' removed", name));
Ok(())
}
fn list(ui: &UI) -> Result<()> {
let registry = ServerRegistry::load()?;
if registry.servers.is_empty() {
ui.info("No servers registered. Use 'securegit server add' to register one.");
return Ok(());
}
ui.header("Registered Servers");
ui.blank();
for server in ®istry.servers {
let has_token = auth::token_for_server(server).is_some();
let auth_icon = if has_token { "ok" } else { "--" };
ui.raw(format!(
" {} {} ({}) {} [push: {}]",
auth_icon,
server.name,
server.platform,
server.api_url,
if server.push_enabled { "on" } else { "off" }
));
}
ui.blank();
Ok(())
}
async fn status(ui: &UI) -> Result<()> {
let registry = ServerRegistry::load()?;
if registry.servers.is_empty() {
ui.info("No servers registered.");
return Ok(());
}
ui.header("Server Status");
ui.blank();
for server in ®istry.servers {
let token = auth::token_for_server(server);
if let Some(t) = token {
let client = platform::create_client_for_server(server, t, "status", "check");
match client.get_authenticated_user().await {
Ok(user) => {
ui.field(&server.name, format!("ok - {} ({})", user, server.platform));
}
Err(e) => {
ui.field(&server.name, format!("error - {} ({})", e, server.platform));
}
}
} else {
ui.field(&server.name, "no credentials");
}
}
ui.blank();
Ok(())
}
async fn create_repo(
name: String,
server_name: Option<String>,
namespace: Option<String>,
private: bool,
description: Option<String>,
ui: &UI,
) -> Result<()> {
let registry = ServerRegistry::load()?;
let server = if let Some(ref sname) = server_name {
registry
.get(sname)
.ok_or_else(|| anyhow::anyhow!("Server '{}' not found", sname))?
.clone()
} else {
let remote = platform::detect_remote(&std::path::PathBuf::from("."))?;
let platform_str = match remote.host {
platform::PlatformHost::GitHub => "github",
platform::PlatformHost::GitLab => "gitlab",
};
registry
.servers
.iter()
.find(|s| s.platform.to_string().to_lowercase() == platform_str)
.ok_or_else(|| {
anyhow::anyhow!("No registered {} server found. Use --server to specify.", platform_str)
})?
.clone()
};
let token = auth::token_for_server(&server)
.ok_or_else(|| anyhow::anyhow!("No credentials found for server '{}'", server.name))?;
ui.header("Create Repository");
ui.blank();
ui.field("Name", &name);
ui.field("Server", &server.name);
ui.field("Platform", server.platform.to_string());
if let Some(ref ns) = namespace {
ui.field("Namespace", ns);
}
ui.field("Private", if private { "yes" } else { "no" });
if let Some(ref desc) = description {
ui.field("Description", desc);
}
ui.blank();
let spinner = ui.spinner("Creating repository...");
let client = platform::create_client_for_server(&server, token, "", "");
let create_req = platform::types::CreateRepo {
name: name.clone(),
description,
private,
namespace,
};
let repo = client.create_repo(&create_req).await?;
ui.finish_progress(&spinner, "Repository created");
ui.blank();
ui.success(format!("Repository '{}' created", repo.full_name));
ui.field("Web URL", &repo.web_url);
ui.field("Clone (HTTP)", &repo.clone_url_http);
if let Some(ref ssh) = repo.clone_url_ssh {
ui.field("Clone (SSH)", ssh);
}
ui.blank();
Ok(())
}