From e4395beb9ec4522afadc89b135e7be1a10ec80ff Mon Sep 17 00:00:00 2001 From: sean Date: Tue, 5 Sep 2017 10:24:13 +0800 Subject: [PATCH] feat: add plugin module --- .eslintrc.js | 3 +- .eslintrc.json | 1 + client/containers/Home/Home.js | 11 +++-- client/containers/Home/Home.scss | 2 + client/containers/Login/Login.js | 4 +- client/index.js | 1 + client/plugin.js | 34 ++++++++++++++ nodemon.json | 2 +- .../components/Qsso => plugins/qsso}/Qsso.js | 0 plugins/qsso/client.js | 19 ++++++++ plugins/qsso/server.js | 28 +++++++++++ server/app.js | 8 ++-- server/controllers/user.js | 3 +- server/plugin.js | 11 +++++ server/yapi.js | 46 ++++++++++++++++++- ykit.js | 31 +++++++++++-- 16 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 client/plugin.js rename {client/components/Qsso => plugins/qsso}/Qsso.js (100%) create mode 100644 plugins/qsso/client.js create mode 100644 plugins/qsso/server.js create mode 100644 server/plugin.js diff --git a/.eslintrc.js b/.eslintrc.js index ddca4bfe..f4e72797 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -40,4 +40,5 @@ module.exports = { "strict": 0, "comma-dangle": ["error", "never"] } -}; \ No newline at end of file +}; + diff --git a/.eslintrc.json b/.eslintrc.json index 8f036860..2ef48aad 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -31,6 +31,7 @@ "/fekit_modules/*", "/node_modules/*", "/bower_components/*", + "/plugins/*", "/dev/*", "/prd/*" ] diff --git a/client/containers/Home/Home.js b/client/containers/Home/Home.js index f97bf5d5..6a8f391e 100644 --- a/client/containers/Home/Home.js +++ b/client/containers/Home/Home.js @@ -7,8 +7,7 @@ import PropTypes from "prop-types"; import { withRouter } from 'react-router'; import { logoSVG, getImgPath } from '../../common.js'; import { changeMenuItem } from '../../reducer/modules/menu' -import Qsso from '../../components/Qsso/Qsso.js' - +import Plugins from '../../plugin.js' const HomeGuest = () => (
@@ -37,7 +36,11 @@ const HomeGuest = () => (
高效、易用、功能强大的API管理平台
旨在为开发、产品、测试人员提供更优雅的接口管理服务
- + {Plugins.third_login.component != null ? + + : null + } +
@@ -181,7 +184,7 @@ class Home extends Component { } componentDidMount() { - Qsso.attach('qsso-login', '/api/user/login_by_token') + } static propTypes = { introList: PropTypes.array, diff --git a/client/containers/Home/Home.scss b/client/containers/Home/Home.scss index f2682813..56eb2791 100644 --- a/client/containers/Home/Home.scss +++ b/client/containers/Home/Home.scss @@ -68,6 +68,8 @@ $color-bg-lightblue: #c6e2ff; &:hover, &:focus { background-color: #f6f9fc; } + background-color: #fff; + cursor: pointer; } } diff --git a/client/containers/Login/Login.js b/client/containers/Login/Login.js index f51bb00c..655b4662 100644 --- a/client/containers/Login/Login.js +++ b/client/containers/Login/Login.js @@ -6,7 +6,7 @@ import { loginActions } from '../../reducer/modules/user'; import { withRouter } from 'react-router' const FormItem = Form.Item; import './Login.scss' -import Qsso from '../../components/Qsso/Qsso.js' +// import Qsso from '../../components/Qsso/Qsso.js' const formItemStyle = { marginBottom: '.16rem' @@ -54,7 +54,7 @@ class Login extends Component { } componentDidMount() { - Qsso.attach('qsso-login','/api/user/login_by_token') + //Qsso.attach('qsso-login','/api/user/login_by_token') } diff --git a/client/index.js b/client/index.js index 081735df..4c9621af 100644 --- a/client/index.js +++ b/client/index.js @@ -1,3 +1,4 @@ +import './plugin' import React from 'react' import ReactDOM from 'react-dom' import App from './Application' diff --git a/client/plugin.js b/client/plugin.js new file mode 100644 index 00000000..10b74f78 --- /dev/null +++ b/client/plugin.js @@ -0,0 +1,34 @@ +const config = process.env.config; + +var hooks = { + 'third_login': { + type: 'single', + component: null + }, + 'add_interface': { + type: 'mulit', + component: [] + } +}; + +function bindHook(name, component) { + if (!name) throw new Error('缺少hookname'); + if (name in hooks === false) { + throw new Error('不存在的hookname'); + } + if (hooks[name].type === 'multi') { + hooks[name].component.push(component); + } else { + hooks[name].component = component; + } + +} + +if (config.plugins && Array.isArray(config.plugins)) { + config.plugins.forEach(plugin => { + let pluginModule = require(`/Users/sean/qunar/yapi/yapi/node_modules/yapi-plugin-${plugin}/client.js`); + pluginModule(bindHook); + }) +} + +module.exports = hooks; \ No newline at end of file diff --git a/nodemon.json b/nodemon.json index 50f27a94..9e6af8b9 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,3 +1,3 @@ { - "watch": ["server/"] + "watch": ["server/", "common/", "plugins"] } \ No newline at end of file diff --git a/client/components/Qsso/Qsso.js b/plugins/qsso/Qsso.js similarity index 100% rename from client/components/Qsso/Qsso.js rename to plugins/qsso/Qsso.js diff --git a/plugins/qsso/client.js b/plugins/qsso/client.js new file mode 100644 index 00000000..07dc4f93 --- /dev/null +++ b/plugins/qsso/client.js @@ -0,0 +1,19 @@ +import React from 'react'; +const Qsso = require('./Qsso.js'); + +class QssoComponent extends React.Component{ + componentDidMount(){ + Qsso.attach('qsso-login', '/api/user/login_by_token') + } + + render(){ + return + } +} + + + +module.exports = function(bindHook){ + bindHook('third_login', QssoComponent); +}; + diff --git a/plugins/qsso/server.js b/plugins/qsso/server.js new file mode 100644 index 00000000..f5def870 --- /dev/null +++ b/plugins/qsso/server.js @@ -0,0 +1,28 @@ +const request = require('request'); + +module.exports = function () { + this.bindHook('third_login', () => { + return { + request: (token) => { + return new Promise((resolve, reject) => { + request('http://qsso.corp.qunar.com/api/verifytoken.php?token=' + token, function (error, response, body) { + if (!error && response.statusCode == 200) { + let result = JSON.parse(body); + if (result && result.ret === true) { + let ret = { + email: result.userId + '@qunar.com', + username: result.data.userInfo.name + }; + resolve(ret); + } else { + reject(result); + } + } + reject(error); + }); + }); + }, + tokenField: 'token' + }; + }) +} \ No newline at end of file diff --git a/server/app.js b/server/app.js index 07cb93c0..24bf1c45 100644 --- a/server/app.js +++ b/server/app.js @@ -9,12 +9,14 @@ const bodyParser = require('koa-bodyparser'); const router = require('./router.js'); const websockify = require('koa-websocket'); const websocket = require('./websocket.js'); +const plugins = require('./plugin.js'); -yapi.connect = dbModule.connect(); -const app = websockify(new Koa()); let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html'; - +yapi.connect = dbModule.connect(); +const app = websockify(new Koa()); +yapi.app = app; +plugins(); app.use(mockServer); app.use(bodyParser({multipart: true})); app.use(router.routes()); diff --git a/server/controllers/user.js b/server/controllers/user.js index 08819b57..a0c732a5 100644 --- a/server/controllers/user.js +++ b/server/controllers/user.js @@ -109,7 +109,8 @@ class userController extends baseController { } async loginByToken(ctx) { - let config = this.thirdQunarLogin(); + //let config = this.thirdQunarLogin(); + let config = yapi.emitHook('third_login'); let token = ctx.request.body[config.tokenField] || ctx.request.query[config.tokenField]; try { diff --git a/server/plugin.js b/server/plugin.js new file mode 100644 index 00000000..2f6f9fa6 --- /dev/null +++ b/server/plugin.js @@ -0,0 +1,11 @@ +const yapi = require('./yapi.js'); +const plugin_path = yapi.path.join(yapi.WEBROOT, 'plugins') + +module.exports = function(){ + if(yapi.WEBCONFIG.plugins && Array.isArray(yapi.WEBCONFIG.plugins)){ + yapi.WEBCONFIG.plugins.forEach(plugin=>{ + let pluginModule = require(yapi.path.join(plugin_path, 'qsso/server.js')); + pluginModule.call(yapi) + }) + } +} \ No newline at end of file diff --git a/server/yapi.js b/server/yapi.js index f87ceb18..1b425ef2 100644 --- a/server/yapi.js +++ b/server/yapi.js @@ -39,6 +39,48 @@ function delInst(m) { } } +var hooks = { + 'third_login': { + type: 'single', + listener: null + }, + 'add_interface': { + type: 'mulit', + listener: [] + } +}; + +function bindHook(name, listener){ + if(!name) throw new Error('缺少hookname'); + if(name in hooks === false){ + throw new Error('不存在的hookname'); + } + if(hooks[name].type === 'multi'){ + hooks[name].listener.push(listener); + }else{ + hooks[name].listener = listener; + } + +} + +function emitHook(name){ + if(!name) throw new Error('缺少hookname'); + if(name in hooks === false){ + throw new Error('不存在的hookname'); + } + + if(hooks[name] && typeof hooks[name] === 'object'){ + if(hooks[name].type === 'single' && typeof hooks[name].listener === 'function'){ + return hooks[name].listener.call(); + } + if(Array.isArray(hooks[name.listener])){ + hooks[name].listener.forEach(listener=>{ + listener.call() + }) + } + } +} + let r = { fs: fs, path: path, @@ -49,7 +91,9 @@ let r = { WEBCONFIG: WEBCONFIG, getInst: getInst, delInst: delInst, - getInsts: insts + getInsts: insts, + emitHook: emitHook, + bindHook: bindHook }; if (mail) r.mail = mail; module.exports = r; \ No newline at end of file diff --git a/ykit.js b/ykit.js index 74521cf8..50665af6 100644 --- a/ykit.js +++ b/ykit.js @@ -1,12 +1,32 @@ var path = require('path'); +var fs = require('fs'); var AssetsPlugin = require('assets-webpack-plugin') var CompressionPlugin = require('compression-webpack-plugin') +var config = require('../config.json'); var assetsPluginInstance = new AssetsPlugin({ filename: 'static/prd/assets.js', processOutput: function (assets) { return 'window.WEBPACK_ASSETS = ' + JSON.stringify(assets); } -}) +}); + +function fileExist (filePath){ + try { + return fs.statSync(filePath).isFile(); + } catch (err) { + return false; + } +}; + +function initPlugins(){ + if(config.plugins && Array.isArray(config.plugins)){ + config.plugins = config.plugins.filter(item=>{ + return fileExist(path.resolve(__dirname, 'node_modules/yapi-plugin-' + item + '/client.js')) + }) + } +} + +initPlugins(); var compressPlugin = new CompressionPlugin({ asset: "[path].gz[query]", @@ -93,7 +113,8 @@ module.exports = { defaultQuery.plugins.push('transform-decorators-legacy'); defaultQuery.plugins.push(["import", { libraryName: "antd"}]) return defaultQuery; - } + }, + exclude: /node_modules(?!\/yapi\-plugin\-)/ } }], // devtool: 'cheap-source-map', @@ -119,13 +140,15 @@ module.exports = { } baseConfig.plugins.push(new this.webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(ENV_PARAMS) + 'process.env.NODE_ENV': JSON.stringify(ENV_PARAMS), + 'process.env.config': JSON.stringify(config) })) //初始化配置 baseConfig.devtool = 'cheap-module-eval-source-map' baseConfig.context = path.resolve(__dirname, './client'); baseConfig.resolve.alias.common = '/common'; + baseConfig.resolve.alias.plugins = '/node_modules'; baseConfig.output.prd.path = 'static/prd'; baseConfig.output.prd.publicPath = ''; baseConfig.output.prd.filename = '[name]@[chunkhash][ext]' @@ -151,7 +174,7 @@ module.exports = { }) baseConfig.module.preLoaders.push({ test: /\.(js|jsx)$/, - exclude: /node_modules/, + exclude: /node_modules|plugins/, loader: "eslint-loader" });