Skip to content

Commit de1d8ba

Browse files
committedNov 7, 2019
first commit
0 parents  commit de1d8ba

9 files changed

+385
-0
lines changed
 

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
test/

‎.vscode/launch.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
// https://code.visualstudio.com/docs/nodejs/nodejs-debugging
6+
// if auto attach feature is enable, inside vscode terminal run
7+
// node --inspect-brk program.js
8+
"version": "0.2.0",
9+
"configurations": [
10+
{
11+
"type": "node",
12+
"request": "attach",
13+
"name": "Debug (host)",
14+
//"localRoot": "${workspaceFolder}",
15+
//"remoteRoot": "${workspaceFolder}",
16+
"protocol": "inspector",
17+
//"port": 9229,
18+
//"address": "0.0.0.0",
19+
"console": "integratedTerminal",
20+
"stopOnEntry": true,
21+
"outFiles": [
22+
"${workspaceFolder}/**/*.js"
23+
],
24+
"skipFiles": [
25+
"<node_internals>/**/*.js"
26+
],
27+
},
28+
]
29+
}

‎.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"debug.node.autoAttach": "on"
3+
}

‎README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Redis Patterns server
2+
3+
# run project (docker)
4+
docker-compose up
5+
6+
# debug
7+
node --inspect-brk server.js
8+
9+
# dependecies
10+
webSocket: https://github.com/websockets/ws
11+
ioredis: https://github.com/luin/ioredis

‎docker-compose.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
version: "3.5"
2+
services:
3+
4+
server:
5+
image: node:12.9-alpine
6+
command: sh -c "npm install && node server.js"
7+
working_dir: /server
8+
environment:
9+
ENV: stage
10+
REDIS_HOST: redis
11+
ALLOWED_ORIGINS: "http://localhost,http://localhost:4200"
12+
volumes:
13+
- ./:/server
14+
ports:
15+
- '8080:8080'
16+
17+
redis:
18+
image: redis:5
19+
environment:
20+
ENV: stage
21+
volumes:
22+
- redis_data:/data
23+
ports:
24+
- '6379:6379'
25+
26+
volumes:
27+
redis_data:

‎package-lock.json

+113
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "redis-patterns-server",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "server.js",
6+
"dependencies": {
7+
"ioredis": "^4.14.1",
8+
"uuid": "^3.3.2",
9+
"ws": "^7.0.1"
10+
},
11+
"devDependencies": {
12+
"@types/ioredis": "^4.0.15"
13+
},
14+
"scripts": {
15+
"test": "echo \"Error: no test specified\" && exit 1",
16+
"start": "node server.js"
17+
},
18+
"author": "",
19+
"license": "ISC"
20+
}

