Extract remaining backend routes

This commit is contained in:
Cauê Faleiros
2026-05-29 14:42:07 -03:00
parent e64654c820
commit 871d1eb850
7 changed files with 676 additions and 595 deletions

View File

@@ -0,0 +1,155 @@
const express = require('express');
const crypto = require('crypto');
const { requireRole } = require('../middleware/auth');
const { recordActivity } = require('../services/activityService');
const requireApiKey = (req, res) => {
if (req.user.is_api_key) return true;
res.status(403).json({ error: 'Endpoint restrito a chaves de API.' });
return false;
};
const createIntegrationsRouter = ({ pool }) => {
const router = express.Router();
router.get('/integration/users', requireRole(['admin']), async (req, res) => {
if (!requireApiKey(req, res)) return;
try {
const [rows] = await pool.query(
'SELECT u.id, u.name, u.email, t.name as team_name FROM users u LEFT JOIN teams t ON u.team_id = t.id WHERE u.tenant_id = ? AND u.status = "active"',
[req.user.tenant_id]
);
res.json(rows);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.get('/integration/origins', requireRole(['admin']), async (req, res) => {
if (!requireApiKey(req, res)) return;
try {
const [groups] = await pool.query('SELECT id, name FROM origin_groups WHERE tenant_id = ?', [req.user.tenant_id]);
if (groups.length === 0) return res.json([]);
const [items] = await pool.query('SELECT origin_group_id, name FROM origin_items WHERE origin_group_id IN (?) ORDER BY created_at ASC', [groups.map(g => g.id)]);
const [teams] = await pool.query('SELECT id as team_id, name as team_name, origin_group_id FROM teams WHERE tenant_id = ? AND origin_group_id IS NOT NULL', [req.user.tenant_id]);
const result = groups.map(g => ({
group_name: g.name,
origins: items.filter(i => i.origin_group_id === g.id).map(i => i.name),
assigned_teams: teams.filter(t => t.origin_group_id === g.id).map(t => ({ id: t.team_id, name: t.team_name }))
}));
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.get('/integration/funnels', requireRole(['admin']), async (req, res) => {
if (!requireApiKey(req, res)) return;
try {
const [funnels] = await pool.query('SELECT id, name FROM funnels WHERE tenant_id = ?', [req.user.tenant_id]);
if (funnels.length === 0) return res.json([]);
const [stages] = await pool.query('SELECT funnel_id, name, order_index FROM funnel_stages WHERE funnel_id IN (?) ORDER BY order_index ASC', [funnels.map(f => f.id)]);
const [teams] = await pool.query('SELECT id as team_id, name as team_name, funnel_id FROM teams WHERE tenant_id = ? AND funnel_id IS NOT NULL', [req.user.tenant_id]);
const result = funnels.map(f => ({
funnel_name: f.name,
stages: stages.filter(s => s.funnel_id === f.id).map(s => s.name),
assigned_teams: teams.filter(t => t.funnel_id === f.id).map(t => ({ id: t.team_id, name: t.team_name }))
}));
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.post('/integration/attendances', requireRole(['admin']), async (req, res) => {
if (!requireApiKey(req, res)) return;
const {
user_id,
origin,
funnel_stage,
title,
full_summary,
score,
first_response_time_min,
handling_time_min,
product_requested,
product_sold,
converted,
attention_points,
improvement_points
} = req.body;
if (!user_id || !origin || !funnel_stage || !title) {
return res.status(400).json({ error: 'Campos obrigatórios ausentes: user_id, origin, funnel_stage, title' });
}
try {
const [users] = await pool.query('SELECT id FROM users WHERE id = ? AND tenant_id = ? AND status = "active"', [user_id, req.user.tenant_id]);
if (users.length === 0) return res.status(400).json({ error: 'user_id inválido, inativo ou não pertence a esta organização.' });
const attId = `att_${crypto.randomUUID().split('-')[0]}`;
await pool.query(
`INSERT INTO attendances (
id, tenant_id, user_id, title, full_summary, score,
first_response_time_min, handling_time_min,
funnel_stage, origin, product_requested, product_sold,
converted, attention_points, improvement_points
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
attId,
req.user.tenant_id,
user_id,
title,
full_summary || null,
score || 0,
first_response_time_min || 0,
handling_time_min || 0,
funnel_stage,
origin,
product_requested || null,
product_sold || null,
converted ? 1 : 0,
attention_points ? JSON.stringify(attention_points) : null,
improvement_points ? JSON.stringify(improvement_points) : null
]
);
if (converted) {
const [managers] = await pool.query(
"SELECT id FROM users WHERE tenant_id = ? AND role IN ('admin', 'manager') AND id != ?",
[req.user.tenant_id, user_id]
);
const [agentInfo] = await pool.query("SELECT name FROM users WHERE id = ?", [user_id]);
const agentName = agentInfo[0]?.name || 'Um agente';
for (const m of managers) {
await recordActivity(pool, {
userId: m.id,
type: 'success',
title: 'Venda Fechada!',
message: `${agentName} converteu um lead em ${funnel_stage}.`,
link: `/attendances/${attId}`,
});
}
}
res.status(201).json({ id: attId, message: 'Atendimento registrado com sucesso.' });
} catch (error) {
console.error('Integration Error:', error);
res.status(500).json({ error: error.message });
}
});
return router;
};
module.exports = {
createIntegrationsRouter,
};