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.
146 lines
3.3 KiB
146 lines
3.3 KiB
/*
|
|
* index.js
|
|
* Created: Sat May 25 2019 21:45:59 GMT+0530 (IST)
|
|
* Copyright 2019 Harish.K<harish2704@gmail.com>
|
|
*/
|
|
const Koa = require('koa');
|
|
const Router = require('koa-joi-router');
|
|
const util = require('util');
|
|
const {RateLimiterMemory} = require('rate-limiter-flexible');
|
|
|
|
|
|
const app = new Koa();
|
|
const router = new Router();
|
|
const Joi = Router.Joi;
|
|
const _exec = util.promisify( require('child_process').exec );
|
|
const ethDev = process.env.ETH_DEV
|
|
const rateLimitOpts = {
|
|
points: 5, // 1 request for ctx.ip
|
|
duration: 5, // per 1 second
|
|
};
|
|
const rateLimiter = new RateLimiterMemory( rateLimitOpts );
|
|
const MyJoi = {
|
|
ipv6: function(){
|
|
return Joi.string().ip({
|
|
version: [
|
|
'ipv6'
|
|
],
|
|
cidr: 'forbidden'
|
|
})
|
|
},
|
|
ipv6Array: function(){
|
|
return Joi.array().items( MyJoi.ipv6() );
|
|
}
|
|
};
|
|
|
|
|
|
async function exec( cmd ){
|
|
const result = await _exec( cmd );
|
|
if( result.stderr.length ){
|
|
throw new Error( result.stderr );
|
|
}
|
|
return result.stdout;
|
|
}
|
|
|
|
|
|
function setRateLimitHeader( ctx, rateLimitInfo ){
|
|
ctx.response.set({
|
|
"X-RateLimit-Limit": rateLimitOpts.points,
|
|
"X-RateLimit-Remaining": rateLimitInfo.remainingPoints,
|
|
"X-RateLimit-Reset": new Date(Date.now() + rateLimitInfo.msBeforeNext).toUTCString()
|
|
});
|
|
}
|
|
|
|
|
|
async function rateLimiterMiddleWare( ctx, next ){
|
|
try {
|
|
let rlInfo = await rateLimiter.consume(ctx.ip)
|
|
setRateLimitHeader( ctx, rlInfo );
|
|
await next().catch();
|
|
} catch ( rlInfo ) {
|
|
ctx.status = 429
|
|
ctx.response.set({
|
|
"Retry-After": rlInfo.msBeforeNext / 1000,
|
|
});
|
|
setRateLimitHeader( ctx, rlInfo );
|
|
ctx.body = { error: 'Too Many Requests. Please wait for some time' };
|
|
}
|
|
}
|
|
|
|
|
|
async function getIpList( ctx ){
|
|
const ips = await exec(`ip -6 a show dev ${ethDev} scope global | grep inet6 | awk '{print $2}'`)
|
|
ctx.body = { ips : ips.split('\n').filter(v => v.length ).map( v => v.slice(0, -3)) };
|
|
}
|
|
|
|
|
|
async function addIp( ctx ){
|
|
const ip = ctx.request.body.ip;
|
|
try {
|
|
await exec(`sudo ip -6 a add ${ip}/64 dev ${ethDev}`);
|
|
} catch (e) {
|
|
ctx.status = 409;
|
|
ctx.body = { error: 'Failed to assign ip'};
|
|
return;
|
|
}
|
|
|
|
// Remove ip after some time
|
|
setTimeout(function(){
|
|
exec(`sudo ip -6 a del dev ${ethDev} ${ip}/64`);
|
|
}, 5*60*1000 );
|
|
|
|
const ips = await exec(`ip -6 a show dev ${ethDev}| grep inet6.*global | awk '{print $2}'`);
|
|
ctx.body = { ips : ips.split('\n').filter(v => v.length ).map( v => v.slice(0, -3)) };
|
|
}
|
|
|
|
|
|
async function logger( ctx, next ){
|
|
const startTime = Date.now();
|
|
await next();
|
|
const requestTime = Date.now() - startTime;
|
|
console.log(`${new Date().toISOString()} ${ctx.method} ${ctx.response.status} ${ctx.ip} "${ctx.request.headers['user-agent']}" - ${requestTime}`);
|
|
}
|
|
|
|
|
|
router.route({
|
|
method: 'get',
|
|
path: '/ips',
|
|
validate: {
|
|
output: {
|
|
200: {
|
|
body: {
|
|
ips: MyJoi.ipv6Array()
|
|
}
|
|
}
|
|
}
|
|
},
|
|
handler: getIpList
|
|
});
|
|
|
|
|
|
router.route({
|
|
method: 'post',
|
|
path: '/ips',
|
|
validate: {
|
|
body: {
|
|
ip: MyJoi.ipv6()
|
|
},
|
|
type: 'json',
|
|
output: {
|
|
200: {
|
|
body: {
|
|
ips: MyJoi.ipv6Array()
|
|
}
|
|
}
|
|
}
|
|
},
|
|
handler: addIp
|
|
});
|
|
|
|
|
|
app.use( logger );
|
|
app.use( require('koa-static')('./static') );
|
|
app.use( rateLimiterMiddleWare );
|
|
app.use( router.middleware() );
|
|
app.listen( 3553 );
|
|
|
|
|