Files
Proxmox-Coolify-Manager/docs/casos/evolution-go-stack.md
T

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.