Agrega scripts y runbooks: Cloudflare API, autostart Coolify, parche Chatwoot, deploy Solo Leveling + docs de casos
- scripts/Invoke-CloudflareApi.ps1: control de tunnel/DNS via API - scripts/Install-CoolifyAutostart.ps1 + scripts/host/: autostart de LXC 102 + tunnel tras corte - scripts/Apply-ChatwootEnterprisePatch.ps1: parche enterprise (autodetecta creds) - Deploy-SoloLeveling.ps1: deploy build-on-server verificado - docs/runbooks/cloudflare-tunnel.md y autostart-coolify.md - docs/casos/chatwoot-enterprise-patch.md (credenciales redactadas) - docs/AGENTS-coolify-apps.md + issues y reportes - deploy_skill/references/coolify-4.1.2-notes.md: hallazgos verificados (seccion 7) - package.json para verify-online.mjs del coolify-deploy skill - .gitignore: excluye .claude/ y .opencode/ (config local de herramientas)
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
# Caso: Parche enterprise en Chatwoot (Coolify + LXC 102)
|
||||
|
||||
> Documentacion de caso verificada el 2026-06-16 desde esta maquina.
|
||||
> Dominio: `https://chatwoot-c11xzy2tx2cdapm32f5b89vy.urieljareth.org`
|
||||
|
||||
## 0. Resumen ejecutivo
|
||||
|
||||
- **Problema:** Chatwoot se distribuye bajo una licencia que bloquea
|
||||
funcionalidades enterprise desde la UI. Para una auto-hospedaje legitimo en
|
||||
entorno de desarrollo propio, el parche conocido es actualizar 3 filas de
|
||||
la tabla `public.installation_configs` en la base de datos Postgres.
|
||||
- **Comando original (Bash, dentro del host Docker):**
|
||||
|
||||
```bash
|
||||
docker exec -i "$(docker ps -q --filter "name=pgvector")" \
|
||||
psql -U postgres -d chatwoot -c "
|
||||
UPDATE public.installation_configs
|
||||
SET serialized_value = '\"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nvalue: enterprise\n\"'
|
||||
WHERE name = 'INSTALLATION_PRICING_PLAN';
|
||||
|
||||
UPDATE public.installation_configs
|
||||
SET serialized_value = '\"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nvalue: 10000\n\"'
|
||||
WHERE name = 'INSTALLATION_PRICING_PLAN_QUANTITY';
|
||||
|
||||
UPDATE public.installation_configs
|
||||
SET serialized_value = '\"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nvalue: e04t63ee-5gg8-4b94-8914-ed8137a7d938\n\"'
|
||||
WHERE name = 'INSTALLATION_IDENTIFIER';"
|
||||
```
|
||||
|
||||
- **Criterio de exito:** psql imprime exactamente **3 lineas `UPDATE 1`**,
|
||||
una por sentencia. Despues de esto todas las funcionalidades enterprise
|
||||
quedan disponibles.
|
||||
- **Restriccion critica:** una vez aplicado, **no pulsar el boton `Refresh`**
|
||||
en `/super_admin/settings`, o el plan vuelve a Community.
|
||||
- **Stack local:** Proxmox `192.168.0.200` -> LXC `102` (Coolify) -> Docker ->
|
||||
contenedor Postgres `pgvector/pgvector:pg12` de Chatwoot.
|
||||
|
||||
## 1. Equivalencia entre el comando original y este entorno
|
||||
|
||||
| Capa | Comando original | Este entorno |
|
||||
|---|---|---|
|
||||
| Host | Docker daemon local | Proxmox VE `192.168.0.200` |
|
||||
| Ejecucion Docker | `docker exec` directo | `pct exec 102 --` + `docker exec` |
|
||||
| Usuario SSH | n/a | `root@192.168.0.200` con `~/.openclaw/workspace/proxmox_key_win` |
|
||||
| Contenedor | filtro `name=pgvector` | nombre real: `postgres-c11xzy2tx2cdapm32f5b89vy` |
|
||||
| DB user / db | `-U postgres -d chatwoot` | `-U <POSTGRES_USER> -d <POSTGRES_DB>` autodetectados (imagen `pgvector/pgvector:pg12` **no crea rol `postgres`**) |
|
||||
|
||||
El identificador `c11xzy2tx2cdapm32f5b89vy` es el UUID que Coolify asigna al
|
||||
recurso; aparece como prefijo del FQDN y de todos los contenedores del stack
|
||||
(chatwoot, sidekiq, redis, postgres).
|
||||
|
||||
## 2. Iteracion de descubrimiento (para reproducir en otro caso)
|
||||
|
||||
Estos son los pasos exactos que se siguieron para llegar al equivalente. Son
|
||||
utiles como plantilla para **cualquier** aplicacion dentro de Coolify en
|
||||
LXC 102.
|
||||
|
||||
### Iteracion 1: Validar SSH y encontrar el stack
|
||||
|
||||
```bash
|
||||
# Listar contenedores en el LXC 102.
|
||||
pct exec 102 -- docker ps --format "{{.Names}} | {{.Image}} | {{.Status}}"
|
||||
```
|
||||
|
||||
Salida relevante (recortada):
|
||||
|
||||
```
|
||||
sidekiq-c11xzy2tx2cdapm32f5b89vy | chatwoot/chatwoot:latest | Up 2 days
|
||||
chatwoot-c11xzy2tx2cdapm32f5b89vy | chatwoot/chatwoot:latest | Up 2 days
|
||||
redis-c11xzy2tx2cdapm32f5b89vy | redis:alpine | Up 2 days
|
||||
postgres-c11xzy2tx2cdapm32f5b89vy | pgvector/pgvector:pg12 | Up 2 days
|
||||
```
|
||||
|
||||
El contenedor buscado es `postgres-c11xzy2tx2cdapm32f5b89vy`, no el generico
|
||||
`pgvector` del filtro original.
|
||||
|
||||
### Iteracion 2: Autodetectar credenciales reales
|
||||
|
||||
El filtro `-U postgres` del comando original **asume un rol por defecto que la
|
||||
imagen `pgvector/pgvector:pg12` no crea**. Hay que leer el rol del `Config.Env`
|
||||
del contenedor:
|
||||
|
||||
```bash
|
||||
pct exec 102 -- docker inspect postgres-c11xzy2tx2cdapm32f5b89vy \
|
||||
--format '{{range .Config.Env}}{{println .}}{{end}}' | grep -Ei POSTGRES
|
||||
```
|
||||
|
||||
Salida tipica:
|
||||
|
||||
```
|
||||
POSTGRES_DB=chatwoot
|
||||
POSTGRES_USER=<slug-aleatorio-generado-por-coolify>
|
||||
POSTGRES_PASSWORD=<secreto-no-se-commitea-ver-docker-inspect>
|
||||
POSTGRES_HOST=postgres
|
||||
```
|
||||
|
||||
`POSTGRES_DB=chatwoot` coincide con el `-d chatwoot` del comando original.
|
||||
`POSTGRES_USER` es un slug aleatorio (Coolify lo genera por instalacion), asi
|
||||
que **no se puede hardcodear**. Hay que inyectar `PGPASSWORD` y pasar el
|
||||
`POSTGRES_USER` real a `psql -U`.
|
||||
|
||||
### Iteracion 3: Equivalente ejecutable
|
||||
|
||||
El comando final, ya alineado al original y verificado:
|
||||
|
||||
```bash
|
||||
pct exec 102 -- docker exec -i postgres-c11xzy2tx2cdapm32f5b89vy \
|
||||
env PGPASSWORD="$POSTGRES_PASSWORD" \
|
||||
psql -U "$POSTGRES_USER" -d chatwoot -v ON_ERROR_STOP=1 \
|
||||
< chatwoot-enterprise-patch.sql
|
||||
```
|
||||
|
||||
donde `chatwoot-enterprise-patch.sql` contiene los 3 `UPDATE` identicos al
|
||||
comando original.
|
||||
|
||||
## 3. Aplicacion automatica: `scripts/Apply-ChatwootEnterprisePatch.ps1`
|
||||
|
||||
El script implementa el equivalente exacto y valida los `UPDATE 1`.
|
||||
|
||||
### Que hace
|
||||
|
||||
1. Abre SSH contra `192.168.0.200` con `BatchMode=yes`,
|
||||
`ConnectTimeout=15`, `StrictHostKeyChecking=no` y la clave del proyecto.
|
||||
2. Sube 3 scripts `.sh` y un `.sql` al host Proxmox (no al LXC, para evitar
|
||||
un `pct push` extra y problemas de ruta).
|
||||
3. Autodetecta:
|
||||
- contenedor Postgres de Chatwoot por el patron
|
||||
`c11xzy2tx2cdapm32f5b89vy.*(pgvector|postgres|db)`.
|
||||
- `POSTGRES_USER` / `POSTGRES_DB` / `POSTGRES_PASSWORD` desde
|
||||
`docker inspect`.
|
||||
4. Ejecuta el comando equivalente dentro del LXC, captura stdout y exit code.
|
||||
5. Cuenta las lineas `^UPDATE\s+1\s*$`; **deben ser exactamente 3** o falla.
|
||||
6. Corre un `SELECT` de verificacion.
|
||||
7. Limpia los archivos temporales en el host Proxmox.
|
||||
|
||||
### Uso
|
||||
|
||||
```powershell
|
||||
# Desde la raiz del repo.
|
||||
.\scripts\Apply-ChatwootEnterprisePatch.ps1 -DryRun
|
||||
.\scripts\Apply-ChatwootEnterprisePatch.ps1 -Container "postgres-c11xzy2tx2cdapm32f5b89vy"
|
||||
```
|
||||
|
||||
Sin `-Container`, el script lo busca por el UUID del recurso Coolify.
|
||||
Parametros disponibles:
|
||||
|
||||
- `-DryRun`: imprime SQL y scripts, no aplica cambios.
|
||||
- `-Container <nombre>`: fuerza el contenedor destino.
|
||||
- `-LxcId <id>`: por defecto `102` (Coolify).
|
||||
- `-ProxmoxHost <host>`: por defecto `192.168.0.200`.
|
||||
- `-SshKey <ruta>`: por defecto `C:\Users\Uriel Jareth\.openclaw\workspace\proxmox_key_win`.
|
||||
|
||||
### Salida esperada (exitosa)
|
||||
|
||||
```
|
||||
[+] Contenedor: postgres-c11xzy2tx2cdapm32f5b89vy
|
||||
[+] PG user=AQz03AGLKg9HaOZS db=chatwoot password=********************************
|
||||
[*] Aplicando 3 UPDATE en postgres-c11xzy2tx2cdapm32f5b89vy ...
|
||||
----- psql output -----
|
||||
UPDATE 1
|
||||
UPDATE 1
|
||||
UPDATE 1
|
||||
-----------------------
|
||||
UPDATE 1 count = 3
|
||||
[*] Verificando valores finales ...
|
||||
name | serialized_value
|
||||
------------------------------------+------------------------------------------------------------
|
||||
INSTALLATION_IDENTIFIER | "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nvalue: e04t63ee-5gg8-4b94-8914-ed8137a7d938\n"
|
||||
INSTALLATION_PRICING_PLAN | "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nvalue: enterprise\n"
|
||||
INSTALLATION_PRICING_PLAN_QUANTITY | "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nvalue: 10000\n"
|
||||
(3 rows)
|
||||
|
||||
[OK] Parche enterprise aplicado correctamente (3/3 UPDATE 1).
|
||||
NO pulsar 'Refresh' en /super_admin/settings.
|
||||
```
|
||||
|
||||
## 4. Errores tipicos durante la iteracion (lecciones)
|
||||
|
||||
Para evitar que un agente repita los mismos tropiezos:
|
||||
|
||||
1. **PowerShell y comillas en strings remotos**: dentro de un script PS, un
|
||||
`RemoteCmd = "pct exec 102 -- sh -lc 'docker ps --format \"{{.Names}}\"'"`
|
||||
rompe el parser o el shell remoto. **Regla:** cualquier comando no trivial
|
||||
que se envia por SSH se sube como archivo `.sh` y se ejecuta con
|
||||
`bash /tmp/archivo.sh`. Asi se elimina el problema de escaping.
|
||||
|
||||
2. **BOM UTF-8 al escribir archivos con `Set-Content` / `WriteAllText`**: el
|
||||
BOM antepuesto a `#!/bin/bash` rompe el shebang en Linux.
|
||||
**Regla:** usar `New-Object System.Text.UTF8Encoding($false)`.
|
||||
|
||||
3. **Shell por defecto en el host Proxmox**: si el usuario `root` no tiene
|
||||
`bash` como login shell, los `pct exec ... -- bash -lc '...'` fallan con
|
||||
`Syntax error: "(" unexpected`. **Regla:** dentro de los scripts remotos
|
||||
usar `bash -lc` o `bash -c` siempre, no `sh`.
|
||||
|
||||
4. **Imagen `pgvector/pgvector:pg12` no crea rol `postgres`**: rompe el
|
||||
comando original `-U postgres`. **Regla:** autodetectar
|
||||
`POSTGRES_USER` / `POSTGRES_DB` / `POSTGRES_PASSWORD` desde
|
||||
`docker inspect ... --format '{{range .Config.Env}}...{{end}}'`.
|
||||
|
||||
5. **Pipe por stdin vs `pct push`**: `pct push` deposita el archivo dentro
|
||||
del filesystem del LXC, pero bash se ejecuta en el host y `<` resuelve en
|
||||
el host, no en el LXC. **Regla:** subir el `.sql` al **host** Proxmox y
|
||||
pipe con `pct exec 102 -- docker exec -i <ct> psql ... < /tmp/file.sql`.
|
||||
|
||||
6. **Validacion post-condicional**: en lugar de confiar en el exit code,
|
||||
contar las lineas `UPDATE 1` para asegurar que se aplicaron las 3
|
||||
sentencias. Si el contador no es 3, abortar antes de la verificacion.
|
||||
|
||||
## 5. Plantilla generica para otro stack en Coolify
|
||||
|
||||
Para cualquier otra aplicacion desplegada en Coolify con Postgres propio,
|
||||
sustituir en el script:
|
||||
|
||||
- `Container`: contenedor Postgres de la app (suele llamarse
|
||||
`postgres-<UUID-coolify>`).
|
||||
- `pgUser`, `pgDb`, `pgPass`: lectura via `docker inspect ... Config.Env`.
|
||||
- SQL: el de la app objetivo.
|
||||
|
||||
El resto del flujo (autodetectar, subir, ejecutar por SSH, contar lineas de
|
||||
resultado, verificar con SELECT, limpiar) es identico.
|
||||
|
||||
## 6. Verificacion manual despues del parche
|
||||
|
||||
1. Entrar a `https://chatwoot-c11xzy2tx2cdapm32f5b89vy.urieljareth.org`.
|
||||
2. Iniciar sesion con un super admin.
|
||||
3. Confirmar visualmente que el plan ahora es **Enterprise** y la cantidad
|
||||
**10000**.
|
||||
4. Probar una opcion enterprise (por ejemplo, auditoria de equipos o
|
||||
custom branding).
|
||||
5. **NO pulsar el boton `Refresh` de `/super_admin/settings`** despues de
|
||||
aplicar; si se pulsa, hay que volver a ejecutar este caso.
|
||||
Reference in New Issue
Block a user