This repository has been archived by the owner on Nov 16, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
server.js
executable file
·181 lines (155 loc) · 6.34 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env node
var express = require('express');
var web = express();
var server = require('http').Server(web);
var io = require('socket.io')(server);
var geohash = require('ngeohash');
var _ = require('underscore')._;
var redis = require('redis').createClient();
var sanitizer = require('sanitizer');
var crypto = require('crypto');
var moment = require('moment');
var config = require('./public/shared-data.json');
// I really shouldn't be using express.js for this...
web.use('/', express.static(__dirname + '/public'));
// Port number is either the first argument or 80
server.listen(parseInt(process.argv[2], 10) || 80);
// Returns a unique ID. Each time it's run, you should get a number bigger than the last
var getUniqueID = function() {
return new Date().getTime() + "";
};
io.on('connection', function(socket) {
socket.emit('message-to-client', {
time: moment().format(),
size: 5,
body: "Socket Connection Established",
uuid: getUniqueID(),
color: 'FFFFFF',
});
// Client sent us an updated location
socket.on('location', function(coords) {
var hash = geohash.encode(coords.latitude, coords.longitude);
// This is a list of all the rooms I should be in based on my geo hash
var roomsToBeIn = [];
// Building list of rooms to be in
var roomsCurrentlyIn = socket.rooms;
for (var index in roomsCurrentlyIn) {
roomsCurrentlyIn[index] = roomsCurrentlyIn[index].slice(1); // Remove room leading slash
}
var roomName = '';
for (var i = 0; i < config.levels.length; i++) {
roomName = hash.substring(0, config.levels[i].hash_accuracy);
// Here we join a 3x3 matrix of locations. Two people could be a few meteres apart and in two different squares, so this fixes that
for (var adjX = -1; adjX <= 1; adjX++) {
for (var adjY = -1; adjY <= 1; adjY++) {
roomsToBeIn.push(geohash.neighbor(roomName, [adjX,adjY]));
}
}
}
// Rooms I need to join
var roomsToJoin = _.difference(roomsToBeIn, roomsCurrentlyIn);
for (var joinIndex in roomsToJoin) {
(function(joinIndex) {
var thisRoom = roomsToJoin[joinIndex];
socket.join(thisRoom);
// The - below is on purpose
redis.keys('msg:'+thisRoom+'-*', function(err, result) {
if (err) {
socket.emit('error', {
message: "Error grabbing messages from " + thisRoom
});
console.log(err);
return;
}
for (var i in result) {
redis.hgetall(result[i], function(err, result) {
if (err) {
return;
}
socket.emit('message-to-client', {
time: result.time,
size: parseInt(result.size, 10),
body: sanitizer.escape(result.message),
uuid: result.uuid,
area: result.area,
color: result.color,
});
});
}
});
})(joinIndex);
}
// Rooms I need to leave
var roomsToLeave = _.difference(roomsCurrentlyIn, roomsToBeIn);
for (var leaveIndex in roomsToLeave) {
var roomToLeaveName = roomsToLeave[leaveIndex];
socket.leave(roomToLeaveName);
}
if (roomsToLeave.length) {
socket.emit('leave-area', {
areas: roomsToLeave
});
}
});
// The client is sending out a message to other clients
socket.on('message-to-server', function(data) {
var size = parseInt(data.size, 10);
var time = moment().format();
var body = data.body.substring(0, 255);
var coords = data.coords;
// TODO: Need a faster way to do this (e.g. no crypto or md5)
var color = crypto.createHash('md5').update(socket.id).digest('hex').substr(10,6);
// Validations
if (size < 0 || size > 4) {
console.log('invalid size', size);
return;
}
if (!body) {
console.log('invalid body', body);
return;
}
coords.latitude = parseFloat(coords.latitude);
coords.longitude = parseFloat(coords.longitude);
if (coords.latitude > 90 || coords.latitude < -90 || coords.longitude > 180 || coords.longitude < -180) {
console.log('invalid coords', coords);
return;
}
var id = getUniqueID();
// Determine the specificity we need
var hash = geohash.encode(coords.latitude, coords.longitude);
var roomName = hash.substring(0, config.levels[size].hash_accuracy);
// Persist this message in Redis
redis.hmset([
'msg:'+roomName+'-'+id,
'geohash', hash,
'message', body,
'latitude', coords.latitude,
'longitude', coords.longitude,
'time', time,
'size', size,
'uuid', id,
'area', roomName,
'color', color
],
function(err) {
if (err) {
socket.emit('error', {
message: "There was an error persisting your message to the database"
});
console.log(err);
return;
}
redis.expire('msg:'+roomName+'-'+id, config.levels[size].expiration);
// Send message to client
io.sockets.in(roomName).emit('message-to-client', {
time: time,
size: size,
body: sanitizer.escape(body),
uuid: id,
area: roomName,
color: color
});
}
);
});
});