1 | var clc = require('cli-color');
|
2 |
|
3 | var clcError = clc.red;
|
4 | var clcWarn = clc.yellow;
|
5 |
|
6 |
|
7 | exports.extra = function(projectDir) {
|
8 | GLOBAL.projRequire = function(module) {
|
9 | if (module == undefined) {
|
10 | return;
|
11 | }
|
12 | if (module.indexOf('/') != 0) {
|
13 | module = '/' + module;
|
14 | }
|
15 | return require(projectDir + module);
|
16 | }
|
17 | GLOBAL.rootUrl = projectDir;
|
18 | return exports;
|
19 | };
|
20 |
|
21 |
|
22 | exports.server = function() {
|
23 | var envLocation, apiLocation, mongooseSchemaLocation, config;
|
24 | var fn = arguments[1];
|
25 | if (typeof arguments[0] == 'object') {
|
26 | config = arguments[0];
|
27 | envLocation = config.envLocation;
|
28 |
|
29 | if (!config.api) {
|
30 | config.api = {};
|
31 | }
|
32 | config.api.location = apiLocation = config.api.location ? config.api.location : config.apiLocation;
|
33 | mongooseSchemaLocation = config.mongooseSchemaLocation;
|
34 | } else {
|
35 | throw 'You do not have the right parameters.';
|
36 | }
|
37 | if (!config) {
|
38 | throw 'Must include a config file.';
|
39 | }
|
40 | GLOBAL.serverConfig = config;
|
41 | config.server = config.server ? config.server : 'Main';
|
42 | config.servers = config.servers ? config.servers : [config.server];
|
43 |
|
44 |
|
45 | var express = require('express');
|
46 | var mongoose = require('mongoose');
|
47 | var cookie = require('cookie');
|
48 | var connect = require('connect');
|
49 | var session = require('express-session');
|
50 | var errorHandler = require('errorhandler');
|
51 |
|
52 |
|
53 | var app = express();
|
54 | var http = require('http');
|
55 | http.globalAgent.maxSockets = Infinity;
|
56 | var server = http.createServer(app);
|
57 | var RedisStore = require('connect-redis')(session);
|
58 | var os = require('os');
|
59 | var fs = require('fs');
|
60 | var https = require('https');
|
61 | var url = require('url');
|
62 | var signature = require('cookie-signature');
|
63 |
|
64 | if (typeof config.ssl == 'object') {
|
65 | config.ssl.key = config.ssl.key != undefined ? config.ssl.key : 'ssl/key.pem';
|
66 | config.ssl.cert = config.ssl.cert != undefined ? config.ssl.cert : 'ssl/cert.pem';
|
67 | var credentials = {
|
68 | key: fs.readFileSync(rootUrl + '/' + config.ssl.key),
|
69 | cert: fs.readFileSync(rootUrl + '/' + config.ssl.cert)
|
70 | };
|
71 | config.serverSSL = https.createServer(credentials, app);
|
72 | }
|
73 |
|
74 | projRequire(envLocation).configureEnvironment(app, process);
|
75 | if (!process.env.REDIS_URI) {
|
76 | throw 'Must define "REDIS_URI" in the environment variables.';
|
77 | } else if (!process.env.SESSION_KEY) {
|
78 | throw 'Must define "SESSION_KEY" in the environment variables.';
|
79 | } else if (!process.env.SESSION_SECRET) {
|
80 | throw 'Must define "SESSION_SECRET" in the environment variables.';
|
81 | } else if (!process.env.COOKIE_KEY) {
|
82 | throw 'Must define "COOKIE_KEY" in the environment variables.';
|
83 | } else if (!process.env.MONGO_URI) {
|
84 | throw 'Must define "MONGO_URI" in the environment variables.';
|
85 | }
|
86 |
|
87 | var logger;
|
88 | if (config.logger) {
|
89 | var servers;
|
90 | if (config.servers) {
|
91 | servers = config.servers;
|
92 | }
|
93 | logger = require('redis-logger').init(servers, os.hostname());
|
94 | }
|
95 |
|
96 | var redisUrl = url.parse(process.env.REDIS_URI);
|
97 | app.set('redisHost', redisUrl.hostname);
|
98 | app.set('redisPort', redisUrl.port);
|
99 |
|
100 | if (redisUrl.auth) {
|
101 | var redisAuth = redisUrl.auth.split(':');
|
102 | app.set('redisDb', redisAuth[0]);
|
103 | app.set('redisPass', redisAuth[1]);
|
104 | }
|
105 |
|
106 | if (redisUrl.path) {
|
107 | app.set('redisDbIndex', redisUrl.path.slice(1, redisUrl.path.length));
|
108 | } else {
|
109 | app.set('redisDbIndex', 0);
|
110 | }
|
111 |
|
112 | var cookieParser = require('cookie-parser')(process.env.COOKIE_KEY);
|
113 | app.use(cookieParser);
|
114 | app.use(function(req, res, next) {
|
115 | res.header('Access-Control-Allow-Credentials', true);
|
116 | res.header('Access-Control-Allow-Origin', req.headers.origin);
|
117 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
118 | res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
|
119 | next();
|
120 | });
|
121 |
|
122 | var redisStoreConfig = {
|
123 | url: process.env.REDIS_URI
|
124 | };
|
125 | if (!config.dontUseRedisTTL) {
|
126 | redisStoreConfig.ttl = config.ttl || 3600;
|
127 | }
|
128 | var sessionStore = new RedisStore(redisStoreConfig);
|
129 |
|
130 |
|
131 | app.use(session({
|
132 | key: process.env.SESSION_KEY,
|
133 | secret: process.env.SESSION_SECRET,
|
134 | store: sessionStore
|
135 | }));
|
136 |
|
137 | if (config.sessionExpireDate != undefined) {
|
138 | app.use(function(req, res, next) {
|
139 | req.session.cookie.expires = config.sessionExpireDate;
|
140 | next();
|
141 | })
|
142 | }
|
143 | if (app.get('env') === 'development') {
|
144 | app.use(errorHandler({
|
145 | dumpExceptions: true,
|
146 | showStack: true
|
147 | }));
|
148 | } else {
|
149 | process.addListener('uncaughtException', function(err) {
|
150 | console.error((new Date).toUTCString() + ' uncaughtException:', err.message);
|
151 | console.error(err.stack);
|
152 | process.exit();
|
153 | });
|
154 | }
|
155 |
|
156 | var bodyParser = require('body-parser');
|
157 |
|
158 | app.use(bodyParser.urlencoded({
|
159 | extended: true,
|
160 | limit: '2048mb'
|
161 | }));
|
162 |
|
163 | app.use(bodyParser.json({
|
164 | limit: '2048mb'
|
165 | }));
|
166 |
|
167 |
|
168 | var favicon = rootUrl + '/public/img/favicon.ico'
|
169 | if (config.favicon != undefined) {
|
170 | favicon = rootUrl + '/' + config.favicon;
|
171 | }
|
172 |
|
173 | app.use(require('serve-favicon')(favicon), function() {
|
174 | console.log(err, favicon_url)
|
175 | });
|
176 |
|
177 | app.use(require('method-override')());
|
178 | app.use(errorHandler());
|
179 | if (app.get('env') == 'production') {
|
180 |
|
181 | app.all('*', function(req, res, next) {
|
182 | if (req.url.indexOf('healthcheck') !== -1) {
|
183 | res.send('server is running');
|
184 | return;
|
185 | }
|
186 | if (config.sslRedirect) {
|
187 | if (req.headers.host == undefined) {
|
188 | res.send('');
|
189 | } else if (req.headers.host.match(/^www/) !== null) {
|
190 | res.redirect('https://' + req.headers.host.replace(/^www\./, '') + req.url);
|
191 | } else if (req.protocol.indexOf('https') === -1) {
|
192 | res.redirect('https://' + req.headers.host + req.url);
|
193 | } else {
|
194 | next();
|
195 | }
|
196 | } else {
|
197 | next();
|
198 | }
|
199 | });
|
200 | }
|
201 |
|
202 |
|
203 | var io = require('socket.io')(server);
|
204 | io.use(function(socket, next) {
|
205 | var handshakeData = socket.request;
|
206 | if (handshakeData.headers.cookie) {
|
207 | handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
|
208 | try {
|
209 | var real_sid = handshakeData.cookie[process.env.SESSION_KEY].replace('s:', '');
|
210 | real_sid = signature.unsign(real_sid, process.env.SESSION_SECRET);
|
211 |
|
212 | handshakeData.sessionID = real_sid;
|
213 | } catch (err) {
|
214 | console.log(clcError(err));
|
215 | return next('Looks like we\'ve got a hacker on our hands.', false);
|
216 | }
|
217 |
|
218 | if (handshakeData.cookie[process.env.SESSION_KEY] == handshakeData.sessionID) {
|
219 | console.log(clcError('Cookie is invalid'));
|
220 | return next('Cookie is invalid.', false);
|
221 | }
|
222 |
|
223 | } else {
|
224 | return next('No cookie transmitted.', false);
|
225 | }
|
226 | next(null, true);
|
227 | });
|
228 |
|
229 | var ioRedis = require('socket.io-redis');
|
230 | var redisIO = require('redis');
|
231 | var pubIO = redisIO.createClient(app.set('redisPort'), app.set('redisHost'), {
|
232 | return_buffers: true
|
233 | });
|
234 | var subIO = redisIO.createClient(app.set('redisPort'), app.set('redisHost'), {
|
235 | return_buffers: true
|
236 | });
|
237 |
|
238 | pubIO.auth(app.set('redisPass'), function(err) {
|
239 | if (err) {
|
240 | throw err;
|
241 | }
|
242 | });
|
243 | subIO.auth(app.set('redisPass'), function(err) {
|
244 | if (err) {
|
245 | throw err;
|
246 | }
|
247 | });
|
248 |
|
249 |
|
250 | io.adapter(ioRedis({
|
251 | key: 'io:' + config.appName,
|
252 | pubClient: pubIO,
|
253 | subClient: subIO
|
254 | }));
|
255 |
|
256 | if (config.onlineUsersConfig) {
|
257 | config.onlineUsersConfig.type = 'update';
|
258 | var client = redisIO.createClient(app.set('redisPort'), app.set('redisHost'), {});
|
259 | client.select(app.set('redisDbIndex') || 0, function(err) {
|
260 | if (err) {
|
261 | throw err;
|
262 | }
|
263 | });
|
264 | client.auth(app.set('redisPass'), function(err) {
|
265 | if (err) {
|
266 | throw err;
|
267 | }
|
268 | });
|
269 | config.onlineUsersConfig.io = io;
|
270 | config.onlineUsersConfig.dataClient = client;
|
271 | config.onlineUsersConfig.appName = config.appName;
|
272 | config.onlineUsersConfig.serverName = config.server + ':' + os.hostname();
|
273 | var OnlineUsers = require('socket.io-online-users');
|
274 | io.clients = new OnlineUsers.update(config.onlineUsersConfig);
|
275 | }
|
276 |
|
277 | if (!config.turnOffAwesomeLogs && logger) {
|
278 | logger.logPage(app, config.logger.username, config.logger.password, '/redis-logger.html').socket(io);
|
279 | }
|
280 | mongoose.connect(process.env.MONGO_URI, function(err) {
|
281 | if (err) {
|
282 | return console.log(clcError('Connecting to mongoose error::', err));
|
283 | }
|
284 | });
|
285 |
|
286 | mongoose.schema = projRequire(mongooseSchemaLocation);
|
287 | require('./helpers.js').makeGlobal(config, mongoose, io).attachAPI();
|
288 | if (config.preContent) {
|
289 | projRequire(config.preContent).content(app, io, mongoose);
|
290 | }
|
291 |
|
292 | require('./controller.js').sockets(app, io, sessionStore, mongoose, apiLocation);
|
293 |
|
294 | if (config.postContent) {
|
295 | projRequire(config.postContent).content(app, io, mongoose);
|
296 | }
|
297 | if (config.useStaticServer != undefined) {
|
298 | if (config.viewEngine) {
|
299 | app.set('view engine', config.viewEngine);
|
300 | } else {
|
301 | app.set('view engine', 'jade');
|
302 | }
|
303 | if (config.viewDirectory) {
|
304 | app.set('views', rootUrl + '/' + config.viewDirectory);
|
305 | } else {
|
306 | app.set('views', rootUrl + '/views');
|
307 | }
|
308 | app.locals.basedir = app.get('views');
|
309 | if (config.publicDirectory) {
|
310 | app.use(express.static(rootUrl + '/' + config.publicDirectory));
|
311 | } else {
|
312 | app.use(express.static(rootUrl + '/public'));
|
313 | }
|
314 | }
|
315 |
|
316 | var port = process.env.PORT || config.port || 4050;
|
317 | server.listen(port);
|
318 | console.log('NM server started on port ' + port);
|
319 | if (fn) {
|
320 | return fn();
|
321 | }
|
322 |
|
323 | if (typeof config.ssl == 'object') {
|
324 | var sslPort;
|
325 | if (process.env.PORT) {
|
326 | sslPort = 443;
|
327 | } else if (config.ssl.port) {
|
328 | sslPort = config.ssl.port
|
329 | } else {
|
330 | sslPort = 5051;
|
331 | }
|
332 | config.serverSSL.listen(sslPort);
|
333 | console.log('NM server started on ssl port ' + sslPort);
|
334 | if (fn) {
|
335 | return fn();
|
336 | }
|
337 | }
|
338 | };
|