UNPKG

12.5 kBJavaScriptView Raw
1
2const path = require('path');
3const glob = require('glob');
4const favicon = require('serve-favicon');
5const _ = require("lodash");
6const debug = require('debug')('motif:process');
7
8const Macro = require('node-macro');
9
10const http = require( 'http' );
11const express = require('express');
12
13const session = require('express-session');
14const cookieParser = require('cookie-parser');
15const bodyParser = require('body-parser');
16const helmet = require('helmet');
17const cors = require('cors');
18const formidable = require('formidable');
19
20const Context = require('./Context');
21const Swagger = require('./Swagger');
22
23class Process {
24
25 get app() {
26 return this._app;
27 }
28
29 get context() {
30 return this._context;
31 }
32
33 _loadService( file ) {
34 debug("_loadService", file);
35 delete require.cache[require.resolve(path.resolve(this._context.appPath, file))];
36 return require(path.resolve(this._context.appPath, file));
37 }
38
39 _loadServices() {
40
41 debug("_loadServices");
42
43 const macro = new Macro();
44
45 let serviceFiles = [];
46
47 macro.add("CREATE-APP", (action) => {
48
49 let app = express();
50 app.use(helmet());
51 app.use(bodyParser.urlencoded({ extended: false }));
52 app.use(bodyParser.json());
53 app.use(cors());
54
55 if (this._context.useCookie === true) {
56 app.use(cookieParser());
57 }
58
59 if (this._context.useFavicon === true) {
60 app.use(favicon(path.join(this._context.appPath, 'public', 'favicon.ico')));
61 }
62
63 if (this._context.useProxy === true) {
64 app.set('trust proxy', true);
65 }
66
67 app.on('uncaughtException', this.onException);
68
69 // file uploader
70 app.use(function (req, res, next) {
71 if (req.headers['content-type'] && req.headers['content-type'].indexOf('multipart/form-data') > -1) {
72 // 멀티파트 데이터
73 const form = new formidable.IncomingForm({
74 encoding: 'utf-8',
75 multiples: true,
76 type: 'multipart', // or urlencoded
77 maxFieldsSize: 20 * 1024 * 1024,
78 maxFileSize: 200 * 1024 * 1024,
79 maxFields: 1000,
80 hash: true,
81 keepExtensions: false
82 });
83 form.parse(req, function (err, fields) {
84 _.merge(req.body, fields);
85 req.openedFiles = [];
86 });
87 form.on('file', function (name, file) {
88 if (!req.files) {
89 req.files = {};
90 }
91 req.files[name] = file;
92 req.body[name] = file;
93 });
94 form.on('end', function () {
95 req.openedFiles = this.openedFiles;
96 next();
97 });
98 } else {
99 next();
100 }
101 });
102
103 this._app = app;
104 action.complete();
105 });
106
107 macro.add("LOAD-SERVICE-FILES", (action) => {
108 glob("services/*/*Service.js", { cwd: this._context.appPath }, (err, files) => {
109 if (err) {
110 action.error(err);
111 return;
112 }
113
114 serviceFiles = files;
115 action.complete();
116 });
117 });
118
119 macro.add("LOAD-SERVICES", (action) => {
120 _.each(serviceFiles, (file) => {
121 this._services.push(
122 this._loadService( file )
123 );
124 });
125
126 action.complete();
127 });
128
129 if (this._context.createDoc === true) {
130
131 macro.add("CRAETE-SWAGGER-DOCS", async (action) => {
132
133 try {
134 let app = this._app;
135 let swagger = new Swagger( this._context );
136
137 let json = swagger.buildJSON( this._routerPaths, false );
138 let swaggerPath = path.join(this._context.appPath, "public/swagger");
139
140 await swagger.jsonWriteAsync( path.join(swaggerPath, "/data/swag.json"), json );
141
142 app.use(express.static(path.join(this._context.appPath, 'public', 'swagger')));
143
144 app.get('/doc/data', (req, res) => {
145 res.sendFile('data/swag.json', {root: swaggerPath });
146 });
147
148 app.get('/doc', (req, res) => {
149 res.sendFile('index.html', {root: swaggerPath });
150 });
151 }
152 catch(e) {
153 console.log( e );
154 }
155
156 action.complete();
157 });
158
159 macro.add("CRAETE-SWAGGER-ADMIN-DOCS", async (action) => {
160
161 try {
162 let app = this._app;
163 let swagger = new Swagger( this._context );
164
165 let json = swagger.buildJSON( this._routerPaths, true );
166 let swaggerPath = path.join(this._context.appPath, "public/swagger");
167
168 await swagger.jsonWriteAsync( path.join(swaggerPath, "/data/swag.admin.json"), json );
169
170 app.use(express.static(path.join(this._context.appPath, 'public', 'swagger')));
171
172 app.get('/doc/data-admin', (req, res) => {
173 res.sendFile('data/swag.admin.json', {root: swaggerPath });
174 });
175
176 app.get('/doc-admin', (req, res) => {
177 res.sendFile('index.admin.html', {root: swaggerPath });
178 });
179 }
180 catch(e) {
181 console.log( e );
182 }
183
184 action.complete();
185 });
186 }
187
188 macro.add("RUN-SERVER", (action) => {
189
190 let server = http.createServer( this._app );
191 server.listen( this.context.port, () => {
192 debug( `Server is running on port ${this.context.port}` );
193 });
194 server.on('error', this.onError);
195 server.on('listening', this.onListening);
196
197 this._server = server;
198
199 action.complete();
200 });
201
202 macro.start((error) => {
203 if (error) {
204 debug(error);
205 }
206 });
207 }
208
209 _loadViews() {
210
211 const macro = new Macro();
212
213 let serviceFiles = [];
214
215 macro.add("CREATE-APP", (action) => {
216
217 let app = express();
218
219 app.engine('.html', require('ejs').__express);
220
221 app.set('views', path.join(this._context.appPath, 'views'));
222
223 // app.set('view engine', 'ejs');
224 app.set('view engine', 'html');
225
226 app.use(express.static(path.join(this._context.appPath, 'public', 'static')));
227
228 app.use(bodyParser.urlencoded({ extended: false }));
229 // app.use(express.methodOverride());
230
231 if (this._context.session || this._context.useCookie === true) {
232 app.use(cookieParser());
233 }
234
235 if (this._context.session) {
236 app.use(this._context.session);
237 }
238
239 app.on('uncaughtException', this.onException);
240
241 this._app = app;
242
243 action.complete();
244 });
245
246 macro.add("LOAD-SERVICE-FILES", (action) => {
247 glob("services/*/*Service.js", { cwd: this._context.appPath }, (err, files) => {
248 if (err) {
249 action.error(err);
250 return;
251 }
252
253 serviceFiles = files;
254 action.complete();
255 });
256 });
257
258 macro.add("LOAD-SERVICES", (action) => {
259
260 _.each(serviceFiles, (file) => {
261 this._services.push(
262 this._loadService( file )
263 );
264 });
265
266 action.complete();
267 });
268
269 macro.add("DEFAULT_ROUTERS", (action) => {
270
271 let app = this._app;
272 let defaultRouter = express.Router({strict: true});
273 let routersPaths = process.motif._routerPaths.map((routePath) => { return routePath.path });
274
275 glob("**/*.html", { cwd: path.join(this._context.appPath, "views") }, (err, files) => {
276 _.each(files, (file) => {
277 let viewFile = path.join(this._context.appPath, file);
278 let fileName = path.basename(viewFile);
279 let dirName = path.dirname(file).split(path.sep).pop();
280
281 if (dirName.indexOf("_") === -1 || dirName.indexOf("_") > 0) {
282 if (fileName.indexOf(".html") !== -1) {
283 let viewName = file.substr(0, file.length - 5);
284
285 if (dirName !== ".") {
286 let path = "/" + viewName.replace("/index", "/");
287
288 if (!routersPaths.includes(path)) {
289 debug( "auto router", path, viewName );
290
291 defaultRouter.get(path, (request, response) => {
292 //response.render(viewName, {});
293 if (process.motif.main) {
294 process.motif.main.htmlResponse( request, response, viewName, {});
295 }
296 else {
297 response.render(viewName, {});
298 }
299 });
300
301 routersPaths.push(path);
302 }
303 }
304 }
305 }
306 });
307 });
308
309 if (process.motif.main) {
310 process.motif.main._initialize( { app, router: defaultRouter } );
311 }
312
313 app.use("/", defaultRouter);
314
315 app.use((request, response, next) => {
316
317 debug( "404 error", request.path );
318
319 response.status(404);
320 // response.send('HTTP 404');
321 response.render("404", { request });
322 });
323
324 app.use((error, request, response, next) => {
325
326 debug( "500 error", error.message );
327
328 response.status(500);
329 // response.send('HTTP 500');
330 response.render("50x", { error, request, response });
331 });
332
333 action.complete();
334 });
335
336 macro.add("RUN-SERVER", (action) => {
337
338 let server = http.createServer( this._app );
339 server.listen( this.context.port, () => {
340 debug( `Server is running on port ${this.context.port}` );
341 });
342 server.on('error', this.onError);
343 server.on('listening', this.onListening);
344
345 this._server = server;
346
347 action.complete();
348 });
349
350 macro.start((error) => {
351 if (error) {
352 debug(error);
353 }
354 });
355 }
356
357 _attachExceptionHandlers() {
358 process.on('uncaughtException', async function (err) {
359 console.error(err.stack);
360 process.exit(1);
361 });
362
363 process.on('unhandledRejection', async function (err) {
364 console.error(err.stack);
365 process.exit(1);
366 });
367 }
368
369 constructor( options ) {
370
371 this._context = new Context({
372 appPath: path.resolve(process.env.PWD, '.'),
373 ...options
374 });
375
376 this._app = null;
377 this._server = null;
378 this._services = [];
379 this._views = [];
380 this._routerPaths = [];
381
382 process.motif = this;
383 }
384
385 run( serviceType = "api" ) {
386
387 this._attachExceptionHandlers();
388
389 if (serviceType === "web") {
390 this._loadViews();
391 }
392 else {
393 this._loadServices();
394 }
395 }
396
397 onListening() {
398 debug( "onListening" );
399 }
400
401 onException(req, res, route, err) {
402 debug( "onException", err );
403 }
404
405 onError(error) {
406 debug( "onError", error );
407 }
408
409};
410
411module.exports = Process;