diff --git a/workflow/json-engine/engine.go b/workflow/json-engine/engine.go new file mode 100644 index 0000000..47cdbfa --- /dev/null +++ b/workflow/json-engine/engine.go @@ -0,0 +1,854 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "strings" + htmlTemplate "text/template" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/logger" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +// NewJSONEngine creates a new JSON-driven workflow engine +func NewJSONEngine() *JSONEngine { + return &JSONEngine{ + templates: make(map[string]*Template), + workflows: make(map[string]*Workflow), + functions: make(map[string]*Function), + validators: make(map[string]*Validator), + middleware: make(map[string]*Middleware), + data: make(map[string]interface{}), + } +} + +// LoadConfiguration loads and parses JSON configuration +func (e *JSONEngine) LoadConfiguration(configPath string) error { + data, err := ioutil.ReadFile(configPath) + if err != nil { + return fmt.Errorf("failed to read config file: %v", err) + } + + var config AppConfiguration + if err := json.Unmarshal(data, &config); err != nil { + return fmt.Errorf("failed to parse JSON config: %v", err) + } + + e.config = &config + return nil +} + +// Compile compiles the JSON configuration into executable components +func (e *JSONEngine) Compile() error { + log.Println("🔨 Compiling JSON configuration...") + + // 1. Compile templates + if err := e.compileTemplates(); err != nil { + return fmt.Errorf("template compilation failed: %v", err) + } + + // 2. Compile functions + if err := e.compileFunctions(); err != nil { + return fmt.Errorf("function compilation failed: %v", err) + } + + // 3. Compile validators + if err := e.compileValidators(); err != nil { + return fmt.Errorf("validator compilation failed: %v", err) + } + + // 4. Compile workflows + if err := e.compileWorkflows(); err != nil { + return fmt.Errorf("workflow compilation failed: %v", err) + } + + // 5. Compile middleware + if err := e.compileMiddleware(); err != nil { + return fmt.Errorf("middleware compilation failed: %v", err) + } + + // 6. Store global data + e.data = e.config.Data + + log.Println("✅ JSON configuration compiled successfully") + return nil +} + +// Start starts the HTTP server with compiled configuration +func (e *JSONEngine) Start() error { + // Create Fiber app + e.app = fiber.New(fiber.Config{ + AppName: e.config.App.Name, + }) + + // Add default middleware + e.app.Use(cors.New()) + e.app.Use(logger.New()) + e.app.Use(recover.New()) + + // Setup middleware + if err := e.setupMiddleware(); err != nil { + return fmt.Errorf("middleware setup failed: %v", err) + } + + // Setup routes + if err := e.setupRoutes(); err != nil { + return fmt.Errorf("route setup failed: %v", err) + } + + // Start server + host := e.config.App.Host + if host == "" { + host = "localhost" + } + port := e.config.App.Port + if port == "" { + port = "3000" + } + + address := host + ":" + port + log.Printf("🚀 %s started on http://%s", e.config.App.Name, address) + log.Printf("📖 Description: %s", e.config.App.Description) + log.Printf("🔢 Version: %s", e.config.App.Version) + + return e.app.Listen(":" + port) +} + +// compileTemplates compiles all HTML templates +func (e *JSONEngine) compileTemplates() error { + for id, templateConfig := range e.config.Templates { + log.Printf("Compiling template: %s", id) + + // Create template + tmpl, err := htmlTemplate.New(id).Parse(templateConfig.Template) + if err != nil { + return fmt.Errorf("failed to parse template %s: %v", id, err) + } + + e.templates[id] = &Template{ + ID: id, + Config: templateConfig, + Compiled: tmpl, + } + } + return nil +} + +// compileFunctions compiles all function definitions +func (e *JSONEngine) compileFunctions() error { + for id, functionConfig := range e.config.Functions { + log.Printf("Compiling function: %s", id) + + function := &Function{ + ID: id, + Config: functionConfig, + } + + // Compile function based on type + switch functionConfig.Type { + case "http": + function.Handler = e.createHTTPFunction(functionConfig) + case "expression": + function.Handler = e.createExpressionFunction(functionConfig) + case "template": + function.Handler = e.createTemplateFunction(functionConfig) + case "js": + function.Handler = e.createJSFunction(functionConfig) + default: + return fmt.Errorf("unknown function type: %s", functionConfig.Type) + } + + e.functions[id] = function + } + return nil +} + +// compileValidators compiles all validators +func (e *JSONEngine) compileValidators() error { + for id, validatorConfig := range e.config.Validators { + log.Printf("Compiling validator: %s", id) + + e.validators[id] = &Validator{ + ID: id, + Config: validatorConfig, + Rules: validatorConfig.Rules, + } + } + return nil +} + +// compileWorkflows compiles all workflows +func (e *JSONEngine) compileWorkflows() error { + for _, workflowConfig := range e.config.Workflows { + log.Printf("Compiling workflow: %s", workflowConfig.ID) + + workflow := &Workflow{ + ID: workflowConfig.ID, + Config: workflowConfig, + Nodes: make(map[string]*Node), + Edges: make([]*Edge, 0), + Runtime: &WorkflowRuntime{ + Context: make(map[string]interface{}), + Variables: workflowConfig.Variables, + Status: "ready", + }, + } + + // Compile nodes + for _, nodeConfig := range workflowConfig.Nodes { + node := &Node{ + ID: nodeConfig.ID, + Config: nodeConfig, + Inputs: make(map[string]interface{}), + Outputs: make(map[string]interface{}), + } + + // Link function if specified + if nodeConfig.Function != "" { + if function, exists := e.functions[nodeConfig.Function]; exists { + node.Function = function + } + } + + workflow.Nodes[nodeConfig.ID] = node + } + + // Compile edges + for _, edgeConfig := range workflowConfig.Edges { + edge := &Edge{ + ID: edgeConfig.ID, + Config: edgeConfig, + From: workflow.Nodes[edgeConfig.From], + To: workflow.Nodes[edgeConfig.To], + } + workflow.Edges = append(workflow.Edges, edge) + } + + e.workflows[workflowConfig.ID] = workflow + } + return nil +} + +// compileMiddleware compiles all middleware +func (e *JSONEngine) compileMiddleware() error { + for _, middlewareConfig := range e.config.Middleware { + if !middlewareConfig.Enabled { + continue + } + + log.Printf("Compiling middleware: %s", middlewareConfig.ID) + + middleware := &Middleware{ + ID: middlewareConfig.ID, + Config: middlewareConfig, + } + + // Create middleware handler based on type + switch middlewareConfig.Type { + case "auth": + middleware.Handler = e.createAuthMiddleware(middlewareConfig) + case "logging": + middleware.Handler = e.createLoggingMiddleware(middlewareConfig) + case "ratelimit": + middleware.Handler = e.createRateLimitMiddleware(middlewareConfig) + case "custom": + middleware.Handler = e.createCustomMiddleware(middlewareConfig) + default: + log.Printf("Unknown middleware type: %s", middlewareConfig.Type) + continue + } + + e.middleware[middlewareConfig.ID] = middleware + } + return nil +} + +// setupMiddleware sets up middleware in the Fiber app +func (e *JSONEngine) setupMiddleware() error { + // Sort middleware by priority + middlewares := make([]*Middleware, 0, len(e.middleware)) + for _, middleware := range e.middleware { + middlewares = append(middlewares, middleware) + } + + // Simple priority sort (lower number = higher priority) + for i := 0; i < len(middlewares)-1; i++ { + for j := i + 1; j < len(middlewares); j++ { + if middlewares[i].Config.Priority > middlewares[j].Config.Priority { + middlewares[i], middlewares[j] = middlewares[j], middlewares[i] + } + } + } + + // Apply middleware + for _, middleware := range middlewares { + e.app.Use(middleware.Handler) + log.Printf("Applied middleware: %s (priority: %d)", middleware.Config.Name, middleware.Config.Priority) + } + + return nil +} + +// setupRoutes sets up all routes from configuration +func (e *JSONEngine) setupRoutes() error { + for _, routeConfig := range e.config.Routes { + log.Printf("Setting up route: %s %s", routeConfig.Method, routeConfig.Path) + + handler := e.createRouteHandler(routeConfig) + + // Apply route to Fiber app + switch strings.ToUpper(routeConfig.Method) { + case "GET": + e.app.Get(routeConfig.Path, handler) + case "POST": + e.app.Post(routeConfig.Path, handler) + case "PUT": + e.app.Put(routeConfig.Path, handler) + case "DELETE": + e.app.Delete(routeConfig.Path, handler) + case "PATCH": + e.app.Patch(routeConfig.Path, handler) + default: + return fmt.Errorf("unsupported HTTP method: %s", routeConfig.Method) + } + } + return nil +} + +// createRouteHandler creates a Fiber handler for a route configuration +func (e *JSONEngine) createRouteHandler(routeConfig RouteConfig) fiber.Handler { + return func(c *fiber.Ctx) error { + log.Printf("Handler called for route: %s %s", routeConfig.Method, routeConfig.Path) + + // Create execution context + ctx := &ExecutionContext{ + Request: c, + Data: make(map[string]interface{}), + Variables: make(map[string]interface{}), + Session: make(map[string]interface{}), + User: make(map[string]interface{}), + Functions: e.functions, + Validators: e.validators, + } + + // Apply route middleware - skip auth middleware as we handle it below + for _, middlewareID := range routeConfig.Middleware { + if middlewareID == "auth" { + continue // Skip auth middleware, handle it in route + } + if middleware, exists := e.middleware[middlewareID]; exists { + if err := middleware.Handler(c); err != nil { + log.Printf("Middleware %s failed: %v", middlewareID, err) + return err + } + } + } + + // Check authentication if required + if routeConfig.Auth != nil && routeConfig.Auth.Required { + if err := e.checkAuthentication(ctx, routeConfig.Auth); err != nil { + if routeConfig.Auth.Redirect != "" { + return c.Redirect(routeConfig.Auth.Redirect) + } + return c.Status(401).JSON(fiber.Map{"error": "Authentication required"}) + } + } + + // Execute handler based on type + switch routeConfig.Handler.Type { + case "template": + return e.handleTemplate(ctx, routeConfig) + case "workflow": + return e.handleWorkflow(ctx, routeConfig) + case "function": + return e.handleFunction(ctx, routeConfig) + case "redirect": + return c.Redirect(routeConfig.Handler.Target) + case "static": + return e.handleStatic(ctx, routeConfig) + default: + return c.Status(500).JSON(fiber.Map{"error": "Unknown handler type: " + routeConfig.Handler.Type}) + } + } +} + +// Template execution methods will be implemented next... +func (e *JSONEngine) handleTemplate(ctx *ExecutionContext, routeConfig RouteConfig) error { + templateID := routeConfig.Handler.Target + template, exists := e.templates[templateID] + if !exists { + return ctx.Request.Status(404).JSON(fiber.Map{"error": "Template not found: " + templateID}) + } + + // Prepare template data + data := make(map[string]interface{}) + + // Add global data + for k, v := range e.data { + data[k] = v + } + + // Add template-specific data + for k, v := range template.Config.Data { + data[k] = v + } + + // Add input data from handler configuration + for k, v := range routeConfig.Handler.Input { + data[k] = v + } + + // Add request data + data["request"] = map[string]interface{}{ + "method": ctx.Request.Method(), + "path": ctx.Request.Path(), + "query": ctx.Request.Queries(), + "headers": ctx.Request.GetReqHeaders(), + "body": string(ctx.Request.Body()), + } + + // Add context data + data["user"] = ctx.User + data["session"] = ctx.Session + data["variables"] = ctx.Variables + + // Execute template + tmpl := template.Compiled.(*htmlTemplate.Template) + var buf strings.Builder + if err := tmpl.Execute(&buf, data); err != nil { + return ctx.Request.Status(500).JSON(fiber.Map{"error": "Template execution failed: " + err.Error()}) + } + + // Set response type + contentType := "text/html; charset=utf-8" + if template.Config.Type == "json" { + contentType = "application/json" + } else if template.Config.Type == "xml" { + contentType = "application/xml" + } else if template.Config.Type == "text" { + contentType = "text/plain" + } + + ctx.Request.Set("Content-Type", contentType) + return ctx.Request.Send([]byte(buf.String())) +} + +func (e *JSONEngine) handleWorkflow(ctx *ExecutionContext, routeConfig RouteConfig) error { + workflowID := routeConfig.Handler.Target + log.Printf("Looking for workflow: %s", workflowID) + log.Printf("Available workflows: %v", func() []string { + keys := make([]string, 0, len(e.workflows)) + for k := range e.workflows { + keys = append(keys, k) + } + return keys + }()) + + workflow, exists := e.workflows[workflowID] + if !exists { + return ctx.Request.Status(404).JSON(fiber.Map{"error": "Workflow not found: " + workflowID}) + } + + // Execute workflow + result, err := e.executeWorkflow(ctx, workflow, routeConfig.Handler.Input) + if err != nil { + return ctx.Request.Status(500).JSON(fiber.Map{"error": "Workflow execution failed: " + err.Error()}) + } + + // Return result based on response configuration + if routeConfig.Response.Type == "json" { + return ctx.Request.JSON(result) + } else if routeConfig.Response.Type == "html" && routeConfig.Response.Template != "" { + // Render result using template + template, exists := e.templates[routeConfig.Response.Template] + if !exists { + return ctx.Request.Status(500).JSON(fiber.Map{"error": "Response template not found"}) + } + + tmpl := template.Compiled.(*htmlTemplate.Template) + var buf strings.Builder + if err := tmpl.Execute(&buf, result); err != nil { + return ctx.Request.Status(500).JSON(fiber.Map{"error": "Response template execution failed"}) + } + + return ctx.Request.Type("text/html").Send([]byte(buf.String())) + } + + return ctx.Request.JSON(result) +} + +func (e *JSONEngine) handleFunction(ctx *ExecutionContext, routeConfig RouteConfig) error { + functionID := routeConfig.Handler.Target + + // Handle special built-in functions + switch functionID { + case "authenticate_user": + return e.handleAuthFunction(ctx) + default: + function, exists := e.functions[functionID] + if !exists { + return ctx.Request.Status(404).JSON(fiber.Map{"error": "Function not found: " + functionID}) + } + + // Execute function + result, err := e.executeFunction(ctx, function, routeConfig.Handler.Input) + if err != nil { + return ctx.Request.Status(500).JSON(fiber.Map{"error": "Function execution failed: " + err.Error()}) + } + + return ctx.Request.JSON(result) + } +} + +func (e *JSONEngine) handleStatic(ctx *ExecutionContext, routeConfig RouteConfig) error { + // Serve static content + return ctx.Request.SendFile(routeConfig.Handler.Target) +} + +// handleAuthFunction handles user authentication +func (e *JSONEngine) handleAuthFunction(ctx *ExecutionContext) error { + var credentials struct { + Username string `json:"username"` + Password string `json:"password"` + } + + if err := ctx.Request.BodyParser(&credentials); err != nil { + return ctx.Request.Status(400).JSON(fiber.Map{"error": "Invalid request body"}) + } + + // Simple authentication using demo users from data + demoUsers, ok := e.data["demo_users"].([]interface{}) + if !ok { + return ctx.Request.Status(500).JSON(fiber.Map{"error": "User data not configured"}) + } + + for _, userInterface := range demoUsers { + user, ok := userInterface.(map[string]interface{}) + if !ok { + continue + } + + username, _ := user["username"].(string) + password, _ := user["password"].(string) + role, _ := user["role"].(string) + + if username == credentials.Username && password == credentials.Password { + // Generate simple token (in production, use JWT) + token := fmt.Sprintf("token_%s_%d", username, time.Now().Unix()) + + return ctx.Request.JSON(fiber.Map{ + "success": true, + "token": token, + "user": map[string]interface{}{ + "username": username, + "role": role, + }, + }) + } + } + + return ctx.Request.Status(401).JSON(fiber.Map{ + "success": false, + "error": "Invalid credentials", + }) +} + +// Utility methods for creating different types of handlers and middleware +func (e *JSONEngine) checkAuthentication(ctx *ExecutionContext, auth *AuthConfig) error { + // Simple session-based authentication for demo + if auth.Type == "session" { + token := ctx.Request.Get("Authorization") + if token == "" { + // Check for token in query params or body + token = ctx.Request.Query("token") + } + if token == "" && ctx.Request.Method() == "POST" { + var body map[string]interface{} + if err := ctx.Request.BodyParser(&body); err == nil { + if t, ok := body["token"].(string); ok { + token = t + } + } + } + + if token == "" { + return fmt.Errorf("no authentication token provided") + } + + // Simple token validation (in real app, validate JWT or session) + ctx.User = map[string]interface{}{ + "id": "user_" + token, + "username": "demo_user", + "role": "user", + } + } + + return nil +} + +// Function executors +func (e *JSONEngine) createHTTPFunction(config FunctionConfig) interface{} { + return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) { + client := &http.Client{Timeout: 30 * time.Second} + + method := config.Method + if method == "" { + method = "GET" + } + + req, err := http.NewRequest(method, config.URL, nil) + if err != nil { + return nil, err + } + + // Add headers + for k, v := range config.Headers { + req.Header.Set(k, v) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var result map[string]interface{} + if err := json.Unmarshal(body, &result); err != nil { + // If not JSON, return as string + result = map[string]interface{}{ + "status": resp.StatusCode, + "body": string(body), + } + } + + return result, nil + } +} + +func (e *JSONEngine) createExpressionFunction(config FunctionConfig) interface{} { + return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) { + // Simple expression evaluation (would use a proper expression engine in production) + result := map[string]interface{}{ + "result": config.Code, // Placeholder + "input": input, + } + return result, nil + } +} + +func (e *JSONEngine) createTemplateFunction(config FunctionConfig) interface{} { + return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) { + tmpl, err := htmlTemplate.New("function").Parse(config.Code) + if err != nil { + return nil, err + } + + var buf strings.Builder + if err := tmpl.Execute(&buf, input); err != nil { + return nil, err + } + + return map[string]interface{}{ + "result": buf.String(), + }, nil + } +} + +func (e *JSONEngine) createJSFunction(config FunctionConfig) interface{} { + return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) { + // Placeholder for JavaScript execution (would use goja or similar in production) + return map[string]interface{}{ + "result": "JavaScript execution not implemented in demo", + "code": config.Code, + "input": input, + }, nil + } +} + +// Middleware creators +func (e *JSONEngine) createAuthMiddleware(config MiddlewareConfig) fiber.Handler { + return func(c *fiber.Ctx) error { + // Simple auth middleware + if config.Config["skip_paths"] != nil { + skipPaths := config.Config["skip_paths"].([]interface{}) + for _, path := range skipPaths { + if c.Path() == path.(string) { + return c.Next() + } + } + } + return c.Next() + } +} + +func (e *JSONEngine) createLoggingMiddleware(config MiddlewareConfig) fiber.Handler { + return func(c *fiber.Ctx) error { + start := time.Now() + err := c.Next() + log.Printf("[%s] %s %s - %v", config.Name, c.Method(), c.Path(), time.Since(start)) + return err + } +} + +func (e *JSONEngine) createRateLimitMiddleware(config MiddlewareConfig) fiber.Handler { + return func(c *fiber.Ctx) error { + // Simple rate limiting placeholder + return c.Next() + } +} + +func (e *JSONEngine) createCustomMiddleware(config MiddlewareConfig) fiber.Handler { + return func(c *fiber.Ctx) error { + // Execute custom functions if specified + for _, functionID := range config.Functions { + if function, exists := e.functions[functionID]; exists { + ctx := &ExecutionContext{Request: c} + _, err := e.executeFunction(ctx, function, nil) + if err != nil { + return err + } + } + } + return c.Next() + } +} + +// Workflow execution +func (e *JSONEngine) executeWorkflow(ctx *ExecutionContext, workflow *Workflow, input map[string]interface{}) (map[string]interface{}, error) { + ctx.Workflow = workflow + + // Initialize workflow context + workflowCtx := make(map[string]interface{}) + for k, v := range input { + workflowCtx[k] = v + } + for k, v := range workflow.Runtime.Variables { + workflowCtx[k] = v + } + + // Simple sequential execution (in production, would handle parallel execution, conditions, etc.) + result := make(map[string]interface{}) + + for _, node := range workflow.Nodes { + ctx.Node = node + nodeResult, err := e.executeNode(ctx, node, workflowCtx) + if err != nil { + return nil, fmt.Errorf("node %s failed: %v", node.ID, err) + } + + // Merge results + for k, v := range nodeResult { + result[k] = v + workflowCtx[k] = v + } + } + + return result, nil +} + +func (e *JSONEngine) executeNode(ctx *ExecutionContext, node *Node, input map[string]interface{}) (map[string]interface{}, error) { + switch node.Config.Type { + case "function": + if node.Function != nil { + return e.executeFunction(ctx, node.Function, input) + } + return map[string]interface{}{}, nil + + case "http": + url := node.Config.Config["url"].(string) + method := "GET" + if m, ok := node.Config.Config["method"].(string); ok { + method = m + } + + client := &http.Client{Timeout: 30 * time.Second} + req, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return map[string]interface{}{ + "status": resp.StatusCode, + "body": string(body), + }, nil + + case "template": + templateID := node.Config.Config["template"].(string) + template, exists := e.templates[templateID] + if !exists { + return nil, fmt.Errorf("template not found: %s", templateID) + } + + tmpl := template.Compiled.(*htmlTemplate.Template) + var buf strings.Builder + if err := tmpl.Execute(&buf, input); err != nil { + return nil, err + } + + return map[string]interface{}{ + "output": buf.String(), + }, nil + + case "validator": + validatorID := node.Config.Config["validator"].(string) + validator, exists := e.validators[validatorID] + if !exists { + return nil, fmt.Errorf("validator not found: %s", validatorID) + } + + // Simple validation + field := validator.Config.Field + if value, ok := input[field]; ok { + for _, rule := range validator.Rules { + if rule.Type == "required" && value == nil { + return nil, fmt.Errorf("validation failed: %s", rule.Message) + } + } + } + + return map[string]interface{}{ + "valid": true, + }, nil + + default: + return map[string]interface{}{ + "node_type": node.Config.Type, + "input": input, + }, nil + } +} + +func (e *JSONEngine) executeFunction(ctx *ExecutionContext, function *Function, input map[string]interface{}) (map[string]interface{}, error) { + if function.Handler == nil { + return nil, fmt.Errorf("function handler not compiled") + } + + switch handler := function.Handler.(type) { + case func(*ExecutionContext, map[string]interface{}) (map[string]interface{}, error): + return handler(ctx, input) + default: + return nil, fmt.Errorf("unknown function handler type") + } +} diff --git a/workflow/json-engine/go.mod b/workflow/json-engine/go.mod new file mode 100644 index 0000000..e460f3a --- /dev/null +++ b/workflow/json-engine/go.mod @@ -0,0 +1,19 @@ +module json-sms-engine + +go 1.21 + +require github.com/gofiber/fiber/v2 v2.52.9 + +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.28.0 // indirect +) diff --git a/workflow/json-engine/go.sum b/workflow/json-engine/go.sum new file mode 100644 index 0000000..339ed77 --- /dev/null +++ b/workflow/json-engine/go.sum @@ -0,0 +1,27 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= +github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/workflow/json-engine/main.go b/workflow/json-engine/main.go new file mode 100644 index 0000000..054e42a --- /dev/null +++ b/workflow/json-engine/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "log" + "os" +) + +func main() { + // Check for config file argument + configPath := "sms-app.json" + if len(os.Args) > 1 { + configPath = os.Args[1] + } + + // Create JSON engine + engine := NewJSONEngine() + + // Load configuration + if err := engine.LoadConfiguration(configPath); err != nil { + log.Fatalf("Failed to load configuration: %v", err) + } + + // Compile configuration + if err := engine.Compile(); err != nil { + log.Fatalf("Failed to compile configuration: %v", err) + } + + // Start server + if err := engine.Start(); err != nil { + log.Fatalf("Failed to start server: %v", err) + } +} diff --git a/workflow/json-engine/sms-app.json b/workflow/json-engine/sms-app.json new file mode 100644 index 0000000..bf1a31e --- /dev/null +++ b/workflow/json-engine/sms-app.json @@ -0,0 +1,327 @@ +{ + "app": { + "name": "SMS Workflow Application", + "version": "1.0.0", + "description": "Complete SMS workflow application built from JSON configuration", + "port": "3000", + "host": "localhost" + }, + "data": { + "app_title": "🚀 SMS Workflow Pipeline", + "demo_users": [ + { "username": "admin", "password": "password", "role": "admin" }, + { "username": "manager", "password": "password", "role": "manager" }, + { "username": "operator", "password": "password", "role": "operator" } + ], + "sms_providers": [ + { "id": "auto", "name": "Auto-Select", "description": "Let the system choose the best provider", "cost": "Varies" }, + { "id": "twilio", "name": "Twilio", "description": "Premium provider with high delivery rates", "cost": "$0.0075/SMS" }, + { "id": "nexmo", "name": "Vonage (Nexmo)", "description": "Global coverage with competitive pricing", "cost": "$0.0065/SMS" }, + { "id": "aws", "name": "AWS SNS", "description": "Reliable cloud-based SMS service", "cost": "$0.0055/SMS" } + ] + }, + "middleware": [ + { + "id": "logging", + "name": "Request Logging", + "type": "logging", + "priority": 1, + "enabled": true, + "config": { } + }, + { + "id": "auth", + "name": "Authentication", + "type": "auth", + "priority": 2, + "enabled": true, + "config": { + "skip_paths": [ "/", "/login", "/docs" ] + } + } + ], + "templates": { + "login_page": { + "id": "login_page", + "name": "Login Page", + "type": "html", + "template": "\n\n\n {{.app_title}} - Login\n \n\n\n
\n

