diff --git a/.gitignore b/.gitignore index cef0ae3..a3e55f4 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ bower_components haraka-update.sh data/config/* data/resque/* +resque/* diff --git a/index.js b/index.js index 3744519..e3671f9 100644 --- a/index.js +++ b/index.js @@ -4,10 +4,14 @@ const fs = require('fs') const path = require('path') exports.register = function () { - this.inherits('auth/auth_base') this.logdebug('register called') + + // this allow us to handle authentication here + this.inherits('auth/auth_base') this.load_resque_json() + // based on our debug, queue_outbound is called before queue + // so we can POST resque, then discard trans inside of queue this.register_hook('queue_outbound', 'do_resque'); this.register_hook('queue', 'discard'); } @@ -58,6 +62,11 @@ function resqueInitUsers (plugin) { plugin.cfg.users = users } +/** + * Method use to load configuration + * + * @return void + */ exports.load_resque_json = function () { const plugin = this plugin.loginfo(plugin, 'loading config') @@ -74,62 +83,71 @@ exports.load_resque_json = function () { resqueInitUsers(plugin) } -// Hook to add to queue +/** + * This is the main method of our plugin. + * + */ exports.do_resque = async function (next, connection) { const plugin = this + const transaction = connection.transaction // get current user const auth = connection.results.get('auth')?.user if (!auth) { + // auth failed if we don't have the credential return next(DENYDISCONNECT, '5.7.3 Authentication unsuccessful.') } const user = plugin.cfg.users[auth] if (!auth) { + // If somehow user get there and we can't find user in config + // then it should fail return next(DENYDISCONNECT, '5.3.5 Incorrect authentication data.') } - const transaction = connection.transaction - const data = { + // proceed in prepping post data + const postData = { "uuid": transaction.uuid, "resque-user": auth } - plugin.loginfo(plugin, `Processing transaction '${data.uuid} for user '${auth}'`) + plugin.loginfo(plugin, `Processing transaction '${postData.uuid} for user '${auth}'`) - const file = path.join(plugin.qDir, transaction.uuid) + const filePath = path.join(plugin.qDir, transaction.uuid) try { // create temp file so we can read as string - plugin.loginfo(plugin, `Creating '${file}'`) - const ws = fs.createWriteStream(file) + plugin.loginfo(plugin, `Creating '${filePath}'`) + const ws = fs.createWriteStream(filePath) await new Promise((resolve, reject) => { ws.on('finish', resolve).on('error', reject) transaction.message_stream.pipe(ws) }) ws.end() // close the stream - const eml = fs.readFileSync(file).toString() + + const eml = fs.readFileSync(filePath).toString() if (! plugin.cfg.main.keep_message) { // cleanup file after success - plugin.loginfo(plugin, `Deleting '${file}'`) - await fs.promises.unlink(file) + plugin.loginfo(plugin, `Deleting '${filePath}'`) + await fs.promises.unlink(filePath) } + // map eml message base on configuration if (plugin.cfg.map.message) { - data[plugin.cfg.map.message] = eml + postData[plugin.cfg.map.message] = eml } } catch (err) { - transaction.results.add(plugin, `Error reading message_stream: '${err}'`) - + plugin.logwarn(plugin, `Stream read error: '${err}'`) return next(DENYSOFT, `458 – Unable to queue messages for node: '${err}'`) } - // https://oxylabs.io/blog/nodejs-fetch-api + // fallback main api_url if user doesn't have one const api_url = user.api_url ?? plugin.cfg.main.api_url + // initialize our custom POST header const customHeaders = { "accept": "application/json", "Content-Type": "application/json", @@ -137,30 +155,30 @@ exports.do_resque = async function (next, connection) { // fyi, NO need for content length } + // build axios options const options = { headers: customHeaders } try { plugin.loginfo(plugin, `Posting message to: ${api_url}`) - await axios.post(api_url, data, options) + await axios.post(api_url, postData, options) } catch (err) { if (err.response) { const rsp = JSON.stringify(err.response.data) - plugin.logerror(plugin, `HTTP error posting message to resque: '${rsp}'`) + plugin.logerror(plugin, `HTTP ${err.response.status} error posting: '${rsp}'`) } else { plugin.logerror(plugin, `Error posting message to resque: '${err}'`) } - // transaction.results.add(this, {err}) // blackhole this message as deny return next(DENYSOFT, '458 – Unable to queue messages for node resque.') } - // transaction.results.add(this, { pass: 'message-queued' }) - // successful POST, send next(OK) implies we blackhole this message from further processing. + // successful POST, send next(OK) implies we blackhole this + // message from downstream processing return next(OK) } @@ -217,13 +235,16 @@ exports.hook_capabilities = function (next, connection) { } /** - * Implement to get plain password. + * Implement to get plain password from configuration and is + * required by Haraka. * * Password length must also be greater than 8 characters. */ exports.get_plain_passwd = function (user, connection, cb) { if (this.cfg.users[user]) { const pw = this.cfg.users[user].password ?? '' + + // password length must be greater than 8 characters if (pw && pw.length > 8) { return cb(pw) } diff --git a/package.json b/package.json index 93fb692..f4fd3db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haraka-plugin-resque", - "version": "1.1.3", + "version": "1.1.4", "description": "Haraka plugin that act as a queue and perform REST post to a remote url", "main": "index.js", "scripts": { @@ -31,7 +31,7 @@ "eslint-plugin-haraka": "*", "haraka-test-fixtures": "*", "mocha": "^10.2.0", - "nock": "^13.4.0" + "nock": "^13.5.0" }, "dependencies": { "axios": "^1.6.5" diff --git a/test/index.js b/test/index.js index c9784ec..3fc4d2d 100644 --- a/test/index.js +++ b/test/index.js @@ -38,7 +38,7 @@ describe('resque', function () { nock(host) .post('/test') .reply(422, 'test data') - + connection.results.add({name:'auth'}, { user: 'usertest1' })