fix(handlers): auto-restart workspace after file write/delete/replace

PUT /workspaces/:id/files and DELETE /workspaces/:id/files updated the
config volume but never restarted the container, so the running agent
continued serving stale file content from its in-memory cache. The
SecretsHandler already had this pattern (issue #15); TemplatesHandler
was missing it.

Fix: after every successful write/delete in WriteFile, DeleteFile, and
ReplaceFiles, call h.wh.RestartByID(workspaceID) asynchronously, guarded
by h.wh != nil (nil-tolerant for callers that only use read-only
surfaces). The RestartByID coalescing gate prevents thundering-herd on
concurrent requests.

Fixes #151.
Fixes #87 (duplicate effort closed — core-be also filed #183).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Molecule AI · core-devops 2026-05-09 22:43:27 +00:00
parent e65633bf15
commit eaf7dbb7c4
2 changed files with 27 additions and 0 deletions

View File

@ -233,6 +233,9 @@ func (h *TemplatesHandler) ReplaceFiles(c *gin.Context) {
"files": len(body.Files),
"source": "ec2-ssh",
})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
return
}
@ -264,6 +267,9 @@ func (h *TemplatesHandler) ReplaceFiles(c *gin.Context) {
"files": len(body.Files),
"source": "container",
})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
return
}
@ -281,4 +287,7 @@ func (h *TemplatesHandler) ReplaceFiles(c *gin.Context) {
}
c.JSON(http.StatusOK, gin.H{"status": "replaced", "workspace": workspaceID, "files": len(body.Files), "source": "volume"})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
}

View File

@ -524,6 +524,9 @@ func (h *TemplatesHandler) WriteFile(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"status": "saved", "path": filePath})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
return
}
@ -535,6 +538,9 @@ func (h *TemplatesHandler) WriteFile(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"status": "saved", "path": filePath})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
return
}
@ -546,6 +552,9 @@ func (h *TemplatesHandler) WriteFile(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"status": "saved", "path": filePath})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
}
// DeleteFile handles DELETE /workspaces/:id/files/*path
@ -592,6 +601,9 @@ func (h *TemplatesHandler) DeleteFile(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"status": "deleted", "path": filePath})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
return
}
@ -607,6 +619,9 @@ func (h *TemplatesHandler) DeleteFile(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"status": "deleted", "path": filePath})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
return
}
@ -617,5 +632,8 @@ func (h *TemplatesHandler) DeleteFile(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"status": "deleted", "path": filePath})
if h.wh != nil {
go h.wh.RestartByID(workspaceID)
}
}