Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped
184 lines
4.9 KiB
Go
184 lines
4.9 KiB
Go
package generator
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ProviderHealthDefinition captures optional health-check settings for providers.
|
|
type ProviderHealthDefinition struct {
|
|
Enable *bool
|
|
URL string
|
|
Interval *int
|
|
Lazy *bool
|
|
Tolerance *int
|
|
Timeout *int
|
|
Method string
|
|
Headers string
|
|
}
|
|
|
|
// ProviderDefinition represents a parsed proxy provider entry.
|
|
type ProviderDefinition struct {
|
|
Name string
|
|
Type string
|
|
URL string
|
|
Path string
|
|
Interval *int
|
|
Flags map[string]bool
|
|
Fields map[string]string
|
|
Health *ProviderHealthDefinition
|
|
}
|
|
|
|
// ParseProviderDefinition converts an encoded provider definition (query string style)
|
|
// into a structured ProviderDefinition instance.
|
|
func ParseProviderDefinition(definition string) (*ProviderDefinition, error) {
|
|
values, err := url.ParseQuery(definition)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid provider definition: %v", err)
|
|
}
|
|
|
|
name := strings.TrimSpace(values.Get("name"))
|
|
if name == "" {
|
|
return nil, fmt.Errorf("custom provider missing name")
|
|
}
|
|
|
|
providerType := strings.TrimSpace(values.Get("type"))
|
|
if providerType == "" {
|
|
providerType = "http"
|
|
}
|
|
|
|
urlValue := strings.TrimSpace(values.Get("url"))
|
|
if urlValue == "" {
|
|
return nil, fmt.Errorf("custom provider %s missing url", name)
|
|
}
|
|
|
|
pathValue := strings.TrimSpace(values.Get("path"))
|
|
if pathValue == "" {
|
|
return nil, fmt.Errorf("custom provider %s missing path", name)
|
|
}
|
|
|
|
def := &ProviderDefinition{
|
|
Name: name,
|
|
Type: providerType,
|
|
URL: urlValue,
|
|
Path: pathValue,
|
|
Flags: make(map[string]bool),
|
|
Fields: make(map[string]string),
|
|
}
|
|
|
|
if raw := strings.TrimSpace(values.Get("interval")); raw != "" {
|
|
if n, err := strconv.Atoi(raw); err == nil && n >= 0 {
|
|
def.Interval = &n
|
|
} else {
|
|
return nil, fmt.Errorf("invalid interval for provider %s: %v", name, err)
|
|
}
|
|
}
|
|
|
|
boolFields := []struct {
|
|
Query string
|
|
Key string
|
|
}{
|
|
{"lazy", "lazy"},
|
|
{"skip_cert_verify", "skip-cert-verify"},
|
|
{"override", "override"},
|
|
{"failover", "failover"},
|
|
}
|
|
|
|
for _, field := range boolFields {
|
|
if raw := strings.TrimSpace(values.Get(field.Query)); raw != "" {
|
|
val, err := strconv.ParseBool(raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid %s for provider %s: %v", field.Query, name, err)
|
|
}
|
|
def.Flags[field.Key] = val
|
|
}
|
|
}
|
|
|
|
stringFields := []string{"header", "headers", "filter", "format", "path_backup"}
|
|
for _, key := range stringFields {
|
|
if raw := strings.TrimSpace(values.Get(key)); raw != "" {
|
|
def.Fields[strings.ReplaceAll(key, "_", "-")] = raw
|
|
}
|
|
}
|
|
|
|
health := &ProviderHealthDefinition{}
|
|
setHealth := false
|
|
if raw := strings.TrimSpace(values.Get("health_enable")); raw != "" {
|
|
val, err := strconv.ParseBool(raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid health_enable for provider %s: %v", name, err)
|
|
}
|
|
health.Enable = &val
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_url")); raw != "" {
|
|
health.URL = raw
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_interval")); raw != "" {
|
|
if n, err := strconv.Atoi(raw); err == nil && n >= 0 {
|
|
health.Interval = &n
|
|
} else {
|
|
return nil, fmt.Errorf("invalid health_interval for provider %s: %v", name, err)
|
|
}
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_lazy")); raw != "" {
|
|
val, err := strconv.ParseBool(raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid health_lazy for provider %s: %v", name, err)
|
|
}
|
|
health.Lazy = &val
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_tolerance")); raw != "" {
|
|
if n, err := strconv.Atoi(raw); err == nil && n >= 0 {
|
|
health.Tolerance = &n
|
|
} else {
|
|
return nil, fmt.Errorf("invalid health_tolerance for provider %s: %v", name, err)
|
|
}
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_timeout")); raw != "" {
|
|
if n, err := strconv.Atoi(raw); err == nil && n >= 0 {
|
|
health.Timeout = &n
|
|
} else {
|
|
return nil, fmt.Errorf("invalid health_timeout for provider %s: %v", name, err)
|
|
}
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_method")); raw != "" {
|
|
health.Method = raw
|
|
setHealth = true
|
|
}
|
|
if raw := strings.TrimSpace(values.Get("health_headers")); raw != "" {
|
|
health.Headers = raw
|
|
setHealth = true
|
|
}
|
|
if setHealth {
|
|
def.Health = health
|
|
}
|
|
|
|
// Capture any remaining custom fields not explicitly handled.
|
|
for key, vals := range values {
|
|
if key == "name" || key == "type" || key == "url" || key == "path" || key == "interval" {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(key, "health_") || key == "lazy" || key == "skip_cert_verify" || key == "override" || key == "failover" || key == "header" || key == "headers" || key == "filter" || key == "format" || key == "path_backup" {
|
|
continue
|
|
}
|
|
if len(vals) == 0 {
|
|
continue
|
|
}
|
|
value := strings.TrimSpace(vals[len(vals)-1])
|
|
if value == "" {
|
|
continue
|
|
}
|
|
def.Fields[strings.ReplaceAll(key, "_", "-")] = value
|
|
}
|
|
|
|
return def, nil
|
|
}
|