UNPKG

11.4 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 return new Promise.resolve();
83 }
84
85 init () {
86 this[debug]('init');
87 return new Initializer(this.project, this.server).initialize();
88 }
89
90 connectToMongo () {
91 this[debug]('connectToMongo');
92 let { config } = this.project;
93
94 if (config.orm === 'mongoose' && config.mongodb) {
95 return new Promise( resolve => {
96 let mongoose = require('mongoose');
97 mongoose.connect('mongodb://' + config.mongodb.host + ':' + config.mongodb.port + '/' + config.mongodb.database, { useMongoClient: true });
98 resolve();
99 });
100 }
101 }
102
103 id () {
104 this[debug]('id');
105 return new Promise( resolve => {
106 let identifier = new RequestIdentifier(this.project);
107 this.server.app.use(identifier.id.bind(identifier));
108 resolve();
109 });
110 }
111
112 disablePoweredBy () {
113 this[debug]('disablePoweredBy');
114 this.server.app.disable('x-powered-by');
115 return Promise.resolve();
116 }
117
118 getMiddleware () {
119 this[debug]('getMiddleware');
120 return new Promise( resolve => {
121 this.project.middleware = require(join(this.project.projectPath, 'middleware'));
122 resolve();
123 });
124 }
125
126 getHooks () {
127 this[debug]('getHooks');
128 this.project.hooks = require(join(this.project.projectPath, 'hooks'));
129 return Promise.resolve();
130 }
131
132 // THIS NEEDS TO BE MOVED IN THE ROUTER CHAIN vvvvvvvvvvv
133 after () {
134 this[debug]('after');
135 return new Promise( resolve => {
136 let after = new RequestEnd(this.project);
137 this.server.app.use(after.end.bind(after));
138 resolve();
139 });
140 }
141
142 /**
143 * If there is a session key in the config then implement sessions based on the config.
144 * Otherwise it is assumed that a roll your own implementation or no sessions will be used.
145 */
146 session () {
147 this[debug]('session');
148 return new Promise( resolve => {
149 let { config } = this.project;
150 let userSessionModule;
151
152 try {
153 userSessionModule = require(join(this.project.projectPath, '/session'));
154 } catch (err) {
155 userSessionModule = false;
156 }
157
158 if (config.session) {
159
160 let options = extend({
161 instance: this.server.redis,
162 storage: 'redis'
163 }, config.session);
164
165 this._sessions = session({
166 secret: config.cookie.secret,
167 resave: config.cookie.resave || false,
168 store: new sessionStore(options),
169 saveUninitialized: true,
170 cookie : config.cookie,
171 name : config.cookie.name
172 });
173
174 this.server.app.use( (req, res, next) => {
175 let debugMiddleware = _debug('glad');
176 debugMiddleware('Session:middleware');
177 if (userSessionModule) {
178 debugMiddleware('Session:middleware: using session.js');
179 userSessionModule(req, res, next).then(result => {
180 if (result) {
181 debugMiddleware('Session:middleware: session.js > Use Glad Session');
182 this._sessions(req, res, next);
183 } else {
184 debugMiddleware('middleware: session.js > Not using Session');
185 }
186 });
187 } else {
188 debugMiddleware('middleware: Using Glad Session');
189 this._sessions(req, res, next);
190 }
191 });
192 }
193 resolve();
194 });
195 }
196
197 /**
198 * If there is a session key in the config then attach the session to websocket events.
199 * Otherwise look for setupWebsockets in the config, if it exists, pass the setup over to there and waith for that promise to resolve.
200 */
201 attachSessionToWebsockets () {
202 this[debug]('attachSessionToWebsockets');
203 return new Promise( resolve => {
204 let { config } = this.project;
205 if (config.session) {
206 this.server.websockets.use((socket, next) => {
207 this._sessions(socket.request, socket.request.res, next);
208 });
209 this.server.websockets.on('connection', conn => {
210 if (conn.request.session) {
211 conn.request.session.socketId = conn.id;
212 } else {
213 error("ERROR: You are trying to attach a websocket id to a session, but there is NO session");
214 }
215 });
216 resolve();
217 } else if (config.setupWebsockets && typeof config.setupWebsockets === 'function' ) {
218 config.setupWebsockets(this.server.websockets).then(resolve);
219 } else {
220 resolve();
221 }
222 });
223 }
224
225 createRouter () {
226 this[debug]('createRouter');
227 return new Promise( (resolve, reject) => {
228 this.router = new Router(this.project, this.server);
229 this.router.buildRoutes().then(resolve).catch(reject);
230 });
231 }
232
233 exposeModels () {
234 this[debug]('exposeModels');
235 let { config } = this.project;
236 return new Promise( (resolve, reject) => {
237 if (config.exposeModelsGlobally && config.orm === 'mongoose') {
238 exposeModelsGlobally(this.router).then(resolve).catch(reject);
239 } else if (config.exposeModelsGlobally) {
240 warn('You can only automatically expose model globally when specifying mongoose as your ORM');
241 warn('If you are using mongoose, please set `orm : "mongoose"` in your config.js file.');
242 warn('If you are not using mongoose, please set "exposeModelsGlobally : false" in your config.js file to supress this warning');
243 }
244 });
245 }
246
247 middleware () {
248 this[debug]('middleware');
249 return new Promise( (resolve, reject) => {
250 Promise.each(this.project.middleware, exec => exec(this.server))
251 .then(resolve)
252 .catch(reject);
253 });
254 }
255
256 /**
257 * If Waterline is being used, then the developer needs to initialize it.
258 * 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).
259 */
260 initializeWaterline () {
261 this[debug]('initializeWaterline');
262 return new Promise( (resolve, reject) => {
263 if (this.project.orm === 'waterline') {
264 this[debug]('initializeWaterline: Using Waterline');
265 try {
266 let hooks = require(`${this.project.projectPath}/hooks`);
267 hooks.initializeWaterline(this.router).then(resolve).catch(reject);
268 } catch (err) {
269 error(err);
270 reject("It seems like you have initialized this project to use waterline, but you are missing the onAfterModels hook in your hooks file.");
271 }
272 } else {
273 this[debug]('initializeWaterline: Not using Waterline');
274 // no need to initialize for waterline.
275 resolve();
276 }
277 });
278 }
279
280 drawSocketIo () {
281 this[debug]('drawSocketIo');
282 return new Promise( resolve => {
283 try {
284 let socketRouter = require(`${this.project.projectPath}/sockets/router`);
285 let socketPolicies = require(`${this.project.projectPath}/sockets/policies`);
286 this.server.websockets.on('connection', conn => {
287 let connectionDebug = _debug('glad');
288 connectionDebug('WebSocket:connection');
289 socketRouter.forEach(route => {
290 connectionDebug('WebSocket:connection: Drawing Route for %s', route.event);
291 conn.on(route.event, data => {
292 let routeDebug = _debug('glad');
293 routeDebug('WebSocket:Route: Received %s', route.event);
294 if (route.policy && socketPolicies[route.policy]) {
295 routeDebug('WebSocket:Route: Applying policy for %s', route.event);
296 socketPolicies[route.policy](conn, () => {
297 routeDebug('WebSocket:Route: Policy accepted for %s', route.event);
298 route.action.call(this.server.websockets, data, conn);
299 });
300 } else if (route.policy) {
301 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`);
302 } else {
303 routeDebug('WebSocket:Route: No policy for %s > Calling action', route.event);
304 route.action.call(this.server.websockets, data, conn);
305 }
306 });
307 });
308 });
309 resolve();
310 } catch (err) {
311 this[debug]('Not using websockets');
312 log("Not using websockets");
313 resolve();
314 }
315 });
316 }
317
318 draw () {
319 this[debug]('draw');
320 return new Promise( resolve => {
321 let key;
322 for (key in this.router.routes) {
323 if (this.router.routes.hasOwnProperty(key)) {
324 let target = this.router.routes[key];
325 let method;
326 for (method in target) {
327 if (target.hasOwnProperty(method)) {
328 target[method].forEach(cfg => {
329 cfg.controller = this.router.controllers[key];
330 this.router.route(method, cfg);
331 });
332 }
333 }
334 }
335 }
336 resolve();
337 });
338 }
339
340 setViewEngine () {
341 this[debug]('setViewEngine');
342 let { config } = this.project;
343 this.server.app.set('view engine', config.defaultViewEngine || 'pug');
344 return Promise.resolve();
345 }
346
347}