181 lines
9.0 KiB
Markdown
181 lines
9.0 KiB
Markdown
# Caso: evolution-go (API WhatsApp en Go) desplegado junto a Chatwoot
|
|
|
|
> Resuelto el **2026-09-02** contra el host real.
|
|
> Target: **LXC 102** (`coolify`), proyecto `AI AGENCY` / `production`.
|
|
> Resultado: `https://evo.urieljareth.org/server/ok` → **HTTP 200**
|
|
> (`{"status":"ok"}`), Manager UI operativo, contenedores `healthy`.
|
|
> **Licencia ACTIVA desde el 2026-09-02** (vía OAuth Google del portal; ver §3.0).
|
|
|
|
---
|
|
|
|
## 0. Resumen ejecutivo
|
|
|
|
El usuario pidió clonar
|
|
[evolution-foundation/evolution-go](https://github.com/evolution-foundation/evolution-go)
|
|
(rewritten en Go de Evolution API, motor WhatsApp sobre whatsmeow) e
|
|
"implementar la funcionalidad" en el servicio Chatwoot
|
|
(`/project/cho4d488omzjm4noyz98mwq7/environment/xhcy6urwmtk0onnxoeec24hq/service/c11xzy2tx2cdapm32f5b89vy`).
|
|
|
|
**Decisión:** el stack se desplegó como **servicio hermano** (`evolution-go`,
|
|
uuid `j0jkacsfcgypm2jmpillls01`) en el **mismo proyecto y entorno** que Chatwoot,
|
|
no dentro del compose de Chatwoot. Motivos:
|
|
|
|
- Editar el compose del stack Chatwoot fuerza un redeploy completo de Chatwoot
|
|
(riesgo sobre un stack parcheado a mano — ver
|
|
[chatwoot-enterprise-patch.md](chatwoot-enterprise-patch.md)).
|
|
- Evolution-go necesita su propio PostgreSQL; meterle una DB ajena al stack de
|
|
Chatwoot complica el rollback.
|
|
- La integración WhatsApp→Chatwoot se hace por webhook/API hacia el FQDN público
|
|
de Chatwoot — no requiere red compartida.
|
|
|
|
El clon local vive en `projects/evolution-go` (tag 0.7.2). El compose de
|
|
despliegue vive versionado en [`stacks/evolution-go/docker-compose.coolify.yml`](../../stacks/evolution-go/docker-compose.coolify.yml).
|
|
|
|
| Pieza | Valor |
|
|
|---|---|
|
|
| Servicio Coolify | `evolution-go` (`j0jkacsfcgypm2jmpillls01`) |
|
|
| Proyecto / entorno | `AI AGENCY` / `production` (mismo que Chatwoot) |
|
|
| Imagen app | `evoapicloud/evolution-go:0.7.2` (pinada, Docker Hub) |
|
|
| Imagen DB | `postgres:16-alpine` (hermana, `evolution-postgres`) |
|
|
| FQDN | `https://evo.urieljareth.org` (wildcard del túnel, sin cambios CF) |
|
|
| Healthcheck | `wget http://127.0.0.1:8080/server/ok` (200 sin licencia) |
|
|
| Secretos | `GLOBAL_API_KEY` (40 car.) y `POSTGRES_PASSWORD` (24 car.) como envs del servicio, generados en memoria |
|
|
|
|
---
|
|
|
|
## 1. Bugs y trampas encontrados (lo que costó tiempo)
|
|
|
|
### 1.1 `POSTGRES_AUTH_DB` vacía = panic (bug upstream 0.7.2)
|
|
|
|
El primer arranque crash-loopeaba (exit 2). Stack trace:
|
|
`NewPollService → autoMigrate` sobre un `*sql.DB` **nil**.
|
|
|
|
Cadena exacta en el código:
|
|
|
|
- `cmd/evolution-go/main.go:300` — `initPostgresAuthDB()` devuelve `(nil, nil)`
|
|
cuando `POSTGRES_AUTH_DB == ""`: **sin error**.
|
|
- `main.go:408` pasa ese nil a `setupRouter`.
|
|
- `pkg/poll/service/poll_service.go:39` — `autoMigrate` dereferencia el nil → panic.
|
|
|
|
Es decir: la variable **parece opcional** (README no la marca obligatoria) pero
|
|
sin ella el binario muere en loop. El fix fue setearla como URI completa:
|
|
`postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@evolution-postgres:5432/evogo_auth?sslmode=disable`
|
|
(interpolada por Coolify al deploy, el secreto no vive en el compose).
|
|
|
|
### 1.2 Los nombres de env del README están desactualizados
|
|
|
|
`docker/examples/docker-compose.yml` y el README usan `WADEBUG`/`LOGTYPE`, pero
|
|
el código 0.7.2 (`pkg/config/env/env.go`) lee **`DEBUG_ENABLED`** y
|
|
**`LOG_TYPE`**. Además `DATABASE_SAVE_MESSAGES` es obligatoria no-vacía
|
|
(`panicIfEmpty`) aunque parezca opcional. La fuente de verdad es `env.go`.
|
|
|
|
### 1.3 `PATCH /services/{uuid}` rechaza campos de creación
|
|
|
|
`New-CoolifyService.ps1` en su flujo de actualización (`-ServiceUuid`) enviaba
|
|
`project_uuid`/`environment_name`/`server_uuid` y la API responde
|
|
**422 "This field is not allowed"** para los tres. Solo acepta `name`,
|
|
`docker_compose_raw` y `urls`. Corregido en el script el 2026-09-02 (el caso
|
|
firecrawl solo ejercitó la creación, no la actualización).
|
|
|
|
### 1.4 La licencia bloquea la API — pero no al Manager
|
|
|
|
`GateMiddleware` (`pkg/core/c0.go:638`) devuelve **503 `LICENSE_REQUIRED`** en
|
|
todo hasta activar licencia, con excepciones: `/server/ok`, `/health`,
|
|
`/manager*`, `/assets*`, `/license/*`, `/swagger*`, `/ws`. Esto es crítico para
|
|
el healthcheck: como Traefik solo enruta contenedores `healthy` (ver
|
|
[el caso del 503](coolify-servicio-nuevo-503-no-available-server.md)), un
|
|
healthcheck contra cualquier endpoint bloqueado habría dejado al Manager
|
|
**inalcanzable** — deadlock imposible de activar. `/server/ok` responde 200
|
|
siempre.
|
|
|
|
### 1.5 No se puede montar `init-db.sql` por ruta
|
|
|
|
El compose de upstream monta `./init-db.sql` en el postgres. En Coolify el
|
|
compose se guarda como `docker_compose_raw` en la DB — no hay árbol de archivos.
|
|
No hace falta: `ensureDBExists()` (`pkg/config/config.go:79`) crea las DBs del
|
|
DSN al arrancar (evogo_auth, evogo_users).
|
|
|
|
---
|
|
|
|
## 2. Cómo se reproduce
|
|
|
|
```powershell
|
|
. .\.env.local.ps1
|
|
|
|
# 1. Crear (sin arrancar)
|
|
.\deploy_skill\scripts\New-CoolifyService.ps1 `
|
|
-AppPath .\stacks\evolution-go `
|
|
-AppName evolution-go `
|
|
-Fqdn https://evo.urieljareth.org `
|
|
-PrimaryService evolution-go `
|
|
-ProjectName "AI AGENCY" -EnvironmentName production -NoDeploy -Force
|
|
|
|
# 2. Secretos (POST /envs da 409 con vars ya sembradas: usar bulk PATCH;
|
|
# generados en memoria, nunca en disco)
|
|
# PATCH /services/j0jkacsfcgypm2jmpillls01/envs/bulk
|
|
# data: [{POSTGRES_PASSWORD}, {GLOBAL_API_KEY}] con is_literal
|
|
|
|
# 3. Arrancar y esperar (primer boot: minutos)
|
|
Invoke-CoolifyApi.ps1 -Method POST -Path "/services/j0jkacsfcgypm2jmpillls01/start"
|
|
.\coolify_skill\scripts\Test-CoolifyServiceReady.ps1 -Uuid j0jkacsfcgypm2jmpillls01 -WaitSeconds 600
|
|
|
|
# 4. Verificación funcional
|
|
curl.exe -sS https://evo.urieljareth.org/server/ok # {"status":"ok"}
|
|
curl.exe -sSI https://evo.urieljareth.org/manager/login # 200
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Qué queda pendiente del lado del usuario (manual)
|
|
|
|
0. **Si el magic link del portal dice "ya usado o expiró"**: `GET /license/register`
|
|
**cachea en memoria** la primera sesión de registro (`rc._v8` en
|
|
`pkg/core/c0.go:710`) y devuelve siempre la misma `register_url` aunque el
|
|
portal ya la haya invalidado (link consumido por preview del cliente de
|
|
correo, doble click, o expirado). Fix verificado: `docker restart` del
|
|
contenedor app → siguiente `/license/register` pide sesión nueva al portal.
|
|
Después: abrir el magic link **una sola vez y directamente** (los previews
|
|
de Outlook/Gmail consumen links single-use sin que los abras). Mejor aún:
|
|
la página del portal ofrece **OAuth con Google/GitHub**, que evita el magic
|
|
link por completo (verificado 2026-09-02: la sesión sobrevive aunque el
|
|
magic link muera; tras el OAuth queda un `code` que se canjea con
|
|
`GET /license/activate?code=...`).
|
|
|
|
**Resultado final:** el magic link falló 3/3 (el cliente de correo del
|
|
usuario consumía el link single-use antes que el navegador — el portal
|
|
respondía `authorization code expired or already used` al canjear). La
|
|
activación se completó así: `docker restart` (limpia la sesión cacheada) →
|
|
iniciar el registro **desde el Manager** (para que mande su `redirect_uri`
|
|
y la vuelta sea automática) → en el portal, botón **Entrar com Google** →
|
|
redirección de vuelta al Manager → `/license/status` = `active`.
|
|
|
|
1. **Activar la licencia** (requiere cuenta en Evolution Foundation):
|
|
abrir `https://evo.urieljareth.org/manager/login`, entrar con la API URL
|
|
(`https://evo.urieljareth.org`) y la `GLOBAL_API_KEY` — visible en Coolify:
|
|
proyecto AI AGENCY → servicio evolution-go → pestaña Environment. Hasta
|
|
entonces toda la API responde 503 `LICENSE_REQUIRED` (el Manager sí funciona).
|
|
Alternativa por API: `GET /license/register` devuelve la URL de registro.
|
|
2. **Conectar WhatsApp**: desde el Manager crear una instancia → escanear el QR
|
|
(`POST /instance/create`, `GET /instance/qr` con header `apikey`). Las
|
|
sesiones persisten en el volumen `evolution-data` (`/app/dbdata`).
|
|
3. **Enlazar con Chatwoot**: evolution-go **no trae** integración Chatwoot
|
|
nativa (cero menciones en el código, a diferencia de evolution-api Node). El
|
|
puente sería por webhook (`WEBHOOK_URL`) hacia un inbox tipo API de Chatwoot,
|
|
o usar el servicio evolution-api Node viejo que sí la tiene.
|
|
|
|
## 4. Notas de estado
|
|
|
|
- El servicio viejo `evolution-api` (`q6tnsvkvrjw4g0ab532l3r1s`, imagen Node
|
|
`evoapicloud/evolution-api:v2.3.7`, mismo entorno) **está en producción en
|
|
https://evoapi.urieljareth.org** (api/postgres/redis, 13+ días healthy; el
|
|
contenedor app se llama `api-q6tn...`, no `evolution-api-...` — cuidado con
|
|
los greps). Tiene 4 instancias WhatsApp (Personal, JM, INSTA, Asesoria
|
|
Personal) sin integración Chatwoot configurada. NOTA 2026-09-02: este repo
|
|
documentó erróneamente "app detenida" por un filtro truncado de
|
|
`Get-CoolifyDockerStatus`; corregido tras verificación directa.
|
|
- El compose normalizado por Coolify borra los comentarios; la versión
|
|
documentada es la del repo (`stacks/evolution-go/`).
|
|
- Imagen pinada a `0.7.2` (tag del repo clonado). Para subir de versión:
|
|
cambiar el tag, repasar `pkg/config/env/env.go` del nuevo tag (los nombres de
|
|
variables cambian entre versiones) y PATCH + start de nuevo.
|