153 lines
6.5 KiB
JavaScript
153 lines
6.5 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.buildContactRoutes = buildContactRoutes;
|
|
const express_1 = require("express");
|
|
const zod_1 = require("zod");
|
|
const prisma_1 = require("../../infra/database/prisma");
|
|
const whatsapp_1 = require("../../shared/utils/whatsapp");
|
|
const chat_cache_1 = require("../chats/chat.cache");
|
|
function buildContactRoutes() {
|
|
const router = (0, express_1.Router)();
|
|
// ── GET /api/contacts/stats/overview — deve vir ANTES de /:id ─────────────
|
|
router.get('/stats/overview', async (req, res) => {
|
|
const tenantId = req.tenantId;
|
|
const instanceId = req.query['instanceId'];
|
|
const where = { tenantId };
|
|
if (instanceId)
|
|
where['instanceId'] = instanceId;
|
|
const [total, blocked, flagged] = await Promise.all([
|
|
prisma_1.prisma.contact.count({ where }),
|
|
prisma_1.prisma.contact.count({ where: { ...where, isBlocked: true } }),
|
|
prisma_1.prisma.contact.count({ where: { ...where, flagRestricao: true } }),
|
|
]);
|
|
res.json({ total, blocked, flagged, active: total - blocked });
|
|
});
|
|
// ── GET /api/contacts — lista paginada ────────────────────────────────────
|
|
router.get('/', async (req, res) => {
|
|
const tenantId = req.tenantId;
|
|
const instanceId = req.query['instanceId'];
|
|
const search = req.query['search'];
|
|
const blocked = req.query['blocked'];
|
|
const limit = Math.min(Number(req.query['limit'] ?? 50), 200);
|
|
const offset = Number(req.query['offset'] ?? 0);
|
|
const where = { tenantId };
|
|
if (instanceId)
|
|
where['instanceId'] = instanceId;
|
|
if (blocked === 'true')
|
|
where['isBlocked'] = true;
|
|
if (blocked === 'false')
|
|
where['isBlocked'] = false;
|
|
if (search) {
|
|
where['OR'] = [
|
|
{ name: { contains: search, mode: 'insensitive' } },
|
|
{ verifiedName: { contains: search, mode: 'insensitive' } },
|
|
{ notify: { contains: search, mode: 'insensitive' } },
|
|
{ phone: { contains: search } },
|
|
{ jid: { contains: search } },
|
|
];
|
|
}
|
|
const [total, contacts] = await Promise.all([
|
|
prisma_1.prisma.contact.count({ where }),
|
|
prisma_1.prisma.contact.findMany({
|
|
where,
|
|
// Tiebreaker por id é obrigatório: muitos contatos compartilham o mesmo
|
|
// updatedAt (bulk sync), e sem segunda coluna a paginação por OFFSET
|
|
// devolve linhas sobrepostas entre páginas — quebra "carregar mais".
|
|
orderBy: [{ updatedAt: 'desc' }, { id: 'asc' }],
|
|
take: limit,
|
|
skip: offset,
|
|
select: {
|
|
id: true,
|
|
instanceId: true,
|
|
jid: true,
|
|
name: true,
|
|
verifiedName: true,
|
|
notify: true,
|
|
phone: true,
|
|
avatarUrl: true,
|
|
scoreReputacao: true,
|
|
flagRestricao: true,
|
|
isBlocked: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
}),
|
|
]);
|
|
res.json({
|
|
total,
|
|
contacts: contacts.map(c => ({
|
|
...c,
|
|
displayName: c.name || c.verifiedName || c.notify || (c.phone ? `+${c.phone}` : (0, whatsapp_1.normalizeJid)(c.jid).split('@')[0])
|
|
}))
|
|
});
|
|
});
|
|
// ── GET /api/contacts/:id — detalhe ──────────────────────────────────────
|
|
router.get('/:id', async (req, res) => {
|
|
const tenantId = req.tenantId;
|
|
const id = req.params['id'];
|
|
const contact = await prisma_1.prisma.contact.findFirst({
|
|
where: { id, tenantId },
|
|
include: {
|
|
chats: {
|
|
orderBy: { lastMessageAt: 'desc' },
|
|
take: 1,
|
|
select: { id: true, unreadCount: true, lastMessageAt: true },
|
|
},
|
|
},
|
|
});
|
|
if (!contact) {
|
|
res.status(404).json({ error: 'Contato não encontrado' });
|
|
return;
|
|
}
|
|
res.json(contact);
|
|
});
|
|
// ── PATCH /api/contacts/:id — editar nome / bloquear ─────────────────────
|
|
router.patch('/:id', async (req, res) => {
|
|
const tenantId = req.tenantId;
|
|
const id = req.params['id'];
|
|
const schema = zod_1.z.object({
|
|
name: zod_1.z.string().min(1).max(255).optional(),
|
|
isBlocked: zod_1.z.boolean().optional(),
|
|
});
|
|
let body;
|
|
try {
|
|
body = schema.parse(req.body);
|
|
}
|
|
catch (err) {
|
|
res.status(400).json({ error: err.errors ?? err.message });
|
|
return;
|
|
}
|
|
const existing = await prisma_1.prisma.contact.findFirst({ where: { id, tenantId } });
|
|
if (!existing) {
|
|
res.status(404).json({ error: 'Contato não encontrado' });
|
|
return;
|
|
}
|
|
const updated = await prisma_1.prisma.contact.update({ where: { id }, data: body });
|
|
if (body.name && existing.instanceId) {
|
|
(0, chat_cache_1.invalidateChatListCache)(tenantId, existing.instanceId).catch(() => { });
|
|
}
|
|
res.json(updated);
|
|
});
|
|
// ── DELETE /api/contacts/:id ──────────────────────────────────────────────
|
|
router.delete('/:id', async (req, res) => {
|
|
const tenantId = req.tenantId;
|
|
const id = req.params['id'];
|
|
const existing = await prisma_1.prisma.contact.findFirst({ where: { id, tenantId } });
|
|
if (!existing) {
|
|
res.status(404).json({ error: 'Contato não encontrado' });
|
|
return;
|
|
}
|
|
// Desvincular chats antes de deletar o contato
|
|
await prisma_1.prisma.chat.updateMany({
|
|
where: { contactId: id },
|
|
data: { contactId: null },
|
|
});
|
|
await prisma_1.prisma.contact.delete({ where: { id } });
|
|
if (existing.instanceId) {
|
|
(0, chat_cache_1.invalidateChatListCache)(tenantId, existing.instanceId).catch(() => { });
|
|
}
|
|
res.status(204).send();
|
|
});
|
|
return router;
|
|
}
|