feat: add observability logs to stock waiting room for easier debugging in Portainer
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m8s

This commit is contained in:
Cauê Faleiros
2026-05-26 15:11:00 -03:00
parent 1f8baabf69
commit 69f99b97c5

View File

@@ -199,6 +199,10 @@ app.post('/api/data', authenticateAPIKey, async (req, res) => {
})();
});
// In-memory Waiting Room for WhatsApp Marketing
const waitingRoom = {};
const N8N_WHATSAPP_TRIGGER_URL = process.env.N8N_WHATSAPP_TRIGGER_URL || 'http://localhost:5678/webhook/whatsapp';
// POST stock (for n8n)
app.post('/api/stock', authenticateAPIKey, async (req, res) => {
res.status(201).json({ message: 'Stock data received, processing in background' });
@@ -225,13 +229,91 @@ app.post('/api/stock', authenticateAPIKey, async (req, res) => {
const idProduto = item.idProduto || item.ID_Produto || '';
if (!idProduto) continue;
const delta = parseInt(item.delta_estoque) || 0;
const nomeStr = item.nome || item.Descricao_Produto || 'Unknown';
const values = [
String(idProduto),
item.nome || item.Descricao_Produto || 'Unknown',
nomeStr,
parseInt(item.saldo) || 0,
parseInt(item.delta_estoque) || 0
delta
];
await client.query(insertQuery, values);
// Waiting Room / Debounce Logic
if (delta >= 100) {
const baseProductName = nomeStr.split(' TAMANHO')[0].trim();
if (!waitingRoom[baseProductName]) {
console.log(`[Waiting Room] Nova entrada: ${baseProductName}. Iniciando timer de 30m...`);
waitingRoom[baseProductName] = {
total_delta: 0,
items: [],
timeout: null
};
} else {
console.log(`[Waiting Room] Atualização: ${baseProductName}. Timer reiniciado (30m).`);
}
waitingRoom[baseProductName].total_delta += delta;
waitingRoom[baseProductName].items.push({
id: String(idProduto),
nome: nomeStr,
delta: delta,
saldo: parseInt(item.saldo) || 0
});
if (waitingRoom[baseProductName].timeout) {
clearTimeout(waitingRoom[baseProductName].timeout);
}
waitingRoom[baseProductName].timeout = setTimeout(async () => {
try {
const aggData = waitingRoom[baseProductName];
delete waitingRoom[baseProductName]; // Clear from waiting room
console.log(`[Waiting Room] Timer finalizado para ${baseProductName}. Buscando top buyers...`);
// Find Top 100 buyers for this Base Product
const topBuyersQuery = `
SELECT
MAX(cliente_nome) as nome,
cliente_fone as fone,
SUM(quantidade) as total_comprado
FROM orders
WHERE produto_descricao LIKE $1
AND cliente_fone IS NOT NULL
AND cliente_fone != ''
GROUP BY cliente_fone
ORDER BY total_comprado DESC
LIMIT 100;
`;
const topBuyersResult = await pool.query(topBuyersQuery, [`${baseProductName}%`]);
// Only trigger if we actually have buyers
if (topBuyersResult.rows.length > 0) {
console.log(`[Waiting Room] Disparando webhook do WhatsApp para ${topBuyersResult.rows.length} clientes (${baseProductName})...`);
fetch(N8N_WHATSAPP_TRIGGER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
baseProduct: baseProductName,
total_delta: aggData.total_delta,
sizes: aggData.items,
customers: topBuyersResult.rows
})
}).then(res => {
if(res.ok) console.log(`[Waiting Room] Sucesso! Webhook disparado.`);
else console.log(`[Waiting Room] Aviso: Webhook retornou status ${res.status}`);
}).catch(err => console.error("Failed to trigger WhatsApp webhook:", err));
} else {
console.log(`[Waiting Room] Nenhum cliente encontrado com telefone válido para ${baseProductName}. Webhook cancelado.`);
}
} catch (err) {
console.error("Error in Waiting Room timeout:", err);
}
}, 1800000); // 30 minutes
}
}
await client.query('COMMIT');