This repository has been archived by the owner on Feb 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathapp.js
261 lines (232 loc) · 7.18 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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/**
* dependencies
*/
var express = require('express'),
async = require('async'),
Search = require('./book/book-search.js').Search,
DataProvider = require('./book/book-data-provider.js').BookProvider,
LOG = require('./lib/log.js'),
_config = require('./configuration'),
config = _config.getConfiguration(),
httpsConfig = _config.getHTTPSConfiguration(),
https = require('https'),
fs = require('fs'),
validator = require('./lib/validator'),
path = require('path'),
mongoStore = require('connect-mongo')(express),
storeConfig = _config.getStoreConfig(),
_ = require('underscore'),
routes = require('./routes');
/**
* The Application
*
* @constructor
*/
function Application() {
this.app = express();
}
/**
* init Application
*
* @param {Function} cb Callback function
*/
Application.prototype.init = function (cb) {
var self = this;
async.series({
"auth": self.authInit.bind(self),
"configure": self.configure.bind(self),
"initDatabase": DataProvider.init.bind(DataProvider),
"initSearchProvider": Search.init.bind(Search),
"mountAPI": self.mountAPI.bind(self)
},
function (err, callback) {
if (err) {
LOG('Error starting up the application. Exiting.');
LOG(err);
process.exit(1);
}
if (cb) {
cb(err);
}
})
};
/**
* init all features required for authentication
*
* there are:
* - valid user allowed to use application,
* - session settings,
* - basic authentication middleware with logout possibilities,
* - protectWithAuth() method is used for protecting `secret` routes
*
* @this {Application}
* @param {Function} cb Callback function
*/
Application.prototype.authInit = function (cb) {
//the only one user exists in the system for now
var validUser = {
name: "valid",
pass: "user"
};
var self = this;
//we need to use session here
this.app.use(express.cookieParser(config.cookieParser.secret));
// it is required to use external storage for session
//we opt to use mongoDb
this.app.use(
express.session(_.extend(
{},
config.session,
{store: new mongoStore(storeConfig)})));
//we use basic auth
var preAuth = function(req, res, next) {
if (req.session.userLogOut) {
delete req.headers.authorization;
req.session.userLogOut = false;
}
next();
};
var message = ["****Use 'User Name':", validUser.name, "and Password:", validUser.pass].join(" ");
var auth = express.basicAuth(function (user, pass) {
return user === validUser.name && pass === validUser.pass;
}, message);
//define protectWithAuth method
//it helps to protect different routes with auth
this.protectWithAuth = function(route){
self.app.all(route, preAuth, auth);
};
cb();
};
/**
* mount book API routes
*
* @this {Application}
* @param {Function} cb Callback function
*/
Application.prototype.mountAPI = function (cb) {
var BookRestApi = require('./book/book-rest-api.js').BookRestApi;
var bookRestApi = new BookRestApi();
var bookValidationSchemas = require('./book/book-validation-schemas');
//protect with auth
if(this.protectWithAuth){
this.protectWithAuth('/rest/*');
}
// mounting REST endpoints. All the other urls would be handled by static (coming from public folder).
this.app.get('/rest/allBooks',
validator.getMiddleware(bookValidationSchemas.findAllBooks),
bookRestApi.findAllBooks);
this.app.post('/rest/newBook',
validator.getMiddleware(bookValidationSchemas.newBook),
bookRestApi.newBook);
this.app.get('/rest/search',
validator.getMiddleware(bookValidationSchemas.searchForBooks),
bookRestApi.searchForBooks);
this.app.post('/rest/update',
validator.getMiddleware(bookValidationSchemas.update),
bookRestApi.update);
cb();
};
/**
* configure Application:
*
* bind middlewares:
* - json parse
* - static routes
* - error handlers in different environments
*
* bind logout route
*
* @this {Application}
* @param {Function} cb Callback function
*/
Application.prototype.configure = function (cb) {
//Configuration for errorHandler and others.
var self = this;
this.app.set('views', path.join(__dirname, 'views'));
this.app.set('view engine', 'jade');
this.app.use(express.favicon(path.join(__dirname, 'favicon.ico')));
this.app.use(express.json());
this.app.configure("development", "production", function () {
//mount secure point
//it forces all request to be redirected on HTTPS
self.app.use(function (req, res, next) {
if (!config.isHTTPS(req)) {
var host = (config.https && config.https.port)
? [config.host, ':', config.https.port] : [config.host];
return res.redirect(['https://'].concat(host, [req.url]).join(''));
}
next();
});
});
//protect static route
if(this.protectWithAuth){
this.protectWithAuth('/secret.html');
}
//map static routes
routes.static(this.app);
this.app.use(this.app.router);
//map static sources
this.app.use(express.static(path.join(__dirname, 'public')));
//mount logout point
this.app.get('/logout', function (req, res) {
req.session.userLogOut = true;
res.redirect('/');
});
//bind route404 as middleware
//if we pass here - no routes were found
this.app.use(routes.route404);
this.app.configure('development', function () {
self.app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
this.app.configure('production', function () {
self.app.use(express.errorHandler());
});
cb();
};
/**
* bind server to listening on defined port
*
* @this {Application}
* @param cb
*/
Application.prototype.bindServer = function (cb) {
// Binding to port provided by Heroku, or to the default one.
this.app.listen(config.port, function (err) {
LOG(['Application started on ULR http://', config.host, ':', config.port].join(''));
if (cb) {
cb(err);
}
});
};
/**
* run secure server
* this is only required when running https on the local server
* on Heroku it is redundant
*
* @this {Application}
* @param {Function} cb Callback function
*/
Application.prototype.bindSecureServerIfNeeded = function(cb){
if (!httpsConfig){
if(cb){
async.nextTick(cb);
}
return;
}
//create secure server
var self = this;
var certData = {
key: fs.readFileSync(httpsConfig.key),
cert: fs.readFileSync(httpsConfig.cert)
};
//create server
var secureServer = https.createServer(certData, self.app);
//run server
secureServer.listen(httpsConfig.port, function(err){
LOG(['Secure server up and running on port: https://', config.host, ':', httpsConfig.port].join(''));
if(cb){
cb(err);
}
});
};
module.exports = Application;