'use strict'; const assert = require('assert'); const is = require('is-type-of'); const Koa = require('koa'); const cors = require('koa2-cors'); const koaBody = require('koa-body'); const https = require('https'); const fs = require('fs'); const path = require('path'); const _ = require('lodash'); const Log = require('../log'); const Ps = require('../ps'); /** * http server */ class HttpServer { constructor (app) { this.app = app; this.options = this.app.config.httpServer; if (this.options.enable == false) { return; } let port = Ps.getHttpPort(); if (!port) { throw new Error('[ee-core] [socket/HttpServer] http port required, and must be a number !'); } this._create(); } /** * 创建服务 */ _create () { const app = this.app; const httpServer = this.options; const isHttps = httpServer?.https?.enable ?? false; let sslOptions = {}; if (isHttps === true) { httpServer.protocol = 'https://'; const keyFile = path.join(app.config.homeDir, httpServer.https.key); const certFile = path.join(app.config.homeDir, httpServer.https.cert); assert(fs.existsSync(keyFile), 'ssl key file is required'); assert(fs.existsSync(certFile), 'ssl cert file is required'); sslOptions.key = fs.readFileSync(keyFile); sslOptions.cert = fs.readFileSync(certFile); } const url = httpServer.protocol + httpServer.host + ':' + httpServer.port; const corsOptions = httpServer.cors; const koaApp = new Koa(); koaApp .use(cors(corsOptions)) .use(koaBody(httpServer.body)) .use(async (ctx, next) => { ctx.eeApp = app; await next(); }) .use(this._dispatch); let msg = '[ee-core] [socket/http] server is: ' + url; const listenOpt = { host: httpServer.host, port: httpServer.port } if (isHttps) { https.createServer(sslOptions, koaApp.callback()).listen(listenOpt, (err) => { msg = err ? err : msg; Log.coreLogger.info(msg); }); } else { koaApp.listen(listenOpt, (e) => { msg = e ? e : msg; Log.coreLogger.info(msg); }); } } /** * 路由分发 */ async _dispatch (ctx, next) { const config = ctx.eeApp.config.httpServer; let uriPath = ctx.request.path; const method = ctx.request.method; let params = ctx.request.query; params = is.object(params) ? JSON.parse(JSON.stringify(params)) : {}; const body = ctx.request.body; const files = ctx.request.files; // 默认 ctx.response.status = 200; // 添加到全局属性 ctx.eeApp.request = ctx.request; ctx.eeApp.response = ctx.response; try { // 找函数 // 去除开头的 '/' if (uriPath.indexOf('/') == 0) { uriPath = uriPath.substring(1); } // 过滤 if (_.includes(config.filterRequest.uris, uriPath)) { ctx.response.body = config.filterRequest.returnData; await next(); return } if (uriPath.slice(0, 10) != 'controller') { uriPath = 'controller/' + uriPath; } const cmd = uriPath.split('/').join('.'); const args = (method == 'POST') ? (ctx.request.header['content-type'] && ctx.request.header['content-type'].startsWith('multipart/form-data;') ? files : body) : params; args.files = ctx.request.files; args.body = ctx.request.body; args.query = ctx.request.query; let fn = null; if (is.string(cmd)) { const actions = cmd.split('.'); let obj = ctx.eeApp; actions.forEach(key => { obj = obj[key]; if (!obj) throw new Error(`class or function '${key}' not exists`); }); fn = obj; } if (!fn) throw new Error('function not exists'); const result = await fn.call(ctx.eeApp, args); ctx.response.body = result; } catch (err) { Log.coreLogger.error('[ee-core/httpServer] throw error:', err); } await next(); } } module.exports = HttpServer;