Stacks: Duplicate, Labels and Network in the form, task verdict badges, a short Where; Cancel really cancels

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-20 13:12:54 -07:00
co-authored by Claude Fable 5.1
parent a229f2b597
commit 1e992ca1eb
8 changed files with 183 additions and 36 deletions
+32 -1
View File
@@ -753,6 +753,16 @@ func (s *Server) stackAction(w http.ResponseWriter, r *http.Request) {
jsonErr(w, 500, err.Error())
return
}
if st.Via != "" {
// compose runs inside st.Via, started by `docker exec`: stopping the exec client does not
// stop what it started in there, so a cancel has to go in after it.
via, pattern := st.Via, "compose -p "+regexp.QuoteMeta(st.Name)+" -f "
t.OnCancel(func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_ = exec.CommandContext(ctx, dockerTool(), "exec", via, "pkill", "-TERM", "-f", pattern).Run()
})
}
stackUpdatesForget(r.PathValue("node") + "/" + st.Name)
log.Printf("stack %s: %s started as task %s (from %s)", st.Name, r.PathValue("action"), t.ID, clientIP(r))
jsonOK(w, 202, t.Snapshot(false))
@@ -929,7 +939,10 @@ func (s *Server) stackCreate(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second)
defer cancel()
raw, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
var body struct{ Name, Text, Env, Home string }
var body struct {
Name, Text, Env, Home string
EnvFrom string `json:"env_from"` // a stack being duplicated: its .env is copied here, on the machine, unseen
}
if err != nil || json.Unmarshal(raw, &body) != nil {
jsonErr(w, 400, "bad request")
return
@@ -975,6 +988,24 @@ func (s *Server) stackCreate(w http.ResponseWriter, r *http.Request) {
return
}
}
if body.EnvFrom != "" && strings.TrimSpace(body.Env) == "" {
var from *stack
for i := range all {
if all[i].Name == body.EnvFrom && all[i].Editable && all[i].HasEnv {
from = &all[i]
}
}
if from == nil {
jsonErr(w, 400, body.EnvFrom+" has no .env this console can read")
return
}
b, err := readAsRoot(ctx, from.envPath())
if err != nil {
jsonErr(w, 500, "reading "+body.EnvFrom+"'s .env: "+err.Error())
return
}
body.Env = string(b)
}
dir := filepath.Join(home.Dir, name)
if readable(dir) || exec.Command("test", "-e", dir).Run() == nil {
jsonErr(w, 409, dir+" already exists")