Feat v0.3.3 public entry background jobs (#17)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #17.
This commit is contained in:
140
server/handlers/workflow_public_handlers.go
Normal file
140
server/handlers/workflow_public_handlers.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"switchboard-core/store"
|
||||
"switchboard-core/workflow"
|
||||
)
|
||||
|
||||
// ── Public Workflow Handlers (v0.3.3) ───────
|
||||
|
||||
// WorkflowPublicHandler manages unauthenticated workflow entry endpoints.
|
||||
type WorkflowPublicHandler struct {
|
||||
engine *workflow.Engine
|
||||
stores store.Stores
|
||||
}
|
||||
|
||||
// NewWorkflowPublicHandler creates a public workflow handler.
|
||||
func NewWorkflowPublicHandler(engine *workflow.Engine, stores store.Stores) *WorkflowPublicHandler {
|
||||
return &WorkflowPublicHandler{engine: engine, stores: stores}
|
||||
}
|
||||
|
||||
// publicInstanceResponse strips internal metadata from an instance for public consumption.
|
||||
type publicInstanceResponse struct {
|
||||
ID string `json:"id"`
|
||||
WorkflowID string `json:"workflow_id"`
|
||||
CurrentStage string `json:"current_stage"`
|
||||
StageData json.RawMessage `json:"stage_data"`
|
||||
Status string `json:"status"`
|
||||
EntryToken *string `json:"entry_token,omitempty"`
|
||||
CreatedAt any `json:"created_at"`
|
||||
UpdatedAt any `json:"updated_at"`
|
||||
}
|
||||
|
||||
// StartPublic creates an anonymous workflow instance.
|
||||
// POST /api/v1/public/workflows/:id/start
|
||||
func (h *WorkflowPublicHandler) StartPublic(c *gin.Context) {
|
||||
workflowID := c.Param("id")
|
||||
|
||||
var body struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil && err.Error() != "EOF" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
||||
return
|
||||
}
|
||||
if len(body.Data) == 0 {
|
||||
body.Data = json.RawMessage(`{}`)
|
||||
}
|
||||
|
||||
inst, err := h.engine.StartPublic(c.Request.Context(), workflowID, body.Data)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, publicInstanceResponse{
|
||||
ID: inst.ID,
|
||||
WorkflowID: inst.WorkflowID,
|
||||
CurrentStage: inst.CurrentStage,
|
||||
StageData: inst.StageData,
|
||||
Status: inst.Status,
|
||||
EntryToken: inst.EntryToken,
|
||||
CreatedAt: inst.CreatedAt,
|
||||
UpdatedAt: inst.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// ResumePublic returns an active instance by entry token.
|
||||
// GET /api/v1/public/workflows/resume/:token
|
||||
func (h *WorkflowPublicHandler) ResumePublic(c *gin.Context) {
|
||||
token := c.Param("token")
|
||||
if token == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "missing token"})
|
||||
return
|
||||
}
|
||||
|
||||
inst, err := h.engine.ResumePublic(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, publicInstanceResponse{
|
||||
ID: inst.ID,
|
||||
WorkflowID: inst.WorkflowID,
|
||||
CurrentStage: inst.CurrentStage,
|
||||
StageData: inst.StageData,
|
||||
Status: inst.Status,
|
||||
EntryToken: inst.EntryToken,
|
||||
CreatedAt: inst.CreatedAt,
|
||||
UpdatedAt: inst.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// AdvancePublic advances a public instance by entry token.
|
||||
// POST /api/v1/public/workflows/advance/:token
|
||||
func (h *WorkflowPublicHandler) AdvancePublic(c *gin.Context) {
|
||||
token := c.Param("token")
|
||||
if token == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "missing token"})
|
||||
return
|
||||
}
|
||||
|
||||
var body struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil && err.Error() != "EOF" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
||||
return
|
||||
}
|
||||
if len(body.Data) == 0 {
|
||||
body.Data = json.RawMessage(`{}`)
|
||||
}
|
||||
|
||||
inst, err := h.engine.AdvancePublic(c.Request.Context(), token, body.Data)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "requires authenticated access") {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, publicInstanceResponse{
|
||||
ID: inst.ID,
|
||||
WorkflowID: inst.WorkflowID,
|
||||
CurrentStage: inst.CurrentStage,
|
||||
StageData: inst.StageData,
|
||||
Status: inst.Status,
|
||||
EntryToken: inst.EntryToken,
|
||||
CreatedAt: inst.CreatedAt,
|
||||
UpdatedAt: inst.UpdatedAt,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user