UNPKG

5.72 kBJavaScriptView Raw
1global.Promise = require('bluebird'); // use bluebird instead of native Promise
2const pkg = require('../package.json');
3const http = require('http');
4const path = require('path');
5const fs = require('fs');
6const os = require('os');
7const Koa = require('koa');
8const convert = require('koa-convert');
9const koaBody = require('koa-body');
10const helmet = require('koa-helmet');
11const cors = require('koa-cors');
12
13const category = 'core.http';
14
15// private method
16const print = Symbol('print');
17const bootstrap = Symbol('bootstrap');
18
19module.exports = class {
20 /**
21 * share-node framework constructor
22 */
23 constructor () {
24 // create 'share' global variable
25 global.share = {};
26 global.share.startTime = Date.now();
27
28 // auto calculate APP_PATH
29 const appPath = path.resolve(process.mainModule.children[0].parent.filename);
30 global.share.APP_PATH = appPath.substr(0, appPath.lastIndexOf(path.sep)).trim().replace('start', '');
31 global.share.FRAMEWORK_PATH = `${share.APP_PATH}node_modules${path.sep}share-node${path.sep}`;
32 global.share.ENV = appPath.substr(appPath.lastIndexOf(path.sep) + 1).trim().replace('.js', '');
33
34 // config file name rule: [appPath]/config/config.[env].js
35 // use app's config as framework's config
36 // init log4js config if developer has not set
37 const defaultConfig = require('./config');
38 const configFile = `${share.APP_PATH}config${path.sep}config.${share.ENV}.js`;
39 global.share.config = fs.existsSync(configFile) ? require(configFile) : {};
40 share.config.env = share.ENV;
41 share.config.port = share.config.port || defaultConfig.port;
42 share.config.middleware = share.config.middleware || defaultConfig.middleware;
43 share.config.websocketMiddleware = share.config.websocketMiddleware || defaultConfig.websocketMiddleware;
44 share.config.apiPrefix = share.config.apiPrefix || defaultConfig.apiPrefix;
45 share.config.websocket = !!share.config.websocket;
46 share.config.requestLog = !!share.config.requestLog;
47 share.config.log4js = share.config.log4js || defaultConfig.log4js;
48
49 // require necessary components
50 require('./globals');
51 this.LOG = getLogger(category);
52 this.LOG.info(`config file: ${configFile}`);
53
54 // run bootstrap
55 this[bootstrap]();
56 }
57
58 /**
59 * start http server
60 */
61 start () {
62 // get a logger
63 const app = new Koa();
64
65 // load npm's middlewares
66 app.use(koaBody({multipart: true}));
67 app.use(helmet());
68 if (share.config.cors) {
69 app.use(convert(cors()));
70 }
71
72 // load config's middlewares
73 share.config.middleware.forEach((m) => {
74 let who = 'app';
75
76 // one layer module
77 let mFile = `${share.APP_PATH}app${path.sep}middleware${path.sep}${m}.js`;
78 if (!fs.existsSync(mFile)) {
79 // two layer modules
80 mFile = `${share.APP_PATH}app${path.sep}common${path.sep}middleware${path.sep}${m}.js`;
81 }
82 if (!fs.existsSync(mFile)) {
83 who = 'framework';
84 mFile = `${share.FRAMEWORK_PATH}lib${path.sep}middleware${path.sep}${m}.js`;
85 }
86 app.use(require(mFile)(share.config[m]));
87 this.LOG.info(`require ${who} middleware: ${m}`);
88 return m;
89 });
90
91 // global exception catch
92 const logger = getLogger(category);
93 process.on('uncaughtException', (err) => {
94 logger.error(err);
95 });
96
97 process.on('SIGTERM', () => {
98 this.LOG.info('on SIGTERM | server is down ...');
99 process.exit(0);
100 });
101
102 process.on('SIGINT', () => {
103 this.LOG.info('on SIGINT | server is down ...');
104 process.exit(0);
105 });
106
107 // start
108 const server = http.createServer(app.callback());
109 server.listen(share.config.port);
110 this[print]();
111
112 // if use websocket
113 if (share.config.websocket) {
114 require('./websocket')(server);
115 }
116 }
117
118 /**
119 * before server start, run bootstrap
120 */
121 [bootstrap] () {
122 // auto load framework bootstrap before server start
123 const bootstrapPath = `${share.FRAMEWORK_PATH}lib${path.sep}bootstrap`;
124 if (fs.existsSync(bootstrapPath)) {
125 fs.readdirSync(bootstrapPath).filter(v => require(`${bootstrapPath}${path.sep}${v}`));
126 }
127
128 // auto load app's bootstrap before server start
129 const appOneLayerBootstrapPath = `${share.APP_PATH}app${path.sep}bootstrap`;
130 if (fs.existsSync(appOneLayerBootstrapPath)) {
131 fs.readdirSync(appOneLayerBootstrapPath).filter(v => require(`${appOneLayerBootstrapPath}${path.sep}${v}`));
132 }
133
134 const appTwoLayersBootstrapPath = `${share.APP_PATH}app${path.sep}common${path.sep}bootstrap`;
135 if (fs.existsSync(appTwoLayersBootstrapPath)) {
136 fs.readdirSync(appTwoLayersBootstrapPath).filter(v => require(`${appTwoLayersBootstrapPath}${path.sep}${v}`));
137 }
138 }
139
140 /**
141 * print logs
142 */
143 [print] () {
144 const appPkg = require(`${share.APP_PATH}${path.sep}package.json`) || {};
145 const _app = `project name: ${appPkg.name || 'share-node framework project'} ${appPkg.version || '1.0.0'}`;
146 const _version = `share node version: ${pkg.version}, node version: ${process.version}, env: ${share.ENV}, log level: ${share.config.log4js.categories.default.level}`;
147 const _instance = `cpu cores: ${os.cpus().length}, server instances: 1, pid: ${process.pid}`;
148 const _server = `http server start in ${((Date.now() - share.startTime) / 1000.0).toFixed(2)} s, running at http://127.0.0.1${share.config.port.toString() === '80' ? '' : `:${share.config.port}`}`;
149 this.LOG.info(_app);
150 this.LOG.info(_version);
151 this.LOG.info(_instance);
152 this.LOG.info(_server);
153 }
154};