mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-07 08:50:54 +08:00
update
This commit is contained in:
854
workflow/json-engine/engine.go
Normal file
854
workflow/json-engine/engine.go
Normal file
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
19
workflow/json-engine/go.mod
Normal file
19
workflow/json-engine/go.mod
Normal file
@@ -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
|
||||||
|
)
|
27
workflow/json-engine/go.sum
Normal file
27
workflow/json-engine/go.sum
Normal file
@@ -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=
|
32
workflow/json-engine/main.go
Normal file
32
workflow/json-engine/main.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
327
workflow/json-engine/sms-app.json
Normal file
327
workflow/json-engine/sms-app.json
Normal file
File diff suppressed because one or more lines are too long
BIN
workflow/json-engine/sms-json-engine
Executable file
BIN
workflow/json-engine/sms-json-engine
Executable file
Binary file not shown.
304
workflow/json-engine/types.go
Normal file
304
workflow/json-engine/types.go
Normal file
@@ -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
|
||||||
|
}
|
Reference in New Issue
Block a user