You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

120 lines
2.9 KiB

const querystring = require('querystring');
const got = require('got');
const cacheManager = require('cache-manager');
const expired = require('expired');
const deepAssign = require('deep-assign');
const pkg = require('../package.json');
class Onionoo {
// Constructor returns a new object so instance properties are private
constructor(options = {}) {
// Set default options
this.options = Object.assign({}, {
baseUrl: 'https://onionoo.torproject.org',
endpoints: [
'summary',
'details',
'bandwidth',
'weights',
'clients',
'uptime'
]
}, options);
if (options.cache !== false) {
this.options.cache = cacheManager.caching(Object.assign({}, {
store: 'memory',
max: 500
}, options.cache));
}
// Return object containing endpoint methods
return this.options.endpoints.reduce((onionoo, endpoint) => {
onionoo[endpoint] = options => this.get(endpoint, options);
return onionoo;
}, {});
}
// Returns a function to make requests to a given endpoint
get(endpoint, options) {
// Build query string (don't encode ':' for search filters)
const qs = querystring.encode(options).replace(/%3A/g, ':');
// Build url
let url = `${this.options.baseUrl}/${endpoint}`;
url += qs ? `?${qs}` : '';
// If caching is disabled, just make the request
if (!this.options.cache) {
return this.makeRequest(url);
}
// If caching is enabled, check for url in cache
return this.options.cache.get(url)
.then(cachedResult => {
let options = {};
// If we have it cached
if (cachedResult) {
// Return the cached entry if it's still valid
if (!expired(cachedResult.headers)) {
return cachedResult;
// If it's stale, add last-modified date to headers
} else if (cachedResult.headers['last-modified']) {
options.headers = {
'if-modified-since': cachedResult.headers['last-modified']
};
}
}
// Make a request
return this.makeRequest(url, options)
.then(response => {
// If we get a 304, fill in the body
if (response.statusCode === 304) {
response.body = cachedResult.body;
}
// If we get a 200 or 304, cache it
if ([200, 304].includes(response.statusCode)) {
this.options.cache.set(url, response);
}
return response;
});
});
}
// Returns a promise for a request
makeRequest(url, options = {}) {
options = deepAssign({
headers: {
'user-agent': `onionoo-node-client v${pkg.version} (${pkg.homepage})`
}
}, options);
return got(url, options)
.catch(err => {
// Don't throw 304 responses
if (err.statusCode === 304) {
return err.response;
}
throw err;
})
.then(response => {
// Format response
response = {
statusCode: response.statusCode,
statusMessage: response.statusMessage,
headers: response.headers,
body: response.body && JSON.parse(response.body)
};
return response;
});
}
}
module.exports = Onionoo;