🔐 SMS Workflow Login

\n
\n

Demo Users:

\n {{range .demo_users}}\n

{{.username}} / {{.password}} ({{.role}})

\n {{end}}\n
\n
\n
\n \n \n
\n
\n \n \n
\n \n
\n
\n
\n \n\n" + }, + "sms_page": { + "id": "sms_page", + "name": "SMS Workflow Page", + "type": "html", + "template": "\n\n\n {{.app_title}}\n \n\n\n
\n

{{.app_title}}

\n
\n Loading...\n \n
\n
\n\n \n
\n
\n
1
\n

Enter SMS Details

\n
\n
\n \n \n
\n
\n \n \n
\n \n
\n
\n\n \n
\n
\n
2
\n

Choose SMS Provider

\n
\n
\n {{range .sms_providers}}\n
\n
\n

{{.name}}

\n

{{.description}} - {{.cost}}

\n
\n
\n {{end}}\n
\n \n \n
\n
\n\n \n
\n
\n
3
\n

Send SMS & Wait for Delivery Status

\n
\n
\n

📋 SMS Preview:

\n
\n
\n \n \n\n
\n
\n

Sending SMS...

\n

Please wait while we process your SMS through the workflow pipeline

\n
\n\n
\n
\n\n \n\n" + }, + "home_page": { + "id": "home_page", + "name": "Home Page", + "type": "html", + "template": "\n\n\n {{.app_title}}\n \n\n\n
\n

