Files
Exchange/server/api/amqp.js
2025-11-07 22:24:40 +01:00

101 lines
3.8 KiB
JavaScript
Executable File

'use strict';
exports.__esModule = true;
exports.publishQueueConnection = void 0;
// This script fetches queued messages from RabbitMQ and delivers these to SMTP
var nodemailer = require("nodemailer");
var amqp = require("amqp");
var queueHost = process.env.AMQP_URL;
var queueName = 'outgoing';
var smtpHost = {
host: process.env.NODEMAILER_HOST,
port: process.env.NODEMAILER_PORT,
// NB! Must be pooled connection, otherwise 'idle' is never fired and nothing gets sent
pool: true,
auth: {
user: process.env.NODEMAILER_USER,
pass: process.env.NODEMAILER_PASS
},
tls: {
// testserver uses self signed certificate, so we need to lax a bit
rejectUnauthorized: false
},
logger: false
};
// array of prefetched messages waiting for delivery
var waiting = [];
// Create a SMTP transporter object
var transporter = nodemailer.createTransport(smtpHost, {
// default message fields
from: process.env.SENDER_ADDRESS
});
// Create connection to RabbitMQ
var queueConnection = amqp.createConnection({
url: queueHost
});
queueConnection.on('error', function (e) {
console.log('Error from amqp: ', e);
});
queueConnection.on('ready', function (err) {
console.log('AMPQ server is running on port 5672.');
queueConnection.queue(queueName, { durable: true }, function (q) {
q.bind('#');
q.subscribe({
ack: true,
prefetchCount: 10 // prefetch 10 messages
}, function (message, headers, deliveryInfo, ack) {
// check if the message object is even valid
if (!message || !message.to) {
console.log('Invalid message, skipping');
// reject, do not requeue
return ack.reject();
}
// push to cache
waiting.push({
message: message,
deliveryTag: deliveryInfo.deliveryTag.toString('hex'),
ack: ack
});
// try to flush cached messages by sending these to SMTP
flushWaitingMessages();
});
});
});
// Whenever transporter gets into idling, try to send some mail
transporter.on('idle', flushWaitingMessages);
// Flushes cached messages to nodemailer for delivery
function flushWaitingMessages() {
// actual send function
var send = function (data) {
// sendMail does not immediatelly send, instead it tries to allocate a free connection to SMTP server
// and if fails, then pushes the message into internal queue. As we only prefetch 10 messages
// then the internal queue can never grow into something too large. At most there will be 5 messages
// idling in the queue (another 5 are being currently sent by the default number of 5 connections)
transporter.sendMail(data.message, function (err, info) {
if (err) {
console.log('Message failed (%s): %s', data.deliveryTag, err.message);
// reject and requeue on error (wait 1 sec. before requeueing)
// NB! If the failure is permanent then this approach results in an
// infinite loop since failing message is never removed from the queue
setTimeout(function () {
data.ack.reject(true);
}, 1000);
return;
}
console.log('Message delivered (%s): %s', data.deliveryTag, info.response);
data.ack.acknowledge();
});
};
// send cached messages if transporter is idling
while (transporter.isIdle() && waiting.length) {
send(waiting.shift());
}
}
exports.publishQueueConnection = function (mailOptions) {
console.log('publishQueueConnection');
queueConnection.publish(queueName, mailOptions, function (err) {
if (err) {
console.log(err);
}
});
};