All files / app index.js

89.71% Statements 122/136
84.62% Branches 44/52
91.67% Functions 22/24
89.71% Lines 122/136
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 2981x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x   1x         61x   61x 61x 61x           61x 53x 53x   1x         61x 1x       61x         61x                   61x 61x   61x               61x 61x           61x 723x   1x   718x       718x   4x         4x         61x 11x 11x       61x         61x     61x         61x             61x 61x     61x 427x 5x         61x 366x 61x   305x 61x 61x     244x 244x 244x 122x 122x   122x 122x                     61x     305x 122x   183x           61x 61x 811x 811x 61x   750x   61x         61x 61x 61x 61x 61x 61x 61x 61x           359x 62x   359x           1x 1x           1x 1x           54x 54x           50x   50x 50x   50x 2x 2x 1x 1x   1x       48x 1x       47x 1x     46x             2x   2x 2x     2x           2x   2x 2x                 2x   2x 2x 2x 2x 2x 2x    
const fs = require('fs');
const Koa = require('koa');
const http = require('http');
const path = require('path');
const assert = require('assert');
const lodash = require('lodash');
const convert = require('koa-convert');
const debug = require('debug')('koahub');
const deprecate = require('depd')('koahub');
 
const common = require('./common');
const pkg = require('./../package.json');
const Hook = require('./lib/hook.class');
const Loader = require('./lib/loader.class');
const config = require('./config/default.config');
const Controller = require('./lib/controller.class');
const httpMiddleware = require('./middleware/http.middleware');
 
