mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-24 17:28: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:
@@ -2,7 +2,11 @@ package route
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -17,6 +21,7 @@ import (
|
||||
netutils "github.com/yusing/godoxy/internal/net"
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
"github.com/yusing/godoxy/internal/proxmox"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
@@ -25,6 +30,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
rulepresets "github.com/yusing/godoxy/internal/route/rules/presets"
|
||||
route "github.com/yusing/godoxy/internal/route/types"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
)
|
||||
@@ -41,7 +47,8 @@ type (
|
||||
|
||||
route.HTTPConfig
|
||||
PathPatterns []string `json:"path_patterns,omitempty" extensions:"x-nullable"`
|
||||
Rules rules.Rules `json:"rules,omitempty" validate:"omitempty,unique=Name" extension:"x-nullable"`
|
||||
Rules rules.Rules `json:"rules,omitempty" extension:"x-nullable"`
|
||||
RuleFile string `json:"rule_file,omitempty" extensions:"x-nullable"`
|
||||
HealthCheck *types.HealthCheckConfig `json:"healthcheck"`
|
||||
LoadBalance *types.LoadBalancerConfig `json:"load_balance,omitempty" extensions:"x-nullable"`
|
||||
Middlewares map[string]types.LabelMap `json:"middlewares,omitempty" extensions:"x-nullable"`
|
||||
@@ -212,7 +219,10 @@ func (r *Route) Validate() gperr.Error {
|
||||
}
|
||||
}
|
||||
|
||||
errs := gperr.NewBuilder("entry validation failed")
|
||||
var errs gperr.Builder
|
||||
if err := r.validateRules(); err != nil {
|
||||
errs.Add(err)
|
||||
}
|
||||
|
||||
var impl types.Route
|
||||
var err gperr.Error
|
||||
@@ -267,6 +277,39 @@ func (r *Route) Validate() gperr.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Route) validateRules() error {
|
||||
if r.RuleFile != "" && len(r.Rules) > 0 {
|
||||
return errors.New("`rule_file` and `rules` cannot be used together")
|
||||
} else if r.RuleFile != "" {
|
||||
src, err := url.Parse(r.RuleFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse rule file url %q: %w", r.RuleFile, err)
|
||||
}
|
||||
switch src.Scheme {
|
||||
case "embed": // embed://<preset_file_name>
|
||||
rules, ok := rulepresets.GetRulePreset(src.Host)
|
||||
if !ok {
|
||||
return fmt.Errorf("rule preset %q not found", src.Host)
|
||||
} else {
|
||||
r.Rules = rules
|
||||
}
|
||||
case "file", "":
|
||||
content, err := os.ReadFile(src.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read rule file %q: %w", src.Path, err)
|
||||
} else {
|
||||
_, err = serialization.ConvertString(string(content), reflect.ValueOf(&r.Rules))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal rule file %q: %w", src.Path, err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported rule file scheme %q", src.Scheme)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Route) Impl() types.Route {
|
||||
return r.impl
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user