'use strict';
/**
* Event Emitter
*
* @external EventEmitter
*
* @see {@link https://nodejs.org/dist/latest-v6.x/docs/api/events.html#events_class_eventemitter|Node.JS - Event module}
*/
const EventEmitter = require('events'),
Crypto = require('crypto'),
SSE = require('express-sse');
function channel(source, filter) {
var target = new SSE();
return function (request, response, next) {
if (! request.accepts('text/event-stream')) return next();
target.init(request, response);
function send(event) {
if ( filter.call(this, event, request) ) {
target.send(event, event.action);
target.send( event );
}
}
source.on('*', send);
request.on('close', function () {
source.removeListener('*', send);
});
};
}
module.exports = function (config) {
const emitter = new EventEmitter();
/**
* @api {post} /hookHub Receive all kinds of Event from a Web Hook
*
* @apiName postEvent
* @apiVersion 0.5.0
* @apiGroup Event
*
* @apiHeader {String} X-GitHub-Event Event name
* @apiHeader {String} X-GitHub-Delivery Unique ID for this delivery
* @apiHeader {String} X-Hub-Signature HMAC hex digest of the payload
*
* @apiParam {String} action Event name
* @apiParam {Object} [repository]
* @apiParam {Object} [organization]
*/
this.post('/hookHub', function (request, response) {
var data = JSON.stringify( request.body ),
sign = (request.get('X-Hub-Signature') || '').split('=');
request.body.id = request.get('X-GitHub-Delivery');
request.body.sign = sign;
if (
sign[0] && (
sign[1] !==
Crypto.createHmac(sign[0], config.HookSecret)
.update( data ).digest('hex')
)
) {
/**
* Signature verification failed
*
* @event external:EventEmitter#error
*
* @type {object}
*
* @property {string} id `X-GitHub-Delivery` header
* @property {string[]} sign Array splited from
* `X-Hub-Signature` header
*
* @see {@link https://developer.github.com/webhooks/#payloads}
*/
emitter.emit('error', request.body);
return response.status( 400 ).end('Signature verification failed');
}
emitter.emit(request.get('X-GitHub-Event'), request.body);
emitter.emit('*', request.body);
response.end();
});
/**
* @api {get} /orgs/:org/events Organization SSE
*
* @apiName getOrgEvent
* @apiVersion 0.5.1
* @apiGroup Event
*
* @apiHeader {String} Accept=text/event-stream
*
* @apiParam {String} org
*/
this.get('/orgs/:org/events', channel(emitter, function (event, request) {
return ((event.organization || '').login === request.params.org);
}));
/**
* @api {get} /repos/:owner/:repo/events Repository SSE
*
* @apiName getRepoEvent
* @apiVersion 0.5.1
* @apiGroup Event
*
* @apiHeader {String} Accept=text/event-stream
*
* @apiParam {String} owner
* @apiParam {String} repo
*/
this.get('/repos/:owner/:repo/events', channel(
emitter, function (event, request) {
return (
(event.repository || '').full_name ===
request.params.owner + '/' + request.params.repo
);
}
));
/**
* @api {get} /repos/:owner/:repo/issues/events Repository issue SSE
*
* @apiName getIssueEvent
* @apiVersion 0.5.1
* @apiGroup Event
*
* @apiHeader {String} Accept=text/event-stream
*
* @apiParam {String} owner
* @apiParam {String} repo
*/
this.get('/repos/:owner/:repo/issues/events', channel(
emitter, function (event, request) {
return event.issue && (
(event.repository || '').full_name ===
request.params.owner + '/' + request.params.repo
);
}
));
return emitter;
};