Migração para PostgreSQL multi-driver + correções de segurança
- Camada de banco unificada (src/database.js): drivers Postgres/Firebird, tradutor de SQL, suporte a schema e pool de conexões - Conexões: novo_local (Postgres externo) e firebird_local (legado) - Tela de rotas da API redesenhada (auth, params, exemplos de body) - Correções de segurança (críticos/altos/médios/baixos): XSS no chat, escalonamento de privilégio, mídia autenticada, SQL restrito a gerente, JWT sem fallback + issuer, IDOR em conversas, CORS por allowlist, rate-limit no login, limites de corpo por rota - Deploy alinhado: install.sh grava .env com PG_*, migracoes.js driver-aware Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
const db = require('../database');
|
||||
const { listAliases } = db;
|
||||
const { isGerente } = require('../middlewares/roles');
|
||||
|
||||
class GenericController {
|
||||
/**
|
||||
* Executa uma consulta SQL genérica (SELECT)
|
||||
*/
|
||||
static async query(req, res) {
|
||||
try {
|
||||
const { alias } = req.params;
|
||||
const { sql, params } = req.body;
|
||||
|
||||
if (!(await isGerente(req))) {
|
||||
return res.status(403).json({ success: false, error: 'Apenas gerentes podem executar SQL direto.' });
|
||||
}
|
||||
|
||||
if (!sql) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'O campo "sql" é obrigatório.',
|
||||
});
|
||||
}
|
||||
|
||||
const result = await db.query(alias, sql, params || []);
|
||||
res.json({ success: true, alias, data: result, count: result.length });
|
||||
} catch (err) {
|
||||
console.error('Erro no controller query:', err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executa um comando SQL (INSERT, UPDATE, DELETE)
|
||||
*/
|
||||
static async execute(req, res) {
|
||||
try {
|
||||
const { alias } = req.params;
|
||||
const { sql, params } = req.body;
|
||||
|
||||
if (!(await isGerente(req))) {
|
||||
return res.status(403).json({ success: false, error: 'Apenas gerentes podem executar SQL direto.' });
|
||||
}
|
||||
|
||||
if (!sql) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'O campo "sql" é obrigatório.',
|
||||
});
|
||||
}
|
||||
|
||||
const result = await db.execute(alias, sql, params || []);
|
||||
res.json({ success: true, alias, ...result });
|
||||
} catch (err) {
|
||||
console.error('Erro no controller execute:', err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista todas as tabelas do banco
|
||||
*/
|
||||
static async listTables(req, res) {
|
||||
try {
|
||||
const { alias } = req.params;
|
||||
const tables = await db.listTables(alias);
|
||||
res.json({ success: true, alias, data: tables, count: tables.length });
|
||||
} catch (err) {
|
||||
console.error('Erro ao listar tabelas:', err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna a estrutura de uma tabela (colunas e tipos)
|
||||
*/
|
||||
static async tableInfo(req, res) {
|
||||
try {
|
||||
const { alias, tableName } = req.params;
|
||||
|
||||
if (!tableName) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'O parâmetro "tableName" é obrigatório.',
|
||||
});
|
||||
}
|
||||
|
||||
const columns = await db.tableInfo(alias, tableName);
|
||||
res.json({ success: true, alias, tableName, columns, count: columns.length });
|
||||
} catch (err) {
|
||||
console.error('Erro ao obter info da tabela:', err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check do alias específico
|
||||
*/
|
||||
static async healthCheck(req, res) {
|
||||
try {
|
||||
const { alias } = req.params;
|
||||
const isConnected = await db.testConnection(alias);
|
||||
res.json({
|
||||
success: true,
|
||||
alias,
|
||||
connected: isConnected,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista todos os aliases disponíveis
|
||||
*/
|
||||
static async listAliases(req, res) {
|
||||
try {
|
||||
const aliases = listAliases();
|
||||
const statuses = {};
|
||||
|
||||
for (const a of aliases) {
|
||||
statuses[a] = await db.testConnection(a);
|
||||
}
|
||||
|
||||
res.json({ success: true, aliases, status: statuses });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GenericController;
|
||||
Reference in New Issue
Block a user