UNPKG

11.5 kBJavaScriptView Raw
1let { log, chalk } = require('../namespace/console');
2let { error,warn } = chalk;
3let Server = require('../classes/server');
4let Router = require('../classes/router');
5let Initializer = require('./initialize');
6let redis = require("redis");
7let session = require('express-session');
8let sessionStore = require('express-sessions');
9let { extend } = require('../namespace/object');
10let Project = require('../classes/project');
11let { join } = require('path');
12let RequestEnd = require('./after-request');
13let Cache = require('../namespace/cache');
14let exposeModelsGlobally = require('./expose-models-globally');
15let RequestIdentifier = require('./request-identifier');
16const _debug = require('debug');
17const debug = Symbol('debug');
18const debugNamespace = Symbol('debugNamespace');
19Promise.promisifyAll(redis.RedisClient.prototype);
20Promise.promisifyAll(redis.Multi.prototype);
21
22module.exports = class Boot {
23
24 constructor (cwd = false) {
25 this[debugNamespace] = _debug('glad');
26 this[debug]('Boot:constructor');
27 this.project = new Project(cwd);
28 this.project.initialize();
29 }
30
31 [debug] (msg) {
32 this[debugNamespace]('Boot: %s', msg);
33 }
34
35 exec () {
36
37 Promise.each([
38 this.createServer,
39 this.connectToRedis,
40 this.gladCache,
41 this.session,
42 this.attachSessionToWebsockets,
43 this.createRouter,
44 this.init,
45 this.connectToMongo,
46 this.id,
47 this.disablePoweredBy,
48 this.setViewEngine,
49 this.getMiddleware,
50 this.getHooks,
51 this.after,
52 this.middleware,
53 this.initializeWaterline,
54 this.drawSocketIo,
55 this.draw,
56 this.exposeModels
57 ], exec => exec.call(this))
58 .then(() => this.server.listen())
59 .catch(err => log(err));
60 }
61
62 createServer () {
63 this[debug]('createServer');
64 return new Promise( resolve => {
65 this.server = new Server(this.project);
66 resolve();
67 });
68 }
69
70 connectToRedis () {
71 this[debug]('connectToRedis');
72 return new Promise( resolve => {
73 let { config } = this.project;
74 this.server.redis = redis.createClient(config.redis);
75 resolve();
76 });
77 }
78
79 gladCache () {
80 this[debug]('gladCache');
81 Glad.cache = new Cache(this.server, this.project);
82 if (Glad.cache.disabled) {
83 chalk.info("Cache: DISABLED");
84 } else {
85 chalk.ok("Cache: ENABLED");
86 }
87 return new Promise.resolve();
88 }
89
90 init () {
91 this[debug]('init');
92 return new Initializer(this.project, this.server).initialize();
93 }
94
95 connectToMongo () {
96 this[debug]('connectToMongo');
97 let { config } = this.project;
98
99 if (config.orm === 'mongoose' && config.mongodb) {
100 return new Promise( resolve => {
101 let mongoose = require('mongoose');
102 mongoose.connect('mongodb://' + config.mongodb.host + ':' + config.mongodb.port + '/' + config.mongodb.database, { useMongoClient: true });
103 resolve();
104 });
105 }
106 }
107
108 id () {
109 this[debug]('id');
110 return new Promise( resolve => {
111 let identifier = new RequestIdentifier(this.project);
112 this.server.app.use(identifier.id.bind(identifier));
113 resolve();
114 });
115 }
116
117 disablePoweredBy () {
118 this[debug]('disablePoweredBy');
119 this.server.app.disable('x-powered-by');
120 return Promise.resolve();
121 }
122
123 getMiddleware () {
124 this[debug]('getMiddleware');
125 return new Promise( resolve => {
126 this.project.middleware = require(join(this.project.projectPath, 'middleware'));
127 resolve();
128 });
129 }
130
131 getHooks () {
132 this[debug]('getHooks');
133 this.project.hooks = require(join(this.project.projectPath, 'hooks'));
134 return Promise.resolve();
135 }
136
137 // THIS NEEDS TO BE MOVED IN THE ROUTER CHAIN vvvvvvvvvvv
138 after () {
139 this[debug]('after');
140 return new Promise( resolve => {
141 let after = new RequestEnd(this.project);
142 this.server.app.use(after.end.bind(after));
143 resolve();
144 });
145 }
146
147 /**
148 * If there is a session key in the config then implement sessions based on the config.
149 * Otherwise it is assumed that a roll your own implementation or no sessions will be used.
150 */
151 session () {
152 this[debug]('session');
153 return new Promise( resolve => {
154 let { config } = this.project;
155 let userSessionModule;
156
157 try {
158 userSessionModule = require(join(this.project.projectPath, '/session'));
159 } catch (err) {
160 userSessionModule = false;
161 }
162
163 if (config.session) {
164
165 let options = extend({
166 instance: this.server.redis,
167 storage: 'redis'
168 }, config.session);
169
170 this._sessions = session({
171 secret: config.cookie.secret,
172 resave: config.cookie.resave || false,
173 store: new sessionStore(options),
174 saveUninitialized: true,
175 cookie : config.cookie,
176 name : config.cookie.name
177 });
178
179 this.server.app.use( (req, res, next) => {
180 let debugMiddleware = _debug('glad');
181 debugMiddleware('Session:middleware');
182 if (userSessionModule) {
183 debugMiddleware('Session:middleware: using session.js');
184 userSessionModule(req, res, next).then(result => {
185 if (result) {
186 debugMiddleware('Session:middleware: session.js > Use Glad Session');
187 this._sessions(req, res, next);
188 } else {
189 debugMiddleware('middleware: session.js > Not using Session');
190 }
191 });
192 } else {
193 debugMiddleware('middleware: Using Glad Session');
194 this._sessions(req, res, next);
195 }
196 });
197 }
198 resolve();
199 });
200 }
201
202 /**
203 * If there is a session key in the config then attach the session to websocket events.
204 * Otherwise look for setupWebsockets in the config, if it exists, pass the setup over to there and waith for that promise to resolve.
205 */
206 attachSessionToWebsockets () {
207 this[debug]('attachSessionToWebsockets');
208 return new Promise( resolve => {
209 let { config } = this.project;
210 if (config.session) {
211 this.server.websockets.use((socket, next) => {
212 this._sessions(socket.request, socket.request.res, next);
213 });
214 this.server.websockets.on('connection', conn => {
215 if (conn.request.session) {
216 conn.request.session.socketId = conn.id;
217 } else {
218 error("ERROR: You are trying to attach a websocket id to a session, but there is NO session");
219 }
220 });
221 resolve();
222 } else if (config.setupWebsockets && typeof config.setupWebsockets === 'function' ) {
223 config.setupWebsockets(this.server.websockets).then(resolve);
224 } else {
225 resolve();
226 }
227 });
228 }
229
230 createRouter () {
231 this[debug]('createRouter');
232 return new Promise( (resolve, reject) => {
233 this.router = new Router(this.project, this.server);
234 this.router.buildRoutes().then(resolve).catch(reject);
235 });
236 }
237
238 exposeModels () {
239 this[debug]('exposeModels');
240 let { config } = this.project;
241 return new Promise( (resolve, reject) => {
242 if (config.exposeModelsGlobally && config.orm === 'mongoose') {
243 exposeModelsGlobally(this.router).then(resolve).catch(reject);
244 } else if (config.exposeModelsGlobally) {
245 warn('You can only automatically expose model globally when specifying mongoose as your ORM');
246 warn('If you are using mongoose, please set `orm : "mongoose"` in your config.js file.');
247 warn('If you are not using mongoose, please set "exposeModelsGlobally : false" in your config.js file to supress this warning');
248 }
249 });
250 }
251
252 middleware () {
253 this[debug]('middleware');
254 return new Promise( (resolve, reject) => {
255 Promise.each(this.project.middleware, exec => exec(this.server))
256 .then(resolve)
257 .catch(reject);
258 });
259 }
260
261 /**
262 * If Waterline is being used, then the developer needs to initialize it.
263 * This is wrapped in a try/catch because we don't expect that this method will be there (especially if the project is not using glad-cli).
264 */
265 initializeWaterline () {
266 this[debug]('initializeWaterline');
267 return new Promise( (resolve, reject) => {
268 if (this.project.orm === 'waterline') {
269 this[debug]('initializeWaterline: Using Waterline');
270 try {
271 let hooks = require(`${this.project.projectPath}/hooks`);
272 hooks.initializeWaterline(this.router).then(resolve).catch(reject);
273 } catch (err) {
274 error(err);
275 reject("It seems like you have initialized this project to use waterline, but you are missing the onAfterModels hook in your hooks file.");
276 }
277 } else {
278 this[debug]('initializeWaterline: Not using Waterline');
279 // no need to initialize for waterline.
280 resolve();
281 }
282 });
283 }
284
285 drawSocketIo () {
286 this[debug]('drawSocketIo');
287 return new Promise( resolve => {
288 try {
289 let socketRouter = require(`${this.project.projectPath}/sockets/router`);
290 let socketPolicies = require(`${this.project.projectPath}/sockets/policies`);
291 this.server.websockets.on('connection', conn => {
292 let connectionDebug = _debug('glad');
293 connectionDebug('WebSocket:connection');
294 socketRouter.forEach(route => {
295 connectionDebug('WebSocket:connection: Drawing Route for %s', route.event);
296 conn.on(route.event, data => {
297 let routeDebug = _debug('glad');
298 routeDebug('WebSocket:Route: Received %s', route.event);
299 if (route.policy && socketPolicies[route.policy]) {
300 routeDebug('WebSocket:Route: Applying policy for %s', route.event);
301 socketPolicies[route.policy](conn, () => {
302 routeDebug('WebSocket:Route: Policy accepted for %s', route.event);
303 route.action.call(this.server.websockets, data, conn);
304 });
305 } else if (route.policy) {
306 error(`WS: The route for the ${route.event} event requires a policy, but the socket policy does not exist. Please ensure that it is defined in ${this.project.projectPath}/sockets/policies.js`);
307 } else {
308 routeDebug('WebSocket:Route: No policy for %s > Calling action', route.event);
309 route.action.call(this.server.websockets, data, conn);
310 }
311 });
312 });
313 });
314 resolve();
315 } catch (err) {
316 this[debug]('Not using websockets');
317 log("Not using websockets");
318 resolve();
319 }
320 });
321 }
322
323 draw () {
324 this[debug]('draw');
325 return new Promise( resolve => {
326 let key;
327 for (key in this.router.routes) {
328 if (this.router.routes.hasOwnProperty(key)) {
329 let target = this.router.routes[key];
330 let method;
331 for (method in target) {
332 if (target.hasOwnProperty(method)) {
333 target[method].forEach(cfg => {
334 cfg.controller = this.router.controllers[key];
335 this.router.route(method, cfg);
336 });
337 }
338 }
339 }
340 }
341 resolve();
342 });
343 }
344
345 setViewEngine () {
346 this[debug]('setViewEngine');
347 let { config } = this.project;
348 this.server.app.set('view engine', config.defaultViewEngine || 'pug');
349 return Promise.resolve();
350 }
351
352}