-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
200 lines (166 loc) · 4.9 KB
/
app.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
* app.js
*
* Entry point for the application's execution. Sets up the database, the web
* server, and the socket.io listener
*/
var flatiron = require('flatiron')
, app = flatiron.app
, config = app.config
, url = require('url')
, util = require('util')
, EventEmitter = require('events').EventEmitter
, connect = require('connect')
, director = require('director')
, ecstatic = require('ecstatic')
, socketIO = require('socket.io')
, mongoose = require('mongoose')
, redis = require('redis')
;
// app.config
setUpConfiguration();
// Loading any local modules only after nconf has been set up.
var hu = require('./server/util/http')
, middleware = require('./server/util/middleware')
, error = require('./server/util/error')
, paths = require('./server/paths')
;
// app.http
setUpMiddleware();
// app.router
setUpRouting();
// Connect to MongoDB
mongoose.connect(config.get('mongoURI'));
// Connect to Redis
setUpRedis();
// Start the web server
app.start(config.get('port'));
// Start socket.io listening
setUpSocketIO();
// Everything started up fine
console.log('OK!');
/*
* Set up the nconf config object
*/
function setUpConfiguration() {
var env;
config
.argv()
.env()
;
env = config.get('NODE_ENV') || 'production';
config
.file({
file: './config/config.json'
})
.add('env', {
type: 'file'
, file: './config/environments/' + env + '.json'
})
.set('root', __dirname + '/server')
;
}
/*
* Set up the app's HTTP middleware stack
*/
function setUpMiddleware() {
app.store = new require('connect/lib/middleware/session/memory');
app.use(flatiron.plugins.http);
app.http.before = [
connect.cookieParser('secret')
, connect.cookieSession({
cookie: {
domain: config.get('domain')
, store: app.store
}
})
, middleware.pageRewrite
, ecstatic(__dirname + '/client')
];
}
/*
* Configure the app's router and load the routing table
*/
function setUpRouting() {
// Set up routing table
app.router.mount(paths.routes);
app.router.configure({
recurse: false
, strict: false
, async: true
});
// Attach HTTP utilities to route context
app.router.attach(function() {
var self = this;
Object.keys(hu).forEach(function(name) {
self[name] = hu[name];
});
});
}
/*
* Start listening for socket.io connections
*/
function setUpSocketIO() {
// Start listening
var io = socketIO.listen(app.server);
// An evented interface on top of redis pub/sub to allow easy communication
// with the socket to which a given user is connected
var subEventer = function() {
EventEmitter.call(this);
};
util.inherits(subEventer, EventEmitter);
// Set up a global channel for broadcasting messages to all users on the site
app.messages['global'] = new subEventer();
app.pubsub.subscribe('global');
// Connect user sessions and sockets.io clients. This attaches the session
// object to the socket.io handshake object
io.configure(function() {
io.set('authorization', function(data, cb) {
var cookies = connect.utils.parseCookie(data.headers.cookie);
data.session = JSON.parse(cookies['connect.sess'].match(/\{.*\}/g)[0]);
data.path = url.parse(data.headers.referer).pathname;
cb(null, true);
});
});
// Upon a new connection, bind the appropriate client listeners for the URL
// from which the socket.io connection was initialized.
io.on('connection', function(socket) {
var path = socket.handshake.path;
var userID = socket.handshake.session.userID;
// Open a listening channel for this user
app.messages[userID] = new subEventer();
app.pubsub.subscribe(userID);
// Upon disconnect, close the user's channel
socket.on('disconnect', function() {
app.pubsub.unsubscribe(userID);
});
(paths.sockets[path] || function() {}).call({
e: error
, socket: socket
}, socket);
});
}
/*
* Opens two Redis clients. One is exposed to the rest of the app through
* `app.store` and is used for queries/updates. The other is used to subscribe
* to redis changes, and can be listened via `app.messages`.
*/
function setUpRedis() {
var redisURI = url.parse(config.get('redisURI'));
app.store = redis.createClient(redisURI.port, redisURI.hostname);
app.pubsub = redis.createClient(redisURI.port, redisURI.hostname);
// When the pubsub client recieves a message, send an event through the
// appropriate `app.messages` eventer
app.pubsub.on('message', function(channel, message) {
var messageObject = JSON.parse(message)
, eventName = messageObject.event;
delete messageObject.event;
app.messages[channel].emit(eventName, messageObject);
});
// Expose a function for easily sending messages
app.messages = function(toUser, eventName, data) {
data = data || {};
data.event = eventName;
app.store.publish(toUser, JSON.stringify(data || {}));
};
};