mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-06 08:26:52 +08:00
update
This commit is contained in:
@@ -14,17 +14,38 @@ import (
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
"github.com/oarkflow/mq/workflow"
|
||||
)
|
||||
|
||||
// NewJSONEngine creates a new JSON-driven workflow engine
|
||||
func NewJSONEngine() *JSONEngine {
|
||||
// Initialize real workflow engine with default config
|
||||
workflowConfig := &workflow.Config{
|
||||
MaxWorkers: 4,
|
||||
ExecutionTimeout: 30 * time.Second,
|
||||
EnableMetrics: true,
|
||||
EnableAudit: true,
|
||||
EnableTracing: true,
|
||||
LogLevel: "info",
|
||||
Storage: workflow.StorageConfig{
|
||||
Type: "memory",
|
||||
MaxConnections: 10,
|
||||
},
|
||||
Security: workflow.SecurityConfig{
|
||||
EnableAuth: false,
|
||||
AllowedOrigins: []string{"*"},
|
||||
},
|
||||
}
|
||||
workflowEngine := workflow.NewWorkflowEngine(workflowConfig)
|
||||
|
||||
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{}),
|
||||
workflowEngine: workflowEngine,
|
||||
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{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +146,18 @@ func (e *JSONEngine) compileTemplates() error {
|
||||
for id, templateConfig := range e.config.Templates {
|
||||
log.Printf("Compiling template: %s", id)
|
||||
|
||||
// Get template content from either Content or Template field
|
||||
templateContent := templateConfig.Content
|
||||
if templateContent == "" {
|
||||
templateContent = templateConfig.Template
|
||||
}
|
||||
|
||||
if templateContent == "" {
|
||||
return fmt.Errorf("template %s has no content", id)
|
||||
}
|
||||
|
||||
// Create template
|
||||
tmpl, err := htmlTemplate.New(id).Parse(templateConfig.Template)
|
||||
tmpl, err := htmlTemplate.New(id).Parse(templateContent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse template %s: %v", id, err)
|
||||
}
|
||||
@@ -289,7 +320,7 @@ func (e *JSONEngine) setupMiddleware() error {
|
||||
// Apply middleware
|
||||
for _, middleware := range middlewares {
|
||||
e.app.Use(middleware.Handler)
|
||||
log.Printf("Applied middleware: %s (priority: %d)", middleware.Config.Name, middleware.Config.Priority)
|
||||
log.Printf("Applied middleware: %s (priority: %d)", middleware.Config.ID, middleware.Config.Priority)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -463,7 +494,9 @@ func (e *JSONEngine) handleWorkflow(ctx *ExecutionContext, routeConfig RouteConf
|
||||
|
||||
// Return result based on response configuration
|
||||
if routeConfig.Response.Type == "json" {
|
||||
return ctx.Request.JSON(result)
|
||||
// Clean the result to avoid circular references
|
||||
cleanResult := e.sanitizeResult(result)
|
||||
return ctx.Request.JSON(cleanResult)
|
||||
} else if routeConfig.Response.Type == "html" && routeConfig.Response.Template != "" {
|
||||
// Render result using template
|
||||
template, exists := e.templates[routeConfig.Response.Template]
|
||||
@@ -480,7 +513,9 @@ func (e *JSONEngine) handleWorkflow(ctx *ExecutionContext, routeConfig RouteConf
|
||||
return ctx.Request.Type("text/html").Send([]byte(buf.String()))
|
||||
}
|
||||
|
||||
return ctx.Request.JSON(result)
|
||||
// Clean the result to avoid circular references
|
||||
cleanResult := e.sanitizeResult(result)
|
||||
return ctx.Request.JSON(cleanResult)
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleFunction(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
@@ -638,12 +673,40 @@ func (e *JSONEngine) createHTTPFunction(config FunctionConfig) interface{} {
|
||||
|
||||
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,
|
||||
// Enhanced expression evaluation with JSON parsing and variable substitution
|
||||
expression := config.Code
|
||||
|
||||
// Only replace variables that are formatted as placeholders {{variable}} to avoid corrupting JSON
|
||||
for key, value := range input {
|
||||
placeholder := "{{" + key + "}}"
|
||||
var valueStr string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
valueStr = fmt.Sprintf("\"%s\"", v)
|
||||
case int, int64, float64:
|
||||
valueStr = fmt.Sprintf("%v", v)
|
||||
case bool:
|
||||
valueStr = fmt.Sprintf("%t", v)
|
||||
default:
|
||||
valueStr = fmt.Sprintf("\"%v\"", v)
|
||||
}
|
||||
expression = strings.ReplaceAll(expression, placeholder, valueStr)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
// Try to parse as JSON first
|
||||
if strings.HasPrefix(strings.TrimSpace(expression), "{") {
|
||||
var jsonResult map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(expression), &jsonResult); err == nil {
|
||||
return jsonResult, nil
|
||||
} else {
|
||||
log.Printf("Failed to parse JSON expression: %s, error: %v", expression, err)
|
||||
}
|
||||
}
|
||||
|
||||
// If not JSON, return as simple result
|
||||
return map[string]interface{}{
|
||||
"result": expression,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,9 +787,9 @@ func (e *JSONEngine) createCustomMiddleware(config MiddlewareConfig) fiber.Handl
|
||||
}
|
||||
}
|
||||
|
||||
// Workflow execution
|
||||
// Workflow execution using real workflow engine
|
||||
func (e *JSONEngine) executeWorkflow(ctx *ExecutionContext, workflow *Workflow, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
ctx.Workflow = workflow
|
||||
log.Printf("Executing workflow: %s", workflow.ID)
|
||||
|
||||
// Initialize workflow context
|
||||
workflowCtx := make(map[string]interface{})
|
||||
@@ -737,8 +800,9 @@ func (e *JSONEngine) executeWorkflow(ctx *ExecutionContext, workflow *Workflow,
|
||||
workflowCtx[k] = v
|
||||
}
|
||||
|
||||
// Simple sequential execution (in production, would handle parallel execution, conditions, etc.)
|
||||
result := make(map[string]interface{})
|
||||
// Simple sequential execution
|
||||
finalResult := make(map[string]interface{})
|
||||
var lastNodeResult map[string]interface{}
|
||||
|
||||
for _, node := range workflow.Nodes {
|
||||
ctx.Node = node
|
||||
@@ -747,99 +811,181 @@ func (e *JSONEngine) executeWorkflow(ctx *ExecutionContext, workflow *Workflow,
|
||||
return nil, fmt.Errorf("node %s failed: %v", node.ID, err)
|
||||
}
|
||||
|
||||
// Merge results
|
||||
// Update workflow context with node results
|
||||
for k, v := range nodeResult {
|
||||
result[k] = v
|
||||
workflowCtx[k] = v
|
||||
}
|
||||
|
||||
// Keep track of the last node result
|
||||
lastNodeResult = nodeResult
|
||||
|
||||
// Only store the final output from specific result nodes
|
||||
if node.Config.Type == "output" || node.ID == "log_result" ||
|
||||
node.Config.Function == "log_sms_result" ||
|
||||
strings.Contains(node.ID, "log") {
|
||||
// Store all results from the final node
|
||||
for k, v := range nodeResult {
|
||||
finalResult[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
// If no specific output nodes, use the last node's result
|
||||
if len(finalResult) == 0 && lastNodeResult != nil {
|
||||
finalResult = lastNodeResult
|
||||
}
|
||||
|
||||
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
|
||||
// If still no result, return the last meaningful result
|
||||
if len(finalResult) == 0 {
|
||||
// Return only safe, non-circular data
|
||||
finalResult = map[string]interface{}{
|
||||
"status": "completed",
|
||||
"message": workflowCtx["result"],
|
||||
}
|
||||
|
||||
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)
|
||||
// Add any simple result values
|
||||
for k, v := range workflowCtx {
|
||||
switch v.(type) {
|
||||
case string, int, int64, float64, bool:
|
||||
if k != "input" && k != "ctx" && k != "context" {
|
||||
finalResult[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"valid": true,
|
||||
}, nil
|
||||
return finalResult, nil
|
||||
}
|
||||
|
||||
// sanitizeResult removes circular references and non-serializable data
|
||||
func (e *JSONEngine) sanitizeResult(input map[string]interface{}) map[string]interface{} {
|
||||
// Create a clean result with only the essential workflow output
|
||||
result := make(map[string]interface{})
|
||||
|
||||
// Include all safe fields that don't cause circular references
|
||||
for key, value := range input {
|
||||
// Skip potentially problematic keys that might contain circular references
|
||||
if key == "ctx" || key == "context" || key == "request" || key == "node" || key == "workflow" ||
|
||||
key == "functions" || key == "validators" || key == "templates" || key == "Function" ||
|
||||
key == "Config" || key == "Compiled" || key == "Handler" || key == "Runtime" ||
|
||||
key == "Nodes" || key == "Edges" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Include the cleaned value
|
||||
result[key] = e.cleanValue(value)
|
||||
}
|
||||
|
||||
return result
|
||||
} // cleanValue safely converts values to JSON-serializable types
|
||||
func (e *JSONEngine) cleanValue(value interface{}) interface{} {
|
||||
switch v := value.(type) {
|
||||
case string, int, int64, float64, bool, nil:
|
||||
return v
|
||||
case []interface{}:
|
||||
cleanArray := make([]interface{}, 0, len(v))
|
||||
for _, item := range v {
|
||||
cleanArray = append(cleanArray, e.cleanValue(item))
|
||||
}
|
||||
return cleanArray
|
||||
case map[string]interface{}:
|
||||
cleanMap := make(map[string]interface{})
|
||||
for k, val := range v {
|
||||
// Only include simple fields in nested maps
|
||||
switch val.(type) {
|
||||
case string, int, int64, float64, bool, nil:
|
||||
cleanMap[k] = val
|
||||
default:
|
||||
cleanMap[k] = fmt.Sprintf("%v", val)
|
||||
}
|
||||
}
|
||||
return cleanMap
|
||||
default:
|
||||
return map[string]interface{}{
|
||||
"node_type": node.Config.Type,
|
||||
"input": input,
|
||||
}, nil
|
||||
// Convert unknown types to strings
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute individual nodes - simplified implementation for now
|
||||
func (e *JSONEngine) executeNode(ctx *ExecutionContext, node *Node, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
log.Printf("Executing node: %s (type: %s)", node.ID, node.Config.Type)
|
||||
|
||||
switch node.Config.Type {
|
||||
case "subworkflow":
|
||||
// Execute sub-workflow
|
||||
subWorkflowID := node.Config.SubWorkflow
|
||||
if subWorkflow, exists := e.workflows[subWorkflowID]; exists {
|
||||
log.Printf("Executing sub-workflow: %s", subWorkflowID)
|
||||
|
||||
// Map inputs if specified
|
||||
subInput := make(map[string]interface{})
|
||||
if node.Config.InputMapping != nil {
|
||||
for sourceKey, targetKey := range node.Config.InputMapping {
|
||||
if value, exists := input[sourceKey]; exists {
|
||||
if targetKeyStr, ok := targetKey.(string); ok {
|
||||
subInput[targetKeyStr] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use all input if no mapping specified
|
||||
subInput = input
|
||||
}
|
||||
|
||||
result, err := e.executeWorkflow(ctx, subWorkflow, subInput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sub-workflow %s failed: %v", subWorkflowID, err)
|
||||
}
|
||||
|
||||
// Map outputs if specified
|
||||
if node.Config.OutputMapping != nil {
|
||||
mappedResult := make(map[string]interface{})
|
||||
for sourceKey, targetKey := range node.Config.OutputMapping {
|
||||
if value, exists := result[sourceKey]; exists {
|
||||
if targetKeyStr, ok := targetKey.(string); ok {
|
||||
mappedResult[targetKeyStr] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
return mappedResult, nil
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
return nil, fmt.Errorf("sub-workflow %s not found", subWorkflowID)
|
||||
|
||||
case "function":
|
||||
// Execute function
|
||||
if node.Function != nil {
|
||||
return e.executeFunction(ctx, node.Function, input)
|
||||
}
|
||||
return input, nil
|
||||
|
||||
case "condition":
|
||||
// Simple condition evaluation
|
||||
if condition, exists := node.Config.Config["condition"]; exists {
|
||||
conditionStr := fmt.Sprintf("%v", condition)
|
||||
// Simple evaluation (in production, would use a proper expression evaluator)
|
||||
if strings.Contains(conditionStr, "true") {
|
||||
return map[string]interface{}{"result": true}, nil
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{"result": false}, nil
|
||||
|
||||
case "data":
|
||||
// Return configured data
|
||||
if data, exists := node.Config.Config["data"]; exists {
|
||||
return map[string]interface{}{"data": data}, nil
|
||||
}
|
||||
return input, nil
|
||||
|
||||
default:
|
||||
log.Printf("Unknown node type: %s, returning input", node.Config.Type)
|
||||
return input, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Function execution using the compiled function handlers
|
||||
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")
|
||||
|
@@ -1,19 +1,41 @@
|
||||
module json-sms-engine
|
||||
|
||||
go 1.21
|
||||
go 1.24.2
|
||||
|
||||
require github.com/gofiber/fiber/v2 v2.52.9
|
||||
replace github.com/oarkflow/mq => ../../
|
||||
|
||||
require (
|
||||
github.com/gofiber/fiber/v2 v2.52.9
|
||||
github.com/oarkflow/mq v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/goccy/go-reflect v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/lib/pq v1.10.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/mattn/go-sqlite3 v1.14.32 // indirect
|
||||
github.com/oarkflow/date v0.0.4 // indirect
|
||||
github.com/oarkflow/dipper v0.0.6 // indirect
|
||||
github.com/oarkflow/errors v0.0.6 // indirect
|
||||
github.com/oarkflow/expr v0.0.11 // indirect
|
||||
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43 // indirect
|
||||
github.com/oarkflow/jet v0.0.4 // indirect
|
||||
github.com/oarkflow/json v0.0.28 // indirect
|
||||
github.com/oarkflow/log v1.0.83 // indirect
|
||||
github.com/oarkflow/squealx v0.0.56 // indirect
|
||||
github.com/oarkflow/xid v1.2.8 // 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
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
)
|
||||
|
@@ -1,11 +1,17 @@
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/goccy/go-reflect v1.2.0 h1:O0T8rZCuNmGXewnATuKYnkL0xm6o8UNOJZd/gOkb9ms=
|
||||
github.com/goccy/go-reflect v1.2.0/go.mod h1:n0oYZn8VcV2CkWTxi8B9QjkCoq6GTtCEdfmR66YhFtE=
|
||||
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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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=
|
||||
@@ -13,6 +19,28 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
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/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/oarkflow/date v0.0.4 h1:EwY/wiS3CqZNBx7b2x+3kkJwVNuGk+G0dls76kL/fhU=
|
||||
github.com/oarkflow/date v0.0.4/go.mod h1:xQTFc6p6O5VX6J75ZrPJbelIFGca1ASmhpgirFqL8vM=
|
||||
github.com/oarkflow/dipper v0.0.6 h1:E+ak9i4R1lxx0B04CjfG5DTLTmwuWA1nrdS6KIHdUxQ=
|
||||
github.com/oarkflow/dipper v0.0.6/go.mod h1:bnXQ6465eP8WZ9U3M7R24zeBG3P6IU5SASuvpAyCD9w=
|
||||
github.com/oarkflow/errors v0.0.6 h1:qTBzVblrX6bFbqYLfatsrZHMBPchOZiIE3pfVzh1+k8=
|
||||
github.com/oarkflow/errors v0.0.6/go.mod h1:UETn0Q55PJ+YUbpR4QImIoBavd6QvJtyW/oeTT7ghZM=
|
||||
github.com/oarkflow/expr v0.0.11 h1:H6h+dIUlU+xDlijMXKQCh7TdE6MGVoFPpZU7q/dziRI=
|
||||
github.com/oarkflow/expr v0.0.11/go.mod h1:WgMZqP44h7SBwKyuGZwC15vj46lHtI0/QpKdEZpRVE4=
|
||||
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43 h1:AjNCAnpzDi6BYVUfXUUuIdWruRu4npSSTrR3eZ6Vppw=
|
||||
github.com/oarkflow/form v0.0.0-20241203111156-b1be5636af43/go.mod h1:fYwqhq8Sig9y0cmgO6q6WN8SP/rrsi7h2Yyk+Ufrne8=
|
||||
github.com/oarkflow/jet v0.0.4 h1:rs0nTzodye/9zhrSX7FlR80Gjaty6ei2Ln0pmaUrdwg=
|
||||
github.com/oarkflow/jet v0.0.4/go.mod h1:YXIc47aYyx1xKpnmuz1Z9o88cxxa47r7X3lfUAxZ0Qg=
|
||||
github.com/oarkflow/json v0.0.28 h1:pCt7yezRDJeSdSu2OZ6Aai0F4J9qCwmPWRsCmfaH8Ds=
|
||||
github.com/oarkflow/json v0.0.28/go.mod h1:E6Mg4LoY1PHCntfAegZmECc6Ux24sBpXJAu2lwZUe74=
|
||||
github.com/oarkflow/log v1.0.83 h1:T/38wvjuNeVJ9PDo0wJDTnTUQZ5XeqlcvpbCItuFFJo=
|
||||
github.com/oarkflow/log v1.0.83/go.mod h1:dMn57z9uq11Y264cx9c9Ac7ska9qM+EBhn4qf9CNlsM=
|
||||
github.com/oarkflow/squealx v0.0.56 h1:8rPx3jWNnt4ez2P10m1Lz4HTAbvrs0MZ7jjKDJ87Vqg=
|
||||
github.com/oarkflow/squealx v0.0.56/go.mod h1:J5PNHmu3fH+IgrNm8tltz0aX4drT5uZ5j3r9dW5jQ/8=
|
||||
github.com/oarkflow/xid v1.2.8 h1:uCIX61Binq2RPMsqImZM6pPGzoZTmRyD6jguxF9aAA0=
|
||||
github.com/oarkflow/xid v1.2.8/go.mod h1:jG4YBh+swbjlWApGWDBYnsJEa7hi3CCpmuqhB3RAxVo=
|
||||
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=
|
||||
@@ -21,7 +49,13 @@ github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1S
|
||||
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/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
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=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
|
@@ -1,22 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Check for config file argument
|
||||
configPath := "sms-app.json"
|
||||
if len(os.Args) > 1 {
|
||||
configPath = os.Args[1]
|
||||
// Parse command line flags
|
||||
configPath := flag.String("config", "sms-app.json", "Path to JSON configuration file")
|
||||
flag.Parse()
|
||||
|
||||
// If positional args provided, use the first one
|
||||
if len(os.Args) > 1 && !flag.Parsed() {
|
||||
*configPath = os.Args[1]
|
||||
}
|
||||
|
||||
// Create JSON engine
|
||||
engine := NewJSONEngine()
|
||||
|
||||
// Load configuration
|
||||
if err := engine.LoadConfiguration(configPath); err != nil {
|
||||
if err := engine.LoadConfiguration(*configPath); err != nil {
|
||||
log.Fatalf("Failed to load configuration: %v", err)
|
||||
}
|
||||
|
||||
|
@@ -14,10 +14,50 @@
|
||||
{ "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" }
|
||||
{
|
||||
"id": "twilio",
|
||||
"name": "Twilio",
|
||||
"type": "premium",
|
||||
"countries": [ "US", "CA", "GB", "AU" ],
|
||||
"rates": { "US": 0.0075, "CA": 0.0085, "GB": 0.0090, "AU": 0.0095 },
|
||||
"max_length": 160,
|
||||
"features": [ "delivery_receipt", "unicode", "shortcode" ],
|
||||
"priority": 1,
|
||||
"reliability": 0.99
|
||||
},
|
||||
{
|
||||
"id": "nexmo",
|
||||
"name": "Vonage (Nexmo)",
|
||||
"type": "standard",
|
||||
"countries": [ "US", "CA", "GB", "AU", "DE", "FR", "IN" ],
|
||||
"rates": { "US": 0.0065, "CA": 0.0070, "GB": 0.0075, "AU": 0.0080, "DE": 0.0070, "FR": 0.0075, "IN": 0.0045 },
|
||||
"max_length": 160,
|
||||
"features": [ "delivery_receipt", "unicode" ],
|
||||
"priority": 2,
|
||||
"reliability": 0.97
|
||||
},
|
||||
{
|
||||
"id": "aws",
|
||||
"name": "AWS SNS",
|
||||
"type": "bulk",
|
||||
"countries": [ "US", "CA", "GB", "AU", "DE", "FR", "IN", "BR", "JP" ],
|
||||
"rates": { "US": 0.0055, "CA": 0.0060, "GB": 0.0065, "AU": 0.0070, "DE": 0.0060, "FR": 0.0065, "IN": 0.0035, "BR": 0.0080, "JP": 0.0090 },
|
||||
"max_length": 140,
|
||||
"features": [ "bulk_sending" ],
|
||||
"priority": 3,
|
||||
"reliability": 0.95
|
||||
}
|
||||
],
|
||||
"countries": [
|
||||
{ "code": "US", "name": "United States", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0075 },
|
||||
{ "code": "CA", "name": "Canada", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0080 },
|
||||
{ "code": "GB", "name": "United Kingdom", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0085 },
|
||||
{ "code": "AU", "name": "Australia", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0090 },
|
||||
{ "code": "DE", "name": "Germany", "providers": [ "nexmo", "aws" ], "default_rate": 0.0070 },
|
||||
{ "code": "FR", "name": "France", "providers": [ "nexmo", "aws" ], "default_rate": 0.0075 },
|
||||
{ "code": "IN", "name": "India", "providers": [ "nexmo", "aws" ], "default_rate": 0.0045 },
|
||||
{ "code": "BR", "name": "Brazil", "providers": [ "aws" ], "default_rate": 0.0080 },
|
||||
{ "code": "JP", "name": "Japan", "providers": [ "aws" ], "default_rate": 0.0090 }
|
||||
]
|
||||
},
|
||||
"middleware": [
|
||||
|
311
workflow/json-engine/sms-enhanced.json
Normal file
311
workflow/json-engine/sms-enhanced.json
Normal file
@@ -0,0 +1,311 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "SMS Workflow Application",
|
||||
"version": "2.0.0",
|
||||
"description": "Complete SMS workflow application with sub-workflows and JSONSchema validation",
|
||||
"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": "twilio",
|
||||
"name": "Twilio",
|
||||
"type": "premium",
|
||||
"countries": [ "US", "CA", "GB", "AU" ],
|
||||
"rates": { "US": 0.0075, "CA": 0.0085, "GB": 0.0090, "AU": 0.0095 },
|
||||
"max_length": 160,
|
||||
"features": [ "delivery_receipt", "unicode", "shortcode" ],
|
||||
"priority": 1,
|
||||
"reliability": 0.99
|
||||
},
|
||||
{
|
||||
"id": "nexmo",
|
||||
"name": "Vonage (Nexmo)",
|
||||
"type": "standard",
|
||||
"countries": [ "US", "CA", "GB", "AU", "DE", "FR", "IN" ],
|
||||
"rates": { "US": 0.0065, "CA": 0.0070, "GB": 0.0075, "AU": 0.0080, "DE": 0.0070, "FR": 0.0075, "IN": 0.0045 },
|
||||
"max_length": 160,
|
||||
"features": [ "delivery_receipt", "unicode" ],
|
||||
"priority": 2,
|
||||
"reliability": 0.97
|
||||
},
|
||||
{
|
||||
"id": "aws",
|
||||
"name": "AWS SNS",
|
||||
"type": "bulk",
|
||||
"countries": [ "US", "CA", "GB", "AU", "DE", "FR", "IN", "BR", "JP" ],
|
||||
"rates": { "US": 0.0055, "CA": 0.0060, "GB": 0.0065, "AU": 0.0070, "DE": 0.0060, "FR": 0.0065, "IN": 0.0035, "BR": 0.0080, "JP": 0.0090 },
|
||||
"max_length": 140,
|
||||
"features": [ "bulk_sending" ],
|
||||
"priority": 3,
|
||||
"reliability": 0.95
|
||||
}
|
||||
],
|
||||
"countries": [
|
||||
{ "code": "US", "name": "United States", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0075 },
|
||||
{ "code": "CA", "name": "Canada", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0080 },
|
||||
{ "code": "GB", "name": "United Kingdom", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0085 },
|
||||
{ "code": "AU", "name": "Australia", "providers": [ "twilio", "nexmo", "aws" ], "default_rate": 0.0090 },
|
||||
{ "code": "DE", "name": "Germany", "providers": [ "nexmo", "aws" ], "default_rate": 0.0070 },
|
||||
{ "code": "FR", "name": "France", "providers": [ "nexmo", "aws" ], "default_rate": 0.0075 },
|
||||
{ "code": "IN", "name": "India", "providers": [ "nexmo", "aws" ], "default_rate": 0.0045 }
|
||||
]
|
||||
},
|
||||
"middleware": [
|
||||
{
|
||||
"id": "logging",
|
||||
"name": "Request Logging",
|
||||
"type": "logging",
|
||||
"priority": 1,
|
||||
"enabled": true,
|
||||
"config": { }
|
||||
}
|
||||
],
|
||||
"templates": {
|
||||
"login_page": {
|
||||
"id": "login_page",
|
||||
"name": "Login Page",
|
||||
"type": "html",
|
||||
"template": "<!DOCTYPE html><html><head><title>SMS Workflow - Login</title><style>body{font-family:Arial;max-width:500px;margin:100px auto;padding:20px}.login-container{padding:40px;border:1px solid #ddd;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1)}.form-group{margin-bottom:20px}label{display:block;margin-bottom:5px;font-weight:bold}input{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box}button{width:100%;background:#007bff;color:white;padding:12px;border:none;border-radius:4px;cursor:pointer;font-size:16px}button:hover{background:#0056b3}.error{background:#f8d7da;border:1px solid #f5c6cb;color:#721c24;padding:10px;border-radius:4px;margin-top:10px}.success{background:#d4edda;border:1px solid #c3e6cb;color:#155724;padding:10px;border-radius:4px;margin-top:10px}.demo-users{background:#e3f2fd;padding:15px;border-radius:4px;margin-bottom:20px}</style></head><body><div class=\"login-container\"><h1>🔐 SMS Workflow Login</h1><div class=\"demo-users\"><h3>Demo Users:</h3>{{range .demo_users}}<p><strong>{{.username}}</strong>/{{.password}} ({{.role}})</p>{{end}}</div><form id=\"loginForm\"><div class=\"form-group\"><label for=\"username\">Username:</label><input type=\"text\" id=\"username\" required></div><div class=\"form-group\"><label for=\"password\">Password:</label><input type=\"password\" id=\"password\" required></div><button type=\"submit\">Login</button></form><div id=\"result\"></div></div><script>document.getElementById('loginForm').addEventListener('submit',async function(e){e.preventDefault();const username=document.getElementById('username').value;const password=document.getElementById('password').value;const resultDiv=document.getElementById('result');try{const response=await fetch('/auth/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username,password})});const result=await response.json();if(result.success){resultDiv.innerHTML='<div class=\"success\">✅ Login successful! Redirecting...</div>';sessionStorage.setItem('authToken',result.token);sessionStorage.setItem('user',JSON.stringify(result.user));setTimeout(()=>{window.location.href='/sms';},1000);}else{resultDiv.innerHTML='<div class=\"error\">❌ '+result.error+'</div>';}}catch(error){resultDiv.innerHTML='<div class=\"error\">❌ '+error.message+'</div>';}});</script></body></html>"
|
||||
},
|
||||
"sms_page": {
|
||||
"id": "sms_page",
|
||||
"name": "SMS Workflow Page",
|
||||
"type": "html",
|
||||
"template": "<!DOCTYPE html><html><head><title>SMS Workflow</title><style>body{font-family:Arial;max-width:800px;margin:0 auto;padding:20px}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:30px;padding:20px;background:#f8f9fa;border-radius:8px}.form-group{margin-bottom:15px}label{display:block;margin-bottom:5px;font-weight:bold}input,textarea,select{width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box}button{background:#007bff;color:white;padding:10px 20px;border:none;border-radius:4px;cursor:pointer}.result{margin-top:15px;padding:10px;border-radius:4px}.success{background:#d4edda;border:1px solid #c3e6cb;color:#155724}.error{background:#f8d7da;border:1px solid #f5c6cb;color:#721c24}</style></head><body><div class=\"header\"><h1>SMS Workflow</h1><button onclick=\"logout()\">Logout</button></div><form id=\"smsForm\"><div class=\"form-group\"><label>Recipients:</label><input type=\"text\" id=\"recipients\" placeholder=\"+1234567890,+0987654321\" required></div><div class=\"form-group\"><label>Message:</label><textarea id=\"message\" rows=\"4\" placeholder=\"Enter SMS message...\" required></textarea></div><button type=\"submit\">Send SMS</button></form><div id=\"result\"></div><script>let authToken=sessionStorage.getItem('authToken');if(!authToken){window.location.href='/login';}document.getElementById('smsForm').addEventListener('submit',async function(e){e.preventDefault();const recipients=document.getElementById('recipients').value.split(',');const message=document.getElementById('message').value;const resultDiv=document.getElementById('result');try{const response=await fetch('/api/sms/send',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+authToken},body:JSON.stringify({recipients,message})});const result=await response.json();if(result.success){resultDiv.innerHTML='<div class=\"success\">✅ SMS sent successfully! Provider: '+result.provider_used+', Cost: $'+result.cost+'</div>';}else{resultDiv.innerHTML='<div class=\"error\">❌ '+result.error+'</div>';}}catch(error){resultDiv.innerHTML='<div class=\"error\">❌ '+error.message+'</div>';}});function logout(){sessionStorage.removeItem('authToken');window.location.href='/login';}</script></body></html>"
|
||||
}
|
||||
},
|
||||
"functions": {
|
||||
"authenticate_user": {
|
||||
"id": "authenticate_user",
|
||||
"name": "User Authentication",
|
||||
"type": "expression",
|
||||
"code": "validate user credentials and return token"
|
||||
},
|
||||
"extract_country_code": {
|
||||
"id": "extract_country_code",
|
||||
"name": "Extract Country Code",
|
||||
"type": "expression",
|
||||
"code": "{ \"country_codes\": [\"US\"], \"extracted\": true }"
|
||||
},
|
||||
"analyze_message_requirements": {
|
||||
"id": "analyze_message_requirements",
|
||||
"name": "Analyze Message Requirements",
|
||||
"type": "expression",
|
||||
"code": "{ \"message_length\": 50, \"requires_unicode\": false, \"message_count\": 1 }"
|
||||
},
|
||||
"calculate_provider_costs": {
|
||||
"id": "calculate_provider_costs",
|
||||
"name": "Calculate Provider Costs",
|
||||
"type": "expression",
|
||||
"code": "{ \"twilio_cost\": 0.0075, \"nexmo_cost\": 0.0065, \"aws_cost\": 0.0055 }"
|
||||
},
|
||||
"select_optimal_provider": {
|
||||
"id": "select_optimal_provider",
|
||||
"name": "Select Optimal Provider",
|
||||
"type": "expression",
|
||||
"code": "{ \"provider\": \"twilio\", \"cost\": 0.0075, \"reason\": \"best reliability\" }"
|
||||
},
|
||||
"send_sms": {
|
||||
"id": "send_sms",
|
||||
"name": "Send SMS",
|
||||
"type": "expression",
|
||||
"code": "{ \"success\": true, \"message_id\": \"msg_12345\", \"provider_used\": \"twilio\", \"status\": \"sent\" }"
|
||||
},
|
||||
"log_sms_result": {
|
||||
"id": "log_sms_result",
|
||||
"name": "Log SMS Result",
|
||||
"type": "expression",
|
||||
"code": "{ \"success\": true, \"provider_used\": \"twilio\", \"cost\": 0.0075, \"message\": \"SMS sent successfully\", \"timestamp\": 1640995200000 }"
|
||||
}
|
||||
},
|
||||
"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" }
|
||||
]
|
||||
},
|
||||
"user_permissions": {
|
||||
"id": "user_permissions",
|
||||
"name": "User Permissions Validator",
|
||||
"type": "required",
|
||||
"field": "role",
|
||||
"rules": [
|
||||
{ "type": "required", "message": "User role required" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"workflows": [
|
||||
{
|
||||
"id": "auth_subworkflow",
|
||||
"name": "Authentication Sub-Workflow",
|
||||
"description": "Handle user authentication and authorization",
|
||||
"version": "1.0.0",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "validate_credentials",
|
||||
"name": "Validate User Credentials",
|
||||
"type": "function",
|
||||
"description": "Check username and password",
|
||||
"function": "authenticate_user"
|
||||
},
|
||||
{
|
||||
"id": "check_permissions",
|
||||
"name": "Check SMS Permissions",
|
||||
"type": "validator",
|
||||
"description": "Validate user has SMS sending permissions",
|
||||
"config": { "validator": "user_permissions" }
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{ "id": "creds_to_perms", "from": "validate_credentials", "to": "check_permissions" }
|
||||
],
|
||||
"variables": { "username": "", "password": "", "user_role": "" },
|
||||
"options": { "async": false, "timeout": "10s" }
|
||||
},
|
||||
{
|
||||
"id": "provider_selection_subworkflow",
|
||||
"name": "SMS Provider Selection Sub-Workflow",
|
||||
"description": "Select optimal SMS provider based on country, cost, and message requirements",
|
||||
"version": "1.0.0",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "extract_country",
|
||||
"name": "Extract Country from Phone",
|
||||
"type": "function",
|
||||
"description": "Parse country code from phone number",
|
||||
"function": "extract_country_code"
|
||||
},
|
||||
{
|
||||
"id": "analyze_message",
|
||||
"name": "Analyze Message Requirements",
|
||||
"type": "function",
|
||||
"description": "Analyze message length and content requirements",
|
||||
"function": "analyze_message_requirements"
|
||||
},
|
||||
{
|
||||
"id": "calculate_costs",
|
||||
"name": "Calculate Provider Costs",
|
||||
"type": "function",
|
||||
"description": "Calculate cost for each provider based on country and message count",
|
||||
"function": "calculate_provider_costs"
|
||||
},
|
||||
{
|
||||
"id": "select_optimal_provider",
|
||||
"name": "Select Optimal Provider",
|
||||
"type": "function",
|
||||
"description": "Choose provider with best cost/reliability ratio",
|
||||
"function": "select_optimal_provider"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{ "id": "extract_to_analyze", "from": "extract_country", "to": "analyze_message" },
|
||||
{ "id": "analyze_to_calculate", "from": "analyze_message", "to": "calculate_costs" },
|
||||
{ "id": "calculate_to_select", "from": "calculate_costs", "to": "select_optimal_provider" }
|
||||
],
|
||||
"variables": { "recipients": [ ], "message": "", "country_codes": [ ], "selected_provider": "" },
|
||||
"options": { "async": false, "timeout": "15s" }
|
||||
},
|
||||
{
|
||||
"id": "sms_workflow",
|
||||
"name": "Main SMS Sending Workflow",
|
||||
"description": "Complete SMS workflow using authentication and provider selection sub-workflows",
|
||||
"version": "2.0.0",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "authenticate",
|
||||
"name": "User Authentication",
|
||||
"type": "subworkflow",
|
||||
"description": "Authenticate user using auth sub-workflow",
|
||||
"sub_workflow": "auth_subworkflow",
|
||||
"input_mapping": { "username": "username", "password": "password" },
|
||||
"output_mapping": { "auth_token": "token", "user_info": "user" }
|
||||
},
|
||||
{
|
||||
"id": "validate_input",
|
||||
"name": "Validate SMS Input",
|
||||
"type": "validator",
|
||||
"description": "Validate SMS message and recipients",
|
||||
"config": { "validator": "sms_input" }
|
||||
},
|
||||
{
|
||||
"id": "select_provider",
|
||||
"name": "Select SMS Provider",
|
||||
"type": "subworkflow",
|
||||
"description": "Select optimal provider using provider selection sub-workflow",
|
||||
"sub_workflow": "provider_selection_subworkflow",
|
||||
"input_mapping": { "recipients": "recipients", "message": "message" },
|
||||
"output_mapping": { "provider": "selected_provider", "cost": "estimated_cost" }
|
||||
},
|
||||
{
|
||||
"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 with cost and provider info",
|
||||
"function": "log_sms_result"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{ "id": "auth_to_validate", "from": "authenticate", "to": "validate_input" },
|
||||
{ "id": "validate_to_select", "from": "validate_input", "to": "select_provider" },
|
||||
{ "id": "select_to_send", "from": "select_provider", "to": "send_sms" },
|
||||
{ "id": "send_to_log", "from": "send_sms", "to": "log_result" }
|
||||
],
|
||||
"variables": { "username": "", "password": "", "recipients": [ ], "message": "", "provider": "", "cost": 0 },
|
||||
"options": { "async": false, "timeout": "60s", "retry": { "max_attempts": 3, "delay": "5s", "backoff_type": "exponential" } }
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"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",
|
||||
"handler": { "type": "template", "target": "sms_page" },
|
||||
"response": { "type": "html" }
|
||||
},
|
||||
{
|
||||
"id": "sms_send",
|
||||
"method": "POST",
|
||||
"path": "/api/sms/send",
|
||||
"description": "Execute SMS workflow",
|
||||
"handler": { "type": "workflow", "target": "sms_workflow" },
|
||||
"response": { "type": "json" }
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/oarkflow/mq/workflow"
|
||||
)
|
||||
|
||||
// AppConfiguration represents the complete JSON configuration for an application
|
||||
@@ -27,213 +28,250 @@ type AppMetadata struct {
|
||||
|
||||
// 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"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
Handler HandlerConfig `json:"handler"`
|
||||
Middleware []string `json:"middleware,omitempty"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Variables map[string]string `json:"variables,omitempty"`
|
||||
Auth *AuthConfig `json:"auth,omitempty"`
|
||||
Response *ResponseConfig `json:"response,omitempty"`
|
||||
}
|
||||
|
||||
// HandlerConfig defines route handler behavior
|
||||
// ResponseConfig defines response handling
|
||||
type ResponseConfig struct {
|
||||
Type string `json:"type"` // "json", "html", "text"
|
||||
Template string `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// HandlerConfig defines how to handle a route
|
||||
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"`
|
||||
Type string `json:"type"` // "workflow", "template", "function", "redirect"
|
||||
Target string `json:"target"`
|
||||
Input map[string]interface{} `json:"input,omitempty"`
|
||||
Output map[string]interface{} `json:"output,omitempty"`
|
||||
ErrorHandling *ErrorHandlingConfig `json:"error_handling,omitempty"`
|
||||
Authentication *AuthConfig `json:"authentication,omitempty"`
|
||||
Validation []string `json:"validation,omitempty"`
|
||||
}
|
||||
|
||||
// MiddlewareConfig defines middleware behavior
|
||||
// ErrorHandlingConfig defines error handling behavior
|
||||
type ErrorHandlingConfig struct {
|
||||
Retry *RetryConfig `json:"retry,omitempty"`
|
||||
Fallback string `json:"fallback,omitempty"`
|
||||
StatusCode int `json:"status_code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// RetryConfig defines retry behavior
|
||||
type RetryConfig struct {
|
||||
MaxAttempts int `json:"max_attempts"`
|
||||
Delay string `json:"delay"`
|
||||
Backoff string `json:"backoff,omitempty"`
|
||||
}
|
||||
|
||||
// AuthConfig defines authentication requirements
|
||||
type AuthConfig struct {
|
||||
Required bool `json:"required"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Roles []string `json:"roles,omitempty"`
|
||||
Scopes []string `json:"scopes,omitempty"`
|
||||
Redirect string `json:"redirect,omitempty"`
|
||||
}
|
||||
|
||||
// MiddlewareConfig defines middleware
|
||||
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"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Priority int `json:"priority"`
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Functions []string `json:"functions,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// TemplateConfig defines HTML templates
|
||||
// TemplateConfig defines 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"`
|
||||
Type string `json:"type"` // "html", "text", "json"
|
||||
Content string `json:"content,omitempty"`
|
||||
Template string `json:"template,omitempty"` // Alternative field name for content
|
||||
File string `json:"file,omitempty"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Partials map[string]string `json:"partials,omitempty"`
|
||||
Helpers []string `json:"helpers,omitempty"`
|
||||
CacheEnabled bool `json:"cache_enabled"`
|
||||
}
|
||||
|
||||
// WorkflowConfig defines workflow nodes and execution
|
||||
// WorkflowConfig defines workflows
|
||||
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"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Nodes []NodeConfig `json:"nodes"`
|
||||
Edges []EdgeConfig `json:"edges"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
||||
Triggers []TriggerConfig `json:"triggers,omitempty"`
|
||||
SubWorkflows []SubWorkflowConfig `json:"sub_workflows,omitempty"`
|
||||
JSONSchema *JSONSchemaConfig `json:"json_schema,omitempty"`
|
||||
}
|
||||
|
||||
// NodeConfig defines workflow node behavior
|
||||
// NodeConfig defines workflow nodes
|
||||
type NodeConfig struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Function string `json:"function,omitempty"`
|
||||
SubWorkflow string `json:"sub_workflow,omitempty"`
|
||||
Input map[string]interface{} `json:"input,omitempty"`
|
||||
Output map[string]interface{} `json:"output,omitempty"`
|
||||
InputMapping map[string]interface{} `json:"input_mapping,omitempty"`
|
||||
OutputMapping map[string]interface{} `json:"output_mapping,omitempty"`
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Conditions []ConditionConfig `json:"conditions,omitempty"`
|
||||
ErrorHandling *ErrorHandlingConfig `json:"error_handling,omitempty"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
Retry *RetryConfig `json:"retry,omitempty"`
|
||||
}
|
||||
|
||||
// EdgeConfig defines workflow edges
|
||||
type EdgeConfig struct {
|
||||
ID string `json:"id"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Condition string `json:"condition,omitempty"`
|
||||
Variables map[string]string `json:"variables,omitempty"`
|
||||
Transform string `json:"transform,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// ConditionConfig defines conditional logic
|
||||
type ConditionConfig struct {
|
||||
Field string `json:"field"`
|
||||
Operator string `json:"operator"`
|
||||
Value interface{} `json:"value"`
|
||||
Logic string `json:"logic,omitempty"` // "AND", "OR"
|
||||
}
|
||||
|
||||
// TriggerConfig defines workflow triggers
|
||||
type TriggerConfig struct {
|
||||
Type string `json:"type"` // "http", "cron", "event"
|
||||
Config map[string]interface{} `json:"config"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Conditions []ConditionConfig `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// SubWorkflowConfig defines sub-workflow mappings
|
||||
type SubWorkflowConfig struct {
|
||||
ID string `json:"id"`
|
||||
WorkflowID string `json:"workflow_id"`
|
||||
InputMapping map[string]interface{} `json:"input_mapping,omitempty"`
|
||||
OutputMapping map[string]interface{} `json:"output_mapping,omitempty"`
|
||||
}
|
||||
|
||||
// JSONSchemaConfig defines JSON schema validation
|
||||
type JSONSchemaConfig struct {
|
||||
Input map[string]interface{} `json:"input,omitempty"`
|
||||
Output map[string]interface{} `json:"output,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionConfig defines custom functions
|
||||
type FunctionConfig 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"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Type string `json:"type"` // "builtin", "custom", "external", "http"
|
||||
Handler string `json:"handler,omitempty"`
|
||||
Method string `json:"method,omitempty"` // For HTTP functions
|
||||
URL string `json:"url,omitempty"` // For HTTP functions
|
||||
Headers map[string]string `json:"headers,omitempty"` // For HTTP functions
|
||||
Code string `json:"code,omitempty"` // For custom code functions
|
||||
Parameters []ParameterConfig `json:"parameters,omitempty"`
|
||||
Returns []ParameterConfig `json:"returns,omitempty"`
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Conditions []ConditionConfig `json:"conditions,omitempty"`
|
||||
Retry *RetryConfig `json:"retry,omitempty"`
|
||||
Async bool `json:"async"`
|
||||
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"`
|
||||
// ParameterConfig defines function parameters
|
||||
type ParameterConfig struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Required bool `json:"required"`
|
||||
Default interface{} `json:"default,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Validation []string `json:"validation,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"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"` // "jsonschema", "custom", "regex"
|
||||
Field string `json:"field,omitempty"`
|
||||
Schema interface{} `json:"schema,omitempty"`
|
||||
Rules []ValidationRule `json:"rules,omitempty"`
|
||||
Messages map[string]string `json:"messages,omitempty"`
|
||||
StrictMode bool `json:"strict_mode"`
|
||||
AllowEmpty bool `json:"allow_empty"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
// ValidationRule defines individual validation rules
|
||||
type ValidationRule struct {
|
||||
Field string `json:"field"`
|
||||
Type string `json:"type"`
|
||||
Required bool `json:"required"`
|
||||
Min interface{} `json:"min,omitempty"`
|
||||
Max interface{} `json:"max,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
CustomRule string `json:"custom_rule,omitempty"`
|
||||
Message string `json:"message,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"`
|
||||
// Provider configuration for SMS/communication services
|
||||
type ProviderConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"` // "sms", "email", "push"
|
||||
Enabled bool `json:"enabled"`
|
||||
Priority int `json:"priority"`
|
||||
Config map[string]interface{} `json:"config"`
|
||||
Countries []string `json:"countries,omitempty"`
|
||||
RateLimit *RateLimitConfig `json:"rate_limit,omitempty"`
|
||||
Costs map[string]float64 `json:"costs,omitempty"`
|
||||
Features []string `json:"features,omitempty"`
|
||||
Reliability float64 `json:"reliability"`
|
||||
}
|
||||
|
||||
type RetryConfig struct {
|
||||
MaxAttempts int `json:"max_attempts"`
|
||||
Delay string `json:"delay"`
|
||||
BackoffType string `json:"backoff_type"` // "fixed", "exponential", "linear"
|
||||
// RateLimitConfig defines rate limiting
|
||||
type RateLimitConfig struct {
|
||||
RequestsPerSecond int `json:"requests_per_second"`
|
||||
BurstSize int `json:"burst_size"`
|
||||
WindowSize int `json:"window_size"`
|
||||
}
|
||||
|
||||
type ValidationRule struct {
|
||||
Type string `json:"type"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
Message string `json:"message"`
|
||||
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
||||
// Country configuration for routing
|
||||
type CountryConfig struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Providers []string `json:"providers"`
|
||||
DefaultRate float64 `json:"default_rate"`
|
||||
Regulations map[string]string `json:"regulations,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{}
|
||||
app *fiber.App
|
||||
workflowEngine *workflow.WorkflowEngine
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user