118 lines
4.2 KiB
JavaScript
118 lines
4.2 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const helmet = require('helmet');
|
|
const path = require('path');
|
|
const routes = require('./routes');
|
|
const authenticateToken = require('./middlewares/auth');
|
|
|
|
const app = express();
|
|
|
|
// Middlewares
|
|
// Segurança: headers HTTP protetores (X-Content-Type-Options, X-Frame-Options, etc.)
|
|
// CSP configurado de forma permissiva para não quebrar inline scripts/styles existentes
|
|
app.use(helmet({
|
|
contentSecurityPolicy: false, // Desabilitado: app usa inline scripts/styles em várias páginas
|
|
crossOriginEmbedderPolicy: false, // Permite carregar recursos cross-origin (Evolution API, etc.)
|
|
}));
|
|
|
|
// CORS: restringível via CORS_ORIGINS (lista separada por vírgula no .env).
|
|
// Requisições same-origin e server-to-server (sem header Origin) sempre passam.
|
|
const corsOrigins = (process.env.CORS_ORIGINS || [process.env.LOCAL_URL, process.env.EXTERNAL_URL].filter(Boolean).join(','))
|
|
.split(',').map(s => s.trim()).filter(Boolean);
|
|
if (corsOrigins.length) {
|
|
app.use(cors({
|
|
origin: function (origin, cb) {
|
|
if (!origin || corsOrigins.indexOf(origin) !== -1) return cb(null, true);
|
|
return cb(null, false);
|
|
},
|
|
}));
|
|
console.log('🔒 CORS restrito a:', corsOrigins.join(', '));
|
|
} else {
|
|
app.use(cors());
|
|
console.log('⚠️ CORS aberto — defina CORS_ORIGINS no .env para restringir.');
|
|
}
|
|
|
|
// Limite de corpo por rota (anti-DoS):
|
|
// - rotas de mídia (envio de mensagem, webhook, evolution) → MAX_FILE_SIZE (grande)
|
|
// - demais rotas → JSON_LIMIT (pequeno), reduz superfície de ataque
|
|
const maxFileSize = process.env.MAX_FILE_SIZE || '50mb';
|
|
const jsonPadrao = process.env.JSON_LIMIT || '2mb';
|
|
console.log('📦 Limite de upload (mídia):', maxFileSize, '| padrão:', jsonPadrao);
|
|
|
|
const bigJson = express.json({ limit: maxFileSize });
|
|
const smallJson = express.json({ limit: jsonPadrao });
|
|
const bigUrl = express.urlencoded({ extended: true, limit: maxFileSize });
|
|
const smallUrl = express.urlencoded({ extended: true, limit: jsonPadrao });
|
|
|
|
function rotaComMidia(req) {
|
|
const p = req.path || '';
|
|
return p.endsWith('/messages') || p.indexOf('/webhook/') !== -1 || p.indexOf('/evolution/') !== -1;
|
|
}
|
|
app.use((req, res, next) => (rotaComMidia(req) ? bigJson : smallJson)(req, res, next));
|
|
app.use((req, res, next) => (rotaComMidia(req) ? bigUrl : smallUrl)(req, res, next));
|
|
|
|
// Static files
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
// Log de requisições
|
|
app.use((req, res, next) => {
|
|
const start = Date.now();
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - start;
|
|
console.log(`${req.method} ${req.originalUrl} - ${res.statusCode} (${duration}ms)`);
|
|
});
|
|
next();
|
|
});
|
|
|
|
// Rotas (a autenticação é aplicada internamente em cada grupo de rotas)
|
|
app.use(routes);
|
|
|
|
// Rota raiz
|
|
app.get('/', (req, res) => {
|
|
res.json({
|
|
name: 'API Chatc2 - Firebird',
|
|
version: '2.0.0',
|
|
description: 'API Firebird com sistema de aliases',
|
|
auth: {
|
|
loginPage: 'GET /app/:alias/login',
|
|
login: 'POST /app/:alias/login',
|
|
myData: 'GET /app/:alias/me',
|
|
dashboard: 'GET /app/:alias/dashboard',
|
|
},
|
|
api: {
|
|
aliases: 'GET /api/aliases',
|
|
health: 'GET /api/:alias/health',
|
|
tables: 'GET /api/:alias/tables',
|
|
tableInfo: 'GET /api/:alias/tables/:tableName',
|
|
query: 'POST /api/:alias/query',
|
|
execute: 'POST /api/:alias/execute',
|
|
},
|
|
authentication: {
|
|
jwt: 'Authorization: Bearer <jwt_token>',
|
|
usu_token: 'X-Usu-Token: <usu_token>',
|
|
},
|
|
routes: 'GET /app/:alias/routes',
|
|
obs: 'Substitua :alias pelo nome do banco (ex: lajedo, alfenas, ...)',
|
|
});
|
|
});
|
|
|
|
// Middleware de erro global — loga o detalhe internamente e responde genérico
|
|
// (evita vazar mensagens de erro do banco/SQL para o cliente)
|
|
app.use((err, req, res, next) => {
|
|
console.error('Erro não tratado:', err);
|
|
res.status(err.status || 500).json({
|
|
success: false,
|
|
error: 'Erro interno do servidor.',
|
|
});
|
|
});
|
|
|
|
// Middleware para rotas não encontradas
|
|
app.use((req, res) => {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: `Rota ${req.method} ${req.originalUrl} não encontrada`,
|
|
});
|
|
});
|
|
|
|
module.exports = app;
|