module.exports = class Koahub {
 
    constructor(options = {}) {
 
        // 加载全局变量
        global.koahub = pkg;
 
        this.koa = koahub.koa = new Koa();
        this.options = koahub.options = options;
        this.init();
    }
 
    loadErrors() {
 
        // 捕捉中间件错误
        this.koa.use(async function (ctx, next) {
            try {
                await next();
            } catch (err) {
                throw err;
            }
        });
 
        // 监控错误日志
        this.koa.on('error', function (err, ctx) {
            common.log(err);
        });
 
        // 捕获promise reject错误
        process.on('unhandledRejection', function (reason, promise) {
            common.log(reason);
        });
 
        // 捕获未知错误
        process.on('uncaughtException', function (err) {
            common.log(err);
            if (err.message.indexOf(' EADDRINUSE ') > -1) {
                process.exit();
            }
        });
    }
 
    loadPaths() {
 
        const root = this.options.root || process.env.ROOT || process.cwd();
        const app = path.resolve(root, this.options.app || process.env.APP || 'app');
 
        koahub.paths = {
            app: app,
            root: root
        };
    }
 
    loadConfigs() {
 
        koahub.configs = new Loader(__dirname, config.loader.configs);
        koahub.configs = lodash.mergeWith(koahub.configs, new Loader(koahub.paths.app, config.loader.configs), common.arrayCustomizer);
    }
 
    loadUtils() {
 
        // config函数
        koahub.config = function (name, value) {
            switch (arguments.length) {
                case 0:
                    return koahub.configs;
                case 1:
                    Iif (name.indexOf('.') !== -1) {
                        const names = name.split('.');
                        return koahub.configs[names[0]][names[1]];
                    }
                    return koahub.configs.default[name];
                case 2:
                    Iif (name.indexOf('.') !== -1) {
                        const names = name.split('.');
                        koahub.configs[names[0]][names[1]] = value;
                        return;
                    }
                    koahub.configs.default[name] = value;
            }
        };
 
        // common函数
        if (fs.existsSync(path.resolve(koahub.paths.app, 'common.js'))) {
            koahub.common = common.requireDefault(path.resolve(koahub.paths.app, 'common.js'));
            assert(lodash.isPlainObject(koahub.common), 'Common.js must export plain object');
        }
 
        // controller继承
        koahub.controller = Controller;
    }
 
    loadHooks() {
 
        koahub.hook = new Hook();
 
        // hook初始化
        Iif (fs.existsSync(path.resolve(koahub.paths.app, 'hook.js'))) {
            koahub.hooks = common.requireDefault(path.resolve(koahub.paths.app, 'hook.js'));
            assert(lodash.isPlainObject(koahub.hooks), 'Hook.js must export plain object');
        }
 
        for (let key in koahub.hooks) {
            koahub.hook.add(key, koahub.hooks[key]);
        }
    }
 
    loadMiddlewares() {
 
        koahub.middlewares = new Loader(__dirname, koahub.config('loader').middlewares);
        koahub.middlewares = lodash.mergeWith(koahub.middlewares, new Loader(koahub.paths.app, koahub.config('loader').middlewares), common.arrayCustomizer);
 
        // 中间件排序
        for (let key in koahub.configs.middleware) {
            if (!lodash.includes(koahub.configs.middleware['middleware'], key) && key !== 'middleware') {
                koahub.configs.middleware['middleware'].push(key);
            }
        }
 
        // 自动加载中间件
        for (let key of koahub.configs.middleware['middleware']) {
            if (!koahub.configs.middleware[key]) {
                continue;
            } else {
                if (koahub.middlewares[key]) {
                    this.use(koahub.middlewares[key](koahub.configs.middleware[key]));
                    continue;
                }
 
                try {
                    Eif (require(key)) {
                        if(koahub.configs.middleware[key] === true){
                            this.use(require(key)());
                            continue;
                        }
                        this.use(require(key)(koahub.configs.middleware[key]));
                        continue;
                    }
                } catch (err) {
                    throw new Error(`The ${key} middleware not found`);
                }
            }
        }
    }
 
    loadLoaders() {
 
        for (let key in koahub.config('loader')) {
 
            // 移除重复加载
            if (key === 'configs' || key === 'middlewares') {
                continue;
            }
            koahub[key] = new Loader(koahub.paths.app, koahub.config('loader')[key]);
        }
    }
 
    loadModules() {
 
        let modules = [];
        for (let key in koahub.controllers) {
            let paths = key.split('/');
            if (paths.length < 3) {
                continue;
            }
            modules.push(paths[1]);
        }
        koahub.modules = lodash.union(modules);
    }
 
    init() {
 
        this.loadErrors();
        this.loadPaths();
        this.loadConfigs();
        this.loadUtils();
        this.loadHooks();
        this.loadMiddlewares();
        this.loadLoaders();
        this.loadModules();
    }
 
    // 默认支持koa middleware
    use(fn) {
 
        if (common.isGeneratorFunction(fn)) {
            fn = convert(fn);
        }
        this.koa.use(fn);
    }
 
    // 支持express middleware
    useExpress(fn) {
 
        fn = common.expressMiddlewareToKoaMiddleware(fn);
        this.use(fn);
    }
 
    // 获取koa
    getKoa() {
 
        deprecate('app.getKoa() has been deprecated, please use the app.use() to use middleware!');
        return this.koa;
    }
 
    // 获取server
    getServer() {
 
        const server = http.Server(this.koa.callback());
        return this.server = server;
    }
 
    loadHttpMiddlewares() {
 
        // 加载http中间件
        this.use(httpMiddleware().skip(function (ctx) {
 
            const path = ctx.path;
            const urlSuffix = koahub.config('url_suffix');
 
            if (urlSuffix) {
                const regexp = new RegExp(`${urlSuffix}$`);
                if (regexp.test(path)) {
                    ctx.path = path.substr(0, path.lastIndexOf(urlSuffix));
                    return false;
                }
                return true;
            }
 
            // path验证,资源文件跳过中间件
            if (/[^\/]+\.+\w+$/.test(path)) {
                return true;
            }
 
            // path验证,无效跳过中间件
            if (/\/\//.test(path)) {
                return true;
            }
 
            return false;
        }));
    }
 
    async run(port) {
 
        // http中间件最后加载
        this.loadHttpMiddlewares();
 
        Eif (!port) {
            port = koahub.config('port');
        }
 
        await this.start(port);
    }
 
    async start(port) {
 
        // 执行钩子 serverStart
        await koahub.hook.run('serverStart');
 
        Eif (this.server) {
            this.server.listen(port, this.started(port));
        } else {
            this.getServer().listen(port, this.started(port));
        }
    }
 
    async started(port) {
 
        // 执行钩子 serverStarted
        await koahub.hook.run('serverStarted');
 
        common.log(`Koahub Version: ${koahub.version}`);
        common.log(`Koahub Website: http://js.koahub.com`);
        common.log(`Nodejs Version: ${process.version}`);
        common.log(`Nodejs Platform: ${process.platform} ${process.arch}`);
        common.log(`Server Enviroment: ${this.koa.env}`);
        common.log(`Server running at: http://127.0.0.1:${port}`);
    }
}