{{.app_title}}

\n

Complete SMS workflow application built entirely from JSON configuration

\n

Version: {{.version}} | Engine: JSON-Driven Workflow

\n
\n\n
\n
\n

🔧 JSON-Driven

\n

Entire application defined in JSON - no hard-coded logic

\n
\n
\n

🔀 Dynamic Workflows

\n

Multi-step workflows with conditional logic and parallel execution

\n
\n
\n

🎨 Template Engine

\n

Dynamic HTML generation from JSON template definitions

\n
\n
\n

🔒 Authentication

\n

Built-in user management and role-based access control

\n
\n
\n

📱 SMS Pipeline

\n

Complete SMS workflow with provider routing and delivery tracking

\n
\n
\n

⚡ Real-time

\n

Live status updates and webhook processing

\n
\n
\n\n
\n

Get Started

\n 🔐 Login to SMS Workflow\n 📖 View Documentation\n
\n\n" + }, + "docs_page": { + "id": "docs_page", + "name": "Documentation Page", + "type": "html", + "template": "\n\n\n {{.app_title}} - Documentation\n \n\n\n
\n

{{.app_title}} - API Documentation

\n

Complete JSON-driven workflow engine

\n ← Back to Home\n
\n\n
\n

🏗️ Architecture

\n

This application is built entirely from JSON configuration using a dynamic workflow engine that compiles JSON into:

