From 6afb915d8a75e08373d6949b5086c0247c2e6b3d Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 27 Mar 2026 19:46:15 +0100 Subject: [PATCH] Initial: AMQP CloudEvent to email forwarder --- Dockerfile | 6 ++++ app.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 7 +++++ module.yml | 34 ++++++++++++++++++++ package.json | 9 ++++++ 5 files changed, 134 insertions(+) create mode 100644 Dockerfile create mode 100644 app.js create mode 100644 docker-compose.yml create mode 100644 module.yml create mode 100644 package.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f69259f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM node:22-alpine +WORKDIR /app +COPY package.json ./ +RUN npm install --production +COPY app.js ./ +CMD ["node", "app.js"] diff --git a/app.js b/app.js new file mode 100644 index 0000000..ef688e1 --- /dev/null +++ b/app.js @@ -0,0 +1,78 @@ +const amqp = require("amqplib"); +const nodemailer = require("nodemailer"); + +const { + AMQP_URL, + AMQP_EXCHANGE, + AMQP_QUEUE = "email-forwarder", + SMTP_HOST, + SMTP_PORT = "25", + NOTIFY_TO, + NOTIFY_FROM = "hal@novox.be", +} = process.env; + +async function main() { + console.log(`Connecting to AMQP: ${AMQP_URL?.replace(/\/\/.*@/, "//***@")}`); + const conn = await amqp.connect(AMQP_URL); + const ch = await conn.createChannel(); + + await ch.assertQueue(AMQP_QUEUE, { durable: true }); + await ch.bindQueue(AMQP_QUEUE, AMQP_EXCHANGE, ""); + console.log(`Bound queue '${AMQP_QUEUE}' to exchange '${AMQP_EXCHANGE}'`); + + const transport = nodemailer.createTransport({ + host: SMTP_HOST, + port: parseInt(SMTP_PORT), + secure: false, + tls: { rejectUnauthorized: false }, + }); + + console.log(`SMTP: ${SMTP_HOST}:${SMTP_PORT}`); + console.log(`Forwarding to: ${NOTIFY_TO}`); + console.log("Waiting for messages...\n"); + + ch.consume(AMQP_QUEUE, async (msg) => { + if (!msg) return; + + try { + const headers = {}; + for (const [k, v] of Object.entries(msg.properties.headers || {})) { + headers[k] = Buffer.isBuffer(v) ? v.toString() : v; + } + + const body = JSON.parse(msg.content.toString()); + const subjectType = headers.subjecttype || "unknown"; + const brandCode = body.brandCode || "unknown"; + const ref = body.transactionReference || "unknown"; + + const subject = `[CloudEvent] ${subjectType} / ${brandCode} / ${ref}`; + const html = ` +

CloudEvent received

+

Headers

+
${JSON.stringify(headers, null, 2)}
+

CDM Payload

+
${JSON.stringify(body, null, 2)}
+
+

From: AH BE Task Connector → ${AMQP_EXCHANGE} → ${AMQP_QUEUE}

+ `; + + await transport.sendMail({ + from: NOTIFY_FROM, + to: NOTIFY_TO, + subject, + html, + }); + + console.log(`Forwarded: ${subject} → ${NOTIFY_TO}`); + ch.ack(msg); + } catch (err) { + console.error("Failed to process message:", err.message); + ch.nack(msg, false, true); + } + }); +} + +main().catch((err) => { + console.error("Fatal:", err); + process.exit(1); +}); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..28cec34 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + amqp-email-forwarder: + image: registry-api.novox.be/novox/amqp-email-forwarder:latest + restart: unless-stopped + env_file: .env + extra_hosts: + - "host.docker.internal:host-gateway" diff --git a/module.yml b/module.yml new file mode 100644 index 0000000..fd2b8a3 --- /dev/null +++ b/module.yml @@ -0,0 +1,34 @@ +manifest: 2 +name: amqp-email-forwarder +version: "1.0.0" +description: "Consumes CloudEvents from AMQP and forwards as email notifications" + +service: + container: amqp-email-forwarder + +docker: + - image: amqp-email-forwarder + context: . + dockerfile: Dockerfile + +env: + AMQP_URL: + default: "amqp://EMAILDELIVERY_T:ke-O8ws9j8EJQCbDmso-MpoB@localhost:5679/EMAILDELIVERY_T" + AMQP_EXCHANGE: + default: "News.TransactionalEmailing.Command" + AMQP_QUEUE: + default: "email-forwarder" + SMTP_HOST: + default: "localhost" + SMTP_PORT: + default: "25" + NOTIFY_TO: + default: "jochen.schoubben@mediahuis.be" + NOTIFY_FROM: + default: "hal@novox.be" + +package: + - docker-compose.yml + - Dockerfile + - app.js + - package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..f85d891 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "amqp-email-forwarder", + "version": "1.0.0", + "private": true, + "dependencies": { + "amqplib": "^0.10.4", + "nodemailer": "^6.9.15" + } +}