summaryrefslogtreecommitdiffstats
path: root/cmd/deploy/ui.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-01-24 09:48:34 -0800
committerbndw <ben@bdw.to>2026-01-24 09:48:34 -0800
commit5861e465a2ccf31d87ea25ac145770786f9cc96e (patch)
tree4ac6b57a06b46d8492717b235909f9e0db0b4f22 /cmd/deploy/ui.go
parentef37850c7090493cf2b26d2e565511fe23cc9bfc (diff)
Rename project from deploy to ship
- Rename module to github.com/bdw/ship - Rename cmd/deploy to cmd/ship - Update all import paths - Update config path from ~/.config/deploy to ~/.config/ship - Update VPS env path from /etc/deploy to /etc/ship - Update README, Makefile, and docs
Diffstat (limited to 'cmd/deploy/ui.go')
-rw-r--r--cmd/deploy/ui.go199
1 files changed, 0 insertions, 199 deletions
diff --git a/cmd/deploy/ui.go b/cmd/deploy/ui.go
deleted file mode 100644
index 2ca88e0..0000000
--- a/cmd/deploy/ui.go
+++ /dev/null
@@ -1,199 +0,0 @@
1package main
2
3import (
4 "embed"
5 "encoding/json"
6 "fmt"
7 "html/template"
8 "net/http"
9 "sort"
10 "strconv"
11
12 "github.com/bdw/deploy/internal/state"
13 "github.com/bdw/deploy/internal/templates"
14 "github.com/spf13/cobra"
15)
16
17//go:embed templates/*.html
18var templatesFS embed.FS
19
20var uiCmd = &cobra.Command{
21 Use: "ui",
22 Short: "Launch web management UI",
23 RunE: runUI,
24}
25
26func init() {
27 uiCmd.Flags().StringP("port", "p", "8080", "Port to run the web UI on")
28}
29
30func runUI(cmd *cobra.Command, args []string) error {
31 port, _ := cmd.Flags().GetString("port")
32
33 tmpl, err := template.ParseFS(templatesFS, "templates/webui.html")
34 if err != nil {
35 return fmt.Errorf("error parsing template: %w", err)
36 }
37
38 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
39 st, err := state.Load()
40 if err != nil {
41 http.Error(w, fmt.Sprintf("Error loading state: %v", err), http.StatusInternalServerError)
42 return
43 }
44
45 type AppData struct {
46 Name string
47 Type string
48 Domain string
49 Port int
50 Env map[string]string
51 Host string
52 }
53
54 type HostData struct {
55 Host string
56 Apps []AppData
57 }
58
59 var hosts []HostData
60 for hostName, host := range st.Hosts {
61 var apps []AppData
62 for appName, app := range host.Apps {
63 apps = append(apps, AppData{
64 Name: appName,
65 Type: app.Type,
66 Domain: app.Domain,
67 Port: app.Port,
68 Env: app.Env,
69 Host: hostName,
70 })
71 }
72
73 sort.Slice(apps, func(i, j int) bool {
74 return apps[i].Name < apps[j].Name
75 })
76
77 hosts = append(hosts, HostData{
78 Host: hostName,
79 Apps: apps,
80 })
81 }
82
83 sort.Slice(hosts, func(i, j int) bool {
84 return hosts[i].Host < hosts[j].Host
85 })
86
87 data := struct {
88 Hosts []HostData
89 }{
90 Hosts: hosts,
91 }
92
93 if err := tmpl.Execute(w, data); err != nil {
94 http.Error(w, fmt.Sprintf("Error rendering template: %v", err), http.StatusInternalServerError)
95 return
96 }
97 })
98
99 http.HandleFunc("/api/state", func(w http.ResponseWriter, r *http.Request) {
100 st, err := state.Load()
101 if err != nil {
102 http.Error(w, fmt.Sprintf("Error loading state: %v", err), http.StatusInternalServerError)
103 return
104 }
105
106 w.Header().Set("Content-Type", "application/json")
107 json.NewEncoder(w).Encode(st)
108 })
109
110 http.HandleFunc("/api/configs", func(w http.ResponseWriter, r *http.Request) {
111 host := r.URL.Query().Get("host")
112 appName := r.URL.Query().Get("app")
113
114 if host == "" || appName == "" {
115 http.Error(w, "Missing host or app parameter", http.StatusBadRequest)
116 return
117 }
118
119 st, err := state.Load()
120 if err != nil {
121 http.Error(w, fmt.Sprintf("Error loading state: %v", err), http.StatusInternalServerError)
122 return
123 }
124
125 app, err := st.GetApp(host, appName)
126 if err != nil {
127 http.Error(w, fmt.Sprintf("App not found: %v", err), http.StatusNotFound)
128 return
129 }
130
131 configs := make(map[string]string)
132
133 if app.Env != nil && len(app.Env) > 0 {
134 envContent := ""
135 for k, v := range app.Env {
136 envContent += fmt.Sprintf("%s=%s\n", k, v)
137 }
138 configs["env"] = envContent
139 configs["envPath"] = fmt.Sprintf("/etc/deploy/env/%s.env", appName)
140 }
141
142 if app.Type == "app" {
143 workDir := fmt.Sprintf("/var/lib/%s", appName)
144 binaryPath := fmt.Sprintf("/usr/local/bin/%s", appName)
145 envFilePath := fmt.Sprintf("/etc/deploy/env/%s.env", appName)
146
147 serviceContent, err := templates.SystemdService(map[string]string{
148 "Name": appName,
149 "User": appName,
150 "WorkDir": workDir,
151 "BinaryPath": binaryPath,
152 "Port": strconv.Itoa(app.Port),
153 "EnvFile": envFilePath,
154 "Args": app.Args,
155 })
156 if err != nil {
157 http.Error(w, fmt.Sprintf("Error rendering systemd service: %v", err), http.StatusInternalServerError)
158 return
159 }
160 configs["systemd"] = serviceContent
161 configs["systemdPath"] = fmt.Sprintf("/etc/systemd/system/%s.service", appName)
162
163 caddyContent, err := templates.AppCaddy(map[string]string{
164 "Domain": app.Domain,
165 "Port": strconv.Itoa(app.Port),
166 })
167 if err != nil {
168 http.Error(w, fmt.Sprintf("Error rendering Caddy config: %v", err), http.StatusInternalServerError)
169 return
170 }
171 configs["caddy"] = caddyContent
172 configs["caddyPath"] = fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", appName)
173 } else if app.Type == "static" {
174 remoteDir := fmt.Sprintf("/var/www/%s", appName)
175 caddyContent, err := templates.StaticCaddy(map[string]string{
176 "Domain": app.Domain,
177 "RootDir": remoteDir,
178 })
179 if err != nil {
180 http.Error(w, fmt.Sprintf("Error rendering Caddy config: %v", err), http.StatusInternalServerError)
181 return
182 }
183 configs["caddy"] = caddyContent
184 configs["caddyPath"] = fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", appName)
185 }
186
187 w.Header().Set("Content-Type", "application/json")
188 json.NewEncoder(w).Encode(configs)
189 })
190
191 addr := fmt.Sprintf("localhost:%s", port)
192 fmt.Printf("Starting web UI on http://%s\n", addr)
193 fmt.Printf("Press Ctrl+C to stop\n")
194
195 if err := http.ListenAndServe(addr, nil); err != nil {
196 return fmt.Errorf("error starting server: %w", err)
197 }
198 return nil
199}