UNPKG

jetstream-microservice

Version:

Jetstream Node Package for easy implementation of Jetstream microservices

263 lines (222 loc) 9.54 kB
var Guid = require('guid'); var amqp = require('amqplib/callback_api'); // Rabbit MQ flavoured events if (global.queueEvents == undefined) global.queueEvents = []; if (global.queueConnection == undefined) global.queueConnection = null; if (global.queueChannel == undefined) global.queueChannel = null; function pub(m, callback, error) { var q = global.jetstreamConfig.name; var msg = process.argv.slice(2).join(' ') || JSON.stringify(m); global.queueChannel.publish(q, '', new Buffer(msg)); if (callback) callback(); } module.exports = { connect: function () { var tryConnection = function () { amqp.connect(global.jetstreamConfig.amqpHost, function (err, conn) { if (err) { global.dependencies.amqp = false; console.log("AMQP connection failed, retrying in 5 seconds..."); setTimeout(function () { tryConnection(); }, 5000) } else { global.dependencies.amqp = true; global.queueConnection = conn; conn.createChannel(function (err, ch) { global.queueChannel = ch; var wq = global.jetstreamConfig.name + ".queue"; ch.assertQueue(wq, { durable: true }); console.log(" [*] Waiting for Work messages in %s.", wq); ch.consume(wq, function (msg) { var e = JSON.parse(msg.content.toString()); global.queueEvents.forEach(function (event) { if (event.name === e.name) { if (!event.distribute) { event.callback(e.payload); } else { pub(e); } } }) }, { noAck: true }); var iq = global.jetstreamConfig.name + "." + Guid.raw().substring(0, 8); ch.assertExchange(global.jetstreamConfig.name, 'fanout', { durable: false }); ch.assertQueue(iq, { exclusive: true }, function (err, q) { console.log(" [*] Waiting for pub/sub messages in %s.", q.queue); ch.bindQueue(q.queue, global.jetstreamConfig.name, ''); ch.consume(q.queue, function (msg) { var e = JSON.parse(msg.content.toString()); global.queueEvents.forEach(function (event) { if (event.name === e.name) { event.callback(e.payload); } }) }, { noAck: true }); }); }); } }); } tryConnection(); }, on: function (event_name, callback, distribute) { global.queueEvents.push({ name: event_name, callback: callback, distribute: distribute || false }) }, emit: function (event_name, payload, callback, error) { var q = "service-registry.queue" // put the message in to the correct format var m = { "trace_id": Guid.raw(), "source": global.jetstreamConfig.name, "name": event_name, "timestamp": new Date(), "payload": payload } var msg = process.argv.slice(2).join(' ') || JSON.stringify(m); global.queueChannel.assertQueue(q, { durable: true }); global.queueChannel.sendToQueue(q, new Buffer(msg), { persistent: true }); if (callback) callback(m); }, publish: function (event_name, payload, callback, error) { // put the message in to the correct format var m = { "trace_id": Guid.raw(), "source": global.jetstreamConfig.name, "name": event_name, "timestamp": new Date(), "payload": payload } pub(m, callback, error); } } var Guid = require('guid'); var errorHandler = function(err, req, res, next) { var id = Guid.raw(); console.error('Something went wrong: ' + id); var error = { "code": id, "type": "Unhandled exception", "message": err, "details": [req.url, req.body] } res.status(500).send(error) } module.exports = errorHandler; var sign = require('../security'); module.exports = { registerMe: function() { var request = require("../request"); var postData = { method: 'POST', uri: jetstreamConfig.registry_url + "/registry", body: { name: jetstreamConfig.name, location: jetstreamConfig.location, documentation: jetstreamConfig.documentation, health_endpoint: "/health" }, json: true } console.log("Registering with Jetstream service registry..."); request(postData, function(err, resp, body) { if (!err && resp.statusCode == 200) { console.log("Registered with Jetstream service registry successfully, your service can now be accessed via " + jetstreamConfig.registry_url + "/services/" + jetstreamConfig.name); global.dependencies.registry = true; } else { console.log(err); console.log("Failed to register with Jetstream service registry, your service is not visible for Jetstream"); global.dependencies.registry = false; } }); // renew every 60 seconds setInterval(function() { request(postData, function(err, resp, body) { if (!err && resp.statusCode == 200) { console.log("Renewed registration with service registry"); global.dependencies.registry = true; } else { console.log(err); console.log("Failed to renew registration with service registry"); global.dependencies.registry = false; } }); }, 60000) } } var func = function (req, res) { // here we need to check if there is a global dependency object // if there is it should be an object of dependencies and statuses { dependency_one_name : boolean, dependency_two_name: boolean } // if any of them are false then we should return a 503 (service unavailable) var valid = true; if (global.dependencies) { Object.keys(dependencies).forEach(function (key) { if (dependencies[key] === false) { valid = false; } }); } var result = { "status": valid ? "ok" : "missing dependency", "dependencies": dependencies } res.status(valid ? 200 : 503).send(result) }; module.exports = func; var jwt = require('jsonwebtoken'); module.exports = { inbound: function(req, res, next) { self = this; // first check for a header if (req.headers['x-jetstream-jwt']) { // now we need to check the jwt is legit var token = req.headers['x-jetstream-jwt']; jwt.verify(token, jetstreamConfig.jwt_secret, { issuer: jetstreamConfig.registry_url, audience: jetstreamConfig.location }, function(err, decoded) { if (decoded) { next(); } else { res.status(401).send("Unauthorized") console.log("Error: " + err); console.log("Token: " + token); console.log("Expected registry: " + jetstreamConfig.registry_url); console.log("Expected audience: " + jetstreamConfig.location); } }); } else { res.status(401).send("Unauthorized") console.log("Unauthorized: No header"); } }, outbound: function() { return jwt.sign({}, jetstreamConfig.jwt_secret, { issuer: jetstreamConfig.location, // iss - issuer audience: jetstreamConfig.registry_url, // aud - audience expiresIn: 5, // exp - expires after noTimestamp: false // iat - issued at }); } } /* *********************************************************************************************** Description: A wrapper for npm request which will add security headers to all outbound requests Created by: David Dyke Created on: 16th June 2017 *********************************************************************************************** */ 'use strict'; const request = require("request"); const security = require("../security"); var updateOptions = function (options) { // check for headers object and create it if it doesn't exist options.headers || (options.headers = []); // add security options.headers['x-jetstream-jwt'] = security.outbound(); // add a user-agent for easy source tracking options.headers['User-Agent'] = jetstreamConfig.name; return options; }; const ret = function(options, next){ request(updateOptions(options), next); }; module.exports = ret;