alterações de melhorias
This commit is contained in:
+67
-322
@@ -1,331 +1,45 @@
|
||||
/**
|
||||
* CAMADA DE BANCO DE DADOS (arquivo único)
|
||||
* CAMADA DE BANCO DE DADOS (dispatcher)
|
||||
* ========================================
|
||||
* Concentra tudo que diz respeito a banco de dados em UM lugar:
|
||||
*
|
||||
* 1) CONFIGURAÇÃO — aliases, drivers, conexões estáticas e customizadas
|
||||
* 2) DRIVER FIREBIRD
|
||||
* 3) DISPATCHER — API pública usada por todo o sistema
|
||||
* API pública usada por todo o sistema. Delega para:
|
||||
* - config/connections.js → aliases, configuração
|
||||
* - drivers/firebird.js → operações Firebird puras
|
||||
*
|
||||
* Suporte ao banco Firebird. Cada conexão declara o campo `driver`.
|
||||
* Conexões adicionadas em runtime (via API /api/databases) são persistidas
|
||||
* em databases_custom.json (ao lado deste arquivo).
|
||||
* Todos os controllers importam deste arquivo. A API permanece
|
||||
* idêntica — apenas a implementação foi modularizada.
|
||||
*/
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const connections = require('./config/connections');
|
||||
const firebird = require('./drivers/firebird');
|
||||
|
||||
// ============================================================
|
||||
// 1) CONFIGURAÇÃO
|
||||
// 1) DISPATCHER — API pública
|
||||
// ============================================================
|
||||
const CUSTOM_DB_PATH = path.resolve(__dirname, 'databases_custom.json');
|
||||
|
||||
// Conexões dinâmicas salvas em arquivo JSON
|
||||
var customDatabases = {};
|
||||
try {
|
||||
if (fs.existsSync(CUSTOM_DB_PATH)) {
|
||||
customDatabases = JSON.parse(fs.readFileSync(CUSTOM_DB_PATH, 'utf8'));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[Databases] Erro ao carregar databases_custom.json:', e.message);
|
||||
}
|
||||
|
||||
const DRIVERS = ['firebird'];
|
||||
const DEFAULT_DRIVER = 'firebird';
|
||||
|
||||
// Valores padrão para Firebird
|
||||
const DRIVER_DEFAULTS = {
|
||||
firebird: {
|
||||
host: 'localhost',
|
||||
port: 3050,
|
||||
user: 'SYSDBA',
|
||||
password: 'masterkey',
|
||||
encoding: 'UTF-8',
|
||||
lowercase_keys: false,
|
||||
pageSize: 4096,
|
||||
wireCrypt: 1, // WIRE_CRYPT_ENABLE
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Conexões estáticas.
|
||||
* - `novo_local` → Firebird (informe o CAMINHO do arquivo .FDB em `database`)
|
||||
* Sobrescrevível pelo .env (DB_*).
|
||||
*/
|
||||
const databases = {
|
||||
novo_local: {
|
||||
driver: 'firebird',
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_PORT, 10) || 3050,
|
||||
// CAMINHO do arquivo .FDB: defina DB_DATABASE no .env (absoluto) ou ajuste
|
||||
// o path.resolve abaixo. Ex.: path.resolve(__dirname, '../db/NOVO.FDB').
|
||||
database: process.env.DB_DATABASE || path.resolve(__dirname, '../db/NOVO.FDB'),
|
||||
user: process.env.DB_USER || 'SYSDBA',
|
||||
password: process.env.DB_PASSWORD || 'masterkey',
|
||||
encoding: process.env.DB_ENCODING || 'UTF-8',
|
||||
lowercase_keys: false,
|
||||
pageSize: 4096,
|
||||
wireCrypt: 1, // WIRE_CRYPT_ENABLE
|
||||
},
|
||||
};
|
||||
|
||||
function salvarCustomDatabases() {
|
||||
try {
|
||||
fs.writeFileSync(CUSTOM_DB_PATH, JSON.stringify(customDatabases, null, 2), 'utf8');
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('[Databases] Erro ao salvar databases_custom.json:', e.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Normaliza uma config bruta aplicando o driver e seus defaults. */
|
||||
function normalize(raw) {
|
||||
const d = DRIVER_DEFAULTS['firebird'];
|
||||
|
||||
return {
|
||||
driver: 'firebird',
|
||||
host: raw.host || d.host,
|
||||
port: raw.port || d.port,
|
||||
database: raw.database,
|
||||
user: raw.user || d.user,
|
||||
password: raw.password || d.password,
|
||||
encoding: raw.encoding || d.encoding,
|
||||
lowercase_keys: raw.lowercase_keys || d.lowercase_keys,
|
||||
role: raw.role || null,
|
||||
pageSize: raw.pageSize || d.pageSize,
|
||||
wireCrypt: raw.wireCrypt !== undefined ? raw.wireCrypt : d.wireCrypt,
|
||||
};
|
||||
}
|
||||
|
||||
/** Configuração normalizada (inclui `driver`) de um alias. */
|
||||
function getConfig(alias) {
|
||||
if (!alias) throw new Error('Alias do banco não informado.');
|
||||
const aliasLower = alias.toLowerCase();
|
||||
const raw = customDatabases[aliasLower] || databases[aliasLower];
|
||||
if (!raw) {
|
||||
throw new Error(`Alias "${alias}" não encontrado. Aliases disponíveis: ${listAliases().join(', ')}`);
|
||||
}
|
||||
return normalize(raw);
|
||||
}
|
||||
|
||||
/** Lista de aliases disponíveis. */
|
||||
function listAliases() {
|
||||
return Object.keys(databases).concat(
|
||||
Object.keys(customDatabases).filter((a) => !databases[a])
|
||||
);
|
||||
}
|
||||
|
||||
/** Todos os aliases com suas configs (custom + estáticas). */
|
||||
function listAllDatabases() {
|
||||
var result = {};
|
||||
Object.keys(databases).forEach(function (alias) {
|
||||
result[alias] = Object.assign({}, databases[alias], { _tipo: 'estatico' });
|
||||
});
|
||||
Object.keys(customDatabases).forEach(function (alias) {
|
||||
result[alias] = Object.assign({}, customDatabases[alias], { _tipo: 'custom' });
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Adiciona/atualiza uma conexão customizada. */
|
||||
function addDatabase(alias, config) {
|
||||
if (!alias || !config || !config.database) {
|
||||
throw new Error('Alias e database são obrigatórios.');
|
||||
}
|
||||
var aliasLower = alias.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
||||
|
||||
var stored = { driver: 'firebird' };
|
||||
['host', 'port', 'database', 'user', 'password',
|
||||
'encoding', 'lowercase_keys', 'role', 'pageSize', 'wireCrypt']
|
||||
.forEach(function (k) {
|
||||
if (config[k] !== undefined && config[k] !== null && config[k] !== '') {
|
||||
stored[k] = config[k];
|
||||
}
|
||||
});
|
||||
|
||||
customDatabases[aliasLower] = stored;
|
||||
return salvarCustomDatabases();
|
||||
}
|
||||
|
||||
/** Remove uma conexão customizada. */
|
||||
function removeDatabase(alias) {
|
||||
if (!alias) return false;
|
||||
var aliasLower = alias.toLowerCase();
|
||||
if (!customDatabases[aliasLower]) return false;
|
||||
delete customDatabases[aliasLower];
|
||||
return salvarCustomDatabases();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2) DRIVER FIREBIRD
|
||||
// ============================================================
|
||||
const Firebird = require('node-firebird');
|
||||
|
||||
function readBlob(blobFunc) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (typeof blobFunc !== 'function') return resolve(blobFunc);
|
||||
blobFunc(function (err, name, emitter) {
|
||||
if (err) return reject(err);
|
||||
if (!emitter || typeof emitter.on !== 'function') return resolve(null);
|
||||
var chunks = [];
|
||||
var total = 0;
|
||||
emitter.on('data', function (c) { chunks.push(c); total += c.length; });
|
||||
emitter.on('end', function () { resolve(Buffer.concat(chunks, total)); });
|
||||
emitter.on('error', reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function firebirdQuery(config, sql, params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Firebird.attach(config, (err, db) => {
|
||||
if (err) return reject(new Error(`Erro ao conectar (firebird): ${err.message}`));
|
||||
var allRows = [];
|
||||
db.sequentially(sql, params, function (row) {
|
||||
var keys = Object.keys(row);
|
||||
var blobPromises = keys.map(function (key) {
|
||||
var val = row[key];
|
||||
if (typeof val === 'function') {
|
||||
return readBlob(val).then(function (data) { row[key] = data; });
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
return Promise.all(blobPromises).then(function () { allRows.push(row); });
|
||||
}, function (queryErr) {
|
||||
db.detach();
|
||||
if (queryErr) return reject(new Error(`Erro na consulta (firebird): ${queryErr.message}`));
|
||||
resolve(allRows);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function firebirdExecute(config, sql, params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Firebird.attach(config, (err, db) => {
|
||||
if (err) return reject(new Error(`Erro ao conectar (firebird): ${err.message}`));
|
||||
db.transaction(Firebird.ISOLATION_READ_COMMITTED, (transErr, transaction) => {
|
||||
if (transErr) {
|
||||
db.detach();
|
||||
return reject(new Error(`Erro ao iniciar transação (firebird): ${transErr.message}`));
|
||||
}
|
||||
transaction.query(sql, params, (queryErr, result) => {
|
||||
if (queryErr) {
|
||||
transaction.rollback();
|
||||
db.detach();
|
||||
return reject(new Error(`Erro na execução (firebird): ${queryErr.message}`));
|
||||
}
|
||||
transaction.commit((commitErr) => {
|
||||
db.detach();
|
||||
if (commitErr) return reject(new Error(`Erro ao commitar (firebird): ${commitErr.message}`));
|
||||
resolve({ affectedRows: result ? result.length : 0, result });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function firebirdTestConnection(config) {
|
||||
await firebirdQuery(config, 'SELECT 1 FROM RDB$DATABASE');
|
||||
return true;
|
||||
}
|
||||
|
||||
async function firebirdListTables(config) {
|
||||
const rows = await firebirdQuery(config, `
|
||||
SELECT TRIM(RDB$RELATION_NAME) AS TABLE_NAME
|
||||
FROM RDB$RELATIONS
|
||||
WHERE RDB$SYSTEM_FLAG = 0 AND RDB$RELATION_TYPE = 0
|
||||
ORDER BY RDB$RELATION_NAME
|
||||
`);
|
||||
return rows.map((r) => (r.TABLE_NAME || '').trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
async function firebirdTableInfo(config, tableName) {
|
||||
const rows = await firebirdQuery(config, `
|
||||
SELECT
|
||||
rf.RDB$FIELD_NAME AS COLUMN_NAME,
|
||||
rf.RDB$FIELD_POSITION AS ORDINAL_POSITION,
|
||||
CASE f.RDB$FIELD_TYPE
|
||||
WHEN 7 THEN 'SMALLINT' WHEN 8 THEN 'INTEGER' WHEN 16 THEN 'BIGINT'
|
||||
WHEN 9 THEN 'QUAD' WHEN 10 THEN 'FLOAT' WHEN 27 THEN 'DOUBLE PRECISION'
|
||||
WHEN 12 THEN 'DATE' WHEN 13 THEN 'TIME' WHEN 35 THEN 'TIMESTAMP'
|
||||
WHEN 37 THEN 'VARCHAR' WHEN 40 THEN 'CSTRING' WHEN 45 THEN 'BLOB_ID'
|
||||
WHEN 261 THEN 'BLOB' WHEN 14 THEN 'CHAR' WHEN 41 THEN 'NUMERIC'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS DATA_TYPE,
|
||||
f.RDB$FIELD_LENGTH AS FIELD_LENGTH,
|
||||
f.RDB$FIELD_SCALE AS FIELD_SCALE,
|
||||
f.RDB$FIELD_PRECISION AS FIELD_PRECISION,
|
||||
rf.RDB$NULL_FLAG AS NULL_FLAG
|
||||
FROM RDB$RELATION_FIELDS rf
|
||||
INNER JOIN RDB$FIELDS f ON rf.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME
|
||||
WHERE rf.RDB$RELATION_NAME = ?
|
||||
ORDER BY rf.RDB$FIELD_POSITION
|
||||
`, [String(tableName).toUpperCase()]);
|
||||
|
||||
return rows.map((row) => ({
|
||||
name: (row.COLUMN_NAME || '').trim(),
|
||||
position: row.ORDINAL_POSITION,
|
||||
type: (row.DATA_TYPE || '').trim(),
|
||||
length: row.FIELD_LENGTH,
|
||||
precision: row.FIELD_PRECISION,
|
||||
scale: row.FIELD_SCALE,
|
||||
nullable: row.NULL_FLAG !== 1,
|
||||
}));
|
||||
}
|
||||
|
||||
async function firebirdTableExists(config, tableName) {
|
||||
const r = await firebirdQuery(config,
|
||||
"SELECT COUNT(*) AS CT FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = ?",
|
||||
[String(tableName).toUpperCase()]);
|
||||
return (r[0] && r[0].CT > 0) || false;
|
||||
}
|
||||
|
||||
async function firebirdColumnExists(config, tableName, columnName) {
|
||||
const r = await firebirdQuery(config,
|
||||
"SELECT COUNT(*) AS CT FROM RDB$RELATION_FIELDS WHERE RDB$RELATION_NAME = ? AND RDB$FIELD_NAME = ?",
|
||||
[String(tableName).toUpperCase(), String(columnName).toUpperCase()]);
|
||||
return (r[0] && r[0].CT > 0) || false;
|
||||
}
|
||||
|
||||
async function firebirdClose() { /* Firebird abre conexão por consulta; nada a encerrar */ }
|
||||
|
||||
// ============================================================
|
||||
// 3) DISPATCHER — API pública
|
||||
// ============================================================
|
||||
const driver = {
|
||||
query: firebirdQuery,
|
||||
execute: firebirdExecute,
|
||||
testConnection: firebirdTestConnection,
|
||||
listTables: firebirdListTables,
|
||||
tableInfo: firebirdTableInfo,
|
||||
tableExists: firebirdTableExists,
|
||||
columnExists: firebirdColumnExists,
|
||||
close: firebirdClose,
|
||||
};
|
||||
|
||||
function getConn(alias) {
|
||||
const config = getConfig(alias);
|
||||
return { config, driver };
|
||||
const config = connections.getConfig(alias);
|
||||
return { config, driver: firebird };
|
||||
}
|
||||
|
||||
function query(alias, sql, params = []) {
|
||||
let d;
|
||||
try { d = getConn(alias); } catch (err) { return Promise.reject(err); }
|
||||
return d.driver.query(d.config, sql, params).catch((err) => { throw new Error(`[${alias}] ${err.message}`); });
|
||||
return d.driver.query(d.config, sql, params)
|
||||
.catch((err) => { throw new Error(`[${alias}] ${err.message}`); });
|
||||
}
|
||||
|
||||
function execute(alias, sql, params = []) {
|
||||
let d;
|
||||
try { d = getConn(alias); } catch (err) { return Promise.reject(err); }
|
||||
return d.driver.execute(d.config, sql, params).catch((err) => { throw new Error(`[${alias}] ${err.message}`); });
|
||||
return d.driver.execute(d.config, sql, params)
|
||||
.catch((err) => { throw new Error(`[${alias}] ${err.message}`); });
|
||||
}
|
||||
|
||||
async function testConnection(alias) {
|
||||
try {
|
||||
const { config } = getConn(alias);
|
||||
await driver.testConnection(config);
|
||||
await firebird.testConnection(config);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(`[${alias}] Falha na conexão:`, err.message);
|
||||
@@ -335,7 +49,7 @@ async function testConnection(alias) {
|
||||
|
||||
async function testAllConnections() {
|
||||
const results = {};
|
||||
for (const alias of listAliases()) {
|
||||
for (const alias of connections.listAliases()) {
|
||||
results[alias] = await testConnection(alias);
|
||||
}
|
||||
return results;
|
||||
@@ -343,36 +57,63 @@ async function testAllConnections() {
|
||||
|
||||
function listTables(alias) {
|
||||
const { config } = getConn(alias);
|
||||
return driver.listTables(config);
|
||||
return firebird.listTables(config);
|
||||
}
|
||||
|
||||
function tableInfo(alias, tableName) {
|
||||
const { config } = getConn(alias);
|
||||
return driver.tableInfo(config, tableName);
|
||||
return firebird.tableInfo(config, tableName);
|
||||
}
|
||||
|
||||
function tableExists(alias, tableName) {
|
||||
const { config } = getConn(alias);
|
||||
return driver.tableExists(config, tableName);
|
||||
return firebird.tableExists(config, tableName);
|
||||
}
|
||||
|
||||
function columnExists(alias, tableName, columnName) {
|
||||
const { config } = getConn(alias);
|
||||
return driver.columnExists(config, tableName, columnName);
|
||||
return firebird.columnExists(config, tableName, columnName);
|
||||
}
|
||||
|
||||
/** Nome do driver de um alias (sempre 'firebird'). */
|
||||
function driverOf(alias) {
|
||||
return 'firebird';
|
||||
return connections.driverOf(alias);
|
||||
}
|
||||
|
||||
/** Encerra recursos (noop para Firebird). */
|
||||
async function closeAll() {
|
||||
await driver.close();
|
||||
await firebird.close();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2) GERADORES DE ID (GEN_ID) — thread-safe
|
||||
// ============================================================
|
||||
|
||||
const GENERATORS = [
|
||||
'GEN_CHATC2_CONVERSAS',
|
||||
'GEN_CONVERSAS_MENSAGENS',
|
||||
'GEN_MENSAGENS_ATENDIMENTOS',
|
||||
'GEN_CHATC2_EQUIPES',
|
||||
'GEN_CHATC2_ETIQUETAS',
|
||||
'GEN_CHATC2_INSTANCIAS',
|
||||
'GEN_CHATC2_MENUS_EMPRESA',
|
||||
'GEN_CHATC2_CSAT_AVALIACOES',
|
||||
'GEN_CHATC2_MOTIVOS_ATENDIMENTO',
|
||||
];
|
||||
|
||||
async function nextId(alias, generatorName) {
|
||||
if (!GENERATORS.includes(generatorName)) {
|
||||
throw new Error('Generator inválido: ' + generatorName);
|
||||
}
|
||||
const result = await query(alias,
|
||||
'SELECT GEN_ID(' + generatorName + ', 1) AS NEW_ID FROM RDB$DATABASE');
|
||||
return result[0].NEW_ID;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3) EXPORTS — API completa (compatível com versão anterior)
|
||||
// ============================================================
|
||||
|
||||
module.exports = {
|
||||
// Acesso a dados
|
||||
// Operações de dados
|
||||
query,
|
||||
execute,
|
||||
testConnection,
|
||||
@@ -383,13 +124,17 @@ module.exports = {
|
||||
columnExists,
|
||||
driverOf,
|
||||
closeAll,
|
||||
// Configuração / aliases
|
||||
databases,
|
||||
DRIVERS,
|
||||
DEFAULT_DRIVER,
|
||||
getConfig,
|
||||
listAliases,
|
||||
listAllDatabases,
|
||||
addDatabase,
|
||||
removeDatabase,
|
||||
nextId,
|
||||
GENERATORS,
|
||||
poolStats: firebird.poolStats,
|
||||
// Configuração / aliases (delegados)
|
||||
databases: connections.databases,
|
||||
customDatabases: connections.customDatabases,
|
||||
DRIVERS: ['firebird'],
|
||||
DEFAULT_DRIVER: 'firebird',
|
||||
getConfig: connections.getConfig,
|
||||
listAliases: connections.listAliases,
|
||||
listAllDatabases: connections.listAllDatabases,
|
||||
addDatabase: connections.addDatabase,
|
||||
removeDatabase: connections.removeDatabase,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user