require('dotenv').config(); const express = require('express'); const path = require('path'); const multer = require('multer'); const { v4: uuidv4 } = require('uuid'); const fs = require('fs'); const pool = require('./db'); const transporter = require('./services/mailer'); const { provisionSuperAdmin } = require('./services/provisioning'); const { createCorsMiddleware } = require('./config/cors'); const { allowedOrigins, getBaseUrl, getStartupBaseUrl, isProduction, jwtSecret: JWT_SECRET, port: PORT } = require('./config/runtime'); const { authenticateToken } = require('./middleware/auth'); const { createAuthRouter } = require('./routes/authRoutes'); const { createUsersRouter } = require('./routes/usersRoutes'); const { createTeamsRouter } = require('./routes/teamsRoutes'); const { createTenantsRouter } = require('./routes/tenantsRoutes'); const { createActivityRouter } = require('./routes/activityRoutes'); const { createOriginsRouter } = require('./routes/originsRoutes'); const { createFunnelsRouter } = require('./routes/funnelsRoutes'); const { createSearchRouter } = require('./routes/searchRoutes'); const { createAttendancesRouter } = require('./routes/attendancesRoutes'); const { createApiKeysRouter } = require('./routes/apiKeysRoutes'); const { createIntegrationsRouter } = require('./routes/integrationsRoutes'); const app = express(); app.use(createCorsMiddleware({ allowedOrigins, isProduction })); app.use(express.json()); // Logger de Requisições app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }); // --- Configuração Multer (Upload Seguro) --- const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, uploadDir); }, filename: (req, file, cb) => { const ext = path.extname(file.originalname).toLowerCase(); cb(null, `${uuidv4()}${ext}`); } }); const upload = multer({ storage: storage, limits: { fileSize: 2 * 1024 * 1024 }, // 2MB fileFilter: (req, file, cb) => { const allowedTypes = ['image/jpeg', 'image/png', 'image/webp']; if (allowedTypes.includes(file.mimetype)) { cb(null, true); } else { cb(new Error('Tipo de arquivo inválido. Apenas JPG, PNG e WEBP são permitidos.')); } } }); app.use('/uploads', express.static(uploadDir, { setHeaders: (res) => { res.set('X-Content-Type-Options', 'nosniff'); } })); // --- API Router --- const apiRouter = express.Router(); apiRouter.use(authenticateToken); // --- Auth Routes --- apiRouter.use(createAuthRouter({ pool, transporter, getBaseUrl, jwtSecret: JWT_SECRET })); apiRouter.use(createUsersRouter({ pool, upload, transporter, getBaseUrl })); apiRouter.use(createTeamsRouter({ pool })); apiRouter.use(createTenantsRouter({ pool, transporter, getBaseUrl })); apiRouter.use(createActivityRouter({ pool })); apiRouter.use(createOriginsRouter({ pool })); apiRouter.use(createFunnelsRouter({ pool })); apiRouter.use(createSearchRouter({ pool })); apiRouter.use(createAttendancesRouter({ pool })); apiRouter.use(createApiKeysRouter({ pool })); apiRouter.use(createIntegrationsRouter({ pool })); // Mount the API Router app.use('/api', apiRouter); // Serve static files if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(__dirname, 'dist'))); app.get('*', (req, res) => { // Avoid hijacking API requests if (req.url.startsWith('/api')) return res.status(404).json({ error: 'API route not found' }); res.sendFile(path.join(__dirname, 'dist/index.html')); }); } app.listen(PORT, async () => { await provisionSuperAdmin({ pool, transporter, getStartupBaseUrl }); console.log(`🚀 Servidor Backend rodando em http://localhost:${PORT}`); });