feat: add pass_headers op, grouped presets (incl. Gemini 4K), and robust JSON fallback

This commit is contained in:
Seefs
2026-02-22 17:16:57 +08:00
parent 11b0788b68
commit 303fff44e7
3 changed files with 2002 additions and 412 deletions

View File

@@ -34,7 +34,7 @@ type ConditionOperation struct {
type ParamOperation struct {
Path string `json:"path"`
Mode string `json:"mode"` // delete, set, move, copy, prepend, append, trim_prefix, trim_suffix, ensure_prefix, ensure_suffix, trim_space, to_lower, to_upper, replace, regex_replace, return_error, prune_objects, set_header, delete_header, copy_header, move_header, sync_fields
Mode string `json:"mode"` // delete, set, move, copy, prepend, append, trim_prefix, trim_suffix, ensure_prefix, ensure_suffix, trim_space, to_lower, to_upper, replace, regex_replace, return_error, prune_objects, set_header, delete_header, copy_header, move_header, pass_headers, sync_fields
Value interface{} `json:"value"`
KeepOrigin bool `json:"keep_origin"`
From string `json:"from,omitempty"`
@@ -494,6 +494,19 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
if err == nil {
contextJSON, err = marshalContextJSON(context)
}
case "pass_headers":
headerNames, parseErr := parseHeaderPassThroughNames(op.Value)
if parseErr != nil {
return "", parseErr
}
for _, headerName := range headerNames {
if err = copyHeaderInContext(context, headerName, headerName, op.KeepOrigin); err != nil {
break
}
}
if err == nil {
contextJSON, err = marshalContextJSON(context)
}
case "sync_fields":
result, err = syncFieldsBetweenTargets(result, context, op.From, op.To)
if err == nil {
@@ -678,6 +691,80 @@ func deleteHeaderOverrideInContext(context map[string]interface{}, headerName st
return nil
}
func parseHeaderPassThroughNames(value interface{}) ([]string, error) {
normalizeNames := func(values []string) []string {
names := lo.FilterMap(values, func(item string, _ int) (string, bool) {
headerName := strings.TrimSpace(item)
if headerName == "" {
return "", false
}
return headerName, true
})
return lo.Uniq(names)
}
switch raw := value.(type) {
case nil:
return nil, fmt.Errorf("pass_headers value is required")
case string:
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
return nil, fmt.Errorf("pass_headers value is required")
}
if strings.HasPrefix(trimmed, "[") || strings.HasPrefix(trimmed, "{") {
var parsed interface{}
if err := common.UnmarshalJsonStr(trimmed, &parsed); err == nil {
return parseHeaderPassThroughNames(parsed)
}
}
names := normalizeNames(strings.Split(trimmed, ","))
if len(names) == 0 {
return nil, fmt.Errorf("pass_headers value is invalid")
}
return names, nil
case []interface{}:
names := lo.FilterMap(raw, func(item interface{}, _ int) (string, bool) {
headerName := strings.TrimSpace(fmt.Sprintf("%v", item))
if headerName == "" {
return "", false
}
return headerName, true
})
names = lo.Uniq(names)
if len(names) == 0 {
return nil, fmt.Errorf("pass_headers value is invalid")
}
return names, nil
case map[string]interface{}:
candidates := make([]string, 0, 8)
if headersRaw, ok := raw["headers"]; ok {
names, err := parseHeaderPassThroughNames(headersRaw)
if err == nil {
candidates = append(candidates, names...)
}
}
if namesRaw, ok := raw["names"]; ok {
names, err := parseHeaderPassThroughNames(namesRaw)
if err == nil {
candidates = append(candidates, names...)
}
}
if headerRaw, ok := raw["header"]; ok {
names, err := parseHeaderPassThroughNames(headerRaw)
if err == nil {
candidates = append(candidates, names...)
}
}
names := normalizeNames(candidates)
if len(names) == 0 {
return nil, fmt.Errorf("pass_headers value is invalid")
}
return names, nil
default:
return nil, fmt.Errorf("pass_headers value must be string, array or object")
}
}
type syncTarget struct {
kind string
key string