-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttpserver.go
More file actions
121 lines (103 loc) · 3.55 KB
/
httpserver.go
File metadata and controls
121 lines (103 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
import (
"context"
"encoding/json"
"net/http"
"wails-launcher/pkg/process"
)
const httpListenAddr = "127.0.0.1:9901"
// serviceStatusResponse is the JSON shape returned by the HTTP API.
// It omits the full log buffer that ServiceInfo carries.
type serviceStatusResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Status process.ServiceStatus `json:"status"`
URL *string `json:"url,omitempty"`
Type string `json:"type"`
}
// startHTTPServer registers routes and starts listening in the background.
// The server is shut down when ctx is cancelled (wails OnShutdown).
func (a *App) startHTTPServer(ctx context.Context) {
mux := http.NewServeMux()
mux.HandleFunc("GET /api/services", a.handleListServices)
mux.HandleFunc("GET /api/services/{id}", a.handleGetService)
mux.HandleFunc("POST /api/services/{id}/start", a.handleStartService)
mux.HandleFunc("POST /api/services/{id}/stop", a.handleStopService)
mux.HandleFunc("POST /api/services/{id}/restart", a.handleRestartService)
srv := &http.Server{Addr: httpListenAddr, Handler: mux}
a.httpServer = srv
go srv.ListenAndServe() //nolint:errcheck
go func() {
<-ctx.Done()
srv.Shutdown(context.Background()) //nolint:errcheck
}()
}
// writeJSON writes v as JSON with the given status code.
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v) //nolint:errcheck
}
// writeError writes {"error": msg} with the given status code.
func writeError(w http.ResponseWriter, status int, msg string) {
writeJSON(w, status, map[string]string{"error": msg})
}
// toStatusResponse converts a ServiceInfo into the HTTP response shape.
func toStatusResponse(id string, info ServiceInfo) serviceStatusResponse {
return serviceStatusResponse{
ID: id,
Name: info.Name,
Status: info.Status,
URL: info.URL,
Type: info.Type,
}
}
// GET /api/services
func (a *App) handleListServices(w http.ResponseWriter, r *http.Request) {
a.mu.RLock()
defer a.mu.RUnlock()
result := make([]serviceStatusResponse, 0, len(a.services))
for id, srv := range a.services {
result = append(result, toStatusResponse(id, srv.GetInfo()))
}
writeJSON(w, http.StatusOK, result)
}
// GET /api/services/{id}
func (a *App) handleGetService(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
a.mu.RLock()
srv, exists := a.services[id]
a.mu.RUnlock()
if !exists {
writeError(w, http.StatusNotFound, "service not found")
return
}
writeJSON(w, http.StatusOK, toStatusResponse(id, srv.GetInfo()))
}
// POST /api/services/{id}/start
func (a *App) handleStartService(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := a.StartService(id); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "starting"})
}
// POST /api/services/{id}/stop
func (a *App) handleStopService(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := a.StopService(id); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "stopped"})
}
// POST /api/services/{id}/restart
func (a *App) handleRestartService(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := a.RestartService(id); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "starting"})
}