/** * ARQUIVO DE MIGRAÇÕES DO BANCO DE DADOS * ======================================== * * Este script contém TODAS as alterações estruturais feitas no banco de dados * desde a criação inicial do projeto. * * USO: * node scripts/migracoes.js [alias] * * Exemplo: * node scripts/migracoes.js lajedo * node scripts/migracoes.js novo * * IMPORTANTE: * - Cada migração tem um ID único e só executa uma vez * - O controle é feito via tabela CHATC2_CONTROLE_MIGRACOES * - Para adicionar NOVA migração, adicione um novo objeto no array MIGRACOES * - Nunca remova ou altere migrações já existentes (apenas adicione novas) */ const db = require('../src/database'); // Este script aplica DDL no dialeto Firebird (BLOB SUB_TYPE, etc.). // O schema do PostgreSQL é gerenciado externamente (banco externo) — use este // script apenas para bancos Firebird. Alias padrão: firebird_local. const alias = process.argv[2] || 'firebird_local'; // ============================================================ // CONTROLE DE MIGRAÇÕES // ============================================================ async function garantirControle() { try { await db.execute(alias, ` CREATE TABLE CHATC2_CONTROLE_MIGRACOES ( MIG_ID INTEGER NOT NULL PRIMARY KEY, MIG_DESCRICAO VARCHAR(200), MIG_DATA_EXECUCAO TIMESTAMP DEFAULT CURRENT_TIMESTAMP, MIG_STATUS CHAR(1) DEFAULT 'A' ) `); console.log('✅ Tabela CHATC2_CONTROLE_MIGRACOES criada'); } catch(e) { if (!e.message.includes('already exists')) { console.log('⚠️ CHATC2_CONTROLE_MIGRACOES:', e.message.substring(0, 100)); } } } async function migracaoJaExecutada(id) { try { var r = await db.query(alias, 'SELECT COUNT(*) AS CT FROM CHATC2_CONTROLE_MIGRACOES WHERE MIG_ID = ?', [id]); return r[0]?.CT > 0; } catch(e) { return false; } } async function registrarMigracao(id, descricao) { try { await db.execute(alias, 'INSERT INTO CHATC2_CONTROLE_MIGRACOES (MIG_ID, MIG_DESCRICAO) VALUES (?, ?)', [id, descricao]); } catch(e) { console.log('⚠️ Erro ao registrar migração:', e.message.substring(0, 80)); } } // ============================================================ // LISTA DE MIGRAÇÕES // ============================================================ // SEMPRE adicione NOVAS migrações no FINAL do array, com ID sequencial. // NUNCA remova ou modifique migrações já existentes. const MIGRACOES = [ // ---------------------------------------------------------- // MIGRAÇÃO 1: Campo USU_TIPO_CHAT em USUARIOS // ---------------------------------------------------------- { id: 1, descricao: 'Adicionar USU_TIPO_CHAT em USUARIOS (A=Atendente, G=Gerente)', sql: [ `ALTER TABLE USUARIOS ADD USU_TIPO_CHAT CHAR(1) DEFAULT 'A'`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 2: Tabela CHATC2_INSTANCIAS (Evolution API) // ---------------------------------------------------------- { id: 2, descricao: 'Criar tabela CHATC2_INSTANCIAS para conexões WhatsApp', sql: [ `CREATE TABLE CHATC2_INSTANCIAS ( INS_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, INS_EMPRESA_ID INTEGER NOT NULL, INS_NOME VARCHAR(100), INS_URL VARCHAR(500), INS_API_KEY VARCHAR(500), INS_INSTANCE_NAME VARCHAR(100), INS_STATUS CHAR(1) DEFAULT 'D', INS_QR_CODE BLOB SUB_TYPE 0, INS_DT_CONEXAO TIMESTAMP, INS_DT_CADASTRO TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INS_SITUACAO CHAR(1) DEFAULT 'A' )`, `CREATE SEQUENCE GEN_INSTANCIAS`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 3: Tabela CHATC2_EQUIPES // ---------------------------------------------------------- { id: 3, descricao: 'Criar tabela CHATC2_EQUIPES', sql: [ `CREATE TABLE CHATC2_EQUIPES ( EQU_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, EQU_EMPRESA_ID INTEGER NOT NULL, EQU_NOME VARCHAR(100), EQU_ORDEM INTEGER DEFAULT 0, EQU_SITUACAO CHAR(1) DEFAULT 'A' )`, `CREATE SEQUENCE GEN_EQUIPES`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 4: Tabela CHATC2_USU_EQUIPES // ---------------------------------------------------------- { id: 4, descricao: 'Criar tabela CHATC2_USU_EQUIPES (relação N:N)', sql: [ `CREATE TABLE CHATC2_USU_EQUIPES ( EQU_EQUIPE_ID INTEGER NOT NULL, EQU_USUARIO_ID INTEGER NOT NULL, PRIMARY KEY (EQU_EQUIPE_ID, EQU_USUARIO_ID) )`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 5: Tabela CHATC2_ETIQUETAS // ---------------------------------------------------------- { id: 5, descricao: 'Criar tabela CHATC2_ETIQUETAS', sql: [ `CREATE TABLE CHATC2_ETIQUETAS ( ETI_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, ETI_EMPRESA_ID INTEGER NOT NULL, ETI_NOME VARCHAR(50), ETI_COR VARCHAR(7) DEFAULT '#667eea', ETI_SITUACAO CHAR(1) DEFAULT 'A' )`, `CREATE SEQUENCE GEN_ETIQUETAS`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 6: Tabela CHATC2_CONVERSAS // ---------------------------------------------------------- { id: 6, descricao: 'Criar tabela CHATC2_CONVERSAS', sql: [ `CREATE TABLE CHATC2_CONVERSAS ( CON_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, CON_EMPRESA_ID INTEGER NOT NULL, CON_INSTANCIA_ID INTEGER, CON_CLIENTE_ID INTEGER, CON_NUMERO VARCHAR(20), CON_NOME_CONTATO VARCHAR(100), CON_STATUS CHAR(1) DEFAULT 'E', CON_USUARIO_ID INTEGER, CON_EQUIPE_ID INTEGER, CON_DT_INICIO TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CON_DT_FINAL TIMESTAMP, CON_DT_ULTIMA_MSG TIMESTAMP, CON_SAUDACAO_ENVIADA CHAR(1) DEFAULT 'N', CON_CSAT_ENVIADO CHAR(1) DEFAULT 'N', CON_PRIMEIRA_MSG TIMESTAMP, CON_ORIGEM VARCHAR(20) DEFAULT 'whatsapp', CON_SITUACAO CHAR(1) DEFAULT 'A' )`, `CREATE SEQUENCE GEN_CONVERSAS`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 7: Tabela CHATC2_CONVERSAS_MENSAGENS // ---------------------------------------------------------- { id: 7, descricao: 'Criar tabela CHATC2_CONVERSAS_MENSAGENS', sql: [ `CREATE TABLE CHATC2_CONVERSAS_MENSAGENS ( CME_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, CME_CONVERSA_ID INTEGER NOT NULL, CME_REMETENTE CHAR(1) DEFAULT 'C', CME_USUARIO_ID INTEGER, CME_MENSAGEM BLOB SUB_TYPE 1, CME_TEXTO VARCHAR(4000), CME_TIPO VARCHAR(20) DEFAULT 'text', CME_DT_ENVIO TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CME_PRIVADA CHAR(1) DEFAULT 'N', CME_LIDA CHAR(1) DEFAULT 'N', CME_MIDIA_ID INTEGER, CME_SITUACAO CHAR(1) DEFAULT 'A' )`, `CREATE SEQUENCE GEN_CONVERSAS_MENSAGENS`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 8: Tabela CHATC2_MENSAGENS_ATENDIMENTOS (blob mídias) // ---------------------------------------------------------- { id: 8, descricao: 'Criar tabela CHATC2_MENSAGENS_ATENDIMENTOS para mídias', sql: [ `CREATE TABLE CHATC2_MENSAGENS_ATENDIMENTOS ( MAT_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, MAT_CONVERSA_ID INTEGER, MAT_MENSAGEM_ID INTEGER, MAT_NOME_ARQUIVO VARCHAR(255), MAT_TIPO_ARQUIVO VARCHAR(50), MAT_ARQUIVO BLOB SUB_TYPE 0, MAT_DT_CADASTRO TIMESTAMP DEFAULT CURRENT_TIMESTAMP, MAT_SITUACAO CHAR(1) DEFAULT 'A' )`, `CREATE SEQUENCE GEN_MENSAGENS_ATENDIMENTO`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 9: Tabela CHATC2_CONVERSAS_ETIQUETAS // ---------------------------------------------------------- { id: 9, descricao: 'Criar tabela CHATC2_CONVERSAS_ETIQUETAS', sql: [ `CREATE TABLE CHATC2_CONVERSAS_ETIQUETAS ( CET_CONVERSA_ID INTEGER NOT NULL, CET_ETIQUETA_ID INTEGER NOT NULL, PRIMARY KEY (CET_CONVERSA_ID, CET_ETIQUETA_ID) )`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 10: Tabela CHATC2_CSAT_AVALIACOES // ---------------------------------------------------------- { id: 10, descricao: 'Criar tabela CHATC2_CSAT_AVALIACOES', sql: [ `CREATE TABLE CHATC2_CSAT_AVALIACOES ( CSA_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, CSA_CONVERSA_ID INTEGER, CSA_EMPRESA_ID INTEGER, CSA_CLIENTE_ID INTEGER, CSA_NOTA INTEGER, CSA_COMENTARIO VARCHAR(500), CSA_DT_AVALIACAO TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`, `CREATE SEQUENCE GEN_CSAT_AVALIACOES`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 11: Tabela CHATC2_CONFIGURACOES_EMPRESA (base) // ---------------------------------------------------------- { id: 11, descricao: 'Criar tabela CHATC2_CONFIGURACOES_EMPRESA (base)', sql: [ `CREATE TABLE CHATC2_CONFIGURACOES_EMPRESA ( CFE_EMPRESA_ID INTEGER NOT NULL PRIMARY KEY, CFE_INSTANCIA_PADRAO_ID INTEGER, CFE_FOTO_CELULAR CHAR(1) DEFAULT 'N', CFE_SAUDACAO_ATIVA CHAR(1) DEFAULT 'S', CFE_SAUDACAO_MENSAGEM VARCHAR(500), CFE_CSAT_ATIVO CHAR(1) DEFAULT 'N', CFE_CSAT_MENSAGEM VARCHAR(500) DEFAULT 'Avalie seu atendimento de 1 a 5 estrelas' )`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 12: EQU_ORDEM em CHATC2_EQUIPES // ---------------------------------------------------------- { id: 12, descricao: 'Adicionar EQU_ORDEM em CHATC2_EQUIPES (ordenação)', sql: [ `ALTER TABLE CHATC2_EQUIPES ADD EQU_ORDEM INTEGER DEFAULT 0`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 13: Campos de triagem em CHATC2_CONFIGURACOES_EMPRESA // ---------------------------------------------------------- { id: 13, descricao: 'Adicionar campos de triagem em CHATC2_CONFIGURACOES_EMPRESA', sql: [ `ALTER TABLE CHATC2_CONFIGURACOES_EMPRESA ADD CFE_ENVIAR_NOME_USUARIO CHAR(1) DEFAULT 'N'`, `ALTER TABLE CHATC2_CONFIGURACOES_EMPRESA ADD CFE_TRIAGEM_ATIVA CHAR(1) DEFAULT 'N'`, `ALTER TABLE CHATC2_CONFIGURACOES_EMPRESA ADD CFE_TRIAGEM_MSG_WELCOME VARCHAR(500)`, `ALTER TABLE CHATC2_CONFIGURACOES_EMPRESA ADD CFE_TRIAGEM_MSG_AFTER VARCHAR(500)`, `ALTER TABLE CHATC2_CONFIGURACOES_EMPRESA ADD CFE_TRIAGEM_BOLETO_NUMERO VARCHAR(10) DEFAULT '0'`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 14: CON_MENU_ESTADO em CHATC2_CONVERSAS // ---------------------------------------------------------- { id: 14, descricao: 'Adicionar CON_MENU_ESTADO em CHATC2_CONVERSAS (navegação triagem)', sql: [ `ALTER TABLE CHATC2_CONVERSAS ADD CON_MENU_ESTADO VARCHAR(100)`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 15: CON_ETIQUETAS_DESC em CHATC2_CONVERSAS // ---------------------------------------------------------- { id: 15, descricao: 'Adicionar CON_ETIQUETAS_DESC em CHATC2_CONVERSAS (estado legado)', sql: [ `ALTER TABLE CHATC2_CONVERSAS ADD CON_ETIQUETAS_DESC VARCHAR(500)`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 16: Tabela CHATC2_MENUS_EMPRESA (fluxo personalizado) // ---------------------------------------------------------- { id: 16, descricao: 'Criar tabela CHATC2_MENUS_EMPRESA para fluxo de atendimento personalizado', sql: [ `CREATE TABLE CHATC2_MENUS_EMPRESA ( MNE_CODIGO_ID INTEGER NOT NULL PRIMARY KEY, MNE_EMPRESA_ID INTEGER, MNE_EQUIPE_ID INTEGER, MNE_MENU_PAI_ID INTEGER, MNE_ORDEM INTEGER DEFAULT 0, MNE_TITULO VARCHAR(100), MNE_TIPO CHAR(1) DEFAULT 'M', MNE_TEXTO BLOB SUB_TYPE TEXT, MNE_ACAO_ROTA VARCHAR(100), MNE_ACAO_METODO VARCHAR(10), MNE_ACAO_PROMPT VARCHAR(300), MNE_SITUACAO CHAR(1) DEFAULT 'A' )`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 17: CON_USUARIO_NOME, CON_EQUIPE_NOME, CON_ETIQUETAS_DESC em CHATC2_CONVERSAS // ---------------------------------------------------------- { id: 17, descricao: 'Adicionar CON_USUARIO_NOME, CON_EQUIPE_NOME, CON_ETIQUETAS_DESC em CHATC2_CONVERSAS', sql: [ `ALTER TABLE CHATC2_CONVERSAS ADD CON_USUARIO_NOME VARCHAR(100)`, `ALTER TABLE CHATC2_CONVERSAS ADD CON_EQUIPE_NOME VARCHAR(100)`, `ALTER TABLE CHATC2_CONVERSAS ADD CON_ETIQUETAS_DESC VARCHAR(500)`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 18: Etiquetas nos menus de fluxo // ---------------------------------------------------------- { id: 18, descricao: 'Adicionar MNE_ETIQUETA_IDS em CHATC2_MENUS_EMPRESA (etiquetas por etapa do fluxo)', sql: [ `ALTER TABLE CHATC2_MENUS_EMPRESA ADD MNE_ETIQUETA_IDS VARCHAR(200)`, ], }, // ---------------------------------------------------------- // MIGRAÇÃO 19: Transcrição de áudio em CHATC2_MENSAGENS_ATENDIMENTOS // ---------------------------------------------------------- { id: 19, descricao: 'Adicionar MAT_TRANSCRICAO em CHATC2_MENSAGENS_ATENDIMENTOS (transcricao de audio)', sql: [ `ALTER TABLE CHATC2_MENSAGENS_ATENDIMENTOS ADD MAT_TRANSCRICAO BLOB SUB_TYPE TEXT`, ], }, // ============================================================ // >>> ADICIONE NOVAS MIGRAÇÕES AQUI <<< // ============================================================ // // Exemplo: // { // id: 19, // descricao: 'Descrição clara do que esta migração faz', // sql: [ // `ALTER TABLE EXEMPLO ADD NOVO_CAMPO VARCHAR(100)`, // `CREATE TABLE NOVA_TABELA ( ... )`, // ], // }, // ]; // ============================================================ // EXECUTOR DE MIGRAÇÕES // ============================================================ async function main() { console.log(`\n=== MIGRAÇÕES DE BANCO DE DADOS ===`); console.log(`Alias: ${alias}\n`); // Blindagem: a DDL aqui é Firebird. Se o alias for Postgres (banco externo, // gerenciado fora), não há o que aplicar — sai sem erro. let driver; try { driver = db.driverOf(alias); } catch (e) { console.error('❌', e.message); process.exit(1); } if (driver !== 'firebird') { console.log(`⚠️ O alias "${alias}" usa o driver "${driver}". Este script aplica DDL Firebird.`); console.log(' O schema do PostgreSQL é gerenciado no banco externo — nada a fazer aqui.'); console.log(' Para migrar um banco Firebird: node scripts/migracoes.js firebird_local\n'); process.exit(0); } await garantirControle(); var executadas = 0; var pendentes = 0; for (var mig of MIGRACOES) { var jaExecutou = await migracaoJaExecutada(mig.id); if (jaExecutou) { console.log(`⏭️ [${mig.id}] ${mig.descricao} — já executada`); continue; } pendentes++; console.log(`▶️ [${mig.id}] ${mig.descricao}...`); var erros = 0; for (var cmd of mig.sql) { try { await db.execute(alias, cmd); console.log(` ✅ ${cmd.substring(0, 80)}...`); } catch(e) { var msg = e.message; // Firebird pode reportar "already exists" de várias formas if (msg.includes('already exists') || msg.includes('Violation') || msg.includes('duplicate') || msg.includes('Unknow')) { console.log(` ℹ️ Já existe: ${cmd.substring(0, 60)}...`); } else { console.log(` ❌ ${msg.substring(0, 120)}`); erros++; } } } if (erros === 0) { await registrarMigracao(mig.id, mig.descricao); executadas++; console.log(` ✅ Migração ${mig.id} concluída\n`); } else { console.log(` ⚠️ Migração ${mig.id} concluída com ${erros} erro(s)\n`); } } console.log(`=== RESUMO ===`); console.log(`Total de migrações: ${MIGRACOES.length}`); console.log(`Executadas agora: ${executadas}`); console.log(`Já executadas antes: ${MIGRACOES.length - pendentes}`); console.log(`Pendentes (com erro): ${pendentes - executadas}`); console.log(`\n✅ Finalizado!`); process.exit(0); } main().catch(function(err) { console.error('❌ Erro fatal:', err.message); process.exit(1); });