‎server.js

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
const WebSocket = require('ws');
2+
const Redis = require("ioredis");
3+
const isAllow = require('./src/blacklist');
4+
5+
const port = 8080;
6+
//https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
7+
const wss = new WebSocket.Server({
8+
port: port,
9+
maxPayload: process.env.MAX_PAYLOAD ? process.env.MAX_PAYLOAD : 1000,
10+
verifyClient: function(info) {
11+
// console.log(process.env);
12+
console.log('Origin:', info.origin);
13+
let allowedOrigins = process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS : 'origin-unknown';
14+
allowedOrigins = allowedOrigins.split(',');
15+
console.log('Allowed Origins:', allowedOrigins);
16+
let verified = false;
17+
allowedOrigins.forEach(function each(allow) {
18+
if(info.origin === allow) {
19+
verified = true;
20+
}
21+
});
22+
return verified;
23+
}
24+
});
25+
console.log('Start WebSocket.Server on: %s', port);
26+
27+
const createRedisClient = function(data) {
28+
const sessionId = data.sessionId ? data.sessionId : 'xxxx';
29+
const client = new Redis({
30+
keyPrefix: `${sessionId}:`,
31+
host: process.env.REDIS_HOST ? process.env.REDIS_HOST : '127.0.0.1'
32+
});
33+
console.log('Create new Redis client with prefix: %s', sessionId);
34+
return client;
35+
};
36+
37+
const noop = function() {};
38+
39+
const heartbeat = function() {
40+
console.log('pong heartbeat...');
41+
this.isAlive = true;
42+
};
43+
44+
const interval = setInterval(function ping() {
45+
wss.clients.forEach(function each(ws) {
46+
if (ws.isAlive === false) {
47+
console.log('terminate ws!');
48+
return ws.terminate();
49+
}
50+
51+
ws.isAlive = false;
52+
ws.ping(noop);
53+
});
54+
}, 10000);
55+
56+
wss.on('connection', function connection(ws) {
57+
58+
ws.isAlive = true;
59+
ws.on('pong', heartbeat);
60+
ws.on('ping', console.info);
61+
62+
// https://github.com/websockets/ws/issues/1417
63+
ws.on('error', console.error);
64+
65+
ws.on('open', function open() {
66+
console.log('open');
67+
});
68+
69+
ws.on('close', function close() {
70+
console.log('close');
71+
});
72+
73+
ws.on('message', async function incoming(data) {
74+
console.log('message: %s', data);
75+
let request,response = {};
76+
let client;
77+
78+
try {
79+
request = JSON.parse(data);//from string to object
80+
} catch (ex) {
81+
response.command = 'unknown';
82+
response.output = 'Invalid JSON';
83+
response.status = 'error';
84+
return ws.send(JSON.stringify(response));
85+
}
86+
87+
try {
88+
client = createRedisClient(request);
89+
client.on("error", function(error) {
90+
console.log(error);
91+
});
92+
93+
const commandString = request.command;
94+
const commandArray = commandString.split(" ");
95+
const command = commandArray[0];
96+
const args = commandArray.slice(1);
97+
98+
response.command = commandString;//early set
99+
100+
if(!isAllow(command)) {
101+
throw new Error('Command not allowed');
102+
}
103+
104+
response.status = '-';
105+
response.output = await client.send_command(command,args);
106+
response.status = 'ok';
107+
108+
} catch (ex) {
109+
// console.log(ex);
110+
console.log('Exception on send_command');
111+
response.output = ex.message;
112+
response.status = 'error';
113+
} finally {
114+
client.quit();
115+
//send response
116+
return ws.send(JSON.stringify(response));
117+
}
118+
119+
});
120+
});

‎src/blacklist.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const commandBlackList = [
2+
//cluster
3+
'CLUSTER', // *
4+
'READONLY',
5+
'READWRITE',
6+
7+
//connection
8+
'AUTH',
9+
'QUIT',
10+
'SELECT',
11+
'SWAPDB',
12+
13+
//Keys
14+
'OBJECT',
15+
'DUMP',
16+
'MIGRATE',
17+
'MOVE',
18+
'RESTORE',
19+
'WAIT',
20+
21+
//Scripting
22+
'EVAL',
23+
'EVALSHA',
24+
'SCRIPT',//*
25+
26+
//Server
27+
'BGREWRITEAOF',
28+
'BGSAVE',
29+
'CLIENT',//*
30+
'COMMAND',//*
31+
'CONFIG',//*
32+
'DBSIZE',
33+
'DEBUG',//*
34+
'FLUSHALL',
35+
'FLUSHDB',
36+
'INFO', // (or limit it)
37+
'MEMORY', //*
38+
'MONITOR',
39+
'ROLE',
40+
'SAVE',
41+
'SHUTDOWN',
42+
'SLAVEOF',
43+
'REPLICAOF',
44+
'SLOWLOG',
45+
'SYNC',
46+
'TIME',
47+
48+
//Streams
49+
//(tbd)
50+
51+
//Transactions
52+
'WATCH',
53+
'UNWATCH',
54+
];
55+
56+
function isAllow(command) {
57+
return commandBlackList.includes(command.toUpperCase()) ? false : true;
58+
}
59+
60+
module.exports = isAllow;

0 commit comments

Comments
 (0)
Please sign in to comment.