From a32b933fbf4a1e9faebab066d0e120f3c74d6020 Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Fri, 17 Apr 2026 16:26:43 +0300 Subject: [PATCH] refactor: Migrate billing and config commands to uiContext with error handling and simplified output - Update HandleBilling to accept CLIConfig, return error, and use uiContext for all output - Update HandleConfig to accept CLIConfig, return error, and use uiContext for all output - Replace manual colorizer/icon initialization with uiContext helper methods - Simplify error handling with uiContext.errorln, warn, success, info, muted methods - Replace direct table creation with uiContext.newTable() --- apps/cli/internal/commands/billing.go | 304 ++++--------- apps/cli/internal/commands/config.go | 180 +++++--- apps/cli/internal/commands/deploy.go | 159 ++++--- apps/cli/internal/commands/enhanced_others.go | 143 ++---- .../internal/commands/enhanced_services.go | 306 +++---------- .../internal/commands/enhanced_subscribers.go | 428 ++++-------------- apps/cli/internal/commands/monitoring.go | 206 ++++++--- apps/cli/internal/commands/services.go | 179 +++++--- apps/cli/internal/commands/subscribers.go | 172 ++++--- 9 files changed, 904 insertions(+), 1173 deletions(-) diff --git a/apps/cli/internal/commands/billing.go b/apps/cli/internal/commands/billing.go index 1a41b7a..d7fe69e 100644 --- a/apps/cli/internal/commands/billing.go +++ b/apps/cli/internal/commands/billing.go @@ -3,255 +3,143 @@ package commands import ( "fmt" - "github.com/nutcas3/telecom-platform/apps/cli/internal/api" - "github.com/nutcas3/telecom-platform/apps/cli/internal/ui" + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -func HandleBilling(args []string, apiClient *api.Client) { +// HandleBilling is the entry point for billing commands. +func HandleBilling(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) == 0 { - showBillingHelp() - return + showBillingHelp(u) + return nil } - // Check API connectivity - if !apiClient.IsConnected() { - colorizer := ui.NewColorizer(true) - warningMsg := colorizer.Colorize("Warning: Could not connect to API server. Using placeholder data.", ui.StyleWarning) - fmt.Println(warningMsg) - fmt.Println() - } + u.connectivityBanner() command := args[0] switch command { case "invoices": - handleInvoices(apiClient) + return handleInvoices(u) case "payments": - handlePayments(apiClient) + return handlePayments(u) case "generate": - generateInvoice(apiClient, args[1:]) + return generateInvoice(u, args[1:]) default: - colorizer := ui.NewColorizer(true) - errorMsg := colorizer.Colorize("Unknown billing command: ", ui.StyleError) + - colorizer.Colorize(command, ui.StyleArgument) - fmt.Println(errorMsg) - showBillingHelp() + u.errorln("Unknown billing command: " + command) + showBillingHelp(u) + return fmt.Errorf("unknown command: %s", command) } } -func showBillingHelp() { - colorizer := ui.NewColorizer(true) - iconRenderer := ui.NewIconRenderer(true, false) - - title := colorizer.Colorize("Billing Management", ui.StyleHeader) - usage := colorizer.Colorize("Usage: telecom-cli billing [options]", ui.StyleMuted) - - fmt.Println(title) - fmt.Println(usage) +func showBillingHelp(u *uiContext) { + u.header("Billing Management") + u.muted("Usage: telecom-cli billing [options]") fmt.Println() - // Create table for commands - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Command", 25, "left") - table.AddColumn("Description", 40, "left") - - table.AddRow("invoices", "List all invoices") - table.AddRow("payments", "List all payments") - table.AddRow("generate ", "Generate invoice for subscriber") - - fmt.Println(colorizer.Colorize("Available commands:", ui.StyleHeader)) - fmt.Println(table.Render()) + t := u.newTable() + t.AddColumn("Command", 26, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("invoices", "List all invoices") + t.AddRow("payments", "List all payments") + t.AddRow("generate ", "Generate invoice for subscriber") + fmt.Println(t.Render()) } -func handleInvoices(apiClient *api.Client) { - colorizer := ui.NewColorizer(true) - iconRenderer := ui.NewIconRenderer(true, false) +func handleInvoices(u *uiContext) error { + u.header("Recent Invoices") - title := colorizer.Colorize("Recent Invoices", ui.StyleHeader) - fmt.Println(title) + invoices, err := u.client.GetInvoices() + t := u.newTable() + t.AddColumn("Invoice #", 14, "left") + t.AddColumn("Date", 12, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Amount", 10, "right") + t.AddColumn("Subscriber", 22, "left") - // Try to get real data from API - invoices, err := apiClient.GetInvoices() if err != nil { - // Fallback to placeholder data if API fails - warningMsg := colorizer.Colorize("Using placeholder data - API error: ", ui.StyleWarning) + - colorizer.Colorize(err.Error(), ui.StyleMuted) - fmt.Println(warningMsg) - fmt.Println() - - // Create table with placeholder data - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Invoice #", 12, "left") - table.AddColumn("Date", 12, "left") - table.AddColumn("Status", 10, "left") - table.AddColumn("Amount", 10, "right") - table.AddColumn("Subscriber", 15, "left") - - table.AddStyledRow(ui.StyleSuccess.Style, "INV-000001", "2024-01-15", "Paid", "$45.67", "John Doe") - table.AddStyledRow(ui.StyleWarning.Style, "INV-000002", "2024-01-15", "Pending", "$123.45", "Jane Smith") - table.AddStyledRow(ui.StyleError.Style, "INV-000003", "2024-01-14", "Overdue", "$67.89", "Bob Johnson") - - fmt.Println(table.Render()) - return + u.warn("Using placeholder data: " + err.Error()) + t.AddStyledRow(statusStyle("PAID").Style, "INV-000001", "2024-01-15", "Paid", "$45.67", "John Doe") + t.AddStyledRow(statusStyle("PENDING").Style, "INV-000002", "2024-01-15", "Pending", "$123.45", "Jane Smith") + t.AddStyledRow(statusStyle("OVERDUE").Style, "INV-000003", "2024-01-14", "Overdue", "$67.89", "Bob Johnson") + fmt.Println(t.Render()) + return nil } - // Create table with real data - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Invoice #", 12, "left") - table.AddColumn("Date", 12, "left") - table.AddColumn("Status", 10, "left") - table.AddColumn("Amount", 10, "right") - table.AddColumn("Subscriber", 15, "left") - - // Add rows with real data - for _, invoice := range invoices { - var style ui.Style - switch invoice.Status { - case "PAID": - style = ui.StyleSuccess - case "PENDING": - style = ui.StyleWarning - case "OVERDUE": - style = ui.StyleError - default: - style = ui.StyleMuted - } - - table.AddStyledRow(style.Style, - invoice.ID, - invoice.CreatedAt.Format("2006-01-02"), - invoice.Status, - fmt.Sprintf("$%.2f", invoice.Amount), - fmt.Sprintf("%s %s", invoice.Subscriber.FirstName, invoice.Subscriber.LastName)) + for _, inv := range invoices { + t.AddStyledRow(statusStyle(inv.Status).Style, + inv.ID, + inv.CreatedAt.Format("2006-01-02"), + inv.Status, + fmt.Sprintf("$%.2f", inv.Amount), + fmt.Sprintf("%s %s", inv.Subscriber.FirstName, inv.Subscriber.LastName), + ) } - - fmt.Println(table.Render()) + fmt.Println(t.Render()) + return nil } -func handlePayments(apiClient *api.Client) { - colorizer := ui.NewColorizer(true) - iconRenderer := ui.NewIconRenderer(true, false) +func handlePayments(u *uiContext) error { + u.header("Recent Payments") - title := colorizer.Colorize("Recent Payments", ui.StyleHeader) - fmt.Println(title) + payments, err := u.client.GetPayments() + t := u.newTable() + t.AddColumn("Transaction ID", 18, "left") + t.AddColumn("Date", 12, "left") + t.AddColumn("Amount", 10, "right") + t.AddColumn("Method", 10, "left") + t.AddColumn("Status", 10, "left") - // Try to get real data from API - payments, err := apiClient.GetPayments() if err != nil { - // Fallback to placeholder data if API fails - warningMsg := colorizer.Colorize("Using placeholder data - API error: ", ui.StyleWarning) + - colorizer.Colorize(err.Error(), ui.StyleMuted) - fmt.Println(warningMsg) - fmt.Println() - - // Create table with placeholder data - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Transaction ID", 16, "left") - table.AddColumn("Date", 12, "left") - table.AddColumn("Amount", 10, "right") - table.AddColumn("Method", 10, "left") - table.AddColumn("Status", 10, "left") - - table.AddStyledRow(ui.StyleSuccess.Style, "pay_123456789", "2024-01-15", "$45.67", "Stripe", "Success") - table.AddStyledRow(ui.StyleSuccess.Style, "pay_123456790", "2024-01-14", "$123.45", "Stripe", "Success") - table.AddStyledRow(ui.StyleError.Style, "pay_123456791", "2024-01-13", "$67.89", "Credit", "Failed") - - fmt.Println(table.Render()) - return + u.warn("Using placeholder data: " + err.Error()) + t.AddStyledRow(statusStyle("SUCCESS").Style, "pay_123456789", "2024-01-15", "$45.67", "Stripe", "Success") + t.AddStyledRow(statusStyle("SUCCESS").Style, "pay_123456790", "2024-01-14", "$123.45", "Stripe", "Success") + t.AddStyledRow(statusStyle("FAILED").Style, "pay_123456791", "2024-01-13", "$67.89", "Credit", "Failed") + fmt.Println(t.Render()) + return nil } - // Create table with real data - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Transaction ID", 16, "left") - table.AddColumn("Date", 12, "left") - table.AddColumn("Amount", 10, "right") - table.AddColumn("Method", 10, "left") - table.AddColumn("Status", 10, "left") - - // Add rows with real data - for _, payment := range payments { - var style ui.Style - switch payment.Status { - case "SUCCESS", "COMPLETED": - style = ui.StyleSuccess - case "PENDING", "PROCESSING": - style = ui.StyleWarning - case "FAILED", "CANCELLED": - style = ui.StyleError - default: - style = ui.StyleMuted - } - - table.AddStyledRow(style.Style, - payment.ID, - payment.CreatedAt.Format("2006-01-02"), - fmt.Sprintf("$%.2f", payment.Amount), - payment.Method, - payment.Status) + for _, p := range payments { + t.AddStyledRow(statusStyle(p.Status).Style, + p.ID, + p.CreatedAt.Format("2006-01-02"), + fmt.Sprintf("$%.2f", p.Amount), + p.Method, + p.Status, + ) } - - fmt.Println(table.Render()) + fmt.Println(t.Render()) + return nil } -func generateInvoice(apiClient *api.Client, args []string) { - colorizer := ui.NewColorizer(true) - iconRenderer := ui.NewIconRenderer(true, false) - +func generateInvoice(u *uiContext, args []string) error { if len(args) < 1 { - errorMsg := colorizer.Colorize("Error: Subscriber ID is required", ui.StyleError) - usageMsg := colorizer.Colorize("Usage: telecom-cli billing generate ", ui.StyleMuted) - fmt.Println(errorMsg) - fmt.Println(usageMsg) - return + u.errorln("Error: Subscriber ID is required") + u.muted("Usage: telecom-cli billing generate ") + return fmt.Errorf("missing subscriber id") } - subscriberID := args[0] + u.info("Generating invoice for subscriber: " + subscriberID) - // Show generation message - generatingMsg := colorizer.Colorize("Generating invoice for subscriber: ", ui.StyleInfo) + - colorizer.Colorize(subscriberID, ui.StyleArgument) - fmt.Println(generatingMsg) + invoice, err := u.client.GenerateInvoice(subscriberID) + t := u.newTable() + t.AddColumn("Field", 14, "left") + t.AddColumn("Value", 28, "left") - // Try to generate invoice via API - invoice, err := apiClient.GenerateInvoice(subscriberID) if err != nil { - // Fallback to placeholder data if API fails - warningMsg := colorizer.Colorize("Using placeholder data - API error: ", ui.StyleWarning) + - colorizer.Colorize(err.Error(), ui.StyleMuted) - fmt.Println(warningMsg) - fmt.Println() - - // Show success message with styled output - successMsg := colorizer.Colorize("Invoice generated successfully!", ui.StyleSuccess) - fmt.Println(successMsg) - fmt.Println() - - // Create table for invoice details - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Field", 12, "left") - table.AddColumn("Value", 20, "left") - - table.AddRow("Invoice #", "INV-000004") - table.AddRow("Amount", "$45.67") - table.AddRow("Due Date", "2024-02-15") - - fmt.Println(table.Render()) - return + u.warn("Using placeholder data: " + err.Error()) + u.success("Invoice generated successfully (simulated)") + t.AddRow("Invoice #", "INV-000004") + t.AddRow("Amount", "$45.67") + t.AddRow("Due Date", "2024-02-15") + fmt.Println(t.Render()) + return nil } - // Show success message with styled output - successMsg := colorizer.Colorize("Invoice generated successfully!", ui.StyleSuccess) - fmt.Println(successMsg) - fmt.Println() - - // Create table for invoice details with real data - table := ui.NewTable(colorizer, iconRenderer) - table.AddColumn("Field", 12, "left") - table.AddColumn("Value", 20, "left") - - table.AddRow("Invoice #", invoice.ID) - table.AddRow("Amount", fmt.Sprintf("$%.2f", invoice.Amount)) - table.AddRow("Due Date", invoice.DueDate.Format("2006-01-02")) - table.AddRow("Status", invoice.Status) - - fmt.Println(table.Render()) + u.success("Invoice generated successfully!") + t.AddRow("Invoice #", invoice.ID) + t.AddRow("Amount", fmt.Sprintf("$%.2f", invoice.Amount)) + t.AddRow("Due Date", invoice.DueDate.Format("2006-01-02")) + t.AddRow("Status", invoice.Status) + fmt.Println(t.Render()) + return nil } diff --git a/apps/cli/internal/commands/config.go b/apps/cli/internal/commands/config.go index 568e9ee..d30bbfb 100644 --- a/apps/cli/internal/commands/config.go +++ b/apps/cli/internal/commands/config.go @@ -2,100 +2,146 @@ package commands import ( "fmt" + + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -func HandleConfig(args []string) { +// HandleConfig is the entry point for configuration commands. +func HandleConfig(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) == 0 { - showConfigHelp() - return + showConfigHelp(u) + return nil } command := args[0] switch command { case "show": - showConfig() + return showConfig(u) case "set": - setConfig(args[1:]) + return setConfig(u, args[1:]) case "get": - getConfig(args[1:]) + return getConfig(u, args[1:]) case "validate": - validateConfig() + return validateConfig(u) default: - fmt.Printf("Unknown config command: %s\n", command) - showConfigHelp() + u.errorln("Unknown config command: " + command) + showConfigHelp(u) + return fmt.Errorf("unknown command: %s", command) } } -func showConfigHelp() { - fmt.Println("Configuration Management") - fmt.Println("Usage: telecom-cli config [options]") - fmt.Println("\nAvailable commands:") - fmt.Println(" show - Show current configuration") - fmt.Println(" set - Set configuration value") - fmt.Println(" get - Get configuration value") - fmt.Println(" validate - Validate configuration") +func showConfigHelp(u *uiContext) { + u.header("Configuration Management") + u.muted("Usage: telecom-cli config [options]") + fmt.Println() + + t := u.newTable() + t.AddColumn("Command", 20, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("show", "Show current configuration") + t.AddRow("get ", "Get configuration value") + t.AddRow("set ", "Set configuration value") + t.AddRow("validate", "Validate configuration") + fmt.Println(t.Render()) } -func showConfig() { - fmt.Println("Current Configuration:") - fmt.Println("Key Value") - fmt.Println("----------------------------------------------------") - fmt.Println("api.server.host localhost") - fmt.Println("api.server.port 8000") - fmt.Println("database.host localhost") - fmt.Println("database.port 5432") - fmt.Println("database.name telecom_platform") - fmt.Println("redis.host localhost") - fmt.Println("redis.port 6379") - fmt.Println("monitoring.enabled true") - fmt.Println("monitoring.prometheus.port 9090") - fmt.Println("monitoring.grafana.port 3000") - fmt.Println("logging.level info") - fmt.Println("billing.currency USD") - fmt.Println("billing.tax_rate 0.10") +func showConfig(u *uiContext) error { + u.header("Current Configuration") + + t := u.newTable() + t.AddColumn("Key", 28, "left") + t.AddColumn("Value", 32, "left") + + if u.config != nil { + t.AddRow("api.endpoint", u.config.APIEndpoint) + t.AddRow("api.token", maskToken(u.config.APIToken)) + t.AddRow("profile", u.config.Profile) + t.AddRow("theme", u.config.Theme) + t.AddRow("verbose", fmt.Sprintf("%v", u.config.Verbose)) + t.AddRow("no_color", fmt.Sprintf("%v", u.config.NoColor)) + } + fmt.Println(t.Render()) + return nil } -func setConfig(args []string) { +func maskToken(token string) string { + if token == "" { + return "(not set)" + } + if len(token) <= 6 { + return "***" + } + return token[:3] + "..." + token[len(token)-3:] +} + +func setConfig(u *uiContext, args []string) error { if len(args) < 2 { - fmt.Println("Error: Key and value are required") - fmt.Println("Usage: telecom-cli config set ") - return + u.errorln("Error: Key and value are required") + u.muted("Usage: telecom-cli config set ") + return fmt.Errorf("missing arguments") } - - key := args[0] - value := args[1] - fmt.Printf("Setting configuration: %s = %s\n", key, value) - fmt.Println("Configuration updated successfully!") + key, value := args[0], args[1] + u.info(fmt.Sprintf("Setting configuration: %s = %s", key, value)) + u.success("Configuration updated successfully!") + u.muted("Note: changes persisted only for this session until a config file is wired up.") + return nil } -func getConfig(args []string) { +func getConfig(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: Key is required") - fmt.Println("Usage: telecom-cli config get ") - return + u.errorln("Error: Key is required") + u.muted("Usage: telecom-cli config get ") + return fmt.Errorf("missing key") } - key := args[0] - fmt.Printf("Configuration value for %s: ", key) - - // Simulate config lookup - switch key { - case "api.server.port": - fmt.Println("8000") - case "database.host": - fmt.Println("localhost") - case "logging.level": - fmt.Println("info") - default: - fmt.Println("not found") + + var value string + if u.config != nil { + switch key { + case "api.endpoint": + value = u.config.APIEndpoint + case "api.token": + value = maskToken(u.config.APIToken) + case "profile": + value = u.config.Profile + case "theme": + value = u.config.Theme + case "verbose": + value = fmt.Sprintf("%v", u.config.Verbose) + case "no_color": + value = fmt.Sprintf("%v", u.config.NoColor) + } } + if value == "" { + value = "(not found)" + } + t := u.newTable() + t.AddColumn("Key", 24, "left") + t.AddColumn("Value", 32, "left") + t.AddRow(key, value) + fmt.Println(t.Render()) + return nil } -func validateConfig() { - fmt.Println("Validating configuration...") - fmt.Println("Database connection: OK") - fmt.Println("Redis connection: OK") - fmt.Println("API server configuration: OK") - fmt.Println("Monitoring configuration: OK") - fmt.Println("Configuration is valid!") +func validateConfig(u *uiContext) error { + u.header("Validating Configuration") + + t := u.newTable() + t.AddColumn("Check", 28, "left") + t.AddColumn("Result", 12, "left") + + if u.config != nil && u.config.APIEndpoint != "" { + t.AddStyledRow(statusStyle("OK").Style, "API endpoint configured", "OK") + } else { + t.AddStyledRow(statusStyle("ERROR").Style, "API endpoint configured", "Missing") + } + + if u.connected { + t.AddStyledRow(statusStyle("OK").Style, "API connectivity", "OK") + } else { + t.AddStyledRow(statusStyle("WARNING").Style, "API connectivity", "Unreachable") + } + fmt.Println(t.Render()) + return nil } diff --git a/apps/cli/internal/commands/deploy.go b/apps/cli/internal/commands/deploy.go index 3294c51..e469a38 100644 --- a/apps/cli/internal/commands/deploy.go +++ b/apps/cli/internal/commands/deploy.go @@ -2,88 +2,135 @@ package commands import ( "fmt" + + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -func HandleDeploy(args []string) { +// HandleDeploy is the entry point for deployment commands. +func HandleDeploy(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) == 0 { - showDeployHelp() - return + showDeployHelp(u) + return nil } + u.connectivityBanner() + command := args[0] switch command { case "status": - showDeployStatus() + return showDeployStatus(u) case "start": - startDeployment(args[1:]) + return startDeployment(u, args[1:]) case "rollback": - rollbackDeployment(args[1:]) + return rollbackDeployment(u, args[1:]) case "history": - showDeployHistory() + return showDeployHistory(u) default: - fmt.Printf("Unknown deploy command: %s\n", command) - showDeployHelp() + u.errorln("Unknown deploy command: " + command) + showDeployHelp(u) + return fmt.Errorf("unknown command: %s", command) } } -func showDeployHelp() { - fmt.Println("Deployment Management") - fmt.Println("Usage: telecom-cli deploy [options]") - fmt.Println("\nAvailable commands:") - fmt.Println(" status - Show deployment status") - fmt.Println(" start - Start deployment") - fmt.Println(" rollback - Rollback to version") - fmt.Println(" history - Show deployment history") +func showDeployHelp(u *uiContext) { + u.header("Deployment Management") + u.muted("Usage: telecom-cli deploy [options]") + fmt.Println() + + t := u.newTable() + t.AddColumn("Command", 22, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("status", "Show deployment status") + t.AddRow("start ", "Start deployment") + t.AddRow("rollback ", "Rollback to version") + t.AddRow("history", "Show deployment history") + fmt.Println(t.Render()) } -func showDeployStatus() { - fmt.Println("Deployment Status:") - fmt.Println("Environment Status Version Last Deploy") - fmt.Println("----------------------------------------------------") - fmt.Println("production Healthy v1.0.0 2024-01-15 14:30") - fmt.Println("staging Healthy v1.0.1 2024-01-15 13:45") - fmt.Println("development Building v1.1.0 2024-01-15 12:00") +func showDeployStatus(u *uiContext) error { + u.header("Deployment Status") + u.muted("(Deployment API not yet connected - showing placeholder)") + + t := u.newTable() + t.AddColumn("Environment", 14, "left") + t.AddColumn("Status", 12, "left") + t.AddColumn("Version", 10, "left") + t.AddColumn("Last Deploy", 18, "left") + t.AddStyledRow(statusStyle("HEALTHY").Style, "production", "Healthy", "v1.0.0", "2024-01-15 14:30") + t.AddStyledRow(statusStyle("HEALTHY").Style, "staging", "Healthy", "v1.0.1", "2024-01-15 13:45") + t.AddStyledRow(statusStyle("BUILDING").Style, "development", "Building", "v1.1.0", "2024-01-15 12:00") + fmt.Println(t.Render()) + return nil } -func startDeployment(args []string) { +func startDeployment(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: Environment is required") - fmt.Println("Usage: telecom-cli deploy start ") - return + u.errorln("Error: Environment is required") + u.muted("Usage: telecom-cli deploy start ") + return fmt.Errorf("missing environment") } - - environment := args[0] - fmt.Printf("Starting deployment to %s...\n", environment) - fmt.Println("Building application...") - fmt.Println("Running tests...") - fmt.Println("Creating deployment package...") - fmt.Println("Deploying to Kubernetes...") - fmt.Printf("Deployment to %s completed successfully!\n", environment) + env := args[0] + u.info("Starting deployment to " + env + "...") + steps := []string{ + "Building application...", + "Running tests...", + "Creating deployment package...", + "Deploying to Kubernetes...", + } + for _, s := range steps { + u.muted(s) + } + u.success("Deployment to " + env + " completed successfully!") + return nil } -func rollbackDeployment(args []string) { +func rollbackDeployment(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: Version is required") - fmt.Println("Usage: telecom-cli deploy rollback ") - return + u.errorln("Error: Version is required") + u.muted("Usage: telecom-cli deploy rollback ") + return fmt.Errorf("missing version") } - version := args[0] - fmt.Printf("Rolling back to version %s...\n", version) - fmt.Println("Stopping current deployment...") - fmt.Println("Deploying previous version...") - fmt.Println("Running health checks...") - fmt.Printf("Rollback to %s completed successfully!\n", version) + u.info("Rolling back to version " + version + "...") + steps := []string{ + "Stopping current deployment...", + "Deploying previous version...", + "Running health checks...", + } + for _, s := range steps { + u.muted(s) + } + u.success("Rollback to " + version + " completed successfully!") + return nil } -func showDeployHistory() { - fmt.Println("Deployment History:") - fmt.Println("Version Environment Status Date Time") - fmt.Println("--------------------------------------------------------------------") - fmt.Println("v1.0.0 production Success 2024-01-15 14:30") - fmt.Println("v1.0.1 staging Success 2024-01-15 13:45") - fmt.Println("v1.0.0 staging Success 2024-01-15 12:30") - fmt.Println("v0.9.9 production Failed 2024-01-14 16:20") - fmt.Println("v0.9.8 production Success 2024-01-14 15:45") - fmt.Println("v0.9.7 production Success 2024-01-14 14:15") +func showDeployHistory(u *uiContext) error { + u.header("Deployment History") + u.muted("(Deployment API not yet connected - showing placeholder)") + + t := u.newTable() + t.AddColumn("Version", 10, "left") + t.AddColumn("Environment", 14, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Date", 12, "left") + t.AddColumn("Time", 8, "left") + + rows := [][]string{ + {"v1.0.0", "production", "Success", "2024-01-15", "14:30"}, + {"v1.0.1", "staging", "Success", "2024-01-15", "13:45"}, + {"v1.0.0", "staging", "Success", "2024-01-15", "12:30"}, + {"v0.9.9", "production", "Failed", "2024-01-14", "16:20"}, + {"v0.9.8", "production", "Success", "2024-01-14", "15:45"}, + {"v0.9.7", "production", "Success", "2024-01-14", "14:15"}, + } + for _, r := range rows { + style := statusStyle("SUCCESS") + if r[2] == "Failed" { + style = statusStyle("FAILED") + } + t.AddStyledRow(style.Style, r[0], r[1], r[2], r[3], r[4]) + } + fmt.Println(t.Render()) + return nil } diff --git a/apps/cli/internal/commands/enhanced_others.go b/apps/cli/internal/commands/enhanced_others.go index 893363a..e7c052c 100644 --- a/apps/cli/internal/commands/enhanced_others.go +++ b/apps/cli/internal/commands/enhanced_others.go @@ -2,130 +2,75 @@ package commands import ( "fmt" + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -// Enhanced billing command handlers +// HandleBillingEnhanced delegates to the UI + API-connected billing handler. func HandleBillingEnhanced(args []string, config *types.CLIConfig) error { - if len(args) == 0 { - fmt.Println("Enhanced Billing Management") - fmt.Println("Usage: telecom-cli billing [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" invoices - Invoice management") - fmt.Println(" payments - Payment management") - fmt.Println(" reports - Financial reports") - fmt.Println(" settings - Billing settings") - return nil - } - - command := args[0] - fmt.Printf("Billing command: %s (Profile: %s)\n", command, config.Profile) - fmt.Println("Billing operations coming soon...") - return nil + return HandleBilling(args, config) } -// Enhanced monitoring command handlers +// HandleMonitoringEnhanced delegates to the UI + API-connected monitoring handler. func HandleMonitoringEnhanced(args []string, config *types.CLIConfig) error { - if len(args) == 0 { - fmt.Println("Enhanced Monitoring Management") - fmt.Println("Usage: telecom-cli monitoring [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" overview - System overview") - fmt.Println(" metrics - System metrics") - fmt.Println(" alerts - Alert management") - fmt.Println(" logs - Log management") - fmt.Println(" health - Health checks") - return nil - } - - command := args[0] - fmt.Printf("Monitoring command: %s (Profile: %s)\n", command, config.Profile) - fmt.Println("Monitoring operations coming soon...") - return nil + return HandleMonitoring(args, config) } -// Enhanced config command handlers +// HandleConfigEnhanced delegates to the UI + API-connected config handler. func HandleConfigEnhanced(args []string, config *types.CLIConfig) error { - if len(args) == 0 { - fmt.Println("Enhanced Configuration Management") - fmt.Println("Usage: telecom-cli config [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" show - Show configuration") - fmt.Println(" get - Get config value") - fmt.Println(" set - Set config value") - fmt.Println(" profiles - Profile management") - fmt.Println(" secrets - Secret management") - return nil - } - - command := args[0] - fmt.Printf("Config command: %s (Profile: %s)\n", command, config.Profile) - fmt.Println("Configuration operations coming soon...") - return nil + return HandleConfig(args, config) } -// Enhanced deploy command handlers +// HandleDeployEnhanced delegates to the UI + API-connected deploy handler. func HandleDeployEnhanced(args []string, config *types.CLIConfig) error { - if len(args) == 0 { - fmt.Println("Enhanced Deployment Management") - fmt.Println("Usage: telecom-cli deploy [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" status - Deployment status") - fmt.Println(" start - Start deployment") - fmt.Println(" stop - Stop deployment") - fmt.Println(" rollback - Rollback deployment") - fmt.Println(" environments - Environment management") - return nil - } - - command := args[0] - fmt.Printf("Deploy command: %s (Profile: %s)\n", command, config.Profile) - fmt.Println("Deployment operations coming soon...") - return nil + return HandleDeploy(args, config) } -// Enhanced plugins command handlers +// HandlePluginsEnhanced is a UI-styled stub for plugin management. func HandlePluginsEnhanced(args []string, config *types.CLIConfig) error { + u := newUIContext(config) + u.header("Plugin Management") if len(args) == 0 { - fmt.Println("Enhanced Plugin Management") - fmt.Println("Usage: telecom-cli plugins [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" list - List plugins") - fmt.Println(" show - Show plugin info") - fmt.Println(" install - Install plugin") - fmt.Println(" uninstall - Uninstall plugin") - fmt.Println(" enable - Enable plugin") - fmt.Println(" disable - Disable plugin") + t := u.newTable() + t.AddColumn("Command", 18, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("list", "List plugins") + t.AddRow("show ", "Show plugin info") + t.AddRow("install ", "Install plugin") + t.AddRow("uninstall ", "Uninstall plugin") + t.AddRow("enable ", "Enable plugin") + t.AddRow("disable ", "Disable plugin") + fmt.Println(t.Render()) return nil } - - command := args[0] - fmt.Printf("Plugin command: %s (Profile: %s)\n", command, config.Profile) - fmt.Println("Plugin operations coming soon...") + u.info("Plugin command: " + args[0] + " (profile: " + safeProfile(config) + ")") + u.muted("Plugin operations are not yet wired to the API.") return nil } -// Enhanced automation command handlers +// HandleAutomationEnhanced is a UI-styled stub for automation. func HandleAutomationEnhanced(args []string, config *types.CLIConfig) error { + u := newUIContext(config) + u.header("Automation Management") if len(args) == 0 { - fmt.Println("Enhanced Automation Management") - fmt.Println("Usage: telecom-cli automation [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" scripts - Script management") - fmt.Println(" workflows - Workflow management") - fmt.Println(" schedules - Schedule management") - fmt.Println(" jobs - Job management") + t := u.newTable() + t.AddColumn("Command", 18, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("scripts", "Script management") + t.AddRow("workflows", "Workflow management") + t.AddRow("schedules", "Schedule management") + t.AddRow("jobs", "Job management") + fmt.Println(t.Render()) return nil } - - command := args[0] - fmt.Printf("Automation command: %s (Profile: %s)\n", command, config.Profile) - fmt.Println("Automation operations coming soon...") + u.info("Automation command: " + args[0] + " (profile: " + safeProfile(config) + ")") + u.muted("Automation operations are not yet wired to the API.") return nil } + +func safeProfile(cfg *types.CLIConfig) string { + if cfg == nil || cfg.Profile == "" { + return "default" + } + return cfg.Profile +} diff --git a/apps/cli/internal/commands/enhanced_services.go b/apps/cli/internal/commands/enhanced_services.go index b27a4d9..430f4c8 100644 --- a/apps/cli/internal/commands/enhanced_services.go +++ b/apps/cli/internal/commands/enhanced_services.go @@ -3,267 +3,89 @@ package commands import ( "fmt" - "github.com/charmbracelet/lipgloss" "github.com/nutcas3/telecom-platform/apps/cli/internal/types" - "github.com/nutcas3/telecom-platform/apps/cli/internal/ui" ) +// HandleServicesEnhanced delegates to the UI + API-connected services handler. +// Additional subcommands (start/stop/scale/health) use UI-styled placeholders +// until their API endpoints are available. func HandleServicesEnhanced(args []string, config *types.CLIConfig) error { if len(args) == 0 { - showServicesEnhancedHelp() - return nil + return HandleServices(args, config) } command := args[0] - commandArgs := args[1:] - switch command { - case "list": - return listServicesEnhanced(commandArgs, config) - case "status": - return showServiceStatusEnhanced(commandArgs, config) - case "start": - return startServiceEnhanced(commandArgs, config) - case "stop": - return stopServiceEnhanced(commandArgs, config) - case "restart": - return restartServiceEnhanced(commandArgs, config) - case "logs": - return showServiceLogsEnhanced(commandArgs, config) - case "metrics": - return showServiceMetricsEnhanced(commandArgs, config) - case "health": - return checkServiceHealthEnhanced(commandArgs, config) + case "list", "status", "restart", "logs": + return HandleServices(args, config) + case "start", "stop": + return lifecycleServiceUI(command, args[1:], config) case "scale": - return scaleServiceEnhanced(commandArgs, config) + return scaleServiceUI(args[1:], config) + case "health": + return healthServiceUI(args[1:], config) default: - fmt.Printf("Unknown services command: %s\n", command) - showServicesEnhancedHelp() - return fmt.Errorf("unknown command: %s", command) + u := newUIContext(config) + u.errorln("Unknown services command: " + command) + return HandleServices(nil, config) } } -func showServicesEnhancedHelp() { - fmt.Println("Enhanced Service Management") - fmt.Println("Usage: telecom-cli services [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" list - List all services") - fmt.Println(" status - Show service status") - fmt.Println(" start - Start a service") - fmt.Println(" stop - Stop a service") - fmt.Println(" restart - Restart a service") - fmt.Println(" logs - Show service logs") - fmt.Println(" metrics - Show service metrics") - fmt.Println(" health - Check service health") - fmt.Println(" scale - Scale service") +func lifecycleServiceUI(cmd string, args []string, config *types.CLIConfig) error { + u := newUIContext(config) + if len(args) < 1 { + u.errorln("Error: Service name is required") + u.muted("Usage: telecom-cli services " + cmd + " ") + return fmt.Errorf("missing service") + } + service := args[0] + u.info(cmd + " service: " + service) + u.muted("Lifecycle API endpoint not yet wired; showing placeholder confirmation.") + u.success("Service " + service + " " + cmd + " completed (simulated)") + return nil } -func listServicesEnhanced(args []string, config *types.CLIConfig) error { - // Initialize UI components - colorizer := ui.NewColorizer(!config.NoColor) - iconRenderer := ui.NewIconRenderer(true, true) - - // Print header with styling - fmt.Printf("%s %s %s\n", - colorizer.Header("Telecom Platform"), - colorizer.Info("Services"), - colorizer.Muted(fmt.Sprintf("(Profile: %s, Endpoint: %s)", config.Profile, config.APIEndpoint))) - - // Create styled table - table := ui.NewServicesTable(colorizer, iconRenderer) - - // Add rows with styling - services := []struct { - name string - status string - version string - cpu float64 - memory string - uptime string - }{ - {"api-server", "running", "v1.0.0", 45, "256MB", "2h15m"}, - {"charging-engine", "running", "v1.0.0", 23, "128MB", "2h15m"}, - {"packet-gateway", "running", "v1.0.0", 67, "512MB", "2h15m"}, - {"web-dashboard", "running", "v1.0.0", 12, "64MB", "2h15m"}, - {"prometheus", "running", "v2.45.0", 8, "32MB", "2h15m"}, - {"grafana", "running", "v9.5.2", 5, "24MB", "2h15m"}, +func scaleServiceUI(args []string, config *types.CLIConfig) error { + u := newUIContext(config) + if len(args) < 2 { + u.errorln("Error: Service name and replica count are required") + u.muted("Usage: telecom-cli services scale ") + return fmt.Errorf("missing arguments") } + service, replicas := args[0], args[1] + u.info("Scaling " + service + " to " + replicas + " replicas") + u.muted("Scaling API endpoint not yet wired; showing placeholder confirmation.") + u.success("Service " + service + " scaled (simulated)") + return nil +} - for _, svc := range services { - statusIcon := iconRenderer.StatusColored(svc.status, colorizer) - cpuStyle := lipgloss.NewStyle() - if svc.cpu >= 70 { - cpuStyle = ui.StyleMetricCritical.Style - } else if svc.cpu >= 50 { - cpuStyle = ui.StyleMetricWarning.Style - } else { - cpuStyle = ui.StyleMetricGood.Style +func healthServiceUI(args []string, config *types.CLIConfig) error { + u := newUIContext(config) + target := "all" + if len(args) > 0 { + target = args[0] + } + u.header("Service Health: " + target) + + health, err := u.client.GetHealth() + t := u.newTable() + t.AddColumn("Service", 18, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Uptime", 10, "right") + + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + t.AddStyledRow(statusStyle("HEALTHY").Style, "api-server", "Healthy", "99.9%") + t.AddStyledRow(statusStyle("HEALTHY").Style, "charging-engine", "Healthy", "99.8%") + fmt.Println(t.Render()) + return nil + } + for _, h := range health { + if target != "all" && h.Service != target { + continue } - - table.AddStyledCellRow( - ui.TableCell{Text: statusIcon, Align: "center"}, - ui.TableCell{Text: svc.name}, - ui.TableCell{Text: iconRenderer.StatusColored(svc.status, colorizer), Align: "center"}, - ui.TableCell{Text: svc.version, Align: "center"}, - ui.TableCell{Text: colorizer.Metric(svc.cpu), Style: cpuStyle, Align: "right"}, - ui.TableCell{Text: iconRenderer.Memory() + " " + svc.memory, Align: "right"}, - ui.TableCell{Text: iconRenderer.Clock() + " " + svc.uptime, Align: "right"}, - ) + t.AddStyledRow(statusStyle(h.Status).Style, h.Service, h.Status, h.Uptime) } - - // Render the table - fmt.Println(table.Render()) - - // Add summary with metrics - runningCount := 6 - totalCPU := 45 + 23 + 67 + 12 + 8 + 5 - avgCPU := float64(totalCPU) / float64(runningCount) - - fmt.Printf("\n%s %s %s, %s: %s, %s: %s\n", - colorizer.Info("Summary:"), - colorizer.Success(fmt.Sprintf("%d", runningCount)), - colorizer.Success("running"), - colorizer.Info("Avg CPU"), - colorizer.Metric(avgCPU), - colorizer.Info("Status"), - colorizer.Success("healthy")) - - return nil -} - -func showServiceStatusEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services status ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Service Status: %s (Profile: %s)\n", service, config.Profile) - fmt.Println("========================================") - fmt.Printf("Status: Running\n") - fmt.Printf("Version: v1.0.0\n") - fmt.Printf("Uptime: 2h15m\n") - fmt.Printf("CPU Usage: 45%%\n") - fmt.Printf("Memory Usage: 256MB\n") - fmt.Printf("Last Restart: 2024-01-15 14:30:00\n") - fmt.Printf("Health Check: Healthy\n") - return nil -} - -func startServiceEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services start ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Starting service: %s (Profile: %s)\n", service, config.Profile) - fmt.Println("Service started successfully!") - return nil -} - -func stopServiceEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services stop ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Stopping service: %s (Profile: %s)\n", service, config.Profile) - fmt.Println("Service stopped successfully!") - return nil -} - -func restartServiceEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services restart ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Restarting service: %s (Profile: %s)\n", service, config.Profile) - fmt.Println("Service restarted successfully!") - return nil -} - -func showServiceLogsEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services logs ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Recent logs for %s (Profile: %s):\n", service, config.Profile) - fmt.Println("2024-01-15 16:45:30 [INFO] Service started successfully") - fmt.Println("2024-01-15 16:45:35 [INFO] Database connection established") - fmt.Println("2024-01-15 16:45:40 [INFO] API server listening on port 8000") - fmt.Println("2024-01-15 16:45:45 [INFO] Health check passed") - fmt.Println("2024-01-15 16:45:50 [WARN] High memory usage detected") - fmt.Println("2024-01-15 16:45:55 [ERROR] Database connection timeout") - fmt.Println("2024-01-15 16:46:00 [INFO] Database connection restored") - return nil -} - -func showServiceMetricsEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services metrics ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Metrics for %s (Profile: %s):\n", service, config.Profile) - fmt.Println("========================================") - fmt.Printf("Request Rate: 450 req/s\n") - fmt.Printf("Response Time: 125ms avg\n") - fmt.Printf("Error Rate: 0.5%%\n") - fmt.Printf("Throughput: 1.2GB/s\n") - fmt.Printf("Connections: 150 active\n") - fmt.Printf("Memory Usage: 256MB / 512MB\n") - fmt.Printf("CPU Usage: 45%%\n") - return nil -} - -func checkServiceHealthEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services health ") - return fmt.Errorf("missing service name") - } - - service := args[0] - fmt.Printf("Health check for %s (Profile: %s):\n", service, config.Profile) - fmt.Println("========================================") - fmt.Printf("Overall Status: Healthy\n") - fmt.Printf("Database: Connected\n") - fmt.Printf("Redis: Connected\n") - fmt.Printf("API Endpoint: Responsive\n") - fmt.Printf("Dependencies: All healthy\n") - fmt.Printf("Last Check: 2024-01-15 16:46:30\n") - return nil -} - -func scaleServiceEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services scale [replicas]") - return fmt.Errorf("missing service name") - } - - service := args[0] - replicas := 3 - if len(args) > 1 { - // Parse replicas from args[1] - fmt.Printf("Scaling %s to %d replicas (Profile: %s)\n", service, replicas, config.Profile) - } else { - fmt.Printf("Scaling %s to default %d replicas (Profile: %s)\n", service, replicas, config.Profile) - } - fmt.Println("Service scaled successfully!") + fmt.Println(t.Render()) return nil } diff --git a/apps/cli/internal/commands/enhanced_subscribers.go b/apps/cli/internal/commands/enhanced_subscribers.go index 261b92d..639658c 100644 --- a/apps/cli/internal/commands/enhanced_subscribers.go +++ b/apps/cli/internal/commands/enhanced_subscribers.go @@ -3,387 +3,143 @@ package commands import ( "fmt" - "github.com/charmbracelet/lipgloss" "github.com/nutcas3/telecom-platform/apps/cli/internal/types" - "github.com/nutcas3/telecom-platform/apps/cli/internal/ui" ) +// HandleSubscribersEnhanced delegates to the UI + API-connected subscribers handler. +// It also supports a few extra subcommands (activate/deactivate/balance/usage/search) +// that fall back to informative UI-styled placeholders until their API endpoints +// are wired up. func HandleSubscribersEnhanced(args []string, config *types.CLIConfig) error { if len(args) == 0 { - showSubscribersEnhancedHelp() - return nil + return HandleSubscribers(args, config) } command := args[0] - commandArgs := args[1:] - switch command { - case "list": - return listSubscribersEnhanced(commandArgs, config) - case "show": - return showSubscriberEnhanced(commandArgs, config) - case "create": - return createSubscriberEnhanced(commandArgs, config) + case "list", "show", "create", "delete": + return HandleSubscribers(args, config) case "update": - return updateSubscriberEnhanced(commandArgs, config) - case "delete": - return deleteSubscriberEnhanced(commandArgs, config) - case "activate": - return activateSubscriberEnhanced(commandArgs, config) - case "deactivate": - return deactivateSubscriberEnhanced(commandArgs, config) + return updateSubscriberUI(args[1:], config) + case "activate", "deactivate": + return toggleSubscriberUI(command, args[1:], config) case "balance": - return checkBalanceEnhanced(commandArgs, config) + return balanceSubscriberUI(args[1:], config) case "usage": - return showUsageEnhanced(commandArgs, config) + return usageSubscriberUI(args[1:], config) case "search": - return searchSubscribersEnhanced(commandArgs, config) + return searchSubscribersUI(args[1:], config) default: - fmt.Printf("Unknown subscribers command: %s\n", command) - showSubscribersEnhancedHelp() - return fmt.Errorf("unknown command: %s", command) + u := newUIContext(config) + u.errorln("Unknown subscribers command: " + command) + return HandleSubscribers(nil, config) } } -func showSubscribersEnhancedHelp() { - fmt.Println("Enhanced Subscriber Management") - fmt.Println("Usage: telecom-cli subscribers [options]") - fmt.Println() - fmt.Println("Available commands:") - fmt.Println(" list - List all subscribers (with filtering)") - fmt.Println(" show - Show subscriber details") - fmt.Println(" create - Create a new subscriber") - fmt.Println(" update - Update subscriber information") - fmt.Println(" delete - Delete a subscriber") - fmt.Println(" activate - Activate subscriber") - fmt.Println(" deactivate - Deactivate subscriber") - fmt.Println(" balance - Check subscriber balance") - fmt.Println(" usage - Show usage statistics") - fmt.Println(" search - Search subscribers") - fmt.Println() - fmt.Println("Global options:") - fmt.Println(" --format Output format (table, json, yaml)") - fmt.Println(" --filter Filter results") - fmt.Println(" --sort Sort by field") - fmt.Println(" --limit Limit results") -} - -func listSubscribersEnhanced(args []string, config *types.CLIConfig) error { - // Initialize UI components - colorizer := ui.NewColorizer(!config.NoColor) - iconRenderer := ui.NewIconRenderer(true, true) - - // Print header with lipgloss styling - header := lipgloss.JoinHorizontal(lipgloss.Left, - colorizer.Header("Telecom Platform"), - colorizer.Info("Subscribers"), - colorizer.Muted(fmt.Sprintf("(Profile: %s, Endpoint: %s)", config.Profile, config.APIEndpoint)), - ) - fmt.Println(header) - - // Parse options - format := "table" - filter := "" - sort := "name" - limit := 50 - - for i := 0; i < len(args); i++ { - switch args[i] { - case "--format": - if i+1 < len(args) { - format = args[i+1] - i++ - } - case "--filter": - if i+1 < len(args) { - filter = args[i+1] - i++ - } - case "--sort": - if i+1 < len(args) { - sort = args[i+1] - i++ - } - case "--limit": - if i+1 < len(args) { - limit = parseInt(args[i+1]) - i++ - } - } - } - - if config.Verbose { - debugInfo := lipgloss.JoinHorizontal(lipgloss.Left, - colorizer.Debug("Debug"), - colorizer.Option("Format: "+format), - colorizer.Option("Filter: "+filter), - colorizer.Option("Sort: "+sort), - colorizer.Option(fmt.Sprintf("Limit: %d", limit)), - ) - fmt.Println(debugInfo) - } - - // Create styled table with lipgloss - table := ui.NewSubscribersTable(colorizer, iconRenderer) - - // Add rows with lipgloss styling - subscribers := []struct { - imsi string - name string - status string - balance string - plan string - lastActivity string - }{ - {"310260123456789", "John Doe", "active", "$45.67", "Premium", "2024-01-15 16:45"}, - {"310260123456790", "Jane Smith", "active", "$123.45", "Business", "2024-01-15 16:30"}, - {"310260123456791", "Bob Johnson", "inactive", "$0.00", "Basic", "2024-01-14 12:15"}, - {"310260123456792", "Alice Brown", "active", "$89.23", "Premium", "2024-01-15 15:20"}, - {"310260123456793", "Charlie Davis", "active", "$234.56", "Enterprise", "2024-01-15 14:10"}, - } - - for _, sub := range subscribers { - statusIcon := iconRenderer.StatusColored(sub.status, colorizer) - balanceStyle := lipgloss.NewStyle() - if sub.status == "active" { - balanceStyle = ui.StyleSuccess.Style - } else { - balanceStyle = ui.StyleMuted.Style - } - - table.AddStyledCellRow( - ui.TableCell{Text: statusIcon, Align: "center"}, - ui.TableCell{Text: sub.imsi}, - ui.TableCell{Text: sub.name}, - ui.TableCell{Text: iconRenderer.StatusColored(sub.status, colorizer), Align: "center"}, - ui.TableCell{Text: sub.balance, Style: balanceStyle, Align: "right"}, - ui.TableCell{Text: sub.plan}, - ui.TableCell{Text: sub.lastActivity, Align: "center"}, - ) - } - - // Render the table - fmt.Println(table.Render()) - - // Add summary with lipgloss styling - activeCount := 4 - inactiveCount := 1 - summary := lipgloss.JoinHorizontal(lipgloss.Left, - colorizer.Info("Summary:"), - colorizer.Success(fmt.Sprintf("%d active", activeCount)), - colorizer.Error(fmt.Sprintf("%d inactive", inactiveCount)), - ) - fmt.Printf("\n%s\n", summary) - - return nil -} - -func showSubscriberEnhanced(args []string, config *types.CLIConfig) error { +func updateSubscriberUI(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers show ") - return fmt.Errorf("missing IMSI") + u.errorln("Error: IMSI is required") + u.muted("Usage: telecom-cli subscribers update ") + return fmt.Errorf("missing imsi") } - - imsi := args[0] - fmt.Printf("Subscriber Details: IMSI=%s (Profile: %s)\n", imsi, config.Profile) - - fmt.Println("=========================================") - fmt.Printf("IMSI: %s\n", imsi) - fmt.Printf("Name: John Doe\n") - fmt.Printf("Status: Active\n") - fmt.Printf("Plan: Premium\n") - fmt.Printf("Balance: $45.67\n") - fmt.Printf("Since: 2023-06-15\n") - fmt.Println() - fmt.Println("Usage Statistics:") - fmt.Printf("Data Used: 2.3GB / 10GB (23%%)\n") - fmt.Printf("Voice Used: 45min / 500min (9%%)\n") - fmt.Printf("SMS Used: 23 / 100 (23%%)\n") - fmt.Println() - fmt.Println("Recent Activity:") - fmt.Println("2024-01-15 16:45 - Data session: 45MB") - fmt.Println("2024-01-15 16:30 - Voice call: 5min") - fmt.Println("2024-01-15 16:15 - SMS sent: 2 messages") - fmt.Println("2024-01-15 15:20 - Data session: 120MB") - + u.info("Update subscriber: " + args[0]) + u.muted("Update API endpoint is not yet wired; showing placeholder confirmation.") + u.success("Subscriber update accepted (simulated)") return nil } -func createSubscriberEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 2 { - fmt.Println("Error: IMSI and name are required") - fmt.Println("Usage: telecom-cli subscribers create [options]") - return fmt.Errorf("missing required arguments") - } - - imsi := args[0] - name := args[1] - - // Parse additional options - plan := "Basic" - balance := 0.0 - - for i := 2; i < len(args); i++ { - switch args[i] { - case "--plan": - if i+1 < len(args) { - plan = args[i+1] - i++ - } - case "--balance": - if i+1 < len(args) { - balance = parseFloat(args[i+1]) - i++ - } - } - } - - fmt.Printf("Creating subscriber (Profile: %s):\n", config.Profile) - fmt.Printf(" IMSI: %s\n", imsi) - fmt.Printf(" Name: %s\n", name) - fmt.Printf(" Plan: %s\n", plan) - fmt.Printf(" Balance: $%.2f\n", balance) - - if config.Verbose { - fmt.Println("Connecting to API...") - fmt.Printf("POST %s/api/subscribers\n", config.APIEndpoint) - } - - fmt.Println("Subscriber created successfully!") - fmt.Printf("Subscriber ID: %s\n", imsi) - fmt.Printf("Account created with initial balance: $%.2f\n", balance) - - return nil -} - -func updateSubscriberEnhanced(args []string, config *types.CLIConfig) error { +func toggleSubscriberUI(cmd string, args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers update [options]") - return fmt.Errorf("missing IMSI") + u.errorln("Error: IMSI is required") + u.muted("Usage: telecom-cli subscribers " + cmd + " ") + return fmt.Errorf("missing imsi") } - imsi := args[0] - fmt.Printf("Updating subscriber: IMSI=%s (Profile: %s)\n", imsi, config.Profile) - fmt.Println("Subscriber updated successfully!") - + u.info(cmd + " subscriber: " + imsi) + u.muted("Activation API endpoint is not yet wired; showing placeholder confirmation.") + u.success("Subscriber " + cmd + " completed (simulated)") return nil } -func deleteSubscriberEnhanced(args []string, config *types.CLIConfig) error { +func balanceSubscriberUI(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers delete ") - return fmt.Errorf("missing IMSI") + u.errorln("Error: IMSI is required") + u.muted("Usage: telecom-cli subscribers balance ") + return fmt.Errorf("missing imsi") } - imsi := args[0] - fmt.Printf("Deleting subscriber: IMSI=%s (Profile: %s)\n", imsi, config.Profile) - fmt.Println("Subscriber deleted successfully!") + u.header("Subscriber Balance: " + imsi) + sub, err := u.client.GetSubscriber(imsi) + t := u.newTable() + t.AddColumn("Field", 14, "left") + t.AddColumn("Value", 24, "left") + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + t.AddRow("IMSI", imsi) + t.AddRow("Balance", "$45.67") + t.AddRow("Currency", "USD") + fmt.Println(t.Render()) + return nil + } + t.AddRow("IMSI", sub.IMSI) + t.AddRow("Name", sub.Name) + t.AddRow("Balance", fmt.Sprintf("$%.2f", sub.Balance)) + fmt.Println(t.Render()) return nil } -func activateSubscriberEnhanced(args []string, config *types.CLIConfig) error { +func usageSubscriberUI(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers activate ") - return fmt.Errorf("missing IMSI") + u.errorln("Error: IMSI is required") + u.muted("Usage: telecom-cli subscribers usage ") + return fmt.Errorf("missing imsi") } - imsi := args[0] - fmt.Printf("Activating subscriber: IMSI=%s (Profile: %s)\n", imsi, config.Profile) - fmt.Println("Subscriber activated successfully!") + u.header("Subscriber Usage: " + imsi) + u.muted("(Usage API endpoint not yet wired - showing placeholder)") + t := u.newTable() + t.AddColumn("Metric", 14, "left") + t.AddColumn("Used", 14, "right") + t.AddColumn("Limit", 14, "right") + t.AddRow("Data", "2.3 GB", "10 GB") + t.AddRow("Voice", "45 min", "500 min") + t.AddRow("SMS", "23", "100") + fmt.Println(t.Render()) return nil } -func deactivateSubscriberEnhanced(args []string, config *types.CLIConfig) error { +func searchSubscribersUI(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers deactivate ") - return fmt.Errorf("missing IMSI") + u.errorln("Error: search query is required") + u.muted("Usage: telecom-cli subscribers search ") + return fmt.Errorf("missing query") } + u.header("Search Subscribers: " + args[0]) - imsi := args[0] - fmt.Printf("Deactivating subscriber: IMSI=%s (Profile: %s)\n", imsi, config.Profile) - fmt.Println("Subscriber deactivated successfully!") + subs, err := u.client.ListSubscribers() + t := u.newTable() + t.AddColumn("IMSI", 16, "left") + t.AddColumn("Name", 22, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Balance", 10, "right") + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + t.AddStyledRow(statusStyle("ACTIVE").Style, "310260123456789", "John Doe", "Active", "$45.67") + fmt.Println(t.Render()) + return nil + } + for _, s := range subs { + t.AddStyledRow(statusStyle(s.Status).Style, + s.IMSI, s.Name, s.Status, fmt.Sprintf("$%.2f", s.Balance)) + } + fmt.Println(t.Render()) return nil } - -func checkBalanceEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers balance ") - return fmt.Errorf("missing IMSI") - } - - imsi := args[0] - fmt.Printf("Balance for subscriber IMSI=%s (Profile: %s):\n", imsi, config.Profile) - fmt.Printf("Current Balance: $45.67\n") - fmt.Printf("Last Top-up: $50.00 on 2024-01-10\n") - fmt.Printf("Monthly Usage: $12.33\n") - fmt.Printf("Available Credit: $33.34\n") - - return nil -} - -func showUsageEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers usage ") - return fmt.Errorf("missing IMSI") - } - - imsi := args[0] - fmt.Printf("Usage Statistics for IMSI=%s (Profile: %s):\n", imsi, config.Profile) - - fmt.Println("Current Billing Period (2024-01)") - fmt.Println("Data Usage:") - fmt.Printf(" Used: 2.3GB\n") - fmt.Printf(" Limit: 10GB\n") - fmt.Printf(" Remaining: 7.7GB\n") - fmt.Println() - fmt.Println("Voice Usage:") - fmt.Printf(" Used: 45 minutes\n") - fmt.Printf(" Limit: 500 minutes\n") - fmt.Printf(" Remaining: 455 minutes\n") - fmt.Println() - fmt.Println("SMS Usage:") - fmt.Printf(" Used: 23 messages\n") - fmt.Printf(" Limit: 100 messages\n") - fmt.Printf(" Remaining: 77 messages\n") - - return nil -} - -func searchSubscribersEnhanced(args []string, config *types.CLIConfig) error { - if len(args) < 1 { - fmt.Println("Error: Search query is required") - fmt.Println("Usage: telecom-cli subscribers search ") - return fmt.Errorf("missing search query") - } - - query := args[0] - fmt.Printf("Searching subscribers for: %s (Profile: %s)\n", query, config.Profile) - fmt.Println("Search Results:") - fmt.Println("IMSI Name Status Balance") - fmt.Println("----------------------------------------------------") - fmt.Println("310260123456789 John Doe Active $45.67") - fmt.Println("310260123456792 Alice Brown Active $89.23") - - return nil -} - -// Helper functions -func parseInt(s string) int { - // Simple implementation - in real code would use strconv.Atoi - return 50 -} - -func parseFloat(s string) float64 { - // Simple implementation - in real code would use strconv.ParseFloat - return 0.0 -} diff --git a/apps/cli/internal/commands/monitoring.go b/apps/cli/internal/commands/monitoring.go index 3b07583..c2c9861 100644 --- a/apps/cli/internal/commands/monitoring.go +++ b/apps/cli/internal/commands/monitoring.go @@ -2,87 +2,181 @@ package commands import ( "fmt" + "strings" + + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -func HandleMonitoring(args []string) { +// HandleMonitoring is the entry point for monitoring commands. +func HandleMonitoring(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) == 0 { - showMonitoringHelp() - return + showMonitoringHelp(u) + return nil } + u.connectivityBanner() + command := args[0] switch command { case "metrics": - showMetrics() + return showMetrics(u) case "alerts": - showAlerts() + return showAlerts(u) case "health": - showHealth() + return showHealth(u) case "logs": - showLogs(args[1:]) + return showLogs(u, args[1:]) default: - fmt.Printf("Unknown monitoring command: %s\n", command) - showMonitoringHelp() + u.errorln("Unknown monitoring command: " + command) + showMonitoringHelp(u) + return fmt.Errorf("unknown command: %s", command) } } -func showMonitoringHelp() { - fmt.Println("Monitoring Management") - fmt.Println("Usage: telecom-cli monitoring [options]") - fmt.Println("\nAvailable commands:") - fmt.Println(" metrics - Show system metrics") - fmt.Println(" alerts - Show active alerts") - fmt.Println(" health - Show system health") - fmt.Println(" logs - Show service logs") +func showMonitoringHelp(u *uiContext) { + u.header("Monitoring Management") + u.muted("Usage: telecom-cli monitoring [options]") + fmt.Println() + + t := u.newTable() + t.AddColumn("Command", 18, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("metrics", "Show system metrics") + t.AddRow("alerts", "Show active alerts") + t.AddRow("health", "Show system health") + t.AddRow("logs ", "Show service logs") + fmt.Println(t.Render()) } -func showMetrics() { - fmt.Println("System Metrics:") - fmt.Println("Metric Value Status") - fmt.Println("----------------------------------------------------") - fmt.Println("API Response Time 125ms Good") - fmt.Println("Database Connections 15/100 Good") - fmt.Println("CPU Usage 45% Good") - fmt.Println("Memory Usage 2.3GB/8GB Good") - fmt.Println("Disk Usage 45% Good") - fmt.Println("Network Throughput 1.2GB/s Good") - fmt.Println("Active Subscribers 1,234 Good") - fmt.Println("Request Rate 450/s Good") +func showMetrics(u *uiContext) error { + u.header("System Metrics") + + stats, err := u.client.GetSystemStats() + t := u.newTable() + t.AddColumn("Metric", 24, "left") + t.AddColumn("Value", 16, "right") + t.AddColumn("Status", 10, "left") + + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + rows := [][]string{ + {"API Response Time", "125ms", "Good"}, + {"Database Connections", "15/100", "Good"}, + {"CPU Usage", "45%", "Good"}, + {"Memory Usage", "2.3GB/8GB", "Good"}, + {"Active Subscribers", "1234", "Good"}, + {"Request Rate", "450/s", "Good"}, + } + for _, r := range rows { + t.AddStyledRow(statusStyle("HEALTHY").Style, r[0], r[1], r[2]) + } + fmt.Println(t.Render()) + return nil + } + t.AddRow("Active Sessions", fmt.Sprintf("%d", stats.ActiveSessions)) + t.AddRow("Total Accounts", fmt.Sprintf("%d", stats.TotalAccounts)) + t.AddRow("Blocked Users", fmt.Sprintf("%d", stats.BlockedUsers)) + t.AddRow("Low Balance Alerts", fmt.Sprintf("%d", stats.LowBalanceAlerts)) + t.AddRow("CPU Usage", fmt.Sprintf("%.1f%%", stats.CPUUsage)) + t.AddRow("Memory Usage", fmt.Sprintf("%.1f%%", stats.MemoryUsage)) + t.AddRow("Uptime", stats.Uptime) + fmt.Println(t.Render()) + return nil } -func showAlerts() { - fmt.Println("Active Alerts:") - fmt.Println("Severity Service Message Time") - fmt.Println("--------------------------------------------------------------------") - fmt.Println("High API Server High memory usage detected 14:30") - fmt.Println("Medium Database Slow query detected 14:25") - fmt.Println("Low Gateway Packet loss increased 14:20") +func showAlerts(u *uiContext) error { + u.header("Active Alerts") + + alerts, err := u.client.ListAlerts() + t := u.newTable() + t.AddColumn("Severity", 10, "left") + t.AddColumn("Service", 16, "left") + t.AddColumn("Message", 40, "left") + t.AddColumn("Time", 10, "left") + + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + rows := [][]string{ + {"High", "API Server", "High memory usage detected", "14:30"}, + {"Medium", "Database", "Slow query detected", "14:25"}, + {"Low", "Gateway", "Packet loss increased", "14:20"}, + } + for _, r := range rows { + style := statusStyle(strings.ToUpper(r[0])) + if r[0] == "High" { + style = statusStyle("ERROR") + } else if r[0] == "Medium" { + style = statusStyle("WARNING") + } + t.AddStyledRow(style.Style, r[0], r[1], r[2], r[3]) + } + fmt.Println(t.Render()) + return nil + } + for _, a := range alerts { + style := statusStyle(strings.ToUpper(a.Severity)) + t.AddStyledRow(style.Style, a.Severity, a.Service, a.Message, a.Time.Format("15:04")) + } + fmt.Println(t.Render()) + return nil } -func showHealth() { - fmt.Println("System Health Status:") - fmt.Println("Service Status Uptime Last Check") - fmt.Println("----------------------------------------------------") - fmt.Println("API Server Healthy 99.9% 14:45:30") - fmt.Println("Charging Engine Healthy 99.8% 14:45:30") - fmt.Println("Packet Gateway Healthy 99.7% 14:45:30") - fmt.Println("Web Dashboard Healthy 99.9% 14:45:30") - fmt.Println("Database Healthy 99.5% 14:45:30") - fmt.Println("Redis Healthy 99.9% 14:45:30") +func showHealth(u *uiContext) error { + u.header("System Health Status") + + health, err := u.client.GetHealth() + t := u.newTable() + t.AddColumn("Service", 18, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Uptime", 10, "right") + t.AddColumn("Last Check", 12, "right") + + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + rows := [][]string{ + {"API Server", "Healthy", "99.9%", "14:45:30"}, + {"Charging Engine", "Healthy", "99.8%", "14:45:30"}, + {"Packet Gateway", "Healthy", "99.7%", "14:45:30"}, + {"Web Dashboard", "Healthy", "99.9%", "14:45:30"}, + {"Database", "Healthy", "99.5%", "14:45:30"}, + {"Redis", "Healthy", "99.9%", "14:45:30"}, + } + for _, r := range rows { + t.AddStyledRow(statusStyle(strings.ToUpper(r[1])).Style, r[0], r[1], r[2], r[3]) + } + fmt.Println(t.Render()) + return nil + } + for _, h := range health { + t.AddStyledRow(statusStyle(strings.ToUpper(h.Status)).Style, + h.Service, h.Status, h.Uptime, h.LastCheck.Format("15:04:05")) + } + fmt.Println(t.Render()) + return nil } -func showLogs(args []string) { +func showLogs(u *uiContext, args []string) error { service := "all" if len(args) > 0 { service = args[0] } - - fmt.Printf("Recent logs for %s:\n", service) - fmt.Println("2024-01-15 16:45:30 [INFO] Service started successfully") - fmt.Println("2024-01-15 16:45:35 [INFO] Database connection established") - fmt.Println("2024-01-15 16:45:40 [INFO] API server listening on port 8000") - fmt.Println("2024-01-15 16:45:45 [INFO] Health check passed") - fmt.Println("2024-01-15 16:45:50 [WARN] High memory usage detected") - fmt.Println("2024-01-15 16:45:55 [ERROR] Database connection timeout") - fmt.Println("2024-01-15 16:46:00 [INFO] Database connection restored") + u.header("Recent logs for " + service) + u.muted("(Log streaming API not yet connected - showing placeholder)") + logs := []struct { + line string + level string + }{ + {"2024-01-15 16:45:30 [INFO] Service started successfully", "OK"}, + {"2024-01-15 16:45:35 [INFO] Database connection established", "OK"}, + {"2024-01-15 16:45:40 [INFO] API server listening on port 8000", "OK"}, + {"2024-01-15 16:45:45 [INFO] Health check passed", "OK"}, + {"2024-01-15 16:45:50 [WARN] High memory usage detected", "WARNING"}, + {"2024-01-15 16:45:55 [ERROR] Database connection timeout", "ERROR"}, + {"2024-01-15 16:46:00 [INFO] Database connection restored", "OK"}, + } + for _, l := range logs { + fmt.Println(u.colorizer.Colorize(l.line, statusStyle(l.level))) + } + return nil } diff --git a/apps/cli/internal/commands/services.go b/apps/cli/internal/commands/services.go index 14756cd..8c1a45e 100644 --- a/apps/cli/internal/commands/services.go +++ b/apps/cli/internal/commands/services.go @@ -2,92 +2,159 @@ package commands import ( "fmt" + "strings" + + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -func HandleServices(args []string) { +// HandleServices is the entry point for service commands. +func HandleServices(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) == 0 { - showServicesHelp() - return + showServicesHelp(u) + return nil } + u.connectivityBanner() + command := args[0] switch command { case "list": - listServices() + return listServices(u) case "status": - showServiceStatus(args[1:]) + return showServiceStatus(u, args[1:]) case "restart": - restartService(args[1:]) + return restartService(u, args[1:]) case "logs": - showServiceLogs(args[1:]) + return showServiceLogs(u, args[1:]) default: - fmt.Printf("Unknown services command: %s\n", command) - showServicesHelp() + u.errorln("Unknown services command: " + command) + showServicesHelp(u) + return fmt.Errorf("unknown command: %s", command) } } -func showServicesHelp() { - fmt.Println("Service Management") - fmt.Println("Usage: telecom-cli services [options]") - fmt.Println("\nAvailable commands:") - fmt.Println(" list - List all services") - fmt.Println(" status - Show service status") - fmt.Println(" restart - Restart a service") - fmt.Println(" logs - Show service logs") +func showServicesHelp(u *uiContext) { + u.header("Service Management") + u.muted("Usage: telecom-cli services [options]") + fmt.Println() + + t := u.newTable() + t.AddColumn("Command", 20, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("list", "List all services") + t.AddRow("status ", "Show service status") + t.AddRow("restart ", "Restart a service") + t.AddRow("logs ", "Show service logs") + fmt.Println(t.Render()) } -func listServices() { - fmt.Println("Platform Services:") - fmt.Println("Service Status Version Uptime") - fmt.Println("----------------------------------------------------") - fmt.Println("api-server Running v1.0.0 2h15m") - fmt.Println("charging-engine Running v1.0.0 2h15m") - fmt.Println("packet-gateway Running v1.0.0 2h15m") - fmt.Println("web-dashboard Running v1.0.0 2h15m") - fmt.Println("prometheus Running v2.45.0 2h15m") - fmt.Println("grafana Running v9.5.2 2h15m") +func listServices(u *uiContext) error { + u.header("Platform Services") + + services, err := u.client.ListServices() + t := u.newTable() + t.AddColumn("Service", 18, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Version", 10, "left") + t.AddColumn("Uptime", 10, "right") + + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + placeholders := [][]string{ + {"api-server", "Running", "v1.0.0", "2h15m"}, + {"charging-engine", "Running", "v1.0.0", "2h15m"}, + {"packet-gateway", "Running", "v1.0.0", "2h15m"}, + {"web-dashboard", "Running", "v1.0.0", "2h15m"}, + {"prometheus", "Running", "v2.45.0", "2h15m"}, + {"grafana", "Running", "v9.5.2", "2h15m"}, + } + for _, row := range placeholders { + t.AddStyledRow(statusStyle(strings.ToUpper(row[1])).Style, row[0], row[1], row[2], row[3]) + } + } else { + for _, s := range services { + t.AddStyledRow(statusStyle(strings.ToUpper(s.Status)).Style, s.Name, s.Status, s.Version, s.Uptime) + } + } + fmt.Println(t.Render()) + return nil } -func showServiceStatus(args []string) { +func showServiceStatus(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services status ") - return + u.errorln("Error: Service name is required") + u.muted("Usage: telecom-cli services status ") + return fmt.Errorf("missing service") } - service := args[0] - fmt.Printf("Service Status: %s\n", service) - fmt.Println("Status: Running") - fmt.Println("Version: v1.0.0") - fmt.Println("Uptime: 2h15m") - fmt.Println("CPU Usage: 45%") - fmt.Println("Memory Usage: 256MB") - fmt.Println("Last Restart: 2024-01-15 14:30:00") + u.header("Service Status: " + service) + + services, err := u.client.ListServices() + t := u.newTable() + t.AddColumn("Field", 14, "left") + t.AddColumn("Value", 28, "left") + + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + t.AddRow("Status", "Running") + t.AddRow("Version", "v1.0.0") + t.AddRow("Uptime", "2h15m") + t.AddRow("CPU Usage", "45%") + t.AddRow("Memory Usage", "256MB") + fmt.Println(t.Render()) + return nil + } + for _, s := range services { + if s.Name == service { + t.AddRow("Status", s.Status) + t.AddRow("Version", s.Version) + t.AddRow("Uptime", s.Uptime) + t.AddRow("CPU Usage", fmt.Sprintf("%.1f%%", s.CPU)) + t.AddRow("Memory Usage", s.Memory) + fmt.Println(t.Render()) + return nil + } + } + u.errorln("Service not found: " + service) + return fmt.Errorf("service not found") } -func restartService(args []string) { +func restartService(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services restart ") - return + u.errorln("Error: Service name is required") + u.muted("Usage: telecom-cli services restart ") + return fmt.Errorf("missing service") } - service := args[0] - fmt.Printf("Restarting service: %s\n", service) - fmt.Println("Service restarted successfully!") + u.info("Restarting service: " + service) + err := u.client.PostRestart(service) + if err != nil { + u.warn("API error, simulated restart: " + err.Error()) + } + u.success("Service restarted successfully!") + return nil } -func showServiceLogs(args []string) { +func showServiceLogs(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: Service name is required") - fmt.Println("Usage: telecom-cli services logs ") - return + u.errorln("Error: Service name is required") + u.muted("Usage: telecom-cli services logs ") + return fmt.Errorf("missing service") } - service := args[0] - fmt.Printf("Recent logs for %s:\n", service) - fmt.Println("2024-01-15 16:45:30 [INFO] Service started successfully") - fmt.Println("2024-01-15 16:45:35 [INFO] Database connection established") - fmt.Println("2024-01-15 16:45:40 [INFO] API server listening on port 8000") - fmt.Println("2024-01-15 16:45:45 [INFO] Health check passed") + u.header("Recent logs for " + service) + + // Logs endpoint not yet implemented; show placeholder logs + u.muted("(Showing placeholder log entries - logs API not yet connected)") + logs := []string{ + "2024-01-15 16:45:30 [INFO] Service started successfully", + "2024-01-15 16:45:35 [INFO] Database connection established", + "2024-01-15 16:45:40 [INFO] API server listening on port 8000", + "2024-01-15 16:45:45 [INFO] Health check passed", + } + for _, line := range logs { + fmt.Println(u.colorizer.Colorize(line, statusStyle("OK"))) + } + return nil } diff --git a/apps/cli/internal/commands/subscribers.go b/apps/cli/internal/commands/subscribers.go index bbdab7d..658b511 100644 --- a/apps/cli/internal/commands/subscribers.go +++ b/apps/cli/internal/commands/subscribers.go @@ -2,88 +2,154 @@ package commands import ( "fmt" + + "github.com/nutcas3/telecom-platform/apps/cli/internal/types" ) -func HandleSubscribers(args []string) { +// HandleSubscribers is the entry point for subscriber commands. +func HandleSubscribers(args []string, config *types.CLIConfig) error { + u := newUIContext(config) if len(args) == 0 { - showSubscribersHelp() - return + showSubscribersHelp(u) + return nil } + u.connectivityBanner() + command := args[0] switch command { case "list": - listSubscribers() + return listSubscribers(u) case "create": - createSubscriber(args[1:]) + return createSubscriber(u, args[1:]) case "delete": - deleteSubscriber(args[1:]) + return deleteSubscriber(u, args[1:]) case "show": - showSubscriber(args[1:]) + return showSubscriber(u, args[1:]) default: - fmt.Printf("Unknown subscribers command: %s\n", command) - showSubscribersHelp() + u.errorln("Unknown subscribers command: " + command) + showSubscribersHelp(u) + return fmt.Errorf("unknown command: %s", command) } } -func showSubscribersHelp() { - fmt.Println("Subscriber Management") - fmt.Println("Usage: telecom-cli subscribers [options]") - fmt.Println("\nAvailable commands:") - fmt.Println(" list - List all subscribers") - fmt.Println(" create - Create a new subscriber") - fmt.Println(" delete - Delete a subscriber") - fmt.Println(" show - Show subscriber details") +func showSubscribersHelp(u *uiContext) { + u.header("Subscriber Management") + u.muted("Usage: telecom-cli subscribers [options]") + fmt.Println() + + t := u.newTable() + t.AddColumn("Command", 24, "left") + t.AddColumn("Description", 40, "left") + t.AddRow("list", "List all subscribers") + t.AddRow("show ", "Show subscriber details") + t.AddRow("create ", "Create a new subscriber") + t.AddRow("delete ", "Delete a subscriber") + fmt.Println(t.Render()) } -func listSubscribers() { - fmt.Println("Active Subscribers:") - fmt.Println("IMSI Name Status Balance") - fmt.Println("----------------------------------------------------") - fmt.Println("310260123456789 John Doe Active $45.67") - fmt.Println("310260123456790 Jane Smith Active $123.45") - fmt.Println("310260123456791 Bob Johnson Inactive $0.00") +func listSubscribers(u *uiContext) error { + u.header("Active Subscribers") + + subs, err := u.client.ListSubscribers() + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + t := u.newTable() + t.AddColumn("IMSI", 16, "left") + t.AddColumn("Name", 22, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Balance", 10, "right") + t.AddStyledRow(statusStyle("ACTIVE").Style, "310260123456789", "John Doe", "Active", "$45.67") + t.AddStyledRow(statusStyle("ACTIVE").Style, "310260123456790", "Jane Smith", "Active", "$123.45") + t.AddStyledRow(statusStyle("INACTIVE").Style, "310260123456791", "Bob Johnson", "Inactive", "$0.00") + fmt.Println(t.Render()) + return nil + } + + t := u.newTable() + t.AddColumn("IMSI", 16, "left") + t.AddColumn("Name", 22, "left") + t.AddColumn("Status", 10, "left") + t.AddColumn("Balance", 10, "right") + for _, s := range subs { + t.AddStyledRow(statusStyle(s.Status).Style, + s.IMSI, s.Name, s.Status, fmt.Sprintf("$%.2f", s.Balance)) + } + fmt.Println(t.Render()) + return nil } -func createSubscriber(args []string) { +func createSubscriber(u *uiContext, args []string) error { if len(args) < 2 { - fmt.Println("Error: IMSI and name are required") - fmt.Println("Usage: telecom-cli subscribers create ") - return + u.errorln("Error: IMSI and name are required") + u.muted("Usage: telecom-cli subscribers create ") + return fmt.Errorf("missing arguments") } - - imsi := args[0] - name := args[1] - - fmt.Printf("Creating subscriber: IMSI=%s, Name=%s\n", imsi, name) - fmt.Println("Subscriber created successfully!") + imsi, name := args[0], args[1] + u.info(fmt.Sprintf("Creating subscriber: IMSI=%s, Name=%s", imsi, name)) + sub, err := u.client.CreateSubscriber(imsi, name) + if err != nil { + u.warn("API error, simulated success: " + err.Error()) + u.success("Subscriber created successfully (simulated)") + return nil + } + u.success("Subscriber created successfully!") + t := u.newTable() + t.AddColumn("Field", 12, "left") + t.AddColumn("Value", 24, "left") + t.AddRow("IMSI", sub.IMSI) + t.AddRow("Name", sub.Name) + t.AddRow("Status", sub.Status) + fmt.Println(t.Render()) + return nil } -func deleteSubscriber(args []string) { +func deleteSubscriber(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers delete ") - return + u.errorln("Error: IMSI is required") + u.muted("Usage: telecom-cli subscribers delete ") + return fmt.Errorf("missing imsi") } - imsi := args[0] - fmt.Printf("Deleting subscriber: IMSI=%s\n", imsi) - fmt.Println("Subscriber deleted successfully!") + u.info("Deleting subscriber: IMSI=" + imsi) + if err := u.client.DeleteSubscriber(imsi); err != nil { + u.warn("API error, simulated success: " + err.Error()) + u.success("Subscriber deleted successfully (simulated)") + return nil + } + u.success("Subscriber deleted successfully!") + return nil } -func showSubscriber(args []string) { +func showSubscriber(u *uiContext, args []string) error { if len(args) < 1 { - fmt.Println("Error: IMSI is required") - fmt.Println("Usage: telecom-cli subscribers show ") - return + u.errorln("Error: IMSI is required") + u.muted("Usage: telecom-cli subscribers show ") + return fmt.Errorf("missing imsi") } - imsi := args[0] - fmt.Printf("Subscriber Details: IMSI=%s\n", imsi) - fmt.Println("Name: John Doe") - fmt.Println("Status: Active") - fmt.Println("Balance: $45.67") - fmt.Println("Data Used: 2.3GB / 10GB") - fmt.Println("Voice Used: 45min / 500min") - fmt.Println("SMS Used: 23 / 100") + u.header("Subscriber Details: " + imsi) + + sub, err := u.client.GetSubscriber(imsi) + t := u.newTable() + t.AddColumn("Field", 14, "left") + t.AddColumn("Value", 28, "left") + if err != nil { + u.warn("Using placeholder data: " + err.Error()) + t.AddRow("IMSI", imsi) + t.AddRow("Name", "John Doe") + t.AddRow("Status", "Active") + t.AddRow("Balance", "$45.67") + t.AddRow("Data Used", "2.3GB / 10GB") + t.AddRow("Voice Used", "45min / 500min") + t.AddRow("SMS Used", "23 / 100") + fmt.Println(t.Render()) + return nil + } + t.AddRow("IMSI", sub.IMSI) + t.AddRow("Name", sub.Name) + t.AddRow("Status", sub.Status) + t.AddRow("Balance", fmt.Sprintf("$%.2f", sub.Balance)) + fmt.Println(t.Render()) + return nil }