webui: Add End button to shutdown container
This change adds a new button to the sketch-app-shell UI alongside the
existing Restart and Stop buttons. When clicked, the End button shows a
confirmation dialog and then sends a request to a new /end endpoint.
The /end endpoint gracefully shuts down the inner sketch container.
Fixes #88
diff --git a/loop/server/loophttp.go b/loop/server/loophttp.go
index 7df354d..534452b 100644
--- a/loop/server/loophttp.go
+++ b/loop/server/loophttp.go
@@ -709,6 +709,47 @@
json.NewEncoder(w).Encode(map[string]string{"status": "cancelled", "reason": cancelReason})
})
+ // Handler for /end - shuts down the inner sketch process
+ s.mux.HandleFunc("/end", func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ // Parse the request body (optional)
+ var requestBody struct {
+ Reason string `json:"reason"`
+ }
+
+ decoder := json.NewDecoder(r.Body)
+ if err := decoder.Decode(&requestBody); err != nil && err != io.EOF {
+ http.Error(w, "Invalid request body: "+err.Error(), http.StatusBadRequest)
+ return
+ }
+ defer r.Body.Close()
+
+ endReason := "user requested end of session"
+ if requestBody.Reason != "" {
+ endReason = requestBody.Reason
+ }
+
+ // Send success response before exiting
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(map[string]string{"status": "ending", "reason": endReason})
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+
+ // Log that we're shutting down
+ slog.Info("Ending session", "reason", endReason)
+
+ // Exit the process after a short delay to allow response to be sent
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ os.Exit(0)
+ }()
+ })
+
debugMux := initDebugMux()
s.mux.HandleFunc("/debug/", func(w http.ResponseWriter, r *http.Request) {
debugMux.ServeHTTP(w, r)