diff --git a/.env.example b/.env.example index 831e431..90574ee 100644 --- a/.env.example +++ b/.env.example @@ -20,10 +20,17 @@ AMQP_GLOBAL_ENABLED=false WEBHOOK_URL=https://webhook.site/2e6af2fa-6b04-497f-b4a1-13a905728d83 +# Proxy Configuration +PROXY_PROTOCOL=http +PROXY_HOST=proxy.empresa.com +PROXY_PORT=8080 +PROXY_USERNAME=usuario +PROXY_PASSWORD=senha + # Minio Configuration MINIO_ENABLED=true MINIO_ENDPOINT=localhost:9000 MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_BUCKET=evolution-media -MINIO_USE_SSL=false \ No newline at end of file +MINIO_USE_SSL=false diff --git a/docs/wiki/fundamentos/configuration.md b/docs/wiki/fundamentos/configuration.md index 35460c0..385c6d8 100644 --- a/docs/wiki/fundamentos/configuration.md +++ b/docs/wiki/fundamentos/configuration.md @@ -436,7 +436,23 @@ MINIO_REGION=us-east-1 ## Proxy -Configuração de proxy HTTP para instâncias WhatsApp. +Configuração de proxy para instâncias WhatsApp. + +O protocolo pode ser definido explicitamente com `PROXY_PROTOCOL`. +Valores suportados: +- `http` +- `https` +- `socks5` + +Se omitido, o Evolution GO tentará inferir o protocolo a partir da porta e usará `http` como padrão. + +### PROXY_PROTOCOL + +Protocolo do proxy. + +```env +PROXY_PROTOCOL=http +``` ### PROXY_HOST diff --git a/docs/wiki/guias-api/api-instances.md b/docs/wiki/guias-api/api-instances.md index 946b15f..4e88110 100644 --- a/docs/wiki/guias-api/api-instances.md +++ b/docs/wiki/guias-api/api-instances.md @@ -514,7 +514,14 @@ curl -X DELETE "http://localhost:4000/instance/delete/vendas" \ ## Configurar Proxy -Configura proxy HTTP para a instância. +Configura proxy para a instância. + +O campo `protocol` é opcional. Valores suportados: +- `http` +- `https` +- `socks5` + +Se omitido, o Evolution GO tentará inferir o protocolo a partir da porta e usará `http` como padrão. ### Endpoint ``` @@ -530,6 +537,7 @@ apikey: SUA-GLOBAL-API-KEY ### Body ```json { + "protocol": "http", "host": "proxy.exemplo.com", "port": "8080", "username": "usuario", @@ -542,6 +550,7 @@ apikey: SUA-GLOBAL-API-KEY { "message": "success", "data": { + "protocol": "http", "host": "proxy.exemplo.com", "port": "8080", "hasAuth": true @@ -555,6 +564,7 @@ curl -X POST "http://localhost:4000/instance/proxy/vendas" \ -H "Content-Type: application/json" \ -H "apikey: SUA-GLOBAL-API-KEY" \ -d '{ + "protocol": "http", "host": "proxy.exemplo.com", "port": "8080", "username": "usuario", diff --git a/docs/wiki/referencia/environment-variables.md b/docs/wiki/referencia/environment-variables.md index a1abf27..a0eff64 100644 --- a/docs/wiki/referencia/environment-variables.md +++ b/docs/wiki/referencia/environment-variables.md @@ -139,6 +139,7 @@ MINIO_REGION=us-east-1 | Variável | Descrição | |----------|-----------| +| `PROXY_PROTOCOL` | Protocolo do proxy (`http`, `https`, `socks5`) | | `PROXY_HOST` | Hostname do proxy | | `PROXY_PORT` | Porta do proxy | | `PROXY_USERNAME` | Usuário (opcional) | @@ -146,6 +147,7 @@ MINIO_REGION=us-east-1 **Exemplo:** ```env +PROXY_PROTOCOL=http PROXY_HOST=proxy.empresa.com PROXY_PORT=8080 PROXY_USERNAME=usuario diff --git a/pkg/config/config.go b/pkg/config/config.go index a7a540a..b67e5c7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -48,6 +48,7 @@ type Config struct { WhatsappVersionMinor int WhatsappVersionPatch int ProxyHost string + ProxyProtocol string ProxyPort string ProxyUsername string ProxyPassword string @@ -266,6 +267,7 @@ func Load() *Config { whatsappVersionPatch := os.Getenv(config_env.WHATSAPP_VERSION_PATCH) proxyHost := os.Getenv(config_env.PROXY_HOST) + proxyProtocol := os.Getenv(config_env.PROXY_PROTOCOL) proxyPort := os.Getenv(config_env.PROXY_PORT) proxyUsername := os.Getenv(config_env.PROXY_USERNAME) proxyPassword := os.Getenv(config_env.PROXY_PASSWORD) @@ -366,6 +368,7 @@ func Load() *Config { WhatsappVersionMinor: minor, WhatsappVersionPatch: patch, ProxyHost: proxyHost, + ProxyProtocol: proxyProtocol, ProxyPort: proxyPort, ProxyUsername: proxyUsername, ProxyPassword: proxyPassword, diff --git a/pkg/config/env/env.go b/pkg/config/env/env.go index 0fbd936..f7e0ece 100644 --- a/pkg/config/env/env.go +++ b/pkg/config/env/env.go @@ -34,6 +34,7 @@ const ( WHATSAPP_VERSION_MINOR = "WHATSAPP_VERSION_MINOR" WHATSAPP_VERSION_PATCH = "WHATSAPP_VERSION_PATCH" PROXY_HOST = "PROXY_HOST" + PROXY_PROTOCOL = "PROXY_PROTOCOL" PROXY_PORT = "PROXY_PORT" PROXY_USERNAME = "PROXY_USERNAME" PROXY_PASSWORD = "PROXY_PASSWORD" diff --git a/pkg/instance/handler/instance_handler.go b/pkg/instance/handler/instance_handler.go index ebf8406..3d7c71a 100644 --- a/pkg/instance/handler/instance_handler.go +++ b/pkg/instance/handler/instance_handler.go @@ -9,6 +9,7 @@ import ( config "github.com/EvolutionAPI/evolution-go/pkg/config" instance_model "github.com/EvolutionAPI/evolution-go/pkg/instance/model" instance_service "github.com/EvolutionAPI/evolution-go/pkg/instance/service" + "github.com/EvolutionAPI/evolution-go/pkg/utils" ) type InstanceHandler interface { @@ -449,6 +450,7 @@ func (i *instanceHandler) SetProxy(ctx *gin.Context) { } responseData := gin.H{ + "protocol": utils.NormalizeProxyProtocol(data.Protocol, data.Port), "host": data.Host, "port": data.Port, "hasAuth": data.Username != "" && data.Password != "", diff --git a/pkg/instance/service/instance_service.go b/pkg/instance/service/instance_service.go index 32d831e..25831fc 100644 --- a/pkg/instance/service/instance_service.go +++ b/pkg/instance/service/instance_service.go @@ -18,6 +18,7 @@ import ( instance_repository "github.com/EvolutionAPI/evolution-go/pkg/instance/repository" event_types "github.com/EvolutionAPI/evolution-go/pkg/internal/event_types" logger_wrapper "github.com/EvolutionAPI/evolution-go/pkg/logger" + "github.com/EvolutionAPI/evolution-go/pkg/utils" whatsmeow_service "github.com/EvolutionAPI/evolution-go/pkg/whatsmeow/service" "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/types" @@ -55,6 +56,7 @@ type instances struct { } type ProxyConfig struct { + Protocol string `json:"protocol,omitempty"` Port string `json:"port"` Password string `json:"password"` Username string `json:"username"` @@ -101,6 +103,7 @@ type PairReturnStruct struct { } type SetProxyStruct struct { + Protocol string `json:"protocol,omitempty"` Host string `json:"host" validate:"required"` Port string `json:"port" validate:"required"` Username string `json:"username"` @@ -152,6 +155,10 @@ func (i *instances) ensureClientConnected(instanceId string) (*whatsmeow.Client, } func (i instances) Create(data *CreateStruct) (*instance_model.Instance, error) { + if data.Proxy != nil { + data.Proxy.Protocol = utils.NormalizeProxyProtocol(data.Proxy.Protocol, data.Proxy.Port) + } + proxyJson, err := json.Marshal(data.Proxy) if err != nil { return nil, err @@ -562,6 +569,8 @@ func (i instances) SetProxy(id string, proxyConfig *ProxyConfig) error { return fmt.Errorf("proxy port is required") } + proxyConfig.Protocol = utils.NormalizeProxyProtocol(proxyConfig.Protocol, proxyConfig.Port) + // Convert proxy config to JSON proxyJSON, err := json.Marshal(proxyConfig) if err != nil { @@ -578,7 +587,7 @@ func (i instances) SetProxy(id string, proxyConfig *ProxyConfig) error { return err } - i.loggerWrapper.GetLogger(id).LogInfo("[%s] Proxy configuration updated: %s:%s", id, proxyConfig.Host, proxyConfig.Port) + i.loggerWrapper.GetLogger(id).LogInfo("[%s] Proxy configuration updated: %s://%s:%s", id, proxyConfig.Protocol, proxyConfig.Host, proxyConfig.Port) // Reconnect to apply proxy changes go i.Reconnect(instance) @@ -592,6 +601,7 @@ func (i instances) SetProxyFromStruct(id string, data *SetProxyStruct) error { } proxyConfig := &ProxyConfig{ + Protocol: data.Protocol, Host: data.Host, Port: data.Port, Username: data.Username, diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index cf1697f..63d42dd 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "encoding/json" "fmt" + "net" "net/http" "net/url" "runtime" @@ -253,6 +254,60 @@ func CreateSocks5Proxy(socks5Host, socks5Port, user, password string) (proxy.Dia // }, nil } +func NormalizeProxyProtocol(protocol, port string) string { + normalized := strings.ToLower(strings.TrimSpace(protocol)) + + switch normalized { + case "socks": + return "socks5" + case "http", "https", "socks5": + return normalized + } + + switch strings.TrimSpace(port) { + case "1080", "2080": + return "socks5" + } + + portNum, err := strconv.Atoi(strings.TrimSpace(port)) + if err == nil && portNum >= 42000 && portNum <= 43000 { + return "socks5" + } + + return "http" +} + +func BuildProxyAddress(protocol, host, port, user, password string) (string, error) { + if strings.TrimSpace(host) == "" { + return "", fmt.Errorf("proxy host is required") + } + + if strings.TrimSpace(port) == "" { + return "", fmt.Errorf("proxy port is required") + } + + normalizedProtocol := NormalizeProxyProtocol(protocol, port) + + if normalizedProtocol != "http" && normalizedProtocol != "https" && normalizedProtocol != "socks5" { + return "", fmt.Errorf("unsupported proxy protocol %q", protocol) + } + + proxyURL := &url.URL{ + Scheme: normalizedProtocol, + Host: net.JoinHostPort(strings.TrimSpace(host), strings.TrimSpace(port)), + } + + if user != "" { + if password != "" { + proxyURL.User = url.UserPassword(user, password) + } else { + proxyURL.User = url.User(user) + } + } + + return proxyURL.String(), nil +} + func UpdateUserInfo(values interface{}, field string, value string) interface{} { v, ok := values.(Values) if !ok { diff --git a/pkg/whatsmeow/service/whatsmeow.go b/pkg/whatsmeow/service/whatsmeow.go index 6af7174..aa0d174 100644 --- a/pkg/whatsmeow/service/whatsmeow.go +++ b/pkg/whatsmeow/service/whatsmeow.go @@ -141,6 +141,7 @@ type UserCollection struct { } type ProxyConfig struct { + Protocol string `json:"protocol,omitempty"` Host string `json:"host"` Password string `json:"password"` Port string `json:"port"` @@ -399,6 +400,7 @@ func (w whatsmeowService) StartClient(cd *ClientData) { } proxyHost := proxyConfig.Host + proxyProtocol := proxyConfig.Protocol proxyPort := proxyConfig.Port proxyUsername := proxyConfig.Username proxyPassword := proxyConfig.Password @@ -411,6 +413,10 @@ func (w whatsmeowService) StartClient(cd *ClientData) { proxyPort = w.config.ProxyPort } + if proxyConfig.Protocol == "" { + proxyProtocol = w.config.ProxyProtocol + } + if proxyConfig.Username == "" { proxyUsername = w.config.ProxyUsername } @@ -419,12 +425,16 @@ func (w whatsmeowService) StartClient(cd *ClientData) { proxyPassword = w.config.ProxyPassword } - proxy, err := utils.CreateSocks5Proxy(proxyHost, proxyPort, proxyUsername, proxyPassword) + proxyAddress, err := utils.BuildProxyAddress(proxyProtocol, proxyHost, proxyPort, proxyUsername, proxyPassword) if err != nil { w.loggerWrapper.GetLogger(cd.Instance.Id).LogWarn("[%s] Proxy error, continuing without proxy: %v", cd.Instance.Id, err) } else { - client.SetSOCKSProxy(proxy) - w.loggerWrapper.GetLogger(cd.Instance.Id).LogInfo("[%s] Proxy enabled", cd.Instance.Id) + err = client.SetProxyAddress(proxyAddress) + if err != nil { + w.loggerWrapper.GetLogger(cd.Instance.Id).LogWarn("[%s] Proxy error, continuing without proxy: %v", cd.Instance.Id, err) + } else { + w.loggerWrapper.GetLogger(cd.Instance.Id).LogInfo("[%s] Proxy enabled (%s)", cd.Instance.Id, utils.NormalizeProxyProtocol(proxyProtocol, proxyPort)) + } } } @@ -2035,7 +2045,21 @@ func (w whatsmeowService) StartInstance(instanceId string) error { } if instance.Proxy == "" && w.config.ProxyHost != "" && w.config.ProxyPort != "" && w.config.ProxyUsername != "" && w.config.ProxyPassword != "" { - instance.Proxy = fmt.Sprintf(`{"host": "%s", "port": "%s", "username": "%s", "password": "%s"}`, w.config.ProxyHost, w.config.ProxyPort, w.config.ProxyUsername, w.config.ProxyPassword) + proxyConfig := ProxyConfig{ + Protocol: utils.NormalizeProxyProtocol(w.config.ProxyProtocol, w.config.ProxyPort), + Host: w.config.ProxyHost, + Port: w.config.ProxyPort, + Username: w.config.ProxyUsername, + Password: w.config.ProxyPassword, + } + + proxyJSON, err := json.Marshal(proxyConfig) + if err != nil { + w.loggerWrapper.GetLogger(instanceId).LogError("[%s] Failed to marshal proxy config: %v", instanceId, err) + return err + } + + instance.Proxy = string(proxyJSON) err = w.instanceRepository.UpdateProxy(instance.Id, instance.Proxy) if err != nil {