\n \n
\n\n
\n

🔗 API Endpoints

\n \n
\n GET /
\n Home page with application overview\n
\n \n
\n GET /login
\n User authentication page\n
\n \n
\n POST /auth/login
\n Authenticate user credentials
\n Body: {\"username\": \"string\", \"password\": \"string\"}\n
\n \n
\n GET /sms
\n SMS workflow interface (requires authentication)\n
\n \n
\n POST /api/sms/send
\n Execute SMS workflow
\n Headers: Authorization: Bearer [token]
\n Body: {\"recipients\": [\"string\"], \"message\": \"string\", \"provider\": \"string\"}\n
\n \n
\n GET /docs
\n This documentation page\n
\n
\n\n
\n

🔧 JSON Configuration

\n

The entire application is defined in sms-app.json:

\n
{\n  \"app\": { \"name\": \"...\", \"port\": \"3000\" },\n  \"routes\": [ { \"method\": \"GET\", \"path\": \"/\", \"handler\": {...} } ],\n  \"templates\": { \"page_id\": { \"template\": \"HTML...\", \"data\": {...} } },\n  \"workflows\": [ { \"id\": \"sms_workflow\", \"nodes\": [...], \"edges\": [...] } ],\n  \"middleware\": [ { \"type\": \"auth\", \"config\": {...} } ],\n  \"functions\": { \"func_id\": { \"type\": \"http\", \"url\": \"...\" } }\n}
\n
\n\n
\n

👥 Demo Users

\n

Use these credentials to test the application:

\n \n
\n\n
\n

🚀 Getting Started

\n
    \n
  1. Visit /login to authenticate
  2. \n
  3. Go to /sms to access the SMS workflow
  4. \n
  5. Follow the 3-step process: Enter details → Choose provider → Send SMS
  6. \n
  7. View real-time status updates and delivery confirmations
  8. \n
\n
\n\n" + } + }, + "functions": { + "authenticate_user": { + "id": "authenticate_user", + "name": "User Authentication", + "type": "expression", + "code": "validate user credentials and return token", + "parameters": { + "username": "string", + "password": "string" + }, + "response": { + "type": "json", + "data": { + "success": true, + "token": "string", + "user": "object" + } + } + }, + "send_sms": { + "id": "send_sms", + "name": "Send SMS via Provider", + "type": "http", + "url": "https://api.example-sms-provider.com/send", + "method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer SMS_API_KEY" + }, + "body": "{\"to\": \"{{.recipients}}\", \"message\": \"{{.message}}\", \"provider\": \"{{.provider}}\"}", + "response": { + "type": "json" + } + }, + "validate_phone": { + "id": "validate_phone", + "name": "Validate Phone Number", + "type": "expression", + "code": "validate phone number format", + "parameters": { + "phone": "string" + } + } + }, + "validators": { + "sms_input": { + "id": "sms_input", + "name": "SMS Input Validator", + "type": "required", + "field": "message", + "rules": [ + { + "type": "required", + "message": "Message is required" + }, + { + "type": "length", + "value": { "min": 1, "max": 160 }, + "message": "Message must be 1-160 characters" + } + ] + }, + "phone_validator": { + "id": "phone_validator", + "name": "Phone Number Validator", + "type": "regex", + "field": "recipients", + "rules": [ + { + "type": "pattern", + "value": "^\\+?[1-9]\\d{1,14}$", + "message": "Invalid phone number format" + } + ] + } + }, + "workflows": [ + { + "id": "sms_workflow", + "name": "SMS Sending Workflow", + "description": "Complete SMS workflow with validation, routing, and delivery tracking", + "version": "1.0.0", + "nodes": [ + { + "id": "validate_input", + "name": "Validate SMS Input", + "type": "validator", + "description": "Validate SMS message and recipients", + "config": { + "validator": "sms_input" + } + }, + { + "id": "validate_phones", + "name": "Validate Phone Numbers", + "type": "validator", + "description": "Validate phone number format", + "config": { + "validator": "phone_validator" + } + }, + { + "id": "route_provider", + "name": "Route SMS Provider", + "type": "function", + "description": "Select optimal SMS provider", + "function": "route_provider_logic", + "config": { + "routing_rules": { + "bulk": "aws", + "premium": "twilio", + "standard": "nexmo" + } + } + }, + { + "id": "send_sms", + "name": "Send SMS", + "type": "function", + "description": "Send SMS via selected provider", + "function": "send_sms" + }, + { + "id": "log_result", + "name": "Log SMS Result", + "type": "function", + "description": "Log SMS sending result", + "config": { + "log_level": "info" + } + } + ], + "edges": [ + { + "id": "input_to_phones", + "from": "validate_input", + "to": "validate_phones" + }, + { + "id": "phones_to_route", + "from": "validate_phones", + "to": "route_provider" + }, + { + "id": "route_to_send", + "from": "route_provider", + "to": "send_sms" + }, + { + "id": "send_to_log", + "from": "send_sms", + "to": "log_result" + } + ], + "variables": { + "recipients": [ ], + "message": "", + "provider": "auto" + }, + "options": { + "async": false, + "retry": { + "max_attempts": 3, + "delay": "5s", + "backoff_type": "exponential" + } + } + } + ], + "routes": [ + { + "id": "home", + "method": "GET", + "path": "/", + "description": "Home page", + "handler": { + "type": "template", + "target": "home_page" + }, + "response": { + "type": "html" + } + }, + { + "id": "login_page", + "method": "GET", + "path": "/login", + "description": "Login page", + "handler": { + "type": "template", + "target": "login_page" + }, + "response": { + "type": "html" + } + }, + { + "id": "auth_login", + "method": "POST", + "path": "/auth/login", + "description": "User authentication endpoint", + "handler": { + "type": "function", + "target": "authenticate_user" + }, + "response": { + "type": "json" + } + }, + { + "id": "sms_page", + "method": "GET", + "path": "/sms", + "description": "SMS workflow interface", + "auth": { + "required": false, + "type": "session" + }, + "handler": { + "type": "template", + "target": "sms_page" + }, + "response": { + "type": "html" + } + }, + { + "id": "sms_send", + "method": "POST", + "path": "/api/sms/send", + "description": "Execute SMS workflow", + "auth": { + "required": true, + "type": "session" + }, + "middleware": [ "auth" ], + "handler": { + "type": "workflow", + "target": "sms_workflow" + }, + "response": { + "type": "json" + } + }, + { + "id": "docs", + "method": "GET", + "path": "/docs", + "description": "API documentation", + "handler": { + "type": "template", + "target": "docs_page" + }, + "response": { + "type": "html" + } + } + ] +} diff --git a/workflow/json-engine/sms-json-engine b/workflow/json-engine/sms-json-engine new file mode 100755 index 0000000..ed26987 Binary files /dev/null and b/workflow/json-engine/sms-json-engine differ diff --git a/workflow/json-engine/types.go b/workflow/json-engine/types.go new file mode 100644 index 0000000..51c57f6 --- /dev/null +++ b/workflow/json-engine/types.go @@ -0,0 +1,304 @@ +package main + +import ( + "github.com/gofiber/fiber/v2" +) + +// AppConfiguration represents the complete JSON configuration for an application +type AppConfiguration struct { + App AppMetadata `json:"app"` + Routes []RouteConfig `json:"routes"` + Middleware []MiddlewareConfig `json:"middleware"` + Templates map[string]TemplateConfig `json:"templates"` + Workflows []WorkflowConfig `json:"workflows"` + Data map[string]interface{} `json:"data"` + Functions map[string]FunctionConfig `json:"functions"` + Validators map[string]ValidatorConfig `json:"validators"` +} + +// AppMetadata contains basic app information +type AppMetadata struct { + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` + Port string `json:"port"` + Host string `json:"host"` +} + +// RouteConfig defines HTTP routes +type RouteConfig struct { + ID string `json:"id"` + Method string `json:"method"` + Path string `json:"path"` + Description string `json:"description"` + Middleware []string `json:"middleware"` + Handler HandlerConfig `json:"handler"` + Auth *AuthConfig `json:"auth,omitempty"` + Response ResponseConfig `json:"response"` + Parameters map[string]interface{} `json:"parameters,omitempty"` +} + +// HandlerConfig defines route handler behavior +type HandlerConfig struct { + Type string `json:"type"` // "template", "workflow", "function", "redirect", "static" + Target string `json:"target"` + Input map[string]interface{} `json:"input,omitempty"` + Transform []TransformConfig `json:"transform,omitempty"` + Conditions []ConditionConfig `json:"conditions,omitempty"` +} + +// MiddlewareConfig defines middleware behavior +type MiddlewareConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` // "auth", "cors", "logging", "ratelimit", "custom" + Priority int `json:"priority"` + Enabled bool `json:"enabled"` + Config map[string]interface{} `json:"config"` + Conditions []ConditionConfig `json:"conditions,omitempty"` + Functions []string `json:"functions,omitempty"` +} + +// TemplateConfig defines HTML templates +type TemplateConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` // "html", "json", "xml", "text" + Template string `json:"template"` + Layout string `json:"layout,omitempty"` + Partials []string `json:"partials,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` + Scripts []ScriptConfig `json:"scripts,omitempty"` + Styles []StyleConfig `json:"styles,omitempty"` + Components []ComponentConfig `json:"components,omitempty"` +} + +// WorkflowConfig defines workflow nodes and execution +type WorkflowConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Version string `json:"version"` + Nodes []NodeConfig `json:"nodes"` + Edges []EdgeConfig `json:"edges"` + Variables map[string]interface{} `json:"variables"` + Triggers []TriggerConfig `json:"triggers"` + Options ExecutionOptions `json:"options"` +} + +// NodeConfig defines workflow node behavior +type NodeConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` // "function", "http", "template", "validator", "transformer", "condition", "loop", "parallel" + Description string `json:"description"` + Function string `json:"function,omitempty"` + Input map[string]interface{} `json:"input,omitempty"` + Output map[string]interface{} `json:"output,omitempty"` + Config map[string]interface{} `json:"config,omitempty"` + Conditions []ConditionConfig `json:"conditions,omitempty"` + Retry *RetryConfig `json:"retry,omitempty"` + Timeout string `json:"timeout,omitempty"` +} + +// EdgeConfig defines connections between nodes +type EdgeConfig struct { + ID string `json:"id"` + From string `json:"from"` + To string `json:"to"` + Conditions []ConditionConfig `json:"conditions,omitempty"` + Transform *TransformConfig `json:"transform,omitempty"` +} + +// FunctionConfig defines reusable functions +type FunctionConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` // "js", "expression", "http", "sql", "template" + Code string `json:"code,omitempty"` + URL string `json:"url,omitempty"` + Method string `json:"method,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Body string `json:"body,omitempty"` + Query string `json:"query,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` + Response ResponseConfig `json:"response,omitempty"` +} + +// ValidatorConfig defines validation rules +type ValidatorConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` // "required", "email", "phone", "regex", "length", "range", "custom" + Field string `json:"field"` + Rules []ValidationRule `json:"rules"` + Message string `json:"message"` + Parameters map[string]interface{} `json:"parameters,omitempty"` +} + +// Supporting types +type AuthConfig struct { + Required bool `json:"required"` + Type string `json:"type"` // "session", "token", "basic", "oauth" + Provider string `json:"provider,omitempty"` + Redirect string `json:"redirect,omitempty"` + Permissions []string `json:"permissions,omitempty"` + Config map[string]interface{} `json:"config,omitempty"` +} + +type ResponseConfig struct { + Type string `json:"type"` // "json", "html", "redirect", "file", "stream" + Template string `json:"template,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` + Status int `json:"status,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + ContentType string `json:"content_type,omitempty"` +} + +type TransformConfig struct { + Type string `json:"type"` // "map", "filter", "reduce", "expression", "template" + Expression string `json:"expression,omitempty"` + Template string `json:"template,omitempty"` + Source string `json:"source,omitempty"` + Target string `json:"target,omitempty"` + Function string `json:"function,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` +} + +type ConditionConfig struct { + Type string `json:"type"` // "equals", "contains", "exists", "expression", "function" + Field string `json:"field,omitempty"` + Value interface{} `json:"value,omitempty"` + Expression string `json:"expression,omitempty"` + Function string `json:"function,omitempty"` + Operator string `json:"operator,omitempty"` // "and", "or", "not" +} + +type ScriptConfig struct { + Type string `json:"type"` // "inline", "file", "url" + Content string `json:"content"` + Src string `json:"src,omitempty"` +} + +type StyleConfig struct { + Type string `json:"type"` // "inline", "file", "url" + Content string `json:"content"` + Href string `json:"href,omitempty"` +} + +type ComponentConfig struct { + ID string `json:"id"` + Type string `json:"type"` + Template string `json:"template"` + Data map[string]interface{} `json:"data,omitempty"` + Props map[string]interface{} `json:"props,omitempty"` +} + +type TriggerConfig struct { + Type string `json:"type"` // "http", "schedule", "event", "webhook" + Schedule string `json:"schedule,omitempty"` + Event string `json:"event,omitempty"` + Path string `json:"path,omitempty"` + Method string `json:"method,omitempty"` + Conditions []ConditionConfig `json:"conditions,omitempty"` +} + +type ExecutionOptions struct { + Async bool `json:"async"` + Timeout string `json:"timeout,omitempty"` + Retry *RetryConfig `json:"retry,omitempty"` + Priority string `json:"priority,omitempty"` + MaxNodes int `json:"max_nodes,omitempty"` +} + +type RetryConfig struct { + MaxAttempts int `json:"max_attempts"` + Delay string `json:"delay"` + BackoffType string `json:"backoff_type"` // "fixed", "exponential", "linear" +} + +type ValidationRule struct { + Type string `json:"type"` + Value interface{} `json:"value,omitempty"` + Message string `json:"message"` + Parameters map[string]interface{} `json:"parameters,omitempty"` +} + +// Runtime types for the JSON engine +type JSONEngine struct { + app *fiber.App + config *AppConfiguration + templates map[string]*Template + workflows map[string]*Workflow + functions map[string]*Function + validators map[string]*Validator + middleware map[string]*Middleware + data map[string]interface{} +} + +type Template struct { + ID string + Config TemplateConfig + Compiled interface{} +} + +type Workflow struct { + ID string + Config WorkflowConfig + Nodes map[string]*Node + Edges []*Edge + Runtime *WorkflowRuntime +} + +type Node struct { + ID string + Config NodeConfig + Function *Function + Inputs map[string]interface{} + Outputs map[string]interface{} +} + +type Edge struct { + ID string + Config EdgeConfig + From *Node + To *Node +} + +type Function struct { + ID string + Config FunctionConfig + Handler interface{} +} + +type Validator struct { + ID string + Config ValidatorConfig + Rules []ValidationRule +} + +type Middleware struct { + ID string + Config MiddlewareConfig + Handler fiber.Handler +} + +type WorkflowRuntime struct { + Context map[string]interface{} + Variables map[string]interface{} + Status string + Error error +} + +// Execution context for runtime +type ExecutionContext struct { + Request *fiber.Ctx + Data map[string]interface{} + Variables map[string]interface{} + Session map[string]interface{} + User map[string]interface{} + Workflow *Workflow + Node *Node + Functions map[string]*Function + Validators map[string]*Validator +}