UNPKG

7.86 kBJavaScriptView Raw
1const path = require("path");
2const glob = require("glob");
3const favicon = require("serve-favicon");
4const _ = require("lodash");
5const debug = require("debug")("motif:process");
6
7const Macro = require("node-macro");
8
9const http = require("http");
10const express = require("express");
11
12const session = require("express-session");
13const cookieParser = require("cookie-parser");
14const bodyParser = require("body-parser");
15const helmet = require("helmet");
16const cors = require("cors");
17const formidable = require("formidable");
18
19const Context = require("./Context");
20const Swagger = require("./Swagger");
21
22class Process {
23
24 get app() {
25 return this._app;
26 }
27
28 get context() {
29 return this._context;
30 }
31
32 get models() {
33 return this._models;
34 }
35
36 _importClassFile(file) {
37 debug("_importClassFile", file);
38 delete require.cache[
39 require.resolve(path.resolve(this._context.appPath, file))
40 ];
41 return require(path.resolve(this._context.appPath, file));
42 }
43
44 _loadServices() {
45 debug("_loadServices");
46
47 const macro = new Macro();
48
49 let modelFiles = [],
50 serviceFiles = [];
51
52 macro.add("CREATE-APP", action => {
53 let app = express();
54 app.use(helmet());
55 app.use(bodyParser.urlencoded({ extended: false }));
56 app.use(bodyParser.json());
57 app.use(cors());
58
59 if(this._context.useCookie === true) {
60 app.use(cookieParser());
61 }
62
63 if(this._context.useFavicon === true) {
64 app.use(
65 favicon(path.join(this._context.appPath, "public", "favicon.ico"))
66 );
67 }
68
69 if(this._context.useProxy === true) {
70 app.set("trust proxy", true);
71 }
72
73 app.on("uncaughtException", this.onException);
74
75 // file uploader
76 app.use(function (req, res, next) {
77 if(
78 req.headers["content-type"] &&
79 req.headers["content-type"].indexOf("multipart/form-data") > -1
80 ) {
81 // 멀티파트 데이터
82 const form = new formidable.IncomingForm({
83 encoding: "utf-8",
84 multiples: true,
85 type: "multipart", // or urlencoded
86 maxFieldsSize: 20 * 1024 * 1024,
87 maxFileSize: 200 * 1024 * 1024,
88 maxFields: 1000,
89 hash: true,
90 keepExtensions: false
91 });
92 form.parse(req, function (err, fields) {
93 _.merge(req.body, fields);
94 req.openedFiles = [];
95 });
96 form.on("file", function (name, file) {
97 if(!req.files) {
98 req.files = {};
99 }
100 req.files[name] = file;
101 req.body[name] = file;
102 });
103 form.on("end", function () {
104 req.openedFiles = this.openedFiles;
105 next();
106 });
107 } else {
108 next();
109 }
110 });
111
112 this._app = app;
113 action.complete();
114 });
115
116 macro.add("LOAD-MODEL-FILES", action => {
117 glob(
118 "services/*/models/*.js", { cwd: this._context.appPath },
119 (err, files) => {
120 if(err) {
121 action.error(err);
122 return;
123 }
124
125 modelFiles = files;
126 action.complete();
127 }
128 );
129 });
130
131 macro.add("LOAD-MODELS", async action => {
132 for(var i = 0, max = modelFiles.length; i < max; i++) {
133 let modelFile = modelFiles[i];
134 let modelClass = this._importClassFile(modelFile);
135 let className = modelClass.prototype.constructor.name;
136 let key = className.replace("Model", "");
137 let model = new modelClass();
138 let design = model.createDesign();
139
140 if(this._models[key]) {
141 console.warn(`${key} model with the same name is already created.`);
142 }
143
144 this._models[key] = {
145 name: key,
146 constructor: modelClass,
147 design: design
148 };
149
150 try {
151 console.log("design initialize", className);
152 await design.initialize();
153 } catch (e) {
154 console.error("className", className, e);
155 }
156 }
157
158 action.complete();
159 });
160
161 macro.add("LOAD-SERVICE-FILES", action => {
162 glob(
163 "services/*/*Service.js", { cwd: this._context.appPath },
164 (err, files) => {
165 if(err) {
166 action.error(err);
167 return;
168 }
169
170 serviceFiles = files;
171 action.complete();
172 }
173 );
174 });
175
176 macro.add("LOAD-SERVICES", action => {
177 _.each(serviceFiles, file => {
178 let service = this._importClassFile(file);
179
180 this._services.push(service);
181 });
182
183 action.complete();
184 });
185
186 if(this._context.createDoc === true) {
187 macro.add("CRAETE-SWAGGER-DOCS", async action => {
188 try {
189 let app = this._app;
190 let swagger = new Swagger(this._context);
191
192 let json = swagger.buildJSON(this._routerPaths, false);
193 let swaggerPath = path.join(this._context.appPath, "public/swagger");
194
195 await swagger.jsonWriteAsync(
196 path.join(swaggerPath, "/data/swag.json"),
197 json
198 );
199
200 app.use(
201 express.static(
202 path.join(this._context.appPath, "public", "swagger")
203 )
204 );
205
206 app.get("/doc/data", (req, res) => {
207 res.sendFile("data/swag.json", { root: swaggerPath });
208 });
209
210 app.get("/doc", (req, res) => {
211 res.sendFile("index.html", { root: swaggerPath });
212 });
213 } catch (e) {
214 console.log(e);
215 }
216
217 action.complete();
218 });
219
220 macro.add("CRAETE-SWAGGER-ADMIN-DOCS", async action => {
221 try {
222 let app = this._app;
223 let swagger = new Swagger(this._context);
224
225 let json = swagger.buildJSON(this._routerPaths, true);
226 let swaggerPath = path.join(this._context.appPath, "public/swagger");
227
228 await swagger.jsonWriteAsync(
229 path.join(swaggerPath, "/data/swag.admin.json"),
230 json
231 );
232
233 app.use(
234 express.static(
235 path.join(this._context.appPath, "public", "swagger")
236 )
237 );
238
239 app.get("/doc/data-admin", (req, res) => {
240 res.sendFile("data/swag.admin.json", { root: swaggerPath });
241 });
242
243 app.get("/doc-admin", (req, res) => {
244 res.sendFile("index.admin.html", { root: swaggerPath });
245 });
246 } catch (e) {
247 console.log(e);
248 }
249
250 action.complete();
251 });
252 }
253
254 macro.add("RUN-SERVER", action => {
255 let server = http.createServer(this._app);
256 server.listen(this.context.port, () => {
257 debug(`Server is running on port ${this.context.port}`);
258 });
259 server.on("error", this.onError);
260 server.on("listening", this.onListening);
261
262 this._server = server;
263
264 action.complete();
265 });
266
267 macro.start(error => {
268 if(error) {
269 debug(error);
270 }
271 });
272 }
273
274 _attachExceptionHandlers() {
275 process.on("uncaughtException", async function (err) {
276 console.error(err.stack);
277 process.exit(1);
278 });
279
280 process.on("unhandledRejection", async function (err) {
281 console.error(err.stack);
282 process.exit(1);
283 });
284 }
285
286 constructor(options) {
287
288 process.motif = this;
289
290 this._context = new Context({
291 appPath: path.resolve(process.env.PWD, "."),
292 ...options
293 });
294
295 this._app = null;
296 this._server = null;
297 this._models = {};
298 this._services = [];
299 this._routerPaths = [];
300 }
301
302 run() {
303 this._attachExceptionHandlers();
304
305 this._loadServices();
306 }
307
308 onListening() {
309 debug("onListening");
310 }
311
312 onException(req, res, route, err) {
313 debug("onException", err);
314 }
315
316 onError(error) {
317 debug("onError", error);
318 }
319}
320
321module.exports = Process;