Actualiza toolkit operativo y documentación
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
# Guard: mantiene la edicion enterprise de Chatwoot en LXC 102.
|
||||
#
|
||||
# Por que existe: Internal::CheckNewVersionsJob hace ping diario a
|
||||
# hub.2.chatwoot.com (a las 16:16 UTC para este installation_identifier) y
|
||||
# reescribe INSTALLATION_PRICING_PLAN con lo que responda el hub ('community'),
|
||||
# y ademas Internal::ReconcilePlanConfigService apaga los 9 feature flags
|
||||
# premium en TODAS las cuentas.
|
||||
#
|
||||
# No se bloquea el hub a proposito: ese mismo host relaya las notificaciones
|
||||
# push del movil (ChatwootHub.send_push) porque FIREBASE_* esta vacio. Bloquearlo
|
||||
# romperia el push. En vez de eso, este guard detecta el revert y lo deshace.
|
||||
#
|
||||
# Cheap por diseno: en el caso normal hace 1 SELECT y sale. Solo cuando detecta
|
||||
# plan != enterprise levanta Rails para limpiar cache y reactivar flags.
|
||||
#
|
||||
# Instalado por el runbook docs/runbooks/chatwoot-update.md
|
||||
# Cron: */15 * * * *
|
||||
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
LXC=102
|
||||
UUID=c11xzy2tx2cdapm32f5b89vy
|
||||
DB_CT="postgres-$UUID"
|
||||
APP_CT="chatwoot-$UUID"
|
||||
LOG=/var/log/chatwoot-enterprise-guard.log
|
||||
LOCK=/var/lock/chatwoot-enterprise-guard.lock
|
||||
MAX_LOG=2097152
|
||||
|
||||
log() { echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*" >> "$LOG"; }
|
||||
|
||||
# Rotacion simple para que el log no crezca sin control.
|
||||
if [ -f "$LOG" ] && [ "$(stat -c %s "$LOG" 2>/dev/null || echo 0)" -gt "$MAX_LOG" ]; then
|
||||
mv -f "$LOG" "$LOG.1"
|
||||
fi
|
||||
|
||||
# Una sola instancia a la vez (el camino de reparacion tarda ~1 min).
|
||||
exec 9>"$LOCK" || exit 0
|
||||
flock -n 9 || exit 0
|
||||
|
||||
# --- 1) Chequeo barato: en que plan estamos ---------------------------------
|
||||
# Se traen todas las filas y se filtra con grep a proposito: un WHERE con
|
||||
# literales necesitaria comillas simples anidadas dentro de bash -lc '...' y eso
|
||||
# es exactamente la clase de escaping que rompe estos scripts.
|
||||
PLAN=$(pct exec "$LXC" -- docker exec -i "$DB_CT" bash -lc \
|
||||
'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -At -F "|" -c "SELECT name, serialized_value::text FROM public.installation_configs"' \
|
||||
2>/dev/null | grep '^INSTALLATION_PRICING_PLAN|')
|
||||
|
||||
if [ -z "$PLAN" ]; then
|
||||
log "ERROR: no se pudo leer INSTALLATION_PRICING_PLAN (stack caido o contenedor renombrado?)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$PLAN" in
|
||||
*enterprise*)
|
||||
# Caso normal: nada que hacer, y no ensuciamos el log.
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
log "DETECTADO revert -> plan actual: $PLAN . Reparando..."
|
||||
|
||||
# --- 2) Reponer las 3 filas del parche --------------------------------------
|
||||
cat > /tmp/cw-guard.sql <<'SQLEOF'
|
||||
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';
|
||||
SQLEOF
|
||||
|
||||
SQL_OUT=$(pct exec "$LXC" -- docker exec -i "$DB_CT" bash -lc \
|
||||
'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1' \
|
||||
< /tmp/cw-guard.sql 2>&1)
|
||||
SQL_RC=$?
|
||||
rm -f /tmp/cw-guard.sql
|
||||
|
||||
UPDATES=$(printf '%s\n' "$SQL_OUT" | grep -c '^UPDATE 1$')
|
||||
if [ "$SQL_RC" -ne 0 ] || [ "$UPDATES" -ne 3 ]; then
|
||||
log "ERROR: los UPDATE fallaron (rc=$SQL_RC, 'UPDATE 1'=$UPDATES). Salida: $SQL_OUT"
|
||||
exit 1
|
||||
fi
|
||||
log "OK: 3/3 UPDATE aplicados"
|
||||
|
||||
# --- 3) Limpiar cache de GlobalConfig y reactivar flags premium -------------
|
||||
# El UPDATE por SQL no dispara el after_commit :clear_cache de InstallationConfig,
|
||||
# y GlobalConfig cachea en Redis con TTL de 1 dia: sin este paso la app puede
|
||||
# seguir sirviendo 'community'. Ademas hay que reactivar los flags por cuenta,
|
||||
# que viven en accounts.feature_flags (bitmask) y el SQL de arriba no toca.
|
||||
cat > /tmp/cw-guard.rb <<'RBEOF'
|
||||
PREMIUM = %w[
|
||||
disable_branding audit_logs sla custom_roles
|
||||
captain_integration captain_integration_v2 captain_document_auto_sync
|
||||
csat_review_notes conversation_required_attributes
|
||||
]
|
||||
GlobalConfig.clear_cache
|
||||
Account.find_each do |account|
|
||||
account.enable_features!(*PREMIUM)
|
||||
account.reload
|
||||
pend = PREMIUM.reject { |f| account.feature_enabled?(f) }
|
||||
puts "account=#{account.id} pendientes=#{pend.empty? ? 'ninguno' : pend.join(',')}"
|
||||
end
|
||||
puts "self_hosted_enterprise=#{ChatwootApp.self_hosted_enterprise?}"
|
||||
RBEOF
|
||||
|
||||
pct push "$LXC" /tmp/cw-guard.rb /tmp/cw-guard.rb
|
||||
pct exec "$LXC" -- docker cp /tmp/cw-guard.rb "$APP_CT":/tmp/cw-guard.rb
|
||||
RB_OUT=$(pct exec "$LXC" -- docker exec -i "$APP_CT" bundle exec rails runner /tmp/cw-guard.rb 2>&1)
|
||||
RB_RC=$?
|
||||
pct exec "$LXC" -- docker exec -i "$APP_CT" rm -f /tmp/cw-guard.rb
|
||||
pct exec "$LXC" -- rm -f /tmp/cw-guard.rb
|
||||
rm -f /tmp/cw-guard.rb
|
||||
|
||||
RESULT=$(printf '%s\n' "$RB_OUT" | grep -E '^(account=|self_hosted_enterprise=)' | tr '\n' ' ')
|
||||
if [ "$RB_RC" -ne 0 ]; then
|
||||
log "ERROR: rails runner fallo (rc=$RB_RC). Salida: $RB_OUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$RESULT" in
|
||||
*"self_hosted_enterprise=true"*)
|
||||
case "$RESULT" in
|
||||
*"pendientes=ninguno"*)
|
||||
log "REPARADO: $RESULT"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
log "PARCIAL: enterprise activo pero quedaron flags pendientes -> $RESULT"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log "ERROR: tras reparar, self_hosted_enterprise no dio true -> $RESULT"
|
||||
exit 1
|
||||
Reference in New Issue
Block a user