From ff2f5aca29d50544d6ec1c1ab874d56c6ba13642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Tue, 5 May 2026 11:26:56 -0300 Subject: [PATCH] feat: add multi-dashboard routing support for n8n - Refactor `sendToN8n` service to accept dynamic webhook URLs. - Update `order-status` controller to fan-out payload to both STATUS and GRAPHS webhooks. - Add new endpoints and controllers for stock and graphs updates (`/stock-update`, `/graphs-update`). - Maintain backward compatibility with existing `N8N_WEBHOOK_URL` environment variable. --- src/controllers/webhook.controller.ts | 77 ++++++++++++++++++++++++++- src/routes/webhook.route.ts | 6 ++- src/services/n8n.service.ts | 4 +- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/controllers/webhook.controller.ts b/src/controllers/webhook.controller.ts index d2166a9..522212f 100644 --- a/src/controllers/webhook.controller.ts +++ b/src/controllers/webhook.controller.ts @@ -110,9 +110,84 @@ export const handleTinyOrderUpdate = async (req: Request, res: Response): Promis }; console.log('Forwarding formatted payload to n8n...'); - await sendToN8n(finalPayload); + + // 1. Send to the Status Dashboard + const statusUrl = process.env.N8N_WEBHOOK_STATUS || process.env.N8N_WEBHOOK_URL; + if (statusUrl) { + console.log('-> Sending to Status Workflow...'); + // We don't await here so they send in parallel, or we can await them one by one. + // Awaiting sequentially is safer for error handling. + await sendToN8n(finalPayload, statusUrl); + } + + // 2. Send a copy to the Graphs Dashboard (it will just use the data it needs, like values and dates) + const graphsUrl = process.env.N8N_WEBHOOK_GRAPHS; + if (graphsUrl) { + console.log('-> Sending copy to Graphs Workflow...'); + await sendToN8n(finalPayload, graphsUrl); + } } catch (error) { console.error('Error handling Tiny webhook:', error); } }; + +export const handleTinyStockUpdate = async (req: Request, res: Response): Promise => { + try { + const expectedToken = process.env.TINY_WEBHOOK_SECRET; + const providedToken = req.query.token; + + if (expectedToken && providedToken !== expectedToken) { + console.warn('Unauthorized webhook attempt on stock. Invalid or missing token.'); + res.status(401).json({ error: 'Unauthorized' }); + return; + } + res.status(200).send('OK'); + + const targetUrl = process.env.N8N_WEBHOOK_STOCK; + if (!targetUrl) { + console.warn('N8N_WEBHOOK_STOCK is not defined in environment variables. Skipping forward.'); + return; + } + + console.log('Received stock webhook from Tiny. Forwarding to n8n...'); + let payload = req.body || {}; + if (payload && typeof payload.dados === 'string') { + try { payload.dados = JSON.parse(payload.dados); } catch (e) {} + } + + await sendToN8n(payload, targetUrl); + } catch (error) { + console.error('Error handling Tiny stock webhook:', error); + } +}; + +export const handleTinyGraphsUpdate = async (req: Request, res: Response): Promise => { + try { + const expectedToken = process.env.TINY_WEBHOOK_SECRET; + const providedToken = req.query.token; + + if (expectedToken && providedToken !== expectedToken) { + console.warn('Unauthorized webhook attempt on graphs. Invalid or missing token.'); + res.status(401).json({ error: 'Unauthorized' }); + return; + } + res.status(200).send('OK'); + + const targetUrl = process.env.N8N_WEBHOOK_GRAPHS; + if (!targetUrl) { + console.warn('N8N_WEBHOOK_GRAPHS is not defined in environment variables. Skipping forward.'); + return; + } + + console.log('Received graphs webhook from Tiny. Forwarding to n8n...'); + let payload = req.body || {}; + if (payload && typeof payload.dados === 'string') { + try { payload.dados = JSON.parse(payload.dados); } catch (e) {} + } + + await sendToN8n(payload, targetUrl); + } catch (error) { + console.error('Error handling Tiny graphs webhook:', error); + } +}; diff --git a/src/routes/webhook.route.ts b/src/routes/webhook.route.ts index 48ecea6..f3e5f80 100644 --- a/src/routes/webhook.route.ts +++ b/src/routes/webhook.route.ts @@ -1,8 +1,10 @@ import { Router } from 'express'; -import { handleTinyOrderUpdate } from '../controllers/webhook.controller'; +import { handleTinyOrderUpdate, handleTinyStockUpdate, handleTinyGraphsUpdate } from '../controllers/webhook.controller'; const router = Router(); router.post('/tiny/order-status', handleTinyOrderUpdate); +router.post('/tiny/stock-update', handleTinyStockUpdate); +router.post('/tiny/graphs-update', handleTinyGraphsUpdate); -export default router; +export default router; \ No newline at end of file diff --git a/src/services/n8n.service.ts b/src/services/n8n.service.ts index 4a98502..059d4d4 100644 --- a/src/services/n8n.service.ts +++ b/src/services/n8n.service.ts @@ -1,7 +1,7 @@ import axios from 'axios'; -export const sendToN8n = async (data: any): Promise => { - const n8nWebhookUrl = process.env.N8N_WEBHOOK_URL; +export const sendToN8n = async (data: any, targetWebhookUrl?: string): Promise => { + const n8nWebhookUrl = targetWebhookUrl || process.env.N8N_WEBHOOK_URL; const n8nAuthToken = process.env.N8N_AUTH_TOKEN; if (!n8nWebhookUrl) {