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 | 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 |
|
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 |
|
149 |
|
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 |
|
204 |
|
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 |
|
263 |
|
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 |
|
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 | }
|