Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
10A7 committed Dec 23, 2017
0 parents commit 5af9c09
Show file tree
Hide file tree
Showing 8 changed files with 1,793 additions and 0 deletions.
60 changes: 60 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env


674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# eth-gas-json

Beginnings of an API wrapper around the [ethgasstation.info](https://ethgasstation.info) oracle. Caches the Oracle JSON in memory.
40 changes: 40 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require('dotenv').config();

const fs = require('fs');
const cluster = require('cluster');

if (cluster.isMaster) {
let processes = 1; // require('os').cpus().length;
console.log("Spawning " + processes + " worker processes...");
for (let i = 0; i < processes; i += 1) {
cluster.fork();
};
} else {
const express = require('express');
const bodyParser = require('body-parser');
const http = require('http');
const path = require('path');
const app = express();
const helmet = require('helmet');

app.set('port', process.env.PORT || 8080);
app.use(helmet());
app.use(bodyParser.json());

fs.readdirSync('./controllers').forEach((jsfile) => {
if (jsfile.substr(-3) === '.js') {
let controller = require('./controllers/' + jsfile);
controller(app);
}
});

app.get('/', (req, res) => {
res.json({
result: 'success'
});
})

http.createServer(app).listen(app.get('port'), () => {
console.log('Express worker process ' + cluster.worker.id + ' listening on ' + app.get('port'));
});
}
30 changes: 30 additions & 0 deletions controllers/gasAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
let HttpJsonCache = require('../lib/HttpJsonCache');

class GasApiController {

constructor() {
}

getKey (req, res) {
let data = {};
new HttpJsonCache().getJSON('https://ethgasstation.info/json/ethgasAPI.json')
.then((data) => {
if (req.params.key in data) {
let ret = { result: 'success' }
ret[req.params.key] = data[req.params.key];
res.json(ret);
} else {
res.status(404).json({ result: 'error', message: 'Property not found' });
}
})
.catch((err) => {
res.status(503).json({ result: 'error', message: 'Cannot fetch from EthGasStation.' });
});
}
}

module.exports = (route) => {
var controller = new GasApiController();
route.get('/gasAPI/:key', controller.getKey);
}

90 changes: 90 additions & 0 deletions lib/HttpJsonCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Cache JSON files from HTTP source to memory.
*/

const request = require('request-promise');
const crypto = require('crypto');

let instance = null;

class HttpJsonCache {

constructor(opts) {
if (!instance) {
instance = this;
let defaults = {
expiry: 60 // expiry time in seconds
};
this.options = Object.assign({}, defaults, opts);
this.memcache = {};
}
return instance;
}

async getJSON (file_uri) {
let key = this._hashKey(file_uri);

return new Promise((resolve, reject) => {
if (key in this.memcache) {
console.log('cache hit');
// cache hit
if (!this._isExpired(this.memcache[key])) {
// still valid
console.log('not expired, run from cache');
resolve(this.memcache[key].data);
return;
}
}

console.log('cache miss');
// cache miss
let result = null;
let attempts = 0;
request.get({
uri: file_uri,
headers: {
'User-Agent': 'eth-gas-json'
},
json: true
})
.then((data) => {
result = data;
if (typeof result !== 'object') {
reject('bad-data');
}
this.memcache[key] = this._makeFileObject(result);
resolve(result);
})
.catch((err) => {
attempts += 1;
if (attempts >= this.options.retries) {
reject('too-many-retries');
}
});
});
}

_hashKey (str) {
let h = crypto.createHash('sha256');
h.update(str);
return h.digest('hex');
}

_isExpired (fileObject) {
return (fileObject.expirationDate < new Date().getTime());
}

_makeFileObject (data, expirationDate) {
if (typeof expirationDate !== 'number') {
expirationDate = new Date().getTime() + this.options.expiry * 1000;
console.log('setting expiration date to ' + expirationDate);
}
return {
expirationDate: expirationDate,
data: data
}
}

}

module.exports = HttpJsonCache;
Loading

0 comments on commit 5af9c09

Please sign in to comment.