yf-fpm-server
Version:
yf fast-platform api server
404 lines (306 loc) • 11.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Biz = exports.Hook = exports.Fpm = undefined;
var _koa = require('koa');
var _koa2 = _interopRequireDefault(_koa);
var _koaBodyparser = require('koa-bodyparser');
var _koaBodyparser2 = _interopRequireDefault(_koaBodyparser);
var _koa2Cors = require('koa2-cors');
var _koa2Cors2 = _interopRequireDefault(_koa2Cors);
var _pubsubJs = require('pubsub-js');
var _pubsubJs2 = _interopRequireDefault(_pubsubJs);
var _koaRouter = require('koa-router');
var _koaRouter2 = _interopRequireDefault(_koaRouter);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _https = require('https');
var _https2 = _interopRequireDefault(_https);
var _koaSslify = require('koa-sslify');
var _koaSslify2 = _interopRequireDefault(_koaSslify);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _pino = require('pino');
var _pino2 = _interopRequireDefault(_pino);
var _pinoMultiStream = require('pino-multi-stream');
var _debug = require('debug');
var _debug2 = _interopRequireDefault(_debug);
var _response = require('../middleware/response.js');
var _response2 = _interopRequireDefault(_response);
var _permission = require('../middleware/permission.js');
var _permission2 = _interopRequireDefault(_permission);
var _compare = require('../middleware/compare.js');
var _compare2 = _interopRequireDefault(_compare);
var _hook = require('../utils/hook.js');
var _hook2 = _interopRequireDefault(_hook);
var _biz = require('../utils/biz.js');
var _biz2 = _interopRequireDefault(_biz);
var _process = require('../utils/process.js');
var _plugin = require('../utils/plugin.js');
var _api = require('../router/api.js');
var _api2 = _interopRequireDefault(_api);
var _ping = require('../router/ping.js');
var _ping2 = _interopRequireDefault(_ping);
var _webhook = require('../router/webhook.js');
var _webhook2 = _interopRequireDefault(_webhook);
var _core = require('./core');
var _core2 = _interopRequireDefault(_core);
var _exception = require('../utils/exception.js');
var _exception2 = _interopRequireDefault(_exception);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const debug = (0, _debug2.default)('yf-fpm-server:app.js');
const packageInfo = require('../../package.json');
const CWD = process.cwd();
const ENV = process.env;
const projectInfo = require(_path2.default.join(CWD, 'package.json'));
const LOCAL = _path2.default.join(__dirname, '../../');
const configPath = _path2.default.join(CWD, 'config.json');
let config = {
server: {
hostname: '0.0.0.0',
domain: 'localhost',
port: 9999
},
defaultVersion: '0.0.1',
dev: 'DEV',
apps: {
'123123': {
appkey: '123123',
approot: '*',
secretkey: '123123'
}
},
pino: {
level: 'debug',
writers: 'stdio',
name: 'yf-fpm-server'
}
};
if (_fs2.default.existsSync(configPath)) {
config = _lodash2.default.assign(config, require(configPath));
}
const RUN_MODE = ENV.NODE_ENV || 'dev';
const RUNTIME_CONFIG = _path2.default.join(CWD, `config.${RUN_MODE.toLowerCase()}.json`);
if (_fs2.default.existsSync(RUNTIME_CONFIG)) {
config = _lodash2.default.assign(config, require(RUNTIME_CONFIG));
}
debug('The Finally Runtime Config: %O', config);
class Fpm {
constructor(options) {
const logStreams = config.pino.writers === 'stdio' ? [{ stream: process.stdout }, { level: 'error', stream: process.stderr }] : [{ level: 'debug', stream: _fs2.default.createWriteStream(_path2.default.join(CWD, config.pino.logfile || 'app.log'), { flags: 'a' }) }, { level: 'error', stream: _fs2.default.createWriteStream(_path2.default.join(CWD, config.pino.errfile || 'error.log'), { flags: 'a' }) }, { level: 'fatal', stream: _fs2.default.createWriteStream(_path2.default.join(CWD, config.pino.fatalfile || 'fatal.log'), { flags: 'a' }) }];
this.logger = (0, _pino2.default)(_lodash2.default.assign(config.pino, { timestamp: () => `,"time":"${new Date(Date.now()).toISOString()}"` }), (0, _pinoMultiStream.multistream)(logStreams));
let app = new _koa2.default();
this.app = app;
this._options = _lodash2.default.assign({
'LOCAL': LOCAL,
'CWD': CWD
}, options);
this.doCommand = _process.doCommand;
this.execShell = _process.execShell;
this._biz_module = {};
this._hook = new _hook2.default();
this._action = {};
this._start_time = _lodash2.default.now();
this._counter = 0;
this._prject_info = projectInfo;
this._env = config.dev;
this._version = packageInfo.version;
this._plugins = (0, _plugin.loadPlugin)(this) || {};
this._publish_topics = [];
this._webhook_events = [];
this.runAction('INIT', this);
this.errorHandler = (err, ctx) => {
this.logger.error('server error', err, ctx);
debug('Server Error Handler: %O', err);
};
this.app.use(async (ctx, next) => {
delete ctx.disableBodyParser;
if (ctx.request.method !== 'POST') {
await next();
return;
}
const disableBodyParser = this.get('disableBodyParser');
if (disableBodyParser) {
let urls;
if (_lodash2.default.isArray(disableBodyParser)) {
urls = disableBodyParser;
} else if (_lodash2.default.isString(disableBodyParser)) {
urls = disableBodyParser.split(',');
}
const urlPath = ctx.request.url;
_lodash2.default.map(urls, url => {
if (_lodash2.default.endsWith(urlPath, url)) {
ctx.disableBodyParser = true;
}
});
}
await next();
});
this.app.use((0, _koaBodyparser2.default)());
this.app.use((0, _koa2Cors2.default)());
this.app.use(_response2.default);
}
set(k, v) {
this._options[k] = v;
}
get(k, udv) {
return this._options[k] === undefined ? udv : this._options[k];
}
getApp() {
return this.app;
}
getVersion() {
return this._version;
}
getPlugins() {
return this._plugins;
}
getPluginRef(pname) {
if (!_lodash2.default.startsWith(pname, 'fpm-plugin-')) {
pname = 'fpm-plugin-' + pname;
}
if (_lodash2.default.has(this._plugins, pname)) {
return this._plugins[pname].ref;
}
return undefined;
}
isPluginInstalled(name) {
return _lodash2.default.has(this._plugins, name);
}
createRouter() {
return (0, _koaRouter2.default)();
}
createBiz(version) {
return new _biz2.default(version);
}
bindRouter(router) {
this.app.use(router.routes()).use(router.allowedMethods());
}
extendModule(name, module, version) {
if (_lodash2.default.isFunction(module)) {
module = module(this);
}
if (!version) {
_lodash2.default.map(this._biz_module, bizModule => {
bizModule[name] = module;
});
} else {
if (!_lodash2.default.has(this.getBizVersions(), version)) {
throw new _exception2.default(_exception.E.System.BIZ_VERSION_NOT_FNOUND(version));
}
this._biz_module[version][name] = module;
}
}
getBizVersions() {
return _lodash2.default.keys(this._biz_module);
}
addBizModules(biz) {
if (biz instanceof _biz2.default) {
let m = biz.convert();
this._biz_module[m.version] = m.modules;
} else {
throw new _exception2.default({
code: 'Biz-Load-Error',
errno: -10011,
message: 'Biz must be instanceof Biz'
});
}
}
registerAction(actionName, action, priority = 100) {
const _list = this._action[actionName] || [];
_list.push({ priority, action });
this._action[actionName] = _list;
}
async runAction(actionName) {
if (_lodash2.default.has(this._action, actionName)) {
let _list = this._action[actionName];
const argument = _lodash2.default.drop(arguments);
_list = _lodash2.default.sortBy(_list, 'priority');
for (let action of _list) {
await action.action(argument);
}
}
}
async execute(method, args, v, ctx) {
return await (0, _core2.default)(method, args, v || config.defaultVersion, this, ctx);
}
addAfterHook(method, hookHandler, v, priority) {
return this._hook.addAfterHook(method, hookHandler, v || config.defaultVersion, priority);
}
addBeforeHook(method, hookHandler, v, priority) {
return this._hook.addBeforeHook(method, hookHandler, v || config.defaultVersion, priority);
}
getConfig(c, defaultValue) {
if (_lodash2.default.isEmpty(c)) {
return config;
}
return config[c] || defaultValue || {};
}
getEnv(key, defaultValue) {
if (_lodash2.default.isEmpty(key)) {
return ENV;
}
return ENV[key] || defaultValue;
}
extendConfig(c) {
config = _lodash2.default.assign(config, c || {});
}
getClients() {
const apps = this.getConfig('apps');
return apps || {};
}
bindErrorHandler(handler) {
this.errorHandler = handler;
}
publish(topic, data) {
if (!_lodash2.default.includes(this._publish_topics, topic)) {
this._publish_topics.push(topic);
}
_pubsubJs2.default.publish(topic, data);
}
subscribe(topic, callback) {
_pubsubJs2.default.subscribe(topic, callback);
}
async run() {
let self = this;
this.app.use(async (ctx, next) => {
ctx.fpm = self;
await next();
});
this.app.use(_permission2.default);
this.app.use(_compare2.default);
await this.runAction('FPM_MIDDLEWARE', this, this.app);
this.bindRouter(_api2.default);
this.bindRouter(_ping2.default);
this.bindRouter(_webhook2.default);
await this.runAction('ADMIN', this, this.app);
await this.runAction('FPM_ROUTER', this, this.app);
await this.runAction('BEFORE_SERVER_START', this, this.app);
this.app.on('error', this.errorHandler);
this.app.listen(config.server.port, config.server.hostname || '0.0.0.0');
if (config.ssl) {
const options = {
key: _fs2.default.readFileSync(_path2.default.join(CWD, `${config.ssl.key}`)),
cert: _fs2.default.readFileSync(_path2.default.join(CWD, `${config.ssl.cert}`))
};
_https2.default.createServer(options, this.app.callback()).listen(config.ssl.port);
}
await this.runAction('AFTER_SERVER_START', this, this.app);
debug(`FPM-SERVER is running on ${config.server.hostname || '0.0.0.0'}:${config.server.port}`);
this.logger.info(`FPM-SERVER is running on ${config.server.hostname || '0.0.0.0'}:${config.server.port}`);
if (config.server.domain) {
this.logger.info(`Bind at HTTP Server: http://${config.server.domain}`);
}
if (config.ssl) {
this.logger.info(`Bind at HTTPS Server: https://${config.server.domain || config.server.hostname}:${config.ssl.port}`);
}
return this;
}
}
exports.Fpm = Fpm;
exports.Hook = _hook2.default;
exports.Biz = _biz2.default;
exports.default = Fpm;