1 | let { log, chalk } = require('../namespace/console');
|
2 | let { error,warn } = chalk;
|
3 | let Server = require('../classes/server');
|
4 | let Router = require('../classes/router');
|
5 | let Initializer = require('./initialize');
|
6 | let redis = require("redis");
|
7 | let session = require('express-session');
|
8 | let sessionStore = require('express-sessions');
|
9 | let { extend } = require('../namespace/object');
|
10 | let Project = require('../classes/project');
|
11 | let { join } = require('path');
|
12 | let RequestEnd = require('./after-request');
|
13 | let Cache = require('../namespace/cache');
|
14 | let exposeModelsGlobally = require('./expose-models-globally');
|
15 | let RequestIdentifier = require('./request-identifier');
|
16 | const _debug = require('debug');
|
17 | const debug = Symbol('debug');
|
18 | const debugNamespace = Symbol('debugNamespace');
|
19 | Promise.promisifyAll(redis.RedisClient.prototype);
|
20 | Promise.promisifyAll(redis.Multi.prototype);
|
21 |
|
22 | module.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 |
|
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 |
|
144 |
|
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 |
|
199 |
|
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 |
|
258 |
|
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 |
|
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 | }
|