mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-24 01:08:31 +02:00
feat(rules): add post-request rules system with response manipulation (#160)
* Add comprehensive post-request rules support for response phase * Enable response body, status, and header manipulation via set commands * Refactor command handlers to support both request and response phases * Implement response modifier system for post-request template execution * Support response-based rule matching with status and header checks * Add comprehensive benchmarks for matcher performance * Refactor authentication and proxying commands for unified error handling * Support negated conditions with ! * Enhance error handling, error formatting and validation * Routes: add `rule_file` field with rule preset support * Environment variable substitution: now supports variables without `GODOXY_` prefix * new conditions: * `on resp_header <key> [<value>]` * `on status <status>` * new commands: * `require_auth` * `set resp_header <key> <template>` * `set resp_body <template>` * `set status <code>` * `log <level> <path> <template>` * `notify <level> <provider> <title_template> <body_template>`
This commit is contained in:
@@ -1,40 +1,134 @@
|
||||
package rules
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/strings/ansi"
|
||||
)
|
||||
|
||||
type Help struct {
|
||||
command string
|
||||
description string
|
||||
description []string
|
||||
args map[string]string // args[arg] -> description
|
||||
}
|
||||
|
||||
/*
|
||||
Generate help string, e.g.
|
||||
func makeLines(lines ...string) []string {
|
||||
return lines
|
||||
}
|
||||
|
||||
rewrite <from> <to>
|
||||
from: the path to rewrite, must start with /
|
||||
to: the path to rewrite to, must start with /
|
||||
*/
|
||||
func (h *Help) String() string {
|
||||
func helpExample(cmd string, args ...string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(h.command)
|
||||
sb.WriteString(" ")
|
||||
for arg := range h.args {
|
||||
sb.WriteString(strings.ToUpper(arg))
|
||||
sb.WriteRune(' ')
|
||||
}
|
||||
if h.description != "" {
|
||||
sb.WriteString("\n\t")
|
||||
sb.WriteString(h.description)
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
sb.WriteRune('\n')
|
||||
for arg, desc := range h.args {
|
||||
sb.WriteRune('\t')
|
||||
sb.WriteString(strings.ToUpper(arg))
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(desc)
|
||||
sb.WriteRune('\n')
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(ansi.WithANSI(cmd, ansi.HighlightGreen))
|
||||
for _, arg := range args {
|
||||
var out strings.Builder
|
||||
pos := 0
|
||||
for {
|
||||
start := strings.Index(arg[pos:], "{{")
|
||||
if start == -1 {
|
||||
if pos < len(arg) {
|
||||
// If no template at all (pos == 0), cyan highlight for whole-arg
|
||||
// Otherwise, for mixed strings containing templates, leave non-template text unhighlighted
|
||||
if pos == 0 {
|
||||
out.WriteString(ansi.WithANSI(arg[pos:], ansi.HighlightCyan))
|
||||
} else {
|
||||
out.WriteString(arg[pos:])
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
start += pos
|
||||
if start > pos {
|
||||
// Non-template text should not be highlighted
|
||||
out.WriteString(arg[pos:start])
|
||||
}
|
||||
end := strings.Index(arg[start+2:], "}}")
|
||||
if end == -1 {
|
||||
// Unmatched template start; write remainder without highlighting
|
||||
out.WriteString(arg[start:])
|
||||
break
|
||||
}
|
||||
end += start + 2
|
||||
inner := strings.TrimSpace(arg[start+2 : end])
|
||||
parts := strings.Split(inner, ".")
|
||||
out.WriteString(helpTemplateVar(parts...))
|
||||
pos = end + 2
|
||||
}
|
||||
fmt.Fprintf(&sb, ` "%s"`, out.String())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func helpListItem(key string, value string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(ansi.WithANSI(key, ansi.HighlightYellow))
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(value)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// helpFuncCall generates a string like "fn(arg1, arg2, arg3)"
|
||||
func helpFuncCall(fn string, args ...string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(ansi.WithANSI(fn, ansi.HighlightRed))
|
||||
sb.WriteString("(")
|
||||
for i, arg := range args {
|
||||
fmt.Fprintf(&sb, `"%s"`, ansi.WithANSI(arg, ansi.HighlightCyan))
|
||||
if i < len(args)-1 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
sb.WriteString(")")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// helpTemplateVar generates a string like "{{ .Request.Method }} {{ .Request.URL.Path }}"
|
||||
func helpTemplateVar(parts ...string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(ansi.WithANSI("{{ ", ansi.HighlightWhite))
|
||||
for i, part := range parts {
|
||||
sb.WriteString(ansi.WithANSI(part, ansi.HighlightCyan))
|
||||
if i < len(parts)-1 {
|
||||
sb.WriteString(".")
|
||||
}
|
||||
}
|
||||
sb.WriteString(ansi.WithANSI(" }}", ansi.HighlightWhite))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
/*
|
||||
Generate help string as error, e.g.
|
||||
|
||||
rewrite <from> <to>
|
||||
from: the path to rewrite, must start with /
|
||||
to: the path to rewrite to, must start with /
|
||||
*/
|
||||
func (h *Help) Error() gperr.Error {
|
||||
var lines gperr.MultilineError
|
||||
|
||||
lines.Adds(ansi.WithANSI(h.command, ansi.HighlightGreen))
|
||||
lines.AddStrings(h.description...)
|
||||
lines.Adds(" args:")
|
||||
|
||||
argKeys := make([]string, 0, len(h.args))
|
||||
longestArg := 0
|
||||
for arg := range h.args {
|
||||
if len(arg) > longestArg {
|
||||
longestArg = len(arg)
|
||||
}
|
||||
argKeys = append(argKeys, arg)
|
||||
}
|
||||
|
||||
// sort argKeys alphabetically to make output stable
|
||||
slices.Sort(argKeys)
|
||||
for _, arg := range argKeys {
|
||||
desc := h.args[arg]
|
||||
lines.Addf(" %-"+strconv.Itoa(longestArg)+"s: %s", ansi.WithANSI(arg, ansi.HighlightCyan), desc)
|
||||
}
|
||||
return &lines
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user