Fix workflow start, delete guard, package buttons, export, settings CSS
All checks were successful
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-sqlite (pull_request) Successful in 2m52s
CI/CD / test-go-pg (pull_request) Successful in 2m53s
CI/CD / build-and-deploy (pull_request) Successful in 1m17s
CI/CD / detect-changes (pull_request) Successful in 4s
CI/CD / test-runners (pull_request) Has been skipped

- Add StartBySlug handler + /api/v1/workflow-entry/:scope/:slug route
  so landing page Start button resolves scope/slug and creates instance
- Guard admin DELETE /api/v1/workflows/:id to reject team-scoped
  workflows (must use team endpoint) preventing accidental global delete
- Hide Delete button for bundled packages (match Export/Update guards)
- Package export uses fetch() with auth + toast on missing assets
- Settings form padding-bottom increased to --sp-12 for save button
- Admin workflow editor: shared StageForm component, public entry URL

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 21:31:30 +00:00
parent e4f0bdbd36
commit 8d765491ed
11 changed files with 456 additions and 198 deletions

View File

@@ -36,6 +36,51 @@ type publicInstanceResponse struct {
UpdatedAt any `json:"updated_at"`
}
// StartBySlug resolves a scope+slug to a workflow and starts a public instance.
// POST /api/v1/workflow-entry/:scope/:slug
// Called by the workflow landing page (/w/:scope/:slug).
func (h *WorkflowPublicHandler) StartBySlug(c *gin.Context) {
scope := c.Param("scope")
slug := c.Param("slug")
var teamID *string
if scope != "global" {
teamID = &scope
}
wf, err := h.stores.Workflows.GetBySlug(c.Request.Context(), teamID, slug)
if err != nil || wf == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "workflow not found"})
return
}
var body struct {
Data json.RawMessage `json:"data"`
}
if err := c.ShouldBindJSON(&body); err != nil && err.Error() != "EOF" {
body.Data = json.RawMessage(`{}`)
}
if len(body.Data) == 0 {
body.Data = json.RawMessage(`{}`)
}
inst, err := h.engine.StartPublic(c.Request.Context(), wf.ID, body.Data)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Return redirect for the landing page JS
redirectTo := "/w/" + inst.ID
if inst.EntryToken != nil {
redirectTo = "/w/" + inst.ID + "?token=" + *inst.EntryToken
}
c.JSON(http.StatusCreated, gin.H{
"id": inst.ID,
"redirect_to": redirectTo,
})
}
// StartPublic creates an anonymous workflow instance.
// POST /api/v1/public/workflows/:id/start
func (h *WorkflowPublicHandler) StartPublic(c *gin.Context) {