From 87752492d0dc7df3cf78011d5ce315a3eb0cad51 Mon Sep 17 00:00:00 2001 From: bndw Date: Fri, 23 Jan 2026 21:52:50 -0800 Subject: Restructure CLI with Cobra Replace custom switch-based routing with Cobra for cleaner command hierarchy. Reorganize commands into logical groups: - Root command handles deployment (--binary, --static, --domain, etc.) - App management at top level: list, logs, status, restart, remove - env subcommand group: list, set, unset - host subcommand group: init, status, update, ssh - Standalone: ui (renamed from webui), version Add version command with ldflags support for build info. --- cmd/deploy/webui.go | 228 ---------------------------------------------------- 1 file changed, 228 deletions(-) delete mode 100644 cmd/deploy/webui.go (limited to 'cmd/deploy/webui.go') diff --git a/cmd/deploy/webui.go b/cmd/deploy/webui.go deleted file mode 100644 index f57400e..0000000 --- a/cmd/deploy/webui.go +++ /dev/null @@ -1,228 +0,0 @@ -package main - -import ( - "embed" - "encoding/json" - "flag" - "fmt" - "html/template" - "net/http" - "os" - "sort" - "strconv" - - "github.com/bdw/deploy/internal/state" - "github.com/bdw/deploy/internal/templates" -) - -//go:embed templates/*.html -var templatesFS embed.FS - -func runWebUI(args []string) { - fs := flag.NewFlagSet("webui", flag.ExitOnError) - port := fs.String("port", "8080", "Port to run the web UI on") - help := fs.Bool("h", false, "Show help") - - fs.Parse(args) - - if *help { - fmt.Fprintf(os.Stderr, `Usage: deploy webui [flags] - -Launch a web interface to view and manage deployments. - -FLAGS: - -port string - Port to run the web UI on (default "8080") - -h Show this help message - -EXAMPLE: - # Launch web UI on default port (8080) - deploy webui - - # Launch on custom port - deploy webui -port 3000 -`) - os.Exit(0) - } - - // Parse template - tmpl, err := template.ParseFS(templatesFS, "templates/webui.html") - if err != nil { - fmt.Fprintf(os.Stderr, "Error parsing template: %v\n", err) - os.Exit(1) - } - - // Handler for the main page - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - // Reload state on each request to show latest changes - st, err := state.Load() - if err != nil { - http.Error(w, fmt.Sprintf("Error loading state: %v", err), http.StatusInternalServerError) - return - } - - // Prepare data for template - type AppData struct { - Name string - Type string - Domain string - Port int - Env map[string]string - Host string - } - - type HostData struct { - Host string - Apps []AppData - } - - var hosts []HostData - for hostName, host := range st.Hosts { - var apps []AppData - for appName, app := range host.Apps { - apps = append(apps, AppData{ - Name: appName, - Type: app.Type, - Domain: app.Domain, - Port: app.Port, - Env: app.Env, - Host: hostName, - }) - } - - // Sort apps by name - sort.Slice(apps, func(i, j int) bool { - return apps[i].Name < apps[j].Name - }) - - hosts = append(hosts, HostData{ - Host: hostName, - Apps: apps, - }) - } - - // Sort hosts by name - sort.Slice(hosts, func(i, j int) bool { - return hosts[i].Host < hosts[j].Host - }) - - data := struct { - Hosts []HostData - }{ - Hosts: hosts, - } - - if err := tmpl.Execute(w, data); err != nil { - http.Error(w, fmt.Sprintf("Error rendering template: %v", err), http.StatusInternalServerError) - return - } - }) - - // API endpoint to get state as JSON - http.HandleFunc("/api/state", func(w http.ResponseWriter, r *http.Request) { - st, err := state.Load() - if err != nil { - http.Error(w, fmt.Sprintf("Error loading state: %v", err), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(st) - }) - - // API endpoint to get rendered configs for an app - http.HandleFunc("/api/configs", func(w http.ResponseWriter, r *http.Request) { - host := r.URL.Query().Get("host") - appName := r.URL.Query().Get("app") - - if host == "" || appName == "" { - http.Error(w, "Missing host or app parameter", http.StatusBadRequest) - return - } - - st, err := state.Load() - if err != nil { - http.Error(w, fmt.Sprintf("Error loading state: %v", err), http.StatusInternalServerError) - return - } - - app, err := st.GetApp(host, appName) - if err != nil { - http.Error(w, fmt.Sprintf("App not found: %v", err), http.StatusNotFound) - return - } - - configs := make(map[string]string) - - // Render environment file - if app.Env != nil && len(app.Env) > 0 { - envContent := "" - for k, v := range app.Env { - envContent += fmt.Sprintf("%s=%s\n", k, v) - } - configs["env"] = envContent - configs["envPath"] = fmt.Sprintf("/etc/deploy/env/%s.env", appName) - } - - // Render configs based on app type - if app.Type == "app" { - // Render systemd service - workDir := fmt.Sprintf("/var/lib/%s", appName) - binaryPath := fmt.Sprintf("/usr/local/bin/%s", appName) - envFilePath := fmt.Sprintf("/etc/deploy/env/%s.env", appName) - - serviceContent, err := templates.SystemdService(map[string]string{ - "Name": appName, - "User": appName, - "WorkDir": workDir, - "BinaryPath": binaryPath, - "Port": strconv.Itoa(app.Port), - "EnvFile": envFilePath, - "Args": app.Args, - }) - if err != nil { - http.Error(w, fmt.Sprintf("Error rendering systemd service: %v", err), http.StatusInternalServerError) - return - } - configs["systemd"] = serviceContent - configs["systemdPath"] = fmt.Sprintf("/etc/systemd/system/%s.service", appName) - - // Render Caddy config - caddyContent, err := templates.AppCaddy(map[string]string{ - "Domain": app.Domain, - "Port": strconv.Itoa(app.Port), - }) - if err != nil { - http.Error(w, fmt.Sprintf("Error rendering Caddy config: %v", err), http.StatusInternalServerError) - return - } - configs["caddy"] = caddyContent - configs["caddyPath"] = fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", appName) - } else if app.Type == "static" { - // Render Caddy config for static site - remoteDir := fmt.Sprintf("/var/www/%s", appName) - caddyContent, err := templates.StaticCaddy(map[string]string{ - "Domain": app.Domain, - "RootDir": remoteDir, - }) - if err != nil { - http.Error(w, fmt.Sprintf("Error rendering Caddy config: %v", err), http.StatusInternalServerError) - return - } - configs["caddy"] = caddyContent - configs["caddyPath"] = fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", appName) - } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(configs) - }) - - addr := fmt.Sprintf("localhost:%s", *port) - fmt.Printf("Starting web UI on http://%s\n", addr) - fmt.Printf("Press Ctrl+C to stop\n") - - if err := http.ListenAndServe(addr, nil); err != nil { - fmt.Fprintf(os.Stderr, "Error starting server: %v\n", err) - os.Exit(1) - } -} -- cgit v1.2.3