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
109 changes: 97 additions & 12 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,111 @@
package cmd

import (
"fmt"
"github.com/spf13/cobra"
"fmt"
"os"
"path/filepath"
"stackroost/config"
"stackroost/internal"
"github.com/spf13/cobra"
)

// rootCmd is the base command
var rootCmd = &cobra.Command{
Use: "stackrooy",
Short: "StackRooy CLI - manage your Linux servers with ease",
Run: func(cmd *cobra.Command, args []string) {
printWelcome()
},
Use: "stackroost",
Short: "StackRoost CLI - manage your Linux servers with ease",
Run: func(cmd *cobra.Command, args []string) {
printWelcome()
},
}

// createDomainCmd is the command to create a web server configuration
var createDomainCmd = &cobra.Command{
Use: "create-domain",
Short: "Create a web server configuration for a domain",
Run: func(cmd *cobra.Command, args []string) {
domain, _ := cmd.Flags().GetString("name")
port, _ := cmd.Flags().GetString("port")
serverType, _ := cmd.Flags().GetString("server")

if internal.IsNilOrEmpty(domain) {
fmt.Println("Error: --name flag is required and cannot be empty")
os.Exit(1)
}
if internal.IsNilOrEmpty(port) {
port = "80" // Default port
}

// Create web server configuration generator
configGen, err := config.NewWebServerConfig(serverType)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}

// Generate configuration
configContent, err := configGen.Generate(domain, port)
if err != nil {
fmt.Printf("Error generating config: %v\n", err)
os.Exit(1)
}

// Write configuration to file
if err := writeConfigFile(domain, configContent, configGen.GetFileExtension()); err != nil {
fmt.Printf("Error writing config file: %v\n", err)
os.Exit(1)
}

filename := fmt.Sprintf("%s%s", domain, configGen.GetFileExtension())

// Enable site using a2ensite
if err := internal.RunCommand("sudo", "a2ensite", filename); err != nil {
fmt.Printf("Failed to enable site: %v\n", err)
os.Exit(1)
}

// Reload apache to apply changes
if err := internal.RunCommand("sudo", "systemctl", "reload", "apache2"); err != nil {
fmt.Printf("Failed to reload apache: %v\n", err)
os.Exit(1)
}

fmt.Printf("%s configuration created and enabled for %s on port %s\n", serverType, domain, port)
},
}

func init() {
rootCmd.AddCommand(createDomainCmd)
createDomainCmd.Flags().StringP("name", "n", "", "Domain name for the configuration (e.g., mahesh.spark.dev)")
createDomainCmd.Flags().StringP("port", "p", "80", "Port for the configuration (default: 80)")
createDomainCmd.Flags().StringP("server", "s", "apache", "Web server type (e.g., apache, nginx, caddy)")
createDomainCmd.MarkFlagRequired("name")
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println("Error:", err)
}
if err := rootCmd.Execute(); err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
}

func printWelcome() {
fmt.Println("Welcome to StackRoot CLI!")
fmt.Println("Your terminal assistant for managing Linux servers.")
fmt.Println("Welcome to StackRoost CLI!")
fmt.Println("Your terminal assistant for managing Linux servers.")
}

// writeConfigFile writes the configuration to a file
func writeConfigFile(domain, content, extension string) error {
outputDir := "/etc/apache2/sites-available"
if err := os.MkdirAll(outputDir, 0755); err != nil {
return fmt.Errorf("failed to create output directory: %v", err)
}

filename := fmt.Sprintf("%s%s", domain, extension)
outputPath := filepath.Join(outputDir, filename)

if err := os.WriteFile(outputPath, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write config file: %v", err)
}

return nil
}
30 changes: 30 additions & 0 deletions config/apache/apache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package apache

import "fmt"

// ApacheConfig implements the WebServerConfig interface for Apache
type ApacheConfig struct{}

// Generate creates an Apache virtual host configuration
func (a *ApacheConfig) Generate(domain, port string) (string, error) {
vhostTemplate := `<VirtualHost *:%s>
ServerName %s
ServerAlias www.%s
DocumentRoot /var/www/%s
ErrorLog ${APACHE_LOG_DIR}/%s-error.log
CustomLog ${APACHE_LOG_DIR}/%s-access.log combined
<Directory /var/www/%s>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>`

config := fmt.Sprintf(vhostTemplate, port, domain, domain, domain, domain, domain, domain)
return config, nil
}

// GetFileExtension returns the file extension for Apache config files
func (a *ApacheConfig) GetFileExtension() string {
return ".conf"
}
22 changes: 22 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package config

import (
"fmt"
"stackroost/config/apache"
)

// WebServerConfig defines the interface for generating web server configurations
type WebServerConfig interface {
Generate(domain, port string) (string, error)
GetFileExtension() string
}

// NewWebServerConfig creates a new configuration generator based on the server type
func NewWebServerConfig(serverType string) (WebServerConfig, error) {
switch serverType {
case "apache":
return &apache.ApacheConfig{}, nil
default:
return nil, fmt.Errorf("unsupported web server type: %s", serverType)
}
}
21 changes: 19 additions & 2 deletions internal/utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
package internal

import (
"fmt"
"os/exec"
)

// RunCommand runs a shell command with args and returns error if any
func RunCommand(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout

Check failure on line 11 in internal/utils.go

View workflow job for this annotation

GitHub Actions / build

undefined: os
cmd.Stderr = os.Stderr

Check failure on line 12 in internal/utils.go

View workflow job for this annotation

GitHub Actions / build

undefined: os
if err := cmd.Run(); err != nil {
return fmt.Errorf("command %s %v failed: %w", name, args, err)
}
return nil
}

// IsNilOrEmpty returns true if the string is empty or "<nil>"
func IsNilOrEmpty(s string) bool {
return s == "" || s == "<nil>"
}
return s == "" || s == "<nil>"
}
Loading