1 | const fs = require('fs');
|
2 | const path = require('path');
|
3 | const { chalk } = require('./../namespace/console');
|
4 | const policy = require('./policy');
|
5 | const bodyParser = require('body-parser');
|
6 | const args = require('optimist').argv;
|
7 | const lodash = require('lodash');
|
8 | const debug = require('debug');
|
9 |
|
10 | module.exports = class Router {
|
11 |
|
12 | constructor (project, server) {
|
13 | this.debug = debug('glad');
|
14 | this.debug('Router:constructor');
|
15 | this.project = project;
|
16 | this.server = server;
|
17 | this.routes = {};
|
18 | this.controllers = {};
|
19 | this.models = {};
|
20 | this.errors = [];
|
21 | this.config = require(this.project.configPath);
|
22 | this.policies = require(path.join(project.projectPath, "policies.js"));
|
23 | this.logging = require(project.configPath).logHTTP;
|
24 | }
|
25 |
|
26 | |
27 |
|
28 |
|
29 | buildRoutes () {
|
30 | this.debug('Router:buildRoutes');
|
31 | return new Promise( (resolve, reject) => {
|
32 | this.setRoutes();
|
33 | this.setControllers();
|
34 | this.setModels();
|
35 | if (this.errors.length) {
|
36 | reject(this.errors);
|
37 | } else {
|
38 | resolve();
|
39 | }
|
40 | });
|
41 | }
|
42 |
|
43 | |
44 |
|
45 |
|
46 | setRoutes () {
|
47 | this.debug('Router:setRoutes');
|
48 | this.setObjectsForSegment(this.project.routesPath, 'routes', 'Router');
|
49 | }
|
50 |
|
51 | |
52 |
|
53 |
|
54 | setModels () {
|
55 | this.debug('Router:setModels');
|
56 | this.setObjectsForSegment(this.project.modelsPath, 'models', 'Model');
|
57 | }
|
58 |
|
59 | |
60 |
|
61 |
|
62 | setControllers () {
|
63 | this.debug('Router:setControllers');
|
64 | this.setObjectsForSegment(this.project.controllersPath, 'controllers', 'Controller');
|
65 | }
|
66 |
|
67 | |
68 |
|
69 |
|
70 | getFilesForPath (path) {
|
71 | this.debug(`Router:getFilesForPath - gets all js files in ${path}`);
|
72 | let paths = fs.readdirSync(path).filter(file => /(\.js)$/.test(file));
|
73 | let only = args.only && args.only.split(',').map(x => `${x}.js`);
|
74 | |
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | if (only && only.length) {
|
81 | paths = paths.filter( p => lodash.includes(only, p));
|
82 | }
|
83 | return paths;
|
84 | }
|
85 |
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | setObjectsForSegment (fpath, segment, error) {
|
94 | this.debug(`Router:setObjectsForSegment ${segment}`);
|
95 | this.getFilesForPath(fpath).forEach(file => {
|
96 | let ref = path.join (fpath, file);
|
97 | try {
|
98 | this[segment][file.replace('.js', '')] = require(ref);
|
99 | } catch (err) {
|
100 | chalk.error(`Glad > ${error}`, err.stack);
|
101 | this.errors.push({
|
102 | message : "> Could Not Bind " + ref.split('/').pop().toString() + '::' + file.replace('.js', '') + " To Any Route!",
|
103 | err : err
|
104 | });
|
105 | }
|
106 | });
|
107 | }
|
108 |
|
109 | |
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | bodyParser (config) {
|
116 | this.debug('Router:bodyParser');
|
117 | let type, options;
|
118 | if (config.bodyParser && config.bodyParser.custom) {
|
119 | return config.bodyParser.custom;
|
120 | } else if (config.bodyParser) {
|
121 | type = config.bodyParser.parser || this.config.defaultBodyParser && this.config.defaultBodyParser.type;
|
122 | options = config.bodyParser || this.config.defaultBodyParser;
|
123 | } else {
|
124 | type = this.config.defaultBodyParser && this.config.defaultBodyParser.type;
|
125 | options = this.config.defaultBodyParser;
|
126 | }
|
127 | type = type || 'json';
|
128 | options = options || {};
|
129 | return bodyParser[type](options);
|
130 | }
|
131 |
|
132 | setViewPath (config) {
|
133 | this.debug('Router:setViewPath');
|
134 | return (req, res, next) => {
|
135 | req.__rootViewPath = `${this.project.viewsPath}`;
|
136 | req.viewPath = req.__rootViewPath + config.action;
|
137 | next();
|
138 | }
|
139 | }
|
140 |
|
141 | |
142 |
|
143 |
|
144 |
|
145 |
|
146 | route (method, config) {
|
147 | if (!config.controller) {
|
148 | this.noControllerError(config);
|
149 | }
|
150 | this.debug(`Router:route ${config.path} => ${config.controller.name}#${config.action}`);
|
151 | let { path, controller, action } = config;
|
152 | let bodyParser = this.bodyParser(config);
|
153 | let viewPath = this.setViewPath(config);
|
154 | method = method.toLowerCase();
|
155 | return this.server.app[method](path, bodyParser, viewPath, (req, res) => {
|
156 | debug('glad')('Router:apply %s => %s#%s with policy %s', path, controller.name, action, config.policy || 'none');
|
157 | return new policy(this.policies, this.logging, this.server, config.policy, controller, action).restrict(req, res);
|
158 | });
|
159 | }
|
160 |
|
161 | noControllerError (config) {
|
162 | chalk.error([
|
163 | '',
|
164 | '-------------------------------------',
|
165 | 'Error: NO_CONTROLLER',
|
166 | 'You can not have a route wthout a controller to handle requests made to it.',
|
167 | 'If you feel that you have encountered this message unexpectedly...',
|
168 | 'Please check that the name of the route file matches the exact name of the intended controller file.',
|
169 | 'Please review the route config that caused this error below.',
|
170 | '-------------------------------------',
|
171 | JSON.stringify(config), ''
|
172 | ].join('\n'));
|
173 |
|
174 | process.exit(1);
|
175 | }
|
176 |
|
177 | }
|