From a569233cca2f756c3947754e3aa003f5022583bc Mon Sep 17 00:00:00 2001 From: mahesh bhatiya Date: Fri, 27 Jun 2025 22:36:55 +0530 Subject: [PATCH] feat(cli): add enable-ssl command with domain-based server detection - Added `enable-ssl` command to install Let's Encrypt SSL certificate for a specific domain - Supports Apache and Nginx via auto-detection based on domain config files - Skips SSL install for Caddy (since it handles SSL automatically) - Introduced reusable utility function `DetectServerType(domain string)` in internal/utils.go - Updated `renew-ssl` and `enable-ssl` to use shared server detection This improves modularity and avoids duplication while supporting on-demand SSL activation. --- cmd/enable_ssl.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/renew_ssl.go | 23 ++-------------------- internal/utils.go | 19 ++++++++++++++++++ 3 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 cmd/enable_ssl.go diff --git a/cmd/enable_ssl.go b/cmd/enable_ssl.go new file mode 100644 index 0000000..ce06bf0 --- /dev/null +++ b/cmd/enable_ssl.go @@ -0,0 +1,50 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "stackroost/internal" + "stackroost/internal/logger" +) + +var sslDomain string + +var enableSSLCmd = &cobra.Command{ + Use: "enable-ssl", + Short: "Enable Let's Encrypt SSL for a specific domain", + Run: func(cmd *cobra.Command, args []string) { + if sslDomain == "" { + logger.Error("Please provide a domain using --domain") + os.Exit(1) + } + + serverType := internal.DetectServerType(sslDomain) + if serverType == "" { + logger.Error(fmt.Sprintf("Could not detect server type for domain: %s", sslDomain)) + os.Exit(1) + } + + if serverType == "caddy" { + logger.Info("Caddy automatically handles SSL — no need to enable manually.") + return + } + + logger.Info(fmt.Sprintf("Detected %s configuration for %s", serverType, sslDomain)) + + err := internal.EnableSSLCertbot(sslDomain, serverType) + if err != nil { + logger.Error(fmt.Sprintf("Failed to enable SSL for %s: %v", sslDomain, err)) + os.Exit(1) + } + + logger.Success(fmt.Sprintf("SSL enabled successfully for %s", sslDomain)) + }, +} + +func init() { + rootCmd.AddCommand(enableSSLCmd) + enableSSLCmd.Flags().StringVar(&sslDomain, "domain", "", "Domain name to enable SSL for") + enableSSLCmd.MarkFlagRequired("domain") +} diff --git a/cmd/renew_ssl.go b/cmd/renew_ssl.go index c5c13c9..ff49bc6 100644 --- a/cmd/renew_ssl.go +++ b/cmd/renew_ssl.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "os" - "path/filepath" "github.com/spf13/cobra" "stackroost/internal" @@ -36,7 +35,7 @@ var renewSSLCmd = &cobra.Command{ os.Exit(1) } - serverType := detectServerType(domainName) + serverType := internal.DetectServerType(domainName) if serverType == "" { logger.Error(fmt.Sprintf("Could not detect server type for domain: %s", domainName)) os.Exit(1) @@ -71,22 +70,4 @@ func init() { renewSSLCmd.Flags().BoolVar(&renewAll, "all", false, "Renew all certificates") renewSSLCmd.Flags().StringVar(&domainName, "domain", "", "Domain to renew certificate for") renewSSLCmd.Flags().BoolVar(&forceFlag, "force", false, "Force renew the certificate") -} - -// detectServerType scans known config directories to guess server type -func detectServerType(domain string) string { - filename := domain + ".conf" - - paths := map[string]string{ - "apache": "/etc/apache2/sites-available", - "nginx": "/etc/nginx/sites-available", - "caddy": "/etc/caddy/sites-available", - } - - for server, dir := range paths { - if _, err := os.Stat(filepath.Join(dir, filename)); err == nil { - return server - } - } - return "" -} +} \ No newline at end of file diff --git a/internal/utils.go b/internal/utils.go index 40db50e..a165280 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -4,6 +4,8 @@ import ( "fmt" "os/exec" "os" + "path/filepath" + ) func RunCommand(name string, args ...string) error { @@ -19,3 +21,20 @@ func RunCommand(name string, args ...string) error { func IsNilOrEmpty(s string) bool { return s == "" || s == "" } + +func DetectServerType(domain string) string { + filename := domain + ".conf" + + paths := map[string]string{ + "apache": "/etc/apache2/sites-available", + "nginx": "/etc/nginx/sites-available", + "caddy": "/etc/caddy/sites-available", + } + + for server, dir := range paths { + if _, err := os.Stat(filepath.Join(dir, filename)); err == nil { + return server + } + } + return "" +} \ No newline at end of file