Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

meddelare-server

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

meddelare-server - npm Package Compare versions

Comparing version 1.3.1 to 1.4.0

261

lib/social-buttons-server-middleware.js

@@ -1,26 +0,67 @@

var express = require("express");
var Promise = require("bluebird");
var request = require("request");
var cache = require("memory-cache");
"use strict";
// Cache results in memory -- but keep good and bad (error thrown) results for different periods of time.
var LOCAL_CACHE_TIME_GOOD_RESULT = 4 * 60 * 1000,
LOCAL_CACHE_TIME_BAD_RESULT = 1 * 60 * 1000,
LOCAL_CACHE_TIME_TIMEOUT_RESULT = 10 * 1000;
var express = require("express"),
Promise = require("bluebird"),
request = require("request"),
// Return this count if none was found or an error was thrown.
var DEFAULT_UNKNOWN_COUNT = -1;
// The memory-cache is per-process and returns pointers, not copies.
// Using process-scoped keys with a SocialButtonsServerMiddleware "namespace", so multiple SocialButtonsServerMiddleware servers share cache.
// Using cache.clear() will clear all caches, even in other parts of the application. Be careful!
cache = require("memory-cache"),
extend = require("extend"),
// How many minutes should we cache the results for a given request
var CACHE_TIME = process.env.CACHE_TIME || 4 * 60;
// TODO: use a package/library instead?
copyDeep = function() {
var args = Array.prototype.slice.call(arguments, 0);
function cacheControl(req, res, next) {
return extend.apply(null, [true, {}].concat(args));
},
// TODO: use a package/library instead?
sortObjectByKeys = function(unsorted) {
var sorted = {},
keys = Object.keys(unsorted);
keys.sort();
keys.forEach(function(key) {
sorted[key] = unsorted[key];
});
return sorted;
};
function SocialButtonsServerMiddleware(options) {
// TODO: better configuration.
this._options = copyDeep(SocialButtonsServerMiddleware._defaultOptions, options);
// Perhaps it's futile to try and clean up before/exit, but one can try.
process
.once("beforeExit", function() {
cache.clear();
})
.once("exit", function() {
cache.clear();
});
this._router = express.Router(this._options.routerOptions);
this._router.get("/", this._cacheControl.bind(this));
this._router.get("/", this._getCount.bind(this));
this._router.use(SocialButtonsServerMiddleware._forbidden);
return this;
}
SocialButtonsServerMiddleware.prototype._cacheControl = function(req, res, next) {
// Setup caching headers (works well with cloudfront)
res.set("Cache-Control", "max-age=" + CACHE_TIME);
res.set("Cache-Control", "max-age=" + this._options.CACHE_TIME);
next();
}
};
var networkCallbacks = {
SocialButtonsServerMiddleware.prototype._networkCallbacks = {
twitter: function(url, callback) {

@@ -103,39 +144,35 @@ // Twitter is nice and easy

function sortObjectByKeys(unsorted) {
var sorted = {},
keys = Object.keys(unsorted);
SocialButtonsServerMiddleware.prototype._isValidNetwork = function(network) {
return Object.prototype.hasOwnProperty.call(this._networkCallbacks, network);
};
keys.sort();
SocialButtonsServerMiddleware.prototype._getInvalidNetworks = function(networks) {
return networks.filter(function(network) {
return !this._isValidNetwork(network);
}, this);
};
keys.forEach(function(key) {
sorted[key] = unsorted[key];
});
SocialButtonsServerMiddleware.prototype._retrieveCount = function(url, network) {
if (!this._isValidNetwork(network)) {
throw new Error("Unknown network: " + network);
}
return sorted;
}
var self = this;
function retrieveCount(url, network) {
if (typeof networkCallbacks[network] === "undefined") {
throw new Error("Unknown network");
}
return Promise.promisify(networkCallbacks[network])(url)
return Promise.promisify(self._networkCallbacks[network])(url)
.catch(function(err) {
console.error("Could not fetch count", network, url, err);
self._options.logger.error("Could not fetch count", network, url, err);
throw err;
});
}
};
function createCacheKey(url, network) {
return network + " '" + url + "'";
}
SocialButtonsServerMiddleware.prototype._getCachedOrRetrieveCount = function(url, network) {
var self = this,
cacheKey = SocialButtonsServerMiddleware._createCacheKey(url, network);
function getCachedOrRetrieveCount(url, network) {
var cacheKey = createCacheKey(url, network);
return Promise.resolve(cache.get(cacheKey))
.then(function(cachedResult) {
if (typeof cachedResult !== "undefined" && cachedResult !== null) {
console.log(cacheKey, "from cache", cachedResult);
self._options.logger.info(cacheKey, "from cache", cachedResult);

@@ -148,24 +185,24 @@ return cachedResult;

// for the same network/url cannot be sent at the same time.
var retrieveCountPromise = retrieveCount(url, network)
var retrieveCountPromise = self._retrieveCount(url, network)
.tap(function(uncachedResult) {
console.log(cacheKey, "fetched good result", uncachedResult);
self._options.logger.info(cacheKey, "fetched good result", uncachedResult);
cache.put(cacheKey, uncachedResult, LOCAL_CACHE_TIME_GOOD_RESULT);
cache.put(cacheKey, uncachedResult, self._options.LOCAL_CACHE_TIME_GOOD_RESULT);
})
.catch(function(err) {
console.error(cacheKey, "fetched bad result", err);
self._options.logger.error(cacheKey, "fetched bad result", err);
cache.put(cacheKey, DEFAULT_UNKNOWN_COUNT, LOCAL_CACHE_TIME_BAD_RESULT);
cache.put(cacheKey, self._options.DEFAULT_UNKNOWN_COUNT, self._options.LOCAL_CACHE_TIME_BAD_RESULT);
return DEFAULT_UNKNOWN_COUNT;
return self._options.DEFAULT_UNKNOWN_COUNT;
});
// Setting a cache timeout just in case, even though it can lead to parallel requests.
cache.put(cacheKey, retrieveCountPromise, LOCAL_CACHE_TIME_TIMEOUT_RESULT);
cache.put(cacheKey, retrieveCountPromise, self._options.LOCAL_CACHE_TIME_TIMEOUT_RESULT);
return retrieveCountPromise;
});
}
};
function retrieveCounts(url, networks) {
SocialButtonsServerMiddleware.prototype._retrieveCounts = function(url, networks) {
// Create an object of callbacks for each of the requested networks It is

@@ -177,33 +214,12 @@ // then passed to the Promise library to executed in parallel All results will

networks.forEach(function(network) {
networksToRequest[network] = getCachedOrRetrieveCount(url, network);
});
networksToRequest[network] = this._getCachedOrRetrieveCount(url, network);
}, this);
return Promise.props(networksToRequest)
.then(sortObjectByKeys);
}
};
function errorAndDie(req, res, next, httpStatus, msg) {
res.status(httpStatus);
res.jsonp({
error: msg,
});
res.end();
}
function inputErrorAndDie(req, res, next, msg) {
errorAndDie(req, res, next, 422, msg);
}
function serverErrorAndDie(req, res, next, msg) {
errorAndDie(req, res, next, 500, msg);
}
function forbiddenErrorAndDie(req, res, next, msg) {
errorAndDie(req, res, next, 403, msg);
}
function getCount(req, res, next) {
var url,
SocialButtonsServerMiddleware.prototype._getCount = function(req, res, next) {
var self = this,
url,
networks,

@@ -214,3 +230,3 @@ nonExistantNetworks;

if (!req.query.networks) {
inputErrorAndDie(req, res, next, "You have to specify which networks you want stats for (networks=facebook,twitter,googleplus)");
SocialButtonsServerMiddleware._inputErrorAndDie(req, res, next, "You have to specify which networks you want stats for (networks=facebook,twitter,googleplus)");
return;

@@ -220,8 +236,6 @@ } else {

nonExistantNetworks = networks.filter(function(network) {
return (typeof networkCallbacks[network] === "undefined");
});
nonExistantNetworks = self._getInvalidNetworks(networks);
if (nonExistantNetworks.length > 0) {
inputErrorAndDie(req, res, next, "Unknown network(s) specified: " + nonExistantNetworks.join());
SocialButtonsServerMiddleware._inputErrorAndDie(req, res, next, "Unknown network(s) specified: '" + nonExistantNetworks.join("', '") + "'");
}

@@ -238,3 +252,3 @@ }

if (!url) {
inputErrorAndDie(req, res, next, "You asked for the referring urls stats but there is no referring url, specify one manually (&url=https://example.com/)");
SocialButtonsServerMiddleware._inputErrorAndDie(req, res, next, "You asked for the referring urls stats but there is no referring url, specify one manually (&url=https://example.com/)");
return;

@@ -244,3 +258,3 @@ }

retrieveCounts(url, networks)
self._retrieveCounts(url, networks)
.then(function(results) {

@@ -251,35 +265,66 @@ res.jsonp(results);

.catch(function(err) {
console.error("getCount", "catch", "retrieveCounts", err);
self._options.logger.error("self._getCount", "catch", "self._retrieveCounts", err);
serverErrorAndDie(req, res, next, "There was an unknown error.");
SocialButtonsServerMiddleware._serverErrorAndDie(req, res, next, "There was an unknown error.");
});
}
};
function forbidden(req, res, next) {
forbiddenErrorAndDie(req, res, next, "Forbidden");
}
SocialButtonsServerMiddleware.prototype.getRouter = function() {
return this._router;
};
function router() {
var routerOptions = {
caseSensitive: true,
strict: true,
},
socialButtonsServerRouter = express.Router(routerOptions);
SocialButtonsServerMiddleware._createCacheKey = function(url, network) {
return "SocialButtonsServerMiddleware " + network + " '" + url + "'";
};
socialButtonsServerRouter.get("/", cacheControl);
socialButtonsServerRouter.get("/", getCount);
socialButtonsServerRouter.use(forbidden);
return socialButtonsServerRouter;
}
// Perhaps it's futile to try and clean up before/exit, but one can try.
process
.once("beforeExit", function() {
cache.clear();
})
.once("exit", function() {
cache.clear();
SocialButtonsServerMiddleware._defaultOptions = {
logger: console,
// Cache results in memory -- but keep good and bad (error thrown) results for different periods of time.
LOCAL_CACHE_TIME_GOOD_RESULT: 4 * 60 * 1000,
LOCAL_CACHE_TIME_BAD_RESULT: 1 * 60 * 1000,
LOCAL_CACHE_TIME_TIMEOUT_RESULT: 10 * 1000,
// How many minutes should HTTP requests' results be cached.
CACHE_TIME: process.env.CACHE_TIME || 4 * 60,
// Return this count if none was found or an error was thrown.
DEFAULT_UNKNOWN_COUNT: -1,
routerOptions: {
caseSensitive: true,
strict: true,
},
};
SocialButtonsServerMiddleware._errorAndDie = function(req, res, next, httpStatus, msg) {
res.status(httpStatus);
res.jsonp({
error: msg,
});
module.exports = router;
res.end();
};
SocialButtonsServerMiddleware._inputErrorAndDie = function(req, res, next, msg) {
SocialButtonsServerMiddleware._errorAndDie(req, res, next, 422, msg);
};
SocialButtonsServerMiddleware._serverErrorAndDie = function(req, res, next, msg) {
SocialButtonsServerMiddleware._errorAndDie(req, res, next, 500, msg);
};
SocialButtonsServerMiddleware._forbiddenErrorAndDie = function(req, res, next, msg) {
SocialButtonsServerMiddleware._errorAndDie(req, res, next, 403, msg);
};
SocialButtonsServerMiddleware._forbidden = function(req, res, next) {
SocialButtonsServerMiddleware._forbiddenErrorAndDie(req, res, next, "Forbidden");
};
module.exports = SocialButtonsServerMiddleware;
{
"name": "meddelare-server",
"version": "1.3.1",
"version": "1.4.0",
"description": "Install custom social share counters on your website with your own hosted solution, which only makes a single API request and loads minimal or zero assets to display the counters.",

@@ -18,2 +18,3 @@ "homepage": "http://meddelare.com/",

"express": "^4.12.4",
"extend": "^3.0.0",
"memory-cache": "^0.1.4",

@@ -20,0 +21,0 @@ "morgan": "^1.5.3",

@@ -7,3 +7,4 @@ var express = require("express"),

socialButtonsServer = require("./lib/social-buttons-server-middleware.js"),
SocialButtonsServerMiddleware = require("./lib/social-buttons-server-middleware.js"),
socialButtonsServerMiddleware = new SocialButtonsServerMiddleware(),

@@ -50,3 +51,3 @@ morgan = require("morgan"),

app.use("/", socialButtonsServer());
app.use("/", socialButtonsServerMiddleware.getRouter());

@@ -53,0 +54,0 @@ app.listen(PORT, function() {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc