Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
MINIO_USE_SSL=false
18 changes: 17 additions & 1 deletion docs/wiki/fundamentos/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
12 changes: 11 additions & 1 deletion docs/wiki/guias-api/api-instances.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand All @@ -530,6 +537,7 @@ apikey: SUA-GLOBAL-API-KEY
### Body
```json
{
"protocol": "http",
"host": "proxy.exemplo.com",
"port": "8080",
"username": "usuario",
Expand All @@ -542,6 +550,7 @@ apikey: SUA-GLOBAL-API-KEY
{
"message": "success",
"data": {
"protocol": "http",
"host": "proxy.exemplo.com",
"port": "8080",
"hasAuth": true
Expand All @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions docs/wiki/referencia/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,15 @@ 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) |
| `PROXY_PASSWORD` | Senha (opcional) |

**Exemplo:**
```env
PROXY_PROTOCOL=http
PROXY_HOST=proxy.empresa.com
PROXY_PORT=8080
PROXY_USERNAME=usuario
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type Config struct {
WhatsappVersionMinor int
WhatsappVersionPatch int
ProxyHost string
ProxyProtocol string
ProxyPort string
ProxyUsername string
ProxyPassword string
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -366,6 +368,7 @@ func Load() *Config {
WhatsappVersionMinor: minor,
WhatsappVersionPatch: patch,
ProxyHost: proxyHost,
ProxyProtocol: proxyProtocol,
ProxyPort: proxyPort,
ProxyUsername: proxyUsername,
ProxyPassword: proxyPassword,
Expand Down
1 change: 1 addition & 0 deletions pkg/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions pkg/instance/handler/instance_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 != "",
Expand Down
12 changes: 11 additions & 1 deletion pkg/instance/service/instance_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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,
Expand Down
55 changes: 55 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package utils
import (
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"runtime"
Expand Down Expand Up @@ -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 {
Expand Down
32 changes: 28 additions & 4 deletions pkg/whatsmeow/service/whatsmeow.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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))
}
}
}

Expand Down Expand Up @@ -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 {
Expand Down