diff --git a/scripts/sync_db_to_hostinger.sh b/scripts/sync_db_to_hostinger.sh new file mode 100755 index 0000000..b1e52b3 --- /dev/null +++ b/scripts/sync_db_to_hostinger.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# ============================================================================== +# SCRIPT DE REPLICAÇÃO REVERSA: MARCOS-VPS (PROD) -> HOSTINGER (BACKUP/RÉPLICA) +# ============================================================================== +# Sincroniza schemas de negócio (TS_ERP, gpi, tracksteel, rdo) e storage +# da VPS Local (marcos-vps) para o Supabase na Hostinger (100.106.199.108 / 187.77.227.172). +# ============================================================================== + +set -euo pipefail + +CYAN='\033[0;36m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' + +LOG_DIR="/root/Apps/TracksteelApp/scripts/logs" +mkdir -p "$LOG_DIR" +LOG_FILE="$LOG_DIR/sync_db_to_hostinger_$(date +'%Y%m%d').log" + +log() { + local msg="[$(date +'%Y-%m-%d %H:%M:%S')] $1" + echo -e "$1" + echo -e "$msg" >> "$LOG_FILE" +} + +log "\n${CYAN}======================================================${NC}" +log "${CYAN}🔄 Iniciando Replicação Reversa: marcos-vps -> Hostinger${NC}" +log "${CYAN}======================================================${NC}" + +# Containers +SRC_DB_CONTAINER="supabase-db-rjbxu55kggzc59d4cmo133cf" +DEST_DB_CONTAINER="supabase-db-h0oggskgs0ws0sco8kc4s8ws" + +# Determinar IP de Conexão com a Hostinger (preferência Tailscale, fallback IP público) +DEST_IP="100.106.199.108" +log "${YELLOW}📡 Testando conexão SSH com Hostinger via Tailscale ($DEST_IP)...${NC}" + +if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "root@$DEST_IP" "true" 2>/dev/null; then + log "${YELLOW}⚠️ Tailscale inacessível. Tentando IP público Hostinger (187.77.227.172)...${NC}" + DEST_IP="187.77.227.172" + if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "root@$DEST_IP" "true" 2>/dev/null; then + log "${RED}❌ ERRO: Não foi possível conectar via SSH à Hostinger ($DEST_IP). Replicação abortada.${NC}" + exit 1 + fi +fi +log "${GREEN}✅ Conectividade com Hostinger confirmada ($DEST_IP).${NC}" + +# Lista de Schemas a sincronizar +SCHEMAS=('TS_ERP' 'gpi' 'tracksteel' 'rdo') + +for schema in "${SCHEMAS[@]}"; do + EXISTS=$(docker exec "$SRC_DB_CONTAINER" psql -U postgres -d postgres -tAc \ + "SELECT 1 FROM pg_namespace WHERE nspname = '$schema';" 2>/dev/null || echo "0") + + if [ "$EXISTS" = "1" ]; then + log "${YELLOW}📦 Sincronizando schema: $schema...${NC}" + TMP_DUMP="/tmp/sync_rev_${schema}_$$.dump" + + # 1. Gerar Dump local comprimido (marcos-vps) + docker exec -i "$SRC_DB_CONTAINER" pg_dump -U postgres -d postgres -n "\"$schema\"" -Fc > "$TMP_DUMP" + + # 2. Transferir para Hostinger via SCP + scp -q -o BatchMode=yes -o ConnectTimeout=5 "$TMP_DUMP" "root@$DEST_IP:$TMP_DUMP" + + # 3. Restaurar na Hostinger e restabelecer permissões + ssh -o BatchMode=yes -o ConnectTimeout=5 "root@$DEST_IP" \ + "docker cp '$TMP_DUMP' '$DEST_DB_CONTAINER:$TMP_DUMP' && \ + (docker exec '$DEST_DB_CONTAINER' pg_restore -U postgres -d postgres --clean --if-exists --no-owner --no-privileges '$TMP_DUMP' || true) && \ + docker exec '$DEST_DB_CONTAINER' psql -U postgres -d postgres -c \"GRANT USAGE ON SCHEMA \\\"$schema\\\" TO anon, authenticated, service_role, authenticator; GRANT ALL ON ALL TABLES IN SCHEMA \\\"$schema\\\" TO anon, authenticated, service_role; GRANT ALL ON ALL SEQUENCES IN SCHEMA \\\"$schema\\\" TO anon, authenticated, service_role; GRANT ALL ON ALL ROUTINES IN SCHEMA \\\"$schema\\\" TO anon, authenticated, service_role; ALTER DEFAULT PRIVILEGES IN SCHEMA \\\"$schema\\\" GRANT ALL ON TABLES TO anon, authenticated, service_role; ALTER DEFAULT PRIVILEGES IN SCHEMA \\\"$schema\\\" GRANT ALL ON SEQUENCES TO anon, authenticated, service_role; ALTER DEFAULT PRIVILEGES IN SCHEMA \\\"$schema\\\" GRANT ALL ON ROUTINES TO anon, authenticated, service_role; NOTIFY pgrst, 'reload schema';\" && \ + rm -f '$TMP_DUMP' && \ + docker exec '$DEST_DB_CONTAINER' rm -f '$TMP_DUMP'" + + # 4. Limpar dump local + rm -f "$TMP_DUMP" + log "${GREEN} ↳ Schema $schema replicado na Hostinger com sucesso.${NC}" + fi +done + +# Sincronização de Metadados de Storage (buckets e objects) +log "${YELLOW}📁 Sincronizando metadados de Storage (buckets & objects)...${NC}" +STORAGE_SQL="/tmp/sync_storage_rev_$$.sql" +docker exec -i "$SRC_DB_CONTAINER" pg_dump -U postgres -d postgres -t storage.buckets -t storage.objects --data-only --on-conflict-do-nothing > "$STORAGE_SQL" 2>/dev/null || true + +if [ -s "$STORAGE_SQL" ]; then + scp -q -o BatchMode=yes -o ConnectTimeout=5 "$STORAGE_SQL" "root@$DEST_IP:$STORAGE_SQL" + ssh -o BatchMode=yes -o ConnectTimeout=5 "root@$DEST_IP" \ + "docker exec -i '$DEST_DB_CONTAINER' psql -U postgres -d postgres < '$STORAGE_SQL' >/dev/null 2>&1 && rm -f '$STORAGE_SQL'" + rm -f "$STORAGE_SQL" + log "${GREEN} ↳ Metadados de storage sincronizados na Hostinger.${NC}" +else + rm -f "$STORAGE_SQL" +fi + +# Sincronização de arquivos físicos de Storage (MinIO) +SRC_STORAGE="/data/coolify/services/rjbxu55kggzc59d4cmo133cf/volumes/storage/" +DEST_STORAGE="/data/coolify/services/h0oggskgs0ws0sco8kc4s8ws/volumes/storage/" + +if [ -d "$SRC_STORAGE" ]; then + log "${YELLOW}☁️ Sincronizando arquivos físicos do Storage para a Hostinger...${NC}" + rsync -aqz --delete -e "ssh -o BatchMode=yes -o ConnectTimeout=5" "$SRC_STORAGE" "root@$DEST_IP:$DEST_STORAGE" || true + log "${GREEN} ↳ Arquivos do Storage espelhados com sucesso na Hostinger.${NC}" +fi + +# Validação final de integridade de tabelas em TS_ERP +ORIG_COUNT=$(docker exec "$SRC_DB_CONTAINER" psql -U postgres -d postgres -tAc "SELECT count(*) FROM pg_tables WHERE schemaname = 'TS_ERP';" 2>/dev/null || echo "0") +DEST_COUNT=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "root@$DEST_IP" "docker exec '$DEST_DB_CONTAINER' psql -U postgres -d postgres -tAc \"SELECT count(*) FROM pg_tables WHERE schemaname = 'TS_ERP';\"" 2>/dev/null || echo "0") + +log "${CYAN}📊 Validação de Integridade:${NC}" +log " - Tabelas TS_ERP (Origem marcos-vps): $ORIG_COUNT" +log " - Tabelas TS_ERP (Destino Hostinger): $DEST_COUNT" + +if [ "$ORIG_COUNT" = "$DEST_COUNT" ] && [ "$ORIG_COUNT" != "0" ]; then + log "${GREEN}✨ Replicação reversa concluída com 100% de paridade!${NC}\n" +else + log "${YELLOW}⚠️ Aviso: Contagem de tabelas ($ORIG_COUNT vs $DEST_COUNT). Verifique os logs.${NC}\n" +fi diff --git a/update.sh b/update.sh index 24ba7bd..d659595 100755 --- a/update.sh +++ b/update.sh @@ -1,46 +1,61 @@ #!/bin/bash # --------------------------------------------------------- -# TRACKSTEEL APP: DEPLOY E SINCRONIZAÇÃO +# TRACKSTEEL APP: DEPLOY & SINCRONIZAÇÃO (MARCOS-VPS -> PROD & HOSTINGER) # --------------------------------------------------------- +set -e + CYAN='\033[0;36m' GREEN='\033[0;32m' YELLOW='\033[1;33m' +RED='\033[0;31m' NC='\033[0m' -echo -e "\n${CYAN}🚀 Iniciando Ciclo Automático de Deploy TrackSteelAPP...${NC}" +echo -e "\n${CYAN}🚀 Iniciando Ciclo Automático de Deploy TrackSteel APP (marcos-vps)...${NC}" -echo -e "${YELLOW}📝 Sincronizando código...${NC}" +# 1. Git Commit & Push +echo -e "\n${YELLOW}📝 Sincronizando código com o repositório GitHub...${NC}" git add . if git diff-index --quiet HEAD --; then echo -e "${GREEN}✨ Nenhuma nova alteração para comitar.${NC}" else TIMESTAMP=$(date +"%d/%m/%Y %H:%M:%S") - MSG="${1:-🚀 Auto-deploy: TrackSteelAPP atualizado em $TIMESTAMP}" + MSG="${1:-🚀 Auto-deploy: TrackSteel APP atualizado em $TIMESTAMP}" echo -e "${CYAN}📤 Criando novo commit: $MSG${NC}" git commit -m "$MSG" fi -echo -e "${CYAN}📤 Sincronizando com os repositórios remotos (Gitea e GitHub)...${NC}" +echo -e "${CYAN}📤 Enviando alterações para o GitHub (origin main)...${NC}" git push origin main -echo -e "\n${YELLOW}🔄 Disparando Deploy via API Oficial no Coolify (Hostinger)...${NC}" +# 2. Build local do Frontend +echo -e "\n${YELLOW}🔨 Executando build de produção local (npm run build)...${NC}" +npm run build + +# 3. Reiniciar container do Tracksteel local +echo -e "\n${YELLOW}🔄 Atualizando container local tracksteel-app...${NC}" +if docker ps -a --format '{{.Names}}' | grep -q "^tracksteel-app$"; then + docker restart tracksteel-app + echo -e "${GREEN}✅ Container tracksteel-app reiniciado com sucesso!${NC}" +else + echo -e "${YELLOW}ℹ️ Container tracksteel-app não encontrado no docker local.${NC}" +fi + +# 4. Replicação Reversa de Banco de Dados para a Hostinger +echo -e "\n${YELLOW}🗄️ Executando replicação reversa de BD e Storage para a Hostinger...${NC}" +SCRIPT_SYNC="/root/Apps/TracksteelApp/scripts/sync_db_to_hostinger.sh" +if [ -f "$SCRIPT_SYNC" ]; then + chmod +x "$SCRIPT_SYNC" + "$SCRIPT_SYNC" || echo -e "${YELLOW}⚠️ Aviso: A replicação para Hostinger apresentou avisos ou falha de conexão.${NC}" +fi + +# 5. Notificar Coolify da Hostinger para deploy de contingência +echo -e "\n${YELLOW}🌐 Atualizando app réplica no Coolify da Hostinger...${NC}" COOLIFY_TOKEN="19|ZJKUbBgyeNGcKrVkMI5gJ1uMHDC39d9VIShHL473a60d9882" TRACKSTEEL_UUID="i8o44gggg00o88ccc8oo48kk" curl -s -X GET "https://painel.reifonas.cloud/api/v1/deploy?uuid=${TRACKSTEEL_UUID}&force=false" \ - -H "Authorization: Bearer $COOLIFY_TOKEN" -echo -e "\n${GREEN}🚀 Deploy local (Hostinger) engatilhado com sucesso!${NC}" + -H "Authorization: Bearer $COOLIFY_TOKEN" >/dev/null 2>&1 || true +echo -e "${GREEN}🚀 Deploy da réplica (Hostinger) acionado com sucesso!${NC}" -echo -e "\n${YELLOW}🌐 Sincronizando código e atualizando app na marcos-vps (100.97.2.16)...${NC}" -ssh -o BatchMode=yes -o ConnectTimeout=5 root@100.97.2.16 \ - "cd /root/Apps/TracksteelApp && git pull origin main && npm run build && docker restart tracksteel-app" \ - && echo -e "${GREEN}✅ App na marcos-vps atualizado com sucesso!${NC}" \ - || echo -e "${YELLOW}⚠️ Aviso: Falha ao atualizar app na marcos-vps via SSH.${NC}" - -echo -e "\n${YELLOW}🗄️ Replicando Banco de Dados e Storage para marcos-vps...${NC}" -if [ -f "/root/Apps/TrackSteelAPP/scripts/sync_db_to_marcos.sh" ]; then - /root/Apps/TrackSteelAPP/scripts/sync_db_to_marcos.sh -fi - -echo -e "\n${GREEN}🏁 Ciclo completo de deploy e replicação concluído com sucesso!${NC}\n" +echo -e "\n${GREEN}🏁 Ciclo de deploy e replicação concluído com sucesso!${NC}\n"