├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── Dockerfile ├── README.md ├── app ├── api-schemas │ ├── index.js │ ├── schema.apiBase.js │ ├── schema.apiModel.js │ ├── schema.appBase.js │ ├── schema.commonData.js │ ├── schema.error.js │ ├── schema.lib.js │ ├── schema.log.js │ ├── schema.project.js │ ├── schema.search.js │ └── schema.sync.js ├── controller │ ├── controller.apiBase.js │ ├── controller.apiModel.js │ ├── controller.appBase.js │ ├── controller.commonData.js │ ├── controller.dynData.js │ ├── controller.error.js │ ├── controller.lib.js │ ├── controller.log.js │ ├── controller.project.js │ ├── controller.search.js │ └── controller.sync.js ├── database │ ├── index.js │ └── v0 │ │ └── index.js ├── index.js ├── middleware │ ├── formatParam.js │ ├── logger.js │ └── respond.js ├── plugin │ ├── file-server.js │ └── json-gate │ │ ├── common.js │ │ ├── format-object.js │ │ ├── formats.js │ │ ├── json-gate.js │ │ ├── valid-object.js │ │ └── valid-schema.js ├── router │ ├── index.js │ ├── router.apiBase.js │ ├── router.apiModel.js │ ├── router.appBase.js │ ├── router.commonData.js │ ├── router.error.js │ ├── router.lib.js │ ├── router.log.js │ ├── router.project.js │ ├── router.search.js │ └── router.sync.js ├── service │ ├── api │ │ ├── service.copy.js │ │ ├── service.edit.js │ │ └── service.get.js │ ├── index.js │ ├── project │ │ ├── service.edit.js │ │ └── service.get.js │ ├── service.ctrlProc.js │ ├── service.log.js │ ├── service.proc.js │ ├── service.upgrade.js │ ├── service.ws.js │ └── sync │ │ ├── service.diff.js │ │ ├── service.download.js │ │ ├── service.get.js │ │ ├── service.push.js │ │ ├── service.receive.js │ │ └── service.request.js └── util │ ├── combineArray.js │ ├── common.js │ ├── definePath.js │ ├── formatCtx.js │ ├── getQuery.js │ ├── index.js │ ├── loadFileList.js │ ├── readfiles.js │ ├── setGz.js │ ├── stringify.js │ └── timer.js ├── bin ├── commands │ ├── base.js │ ├── exit.js │ ├── free.js │ ├── here.js │ ├── list.js │ ├── restart.js │ ├── start.js │ └── stop.js ├── mocker-exit.js ├── mocker-free.js ├── mocker-here.js ├── mocker-list.js ├── mocker-restart.js ├── mocker-start.js ├── mocker-stop.js └── mocker.js ├── doc ├── guide.md ├── v1.txt └── v1 │ ├── api.md │ ├── config.md │ ├── guide.md │ ├── log.md │ ├── project.md │ └── sync.md ├── lower ├── api-schemas │ ├── index.js │ ├── schema.apiBase.js │ ├── schema.apiModel.js │ ├── schema.appBase.js │ ├── schema.commonData.js │ ├── schema.error.js │ ├── schema.lib.js │ ├── schema.log.js │ ├── schema.project.js │ ├── schema.search.js │ └── schema.sync.js ├── controller │ ├── controller.apiBase.js │ ├── controller.apiModel.js │ ├── controller.appBase.js │ ├── controller.commonData.js │ ├── controller.dynData.js │ ├── controller.error.js │ ├── controller.lib.js │ ├── controller.log.js │ ├── controller.project.js │ ├── controller.search.js │ └── controller.sync.js ├── database │ ├── index.js │ └── v0 │ │ └── index.js ├── index.js ├── middleware │ ├── formatParam.js │ ├── logger.js │ └── respond.js ├── plugin │ ├── file-server.js │ └── json-gate │ │ ├── common.js │ │ ├── format-object.js │ │ ├── formats.js │ │ ├── json-gate.js │ │ ├── valid-object.js │ │ └── valid-schema.js ├── router │ ├── index.js │ ├── router.apiBase.js │ ├── router.apiModel.js │ ├── router.appBase.js │ ├── router.commonData.js │ ├── router.error.js │ ├── router.lib.js │ ├── router.log.js │ ├── router.project.js │ ├── router.search.js │ └── router.sync.js ├── service │ ├── api │ │ ├── service.copy.js │ │ ├── service.edit.js │ │ └── service.get.js │ ├── index.js │ ├── project │ │ ├── service.edit.js │ │ └── service.get.js │ ├── service.ctrlProc.js │ ├── service.log.js │ ├── service.proc.js │ ├── service.upgrade.js │ ├── service.ws.js │ └── sync │ │ ├── service.diff.js │ │ ├── service.download.js │ │ ├── service.get.js │ │ ├── service.push.js │ │ ├── service.receive.js │ │ └── service.request.js └── util │ ├── combineArray.js │ ├── common.js │ ├── definePath.js │ ├── formatCtx.js │ ├── getQuery.js │ ├── index.js │ ├── loadFileList.js │ ├── readfiles.js │ ├── setGz.js │ ├── stringify.js │ └── timer.js ├── package-lock.json ├── package.json ├── tool └── gulp │ ├── gulp-provideFile.js │ ├── gulpfile.js │ ├── package-lock.json │ ├── package.json │ └── setGulpOption.js └── web ├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .postcssrc ├── dist ├── css │ ├── app.12a6e1c2.css │ └── app.12a6e1c2.css.gz ├── fonts │ ├── ionicons.143146fa.woff2 │ ├── ionicons.99ac3308.woff │ └── ionicons.d535a25a.ttf ├── img │ ├── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── msapplication-icon-144x144.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg │ └── ionicons.a2c4a261.svg ├── index.html ├── js │ ├── 0.342a6d4d.js │ ├── 0.342a6d4d.js.gz │ ├── 0.342a6d4d.js.map │ ├── 1.83aeb450.js │ ├── 1.83aeb450.js.gz │ ├── 1.83aeb450.js.map │ ├── 2.da1cb8f8.js │ ├── 2.da1cb8f8.js.gz │ ├── 2.da1cb8f8.js.map │ ├── app.3543d77b.js │ ├── app.3543d77b.js.gz │ ├── app.3543d77b.js.map │ ├── manifest.fa2bdef4.js.map │ ├── vendor.7efaf87e.js │ ├── vendor.7efaf87e.js.gz │ └── vendor.7efaf87e.js.map ├── manifest.json ├── monaco-editor │ └── min │ │ └── vs │ │ ├── base │ │ └── worker │ │ │ ├── workerMain.js │ │ │ └── workerMain.js.gz │ │ ├── basic-languages │ │ ├── bat │ │ │ └── bat.js │ │ ├── coffee │ │ │ └── coffee.js │ │ ├── cpp │ │ │ └── cpp.js │ │ ├── csharp │ │ │ └── csharp.js │ │ ├── csp │ │ │ └── csp.js │ │ ├── css │ │ │ └── css.js │ │ ├── dockerfile │ │ │ └── dockerfile.js │ │ ├── fsharp │ │ │ └── fsharp.js │ │ ├── go │ │ │ └── go.js │ │ ├── handlebars │ │ │ └── handlebars.js │ │ ├── html │ │ │ └── html.js │ │ ├── ini │ │ │ └── ini.js │ │ ├── java │ │ │ └── java.js │ │ ├── less │ │ │ └── less.js │ │ ├── lua │ │ │ └── lua.js │ │ ├── markdown │ │ │ └── markdown.js │ │ ├── msdax │ │ │ └── msdax.js │ │ ├── mysql │ │ │ ├── mysql.js │ │ │ └── mysql.js.gz │ │ ├── objective-c │ │ │ └── objective-c.js │ │ ├── pgsql │ │ │ ├── pgsql.js │ │ │ └── pgsql.js.gz │ │ ├── php │ │ │ └── php.js │ │ ├── postiats │ │ │ └── postiats.js │ │ ├── powershell │ │ │ └── powershell.js │ │ ├── pug │ │ │ └── pug.js │ │ ├── python │ │ │ └── python.js │ │ ├── r │ │ │ └── r.js │ │ ├── razor │ │ │ └── razor.js │ │ ├── redis │ │ │ └── redis.js │ │ ├── redshift │ │ │ ├── redshift.js │ │ │ └── redshift.js.gz │ │ ├── ruby │ │ │ └── ruby.js │ │ ├── sb │ │ │ └── sb.js │ │ ├── scss │ │ │ └── scss.js │ │ ├── solidity │ │ │ ├── solidity.js │ │ │ └── solidity.js.gz │ │ ├── sql │ │ │ ├── sql.js │ │ │ └── sql.js.gz │ │ ├── swift │ │ │ └── swift.js │ │ ├── vb │ │ │ └── vb.js │ │ ├── xml │ │ │ └── xml.js │ │ └── yaml │ │ │ └── yaml.js │ │ ├── editor │ │ ├── contrib │ │ │ └── suggest │ │ │ │ └── media │ │ │ │ ├── String_16x.svg │ │ │ │ └── String_inverse_16x.svg │ │ ├── editor.main.css │ │ ├── editor.main.css.gz │ │ ├── editor.main.js │ │ ├── editor.main.js.gz │ │ ├── editor.main.nls.de.js │ │ ├── editor.main.nls.de.js.gz │ │ ├── editor.main.nls.es.js │ │ ├── editor.main.nls.es.js.gz │ │ ├── editor.main.nls.fr.js │ │ ├── editor.main.nls.fr.js.gz │ │ ├── editor.main.nls.it.js │ │ ├── editor.main.nls.it.js.gz │ │ ├── editor.main.nls.ja.js │ │ ├── editor.main.nls.ja.js.gz │ │ ├── editor.main.nls.js │ │ ├── editor.main.nls.js.gz │ │ ├── editor.main.nls.ko.js │ │ ├── editor.main.nls.ko.js.gz │ │ ├── editor.main.nls.ru.js │ │ ├── editor.main.nls.ru.js.gz │ │ ├── editor.main.nls.zh-cn.js │ │ ├── editor.main.nls.zh-cn.js.gz │ │ ├── editor.main.nls.zh-tw.js │ │ ├── editor.main.nls.zh-tw.js.gz │ │ └── standalone │ │ │ └── browser │ │ │ └── quickOpen │ │ │ └── symbol-sprite.svg │ │ ├── language │ │ ├── css │ │ │ ├── cssMode.js │ │ │ ├── cssMode.js.gz │ │ │ ├── cssWorker.js │ │ │ └── cssWorker.js.gz │ │ ├── html │ │ │ ├── htmlMode.js │ │ │ ├── htmlMode.js.gz │ │ │ ├── htmlWorker.js │ │ │ └── htmlWorker.js.gz │ │ ├── json │ │ │ ├── jsonMode.js │ │ │ ├── jsonMode.js.gz │ │ │ ├── jsonWorker.js │ │ │ └── jsonWorker.js.gz │ │ └── typescript │ │ │ ├── lib │ │ │ ├── typescriptServices.js │ │ │ └── typescriptServices.js.gz │ │ │ ├── tsMode.js │ │ │ ├── tsMode.js.gz │ │ │ ├── tsWorker.js │ │ │ └── tsWorker.js.gz │ │ ├── loader.js │ │ └── loader.js.gz ├── precache-manifest.9a609502da82e575b48659cc8b25c64d.js └── service-worker.js ├── package-lock.json ├── package.json ├── public ├── img │ └── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── msapplication-icon-144x144.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg ├── index.html ├── manifest.json └── monaco-editor │ └── min │ └── vs │ ├── base │ └── worker │ │ └── workerMain.js │ ├── basic-languages │ ├── bat │ │ └── bat.js │ ├── coffee │ │ └── coffee.js │ ├── cpp │ │ └── cpp.js │ ├── csharp │ │ └── csharp.js │ ├── csp │ │ └── csp.js │ ├── css │ │ └── css.js │ ├── dockerfile │ │ └── dockerfile.js │ ├── fsharp │ │ └── fsharp.js │ ├── go │ │ └── go.js │ ├── handlebars │ │ └── handlebars.js │ ├── html │ │ └── html.js │ ├── ini │ │ └── ini.js │ ├── java │ │ └── java.js │ ├── less │ │ └── less.js │ ├── lua │ │ └── lua.js │ ├── markdown │ │ └── markdown.js │ ├── msdax │ │ └── msdax.js │ ├── mysql │ │ └── mysql.js │ ├── objective-c │ │ └── objective-c.js │ ├── pgsql │ │ └── pgsql.js │ ├── php │ │ └── php.js │ ├── postiats │ │ └── postiats.js │ ├── powershell │ │ └── powershell.js │ ├── pug │ │ └── pug.js │ ├── python │ │ └── python.js │ ├── r │ │ └── r.js │ ├── razor │ │ └── razor.js │ ├── redis │ │ └── redis.js │ ├── redshift │ │ └── redshift.js │ ├── ruby │ │ └── ruby.js │ ├── sb │ │ └── sb.js │ ├── scss │ │ └── scss.js │ ├── solidity │ │ └── solidity.js │ ├── sql │ │ └── sql.js │ ├── swift │ │ └── swift.js │ ├── vb │ │ └── vb.js │ ├── xml │ │ └── xml.js │ └── yaml │ │ └── yaml.js │ ├── editor │ ├── contrib │ │ └── suggest │ │ │ └── media │ │ │ ├── String_16x.svg │ │ │ └── String_inverse_16x.svg │ ├── editor.main.css │ ├── editor.main.js │ ├── editor.main.nls.de.js │ ├── editor.main.nls.es.js │ ├── editor.main.nls.fr.js │ ├── editor.main.nls.it.js │ ├── editor.main.nls.ja.js │ ├── editor.main.nls.js │ ├── editor.main.nls.ko.js │ ├── editor.main.nls.ru.js │ ├── editor.main.nls.zh-cn.js │ ├── editor.main.nls.zh-tw.js │ └── standalone │ │ └── browser │ │ └── quickOpen │ │ └── symbol-sprite.svg │ ├── language │ ├── css │ │ ├── cssMode.js │ │ └── cssWorker.js │ ├── html │ │ ├── htmlMode.js │ │ └── htmlWorker.js │ ├── json │ │ ├── jsonMode.js │ │ └── jsonWorker.js │ └── typescript │ │ ├── lib │ │ └── typescriptServices.js │ │ ├── tsMode.js │ │ └── tsWorker.js │ └── loader.js ├── src ├── App.vue ├── api │ ├── api.js │ └── index.js ├── assets │ ├── css │ │ ├── common.css │ │ └── markdown.css │ ├── js │ │ └── formatJson.js │ └── logo.png ├── components │ ├── card │ │ ├── api-card-simple.vue │ │ ├── api-card.vue │ │ ├── project-card.vue │ │ └── select-api-card.vue │ ├── code-editor │ │ ├── js-editor.vue │ │ └── json-editor.vue │ ├── controlbar │ │ └── controll-bar.vue │ ├── detail │ │ ├── detail-api.vue │ │ ├── detail-log.vue │ │ └── detail-project.vue │ ├── diag │ │ └── half-diag.vue │ ├── editors │ │ ├── editor-api.vue │ │ ├── editor-apiBase.vue │ │ ├── editor-apiModel.vue │ │ ├── editor-base.vue │ │ ├── editor-lib.vue │ │ └── editor-project.vue │ ├── error-notice.vue │ ├── log-item.vue │ ├── page-action │ │ ├── index.js │ │ ├── project-detail-action.vue │ │ └── project-list-action.vue │ └── speed-dial │ │ ├── speed-api.vue │ │ └── speed-project.vue ├── layout │ └── default.vue ├── main.js ├── mixin.js ├── mixin │ ├── action-api.js │ ├── action-project.js │ └── bread.js ├── pages │ ├── about.vue │ ├── api-copy.vue │ ├── api-detail.vue │ ├── api-editor.vue │ ├── config-editor.vue │ ├── first-page.vue │ ├── lib-editor.vue │ ├── log-list.vue │ ├── project-detail.vue │ ├── project-editor.vue │ ├── project-list.vue │ ├── search-detail.vue │ ├── search-list.vue │ ├── sync-api.vue │ ├── sync-list.vue │ └── template-add.vue ├── registerServiceWorker.js ├── router │ └── index.js ├── store │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── modules │ │ ├── api.js │ │ ├── log.js │ │ └── project.js │ └── mutation-types.js └── util │ └── socket.js └── vue.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": [ 4 | ["transform-runtime", {"polyfill": true, "regenerator": true}] 5 | ], 6 | "comments": false 7 | } 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 8 | extends: 'standard', 9 | // required to lint *.vue files 10 | plugins: [ 11 | 'html' 12 | ], 13 | // add your custom rules here 14 | 'rules': { 15 | "comma-dangle": [2, "always-multiline"], 16 | // allow paren-less arrow functions 17 | 'arrow-parens': 0, 18 | // allow async-await 19 | 'generator-star-spacing': 0, 20 | 'no-new-func': 0, 21 | "no-template-curly-in-string": 0, 22 | // allow debugger during development 23 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 24 | }, 25 | globals: { 26 | "WebSocket": true, 27 | "logger": true, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | node_modules/ 4 | debug.log 5 | db/ 6 | xmocker/ 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 6.11.0 5 | - 8.5.0 6 | 7 | notifications: 8 | email: 9 | - jsyaowenlong@126.com -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node 2 | 3 | MAINTAINER jinghongjun "hongjun.jing@gmail.com" 4 | 5 | ENV NODEJS_HOME=\opt\nodejs \ 6 | NODEJS_INSTALL_PACKAGE=${NODEJS_HOME}\node.tar.xz 7 | 8 | USER root 9 | 10 | RUN apt-get update && \ 11 | apt-get -y install tzdata vim git && \ 12 | ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ 13 | echo "Asia/Shanghai" > /etc/timezone && \ 14 | dpkg-reconfigure -f noninteractive tzdata && \ 15 | apt-get clean && \ 16 | rm -rf /var/lib/apt/lists/* 17 | 18 | RUN git clone https://github.com/wenlonghuo/xmocker-cli.git && \ 19 | cd xmocker-cli && \ 20 | npm link --production 21 | 22 | EXPOSE 6001 23 | EXPOSE 6002 24 | 25 | CMD ["mocker", "start"] 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://www.travis-ci.org/wenlonghuo/xmocker-cli.svg?branch=master)](https://www.travis-ci.org/wenlonghuo/xmocker-cli) 2 | ### 前端mock用工具 3 | #### 介绍 4 | 本工具是定制的mock服务工具,主要针对的是特殊API进行的设置。特色: 5 | * 定义API使用WEB页面操作 6 | 7 | * API添加不仅依赖于URL链接,而是支持从输入参数中判断所属的API名称 8 | 9 | * 使用nodejs和Koa的function进行条件判断,可针对不同的数据返回自定义的结果 10 | 11 | * 使用本地文件数据库nedb,整个环境仅依赖于nodejs环境 12 | 13 | * 提供文件服务器,可执行gulp类型的构建,并提供页面刷新及url展示接口 14 | 15 | ### 说明 16 | 相比于 rap 和 yapi 等服务器类型的 Mock 服务, 本仓库定位于工具服务, 17 | 由于使用了本地的数据库,其单个库最大数据读取为 256M,所以不适合大量的 API 存储。 18 | 相比于服务器类型,主要侧重于编辑的灵活和自由,去中心化,避免多人编辑相互影响的问题, 19 | 同时也造成了 api 数据过于离散,API 数据交换相对麻烦一些。配合 webpack 20 | 的 proxyTable,使用更为方便一些。 21 | 22 | 23 | #### Install 24 | ~~~ 25 | git clone https://github.com/wenlonghuo/xmocker-cli 26 | cd xmocker-cli 27 | npm link --production 28 | ~~~ 29 | 30 | 或 31 | 32 | ``` 33 | npm i -g xmocker-cli 34 | ``` 35 | 36 | #### 使用 37 | 打开网页 http://localhost:6001 即可访问 38 | 在网页中添加完成项目相关的信息后,可使用命令进行启动对应的项目 39 | 命令:mocker 40 | ~~~ 41 | Usage: mocker [options] [command] 42 | 43 | 44 | Options: 45 | 46 | -V, --version output the version number 47 | -h, --help output usage information 48 | 49 | Commands: 50 | 51 | start 启动项目 52 | stop 停止项目 53 | restart 重启项目 54 | list 列出项目 55 | exit 退出项目 56 | free 杀掉指定端口 57 | here 在当前目录启动服务器,更多参数请使用 mocker here -h查看 58 | help [cmd] display help for [cmd] 59 | ~~~ 60 | 61 | 代理转发: 62 | 在输出处理函数中填入 63 | ``` 64 | const url = `${this.origin}${this.originalUrl.replace(this.path, '/url/new')}` 65 | return this.tool.axios.get(url).then(res => res.data) 66 | ``` 67 | 68 | schema 使用的是json-gate 69 | https://github.com/oferei/json-gate 70 | ##### 修改的项目 71 | * json-gate: https://github.com/oferei/json-gate 72 | * koa-send: https://github.com/koajs/send 73 | -------------------------------------------------------------------------------- /app/api-schemas/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const loadFileList = require('../util/loadFileList') 3 | let schema = {} 4 | loadFileList(__dirname, 'schema', (file) => { 5 | var obj = require(file) 6 | Object.keys(obj).forEach((type) => { 7 | if (!schema[type]) schema[type] = {} 8 | Object.keys(obj[type]).forEach((name) => { 9 | schema[type][name] = obj[type][name] 10 | }) 11 | }) 12 | return obj 13 | }) 14 | 15 | module.exports = schema 16 | -------------------------------------------------------------------------------- /app/api-schemas/schema.appBase.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | get: {}, 3 | post: {}, 4 | put: {}, 5 | delete: {}, 6 | } 7 | module.exports.get.online = { 8 | } 9 | 10 | module.exports.get.appBase = { 11 | 12 | } 13 | 14 | module.exports.get.appStatus = { 15 | 16 | } 17 | 18 | module.exports.put.upgradeV0 = { 19 | 20 | } 21 | 22 | module.exports.put.appBase = { 23 | type: 'object', 24 | properties: { 25 | remoteAddress: { 26 | type: 'string', 27 | title: '远程访问地址', 28 | }, 29 | managePort: { 30 | type: 'number', 31 | title: '系统管理端口', 32 | }, 33 | }, 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/api-schemas/schema.error.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | get: {}, 3 | post: {}, 4 | put: {}, 5 | delete: {}, 6 | } 7 | 8 | module.exports.post.errorUpload = { 9 | type: 'object', 10 | properties: { 11 | href: { 12 | type: 'string', 13 | }, 14 | ua: { 15 | type: 'string', 16 | }, 17 | message: { 18 | type: 'string', 19 | }, 20 | source: { 21 | type: 'string', 22 | }, 23 | lineno: { 24 | type: 'string', 25 | }, 26 | colno: { 27 | type: 'string', 28 | }, 29 | stack: { 30 | type: 'string', 31 | }, 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /app/api-schemas/schema.lib.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | get: {}, 3 | post: {}, 4 | put: {}, 5 | delete: {}, 6 | } 7 | 8 | module.exports.get.libDetail = { 9 | type: 'object', 10 | properties: { 11 | id: { 12 | type: 'string', 13 | required: true, 14 | }, 15 | }, 16 | } 17 | module.exports.get.lib = { 18 | type: 'object', 19 | properties: { 20 | type: { 21 | type: 'string', 22 | }, 23 | projs: { 24 | type: 'array', 25 | title: '指定的项目', 26 | }, 27 | pageSize: { 28 | type: 'number', 29 | }, 30 | pageNo: { 31 | type: 'number', 32 | }, 33 | }, 34 | } 35 | 36 | module.exports.get.searchLib = { 37 | type: 'object', 38 | properties: { 39 | words: { 40 | type: 'string', 41 | minLength: 1, 42 | }, 43 | type: { 44 | type: 'string', 45 | minLength: 1, 46 | }, 47 | pageSize: { 48 | type: 'number', 49 | required: true, 50 | }, 51 | pageNo: { 52 | type: 'number', 53 | required: true, 54 | }, 55 | }, 56 | } 57 | 58 | module.exports.post.lib = { 59 | type: 'object', 60 | properties: { 61 | name: { 62 | type: 'string', 63 | required: true, 64 | minLength: 1, 65 | title: '名称', 66 | }, 67 | type: { 68 | type: 'string', 69 | required: true, 70 | title: '类型', 71 | }, 72 | description: { 73 | type: 'string', 74 | title: '说明', 75 | }, 76 | model: { 77 | type: ['object', 'null'], 78 | title: '模板', 79 | }, 80 | projs: { 81 | type: 'array', 82 | title: '归属于哪个项目', 83 | }, 84 | }, 85 | } 86 | 87 | module.exports.put.lib = { 88 | type: 'object', 89 | properties: { 90 | id: { 91 | type: 'string', 92 | required: true, 93 | minLength: 1, 94 | }, 95 | name: { 96 | type: 'string', 97 | required: true, 98 | minLength: 1, 99 | title: '名称', 100 | }, 101 | type: { 102 | type: 'string', 103 | required: true, 104 | title: '类型', 105 | }, 106 | description: { 107 | type: 'string', 108 | title: '说明', 109 | }, 110 | model: { 111 | type: ['object', 'null'], 112 | title: '模板', 113 | }, 114 | projs: { 115 | type: 'array', 116 | title: '归属于哪个项目', 117 | }, 118 | }, 119 | } 120 | 121 | module.exports.delete.lib = { 122 | type: 'object', 123 | properties: { 124 | id: { 125 | type: 'string', 126 | required: true, 127 | minLength: 1, 128 | }, 129 | }, 130 | } 131 | -------------------------------------------------------------------------------- /app/api-schemas/schema.log.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | get: {}, 3 | post: {}, 4 | put: {}, 5 | delete: {}, 6 | } 7 | 8 | module.exports.get.log = { 9 | type: 'object', 10 | properties: { 11 | type: { 12 | type: 'string', 13 | minLength: 1, 14 | }, 15 | project: { 16 | type: 'string', 17 | }, 18 | api: { 19 | type: 'string', 20 | }, 21 | apiModel: { 22 | type: 'string', 23 | }, 24 | ip: { 25 | type: 'string', 26 | }, 27 | client: { 28 | type: 'object', 29 | }, 30 | pageSize: { 31 | type: 'number', 32 | default: 20, 33 | required: true, 34 | }, 35 | pageNo: { 36 | type: 'number', 37 | default: 0, 38 | required: true, 39 | }, 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /app/api-schemas/schema.search.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | get: {}, 3 | post: {}, 4 | put: {}, 5 | delete: {}, 6 | } 7 | 8 | module.exports.get.search = { 9 | type: 'object', 10 | properties: { 11 | type: { 12 | type: 'string', 13 | }, 14 | keyword: { 15 | type: 'string', 16 | }, 17 | pageSize: { 18 | type: 'number', 19 | default: 20, 20 | required: true, 21 | }, 22 | pageNo: { 23 | type: 'number', 24 | default: 0, 25 | required: true, 26 | }, 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /app/controller/controller.apiModel.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../database') 3 | const ApiModel = db.apiModel 4 | 5 | const apiGet = require('../service/api/service.get') 6 | const apiEdit = require('../service/api/service.edit') 7 | 8 | module.exports = { 9 | getApiModel: getApiModel, 10 | addApiModel: addApiModel, 11 | editApiModel: editApiModel, 12 | deleteApiModel: deleteApiModel, 13 | getApiModelList, 14 | } 15 | 16 | async function getApiModel (ctx, next) { 17 | let finalParams = ctx.finalParams 18 | 19 | let list 20 | try { 21 | list = await ApiModel.cfind(finalParams).exec() 22 | } catch (e) { 23 | return ctx.respond.error('查询api分支信息出错', {e}) 24 | } 25 | 26 | ctx.respond.success('获取分支成功', {list}) 27 | } 28 | 29 | async function getApiModelList (ctx, next) { 30 | let finalParams = ctx.finalParams 31 | 32 | try { 33 | let data = await apiGet.getModelByQuery({baseid: finalParams.baseid}, finalParams) 34 | return ctx.respond.success('获取分支列表成功', data) 35 | } catch (e) { 36 | return ctx.respond.error('分支列表出错', {e}) 37 | } 38 | } 39 | 40 | async function addApiModel (ctx, next) { 41 | let finalParams = ctx.finalParams 42 | 43 | try { 44 | let data = await apiEdit.addModel(finalParams, true) 45 | if (data.code) return ctx.respond.error(data) 46 | return ctx.respond.success('添加API分支成功', { result: data.data }) 47 | } catch (e) { 48 | return ctx.respond.error('添加api分支信息出错', {e}) 49 | } 50 | } 51 | 52 | async function editApiModel (ctx, next) { 53 | let finalParams = ctx.finalParams 54 | 55 | let id = finalParams.id 56 | delete finalParams.id 57 | let data 58 | try { 59 | data = await apiEdit.editModel(id, finalParams) 60 | if (data.code) return ctx.respond.error(data) 61 | return ctx.respond.success('编辑API分支成功', { result: data.data }) 62 | } catch (e) { 63 | return ctx.respond.error('编辑api分支信息出错', {e}) 64 | } 65 | } 66 | 67 | async function deleteApiModel (ctx, next) { 68 | let finalParams = ctx.finalParams 69 | 70 | let data 71 | try { 72 | data = await apiEdit.deleteModel(finalParams.id) 73 | if (data.code) return ctx.respond.error(data) 74 | return ctx.respond.success('删除API分支成功', { result: data.data }) 75 | } catch (e) { 76 | return ctx.respond.error('删除api分支信息出错', {e}) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/controller/controller.appBase.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../database') 3 | const AppBase = db.appBase 4 | 5 | const service = require('../service') 6 | const processList = service.proc.state.proc 7 | const upgrade = service.upgrade.upgradeFromV0 8 | 9 | module.exports = { 10 | getAppBase: getAppBase, 11 | editAppBase: editAppBase, 12 | getAppStatus: getAppStatus, 13 | online: online, 14 | reloadDatabase: reloadDatabase, 15 | upgradeFromV0, 16 | } 17 | 18 | async function online (ctx, next) { 19 | ctx.body = { 20 | code: 0, 21 | data: 'online', 22 | } 23 | return next() 24 | } 25 | 26 | async function reloadDatabase (ctx, next) { 27 | Object.keys(db).forEach(function (name) { 28 | if (db[name].loadDatabase) { 29 | db[name].loadDatabase() 30 | } 31 | }) 32 | ctx.respond.success('重载成功') 33 | } 34 | 35 | async function getAppStatus (ctx, next) { 36 | // let finalParams = ctx.finalParams 37 | let procInfo = [] 38 | processList.forEach(function (proc) { 39 | procInfo.push({ 40 | procInfo: proc.proj, 41 | createdTime: proc.createdTime, 42 | pid: proc.pid, 43 | status: proc.status, 44 | }) 45 | }) 46 | procInfo.sort(function (a, b) { return a.procInfo.name > b.procInfo.name }) 47 | ctx.respond.success('获取状态成功', { runningProject: procInfo }) 48 | } 49 | 50 | async function getAppBase (ctx, next) { 51 | let finalParams = ctx.finalParams 52 | 53 | let result 54 | try { 55 | result = await AppBase.cfindOne(finalParams).exec() 56 | } catch (e) { 57 | return ctx.respond.error('查询基础信息出错', {e}) 58 | } 59 | ctx.respond.success('获取app基础信息成功', {result}) 60 | } 61 | 62 | async function editAppBase (ctx, next) { 63 | let finalParams = ctx.finalParams 64 | 65 | let result 66 | try { 67 | result = await AppBase.update({}, {$set: finalParams}, {returnUpdatedDocs: true, upsert: true}) 68 | result = result[1] 69 | } catch (e) { 70 | return ctx.respond.error('保存基础信息出错', {e}) 71 | } 72 | 73 | ctx.respond.success('更新基础信息成功', {result}) 74 | } 75 | 76 | async function upgradeFromV0 (ctx, next) { 77 | try { 78 | await upgrade() 79 | } catch (e) { 80 | return ctx.respond.error('更新失败,请查看日志' + e.message, {e}) 81 | } 82 | ctx.respond.success('导入成功', {}) 83 | } 84 | -------------------------------------------------------------------------------- /app/controller/controller.dynData.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/app/controller/controller.dynData.js -------------------------------------------------------------------------------- /app/controller/controller.error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../database') 3 | const formatCtx = require('../util/formatCtx') 4 | const broadcast = require('../service/service.ws').broadcast 5 | 6 | module.exports = { 7 | errorUpload, 8 | } 9 | 10 | async function errorUpload (ctx, next) { 11 | let finalParams = ctx.finalParams 12 | try { 13 | let data = formatCtx(ctx, finalParams.message) 14 | data.err = { 15 | msg: finalParams.message, 16 | source: finalParams.source, 17 | lineno: finalParams.lineno, 18 | stack: finalParams.stack, 19 | colno: finalParams.colno, 20 | } 21 | await db.collectorDB.insert(data) 22 | broadcast({ type: 'log', action: 'ADD_LOGS', logType: 'collector', data: data }) 23 | } catch (e) { 24 | return ctx.respond.error('保存错误信息出错', { e }) 25 | } 26 | ctx.respond.success('提交成功') 27 | } 28 | -------------------------------------------------------------------------------- /app/controller/controller.log.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../database') 3 | const logDb = { 4 | 'error': db.errorDB, 5 | 'his': db.hisDB, 6 | 'proxy': db.proxyDB, 7 | 'record': db.recordDB, 8 | 'collector': db.collectorDB, 9 | } 10 | 11 | const getQuery = require('../util/getQuery').getQuery 12 | 13 | module.exports = { 14 | searchLog, 15 | } 16 | 17 | async function searchLog (ctx, next) { 18 | let finalParams = ctx.finalParams 19 | let size = ~~finalParams.pageSize 20 | let no = ~~finalParams.pageNo 21 | let skip = ~~(size * no) 22 | let type = finalParams.type 23 | 24 | delete finalParams.pageSize 25 | delete finalParams.pageNo 26 | 27 | const sort = {time: -1} 28 | 29 | let list, total 30 | if (!logDb[type]) return ctx.respond.error('查询的日志类型不存在!') 31 | try { 32 | let query = getQuery(finalParams) 33 | total = await logDb[type].count(query) 34 | list = await logDb[type].cfind(query).sort(sort).skip(skip).limit(size).exec() 35 | } catch (e) { 36 | return ctx.respond.error('搜索日志出错', {e}) 37 | } 38 | 39 | let res = { 40 | list, 41 | pagination: { 42 | total: total, 43 | pageCnt: Math.ceil(total / size), 44 | pageNo: no, 45 | }, 46 | } 47 | ctx.respond.success('搜索api成功', res) 48 | } 49 | -------------------------------------------------------------------------------- /app/database/v0/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Datastore = require('../promiseNeDb') 4 | const path = require('path') 5 | 6 | // api 基础数据 7 | const apiBase = new Datastore({filename: path.join(__dirname, '../../../db/api/base'), autoload: true}) 8 | 9 | // mock用于显示数据 10 | const apiModel = new Datastore({filename: path.join(__dirname, '../../../db/api/model'), autoload: true}) 11 | 12 | // 基础配置 13 | const appBase = new Datastore({filename: path.join(__dirname, '../../../db/app/base'), autoload: true}) 14 | 15 | // 项目信息 16 | const appProject = new Datastore({filename: path.join(__dirname, '../../../db/app/project'), autoload: true}) 17 | 18 | 19 | module.exports = { 20 | apiBase: apiBase, 21 | apiModel: apiModel, 22 | appBase: appBase, 23 | appProject: appProject, 24 | } 25 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Koa = require('koa') 3 | const app = new Koa() 4 | const http = require('http') 5 | const path = require('path') 6 | 7 | const bodyParser = require('koa-bodyparser') 8 | 9 | const startTime = new Date() 10 | 11 | const sendFile = require('./plugin/file-server.js') 12 | const db = require('./database') 13 | const ws = require('./service/service.ws.js') 14 | const router = require('./router') 15 | const respond = require('./middleware/respond') 16 | const logger = require('./middleware/logger') 17 | const argv = require('minimist')(process.argv.slice(2)) 18 | 19 | let appPORT 20 | 21 | app.proxy = true 22 | 23 | // body parser 24 | app.use(bodyParser()) 25 | 26 | // 静态服务器 添加默认为Index.html 27 | app.use(async function (ctx, next) { 28 | ctx.set({ 29 | 'Access-Control-Allow-Origin': '*', 30 | 'Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS', 31 | }) 32 | return next().then(sendFile(ctx, ctx.path, {root: path.join(__dirname, '../web/dist/'), index: 'index.html'})) 33 | }) 34 | 35 | app.use(logger()) 36 | app.use(respond()) 37 | 38 | // 调用路由 39 | app.use(router.routes()) 40 | 41 | // 建立是的监听及server 42 | const httpServer = http.createServer(app.callback()) 43 | 44 | // 建立 websocket 服务 45 | ws(httpServer) 46 | 47 | // 查询appbase 48 | db.appBase.cfindOne({}).exec().then(function (doc) { 49 | doc = doc || {} 50 | appPORT = doc.managePort || 6001 51 | // 去除原占用的端口 52 | // common.killPort(appPORT) 53 | 54 | httpServer.listen(appPORT, function (e) { 55 | console.log('后台管理界面运行于: http://localhost:%s', appPORT, '耗时:', new Date() - startTime, 'ms') 56 | }) 57 | 58 | let queryObj = {} 59 | let projs = argv.proj 60 | if (projs) { 61 | projs = Array.isArray(projs) ? projs : [projs] 62 | queryObj.$or = [ 63 | {name: {$in: projs}}, 64 | {shortcut: {$in: projs}}, 65 | ] 66 | } else { 67 | return 68 | } 69 | 70 | db.project.cfind(queryObj).exec().then(function (docs) { 71 | if (!docs) { 72 | console.log('未找到需要启动的项目') 73 | return 74 | } 75 | const start = require('./service/service.proc').addToRestart 76 | docs.forEach(p => start(p)) 77 | // processControl.restartProcess(doc) 78 | }) 79 | }) 80 | 81 | process.on('unhandledRejection', function (e) { 82 | throw e 83 | }) 84 | -------------------------------------------------------------------------------- /app/middleware/formatParam.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const gate = require('../plugin/json-gate/json-gate') 3 | const createSchema = gate.createSchema 4 | const lodash = require('lodash') 5 | const clone = lodash.clone 6 | const camelCase = lodash.camelCase 7 | 8 | module.exports = function (apiSchema) { 9 | return async function formatParam (ctx, next) { 10 | let method = ctx.method.toLowerCase() 11 | let query = clone(ctx.query) 12 | let body = ctx.request.body 13 | 14 | let p = ctx.request.path 15 | p = camelCase(p.slice(5)) 16 | 17 | if (!apiSchema[method] || !apiSchema[method][p]) { 18 | return ctx.respond.error('接口' + p + '不存在schema') 19 | } 20 | let schema = apiSchema[method][p] 21 | let pathSchema = createSchema(schema) 22 | 23 | // parse url string to Object 24 | Object.keys(query).forEach(key => { 25 | if (schema && schema.properties && schema.properties[key] && schema.properties[key].type === 'string') return 26 | try { 27 | query[key] = JSON.parse(query[key]) 28 | } catch (e) {} 29 | }) 30 | 31 | let params = Object.assign({}, query, clone(body)) 32 | try { 33 | pathSchema.validate(params) 34 | } catch (e) { 35 | return ctx.respond.error(e.message, {e}) 36 | } 37 | 38 | ctx.finalParams = params 39 | return next() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/middleware/respond.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /** 3 | * 返回格式化模块 4 | * 提供 ctx.respond.success和ctx.respond.error两种方法 5 | */ 6 | const version = 2 7 | 8 | function logError (ctx, code, msg, option = {}) { 9 | if (typeof code === 'object' && code.code != null) { 10 | ctx.body = Object.assign({}, code, {message: code.msg}) 11 | return 12 | } 13 | if (typeof msg === 'object' || msg == null) { 14 | option = msg || {} 15 | msg = code 16 | code = -1 17 | } 18 | ctx.info.e = option.e 19 | ctx.body = { 20 | code: code, 21 | message: msg, 22 | data: option.data, 23 | version, 24 | } 25 | } 26 | 27 | function success (ctx, msg, data) { 28 | ctx.info.message = msg 29 | ctx.body = { 30 | code: 0, 31 | message: msg, 32 | data: data, 33 | version, 34 | } 35 | } 36 | 37 | module.exports = function () { 38 | return async function sendMessage (ctx, next) { 39 | ctx.info = ctx.info || {} 40 | ctx.respond = ctx.respond || {} 41 | ctx.respond.success = success.bind(ctx, ctx) 42 | ctx.respond.error = logError.bind(ctx, ctx) 43 | return next() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/plugin/json-gate/common.js: -------------------------------------------------------------------------------- 1 | exports.getType = function (obj) { 2 | switch (Object.prototype.toString.call(obj)) { 3 | case '[object String]': 4 | return 'string'; 5 | case '[object Number]': 6 | return (obj % 1 === 0) ? 'integer' : 'number'; 7 | case '[object Boolean]': 8 | return 'boolean'; 9 | case '[object Object]': 10 | return 'object'; 11 | case '[object Array]': 12 | return 'array'; 13 | case '[object Null]': 14 | return 'null'; 15 | default: 16 | return 'undefined'; 17 | } 18 | } 19 | 20 | exports.prettyType = function(type) { 21 | switch (type) { 22 | case 'string': 23 | case 'number': 24 | case 'boolean': 25 | return 'a ' + type; 26 | case 'integer': 27 | case 'object': 28 | case 'array': 29 | return 'an ' + type; 30 | case 'null': 31 | return 'null'; 32 | case 'any': 33 | return 'any type'; 34 | case 'undefined': 35 | return 'undefined'; 36 | default: 37 | if (typeof type === 'object') { 38 | return 'a schema' 39 | } else { 40 | return type; 41 | } 42 | } 43 | } 44 | 45 | 46 | exports.isOfType = function (obj, type) { 47 | switch (type) { 48 | case 'string': 49 | case 'boolean': 50 | case 'object': 51 | case 'array': 52 | case 'null': 53 | type = type.charAt(0).toUpperCase() + type.slice(1); 54 | return Object.prototype.toString.call(obj) === '[object ' + type + ']'; 55 | case 'number': 56 | return Object.prototype.toString.call(obj) === '[object ' + type + ']' || (!isNaN(parseFloat(obj)) && !isNaN(obj)); 57 | case 'integer': 58 | return Object.prototype.toString.call(obj) === '[object Number]' && obj % 1 === 0; 59 | case 'any': 60 | default: 61 | return true; 62 | } 63 | } 64 | 65 | exports.getName = function (names) { 66 | return names.length === 0 ? '' : ' property \'' + names.join('.') + '\''; 67 | }; 68 | 69 | exports.deepEquals = function (obj1, obj2) { 70 | var p; 71 | 72 | if (Object.prototype.toString.call(obj1) !== Object.prototype.toString.call(obj2)) { 73 | return false; 74 | } 75 | 76 | switch (typeof obj1) { 77 | case 'object': 78 | if (obj1.toString() !== obj2.toString()) { 79 | return false; 80 | } 81 | for (p in obj1) { 82 | if (!(p in obj2)) { 83 | return false; 84 | } 85 | if (!exports.deepEquals(obj1[p], obj2[p])) { 86 | return false; 87 | } 88 | } 89 | for (p in obj2) { 90 | if (!(p in obj1)) { 91 | return false; 92 | } 93 | } 94 | return true; 95 | case 'function': 96 | return obj1[p].toString() === obj2[p].toString(); 97 | default: 98 | return obj1 === obj2; 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /app/plugin/json-gate/json-gate.js: -------------------------------------------------------------------------------- 1 | var validateSchema = require('./valid-schema'), 2 | validateObject = require('./valid-object'), 3 | formatObject = require('./format-object'); 4 | 5 | var Schema = function(schema) { 6 | this.schema = schema; 7 | validateSchema(schema); 8 | 9 | this.validate = function(obj, done) { 10 | validateObject(obj, schema, done); 11 | } 12 | this.format = function(obj, done) { 13 | formatObject(obj, schema, done); 14 | } 15 | } 16 | 17 | module.exports.createSchema = function (schema) { 18 | return new Schema(schema); 19 | } 20 | -------------------------------------------------------------------------------- /app/router/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | let router = require('koa-router')() 4 | router.prefix('/mock') 5 | const loadFileList = require('../util/loadFileList') 6 | const apiSchema = require('../api-schemas') 7 | 8 | router.formatParam = require('../middleware/formatParam')(apiSchema) 9 | 10 | router.controller = loadFileList(path.join(__dirname, '../controller'), 'controller') 11 | 12 | module.exports = router 13 | loadFileList(__dirname, 'router') 14 | -------------------------------------------------------------------------------- /app/router/router.apiBase.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.apiBase 4 | let formatParam = router.formatParam 5 | 6 | router.get('/apiDetail', formatParam, controller.getApiDetail) 7 | 8 | router.get('/api', formatParam, controller.getApiList) 9 | 10 | router.get('/apiBase', formatParam, controller.getApiBase) 11 | 12 | router.get('/search/api', formatParam, controller.searchApiBase) 13 | 14 | router.post('/apiBase', formatParam, controller.addApiBase) 15 | 16 | router.put('/apiBase', formatParam, controller.editApiBase) 17 | 18 | router.delete('/api', formatParam, controller.deleteApiBase) 19 | 20 | router.put('/copyApi', formatParam, controller.copyApi) 21 | 22 | router.put('/apiStatus', formatParam, controller.setApiStatus) 23 | -------------------------------------------------------------------------------- /app/router/router.apiModel.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.apiModel 4 | let formatParam = router.formatParam 5 | 6 | router.get('/apiModelList', formatParam, controller.getApiModelList) 7 | 8 | router.get('/apiModel', formatParam, controller.getApiModel) 9 | 10 | router.post('/apiModel', formatParam, controller.addApiModel) 11 | 12 | router.put('/apiModel', formatParam, controller.editApiModel) 13 | 14 | router.delete('/apiModel', formatParam, controller.deleteApiModel) 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/router/router.appBase.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.appBase 4 | let formatParam = router.formatParam 5 | const inspect = require('util').inspect 6 | 7 | router.get('/online', controller.online) 8 | 9 | router.get('/reloadDatabase', controller.reloadDatabase) 10 | 11 | router.get('/appBase', formatParam, controller.getAppBase) 12 | 13 | router.put('/appBase', formatParam, controller.editAppBase) 14 | 15 | router.put('/upgradeV0', formatParam, controller.upgradeFromV0) 16 | 17 | router.put('/killMain', async function (ctx, next) { 18 | process.exit(1) 19 | }) 20 | 21 | router.get('/appStatus', formatParam, controller.getAppStatus) 22 | 23 | router.post('/error', async function (ctx, next) { 24 | let time = new Date().toLocaleTimeString() 25 | console.log(`\n[${time}] 网页出现错误啦,下面是错误信息 ~~~`) 26 | console.log(inspect(ctx.request.body, {colors: true, depth: null})) 27 | ctx.body = 'ok' 28 | }) 29 | -------------------------------------------------------------------------------- /app/router/router.commonData.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.commonData 4 | let formatParam = router.formatParam 5 | 6 | router.get('/commonData', formatParam, controller.getCommonData) 7 | 8 | router.get('/searchCommonData', formatParam, controller.searchCommonData) 9 | 10 | router.post('/commonData', formatParam, controller.addCommonData) 11 | 12 | router.put('/commonData', formatParam, controller.editCommonData) 13 | 14 | router.delete('/commonData', formatParam, controller.deleteCommonData) 15 | -------------------------------------------------------------------------------- /app/router/router.error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.error 4 | let formatParam = router.formatParam 5 | 6 | router.post('/error/upload', formatParam, controller.errorUpload) 7 | -------------------------------------------------------------------------------- /app/router/router.lib.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.lib 4 | let formatParam = router.formatParam 5 | 6 | router.get('/libDetail', formatParam, controller.getLibDetail) 7 | 8 | router.get('/lib', formatParam, controller.getLibList) 9 | 10 | router.post('/lib', formatParam, controller.addLib) 11 | 12 | router.put('/lib', formatParam, controller.editLib) 13 | 14 | router.delete('/lib', formatParam, controller.deleteLib) 15 | -------------------------------------------------------------------------------- /app/router/router.log.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.log 4 | let formatParam = router.formatParam 5 | 6 | router.get('/log', formatParam, controller.searchLog) 7 | -------------------------------------------------------------------------------- /app/router/router.project.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.project 4 | let formatParam = router.formatParam 5 | 6 | router.get('/project', formatParam, controller.getProject) 7 | 8 | router.get('/state/project', formatParam, controller.getRunningProject) 9 | 10 | router.post('/project', formatParam, controller.addProject) 11 | 12 | router.put('/project', formatParam, controller.editProject) 13 | 14 | router.delete('/project', formatParam, controller.deleteProject) 15 | 16 | router.put('/startProject', formatParam, controller.startProject) 17 | 18 | router.put('/stopProject', formatParam, controller.stopProject) 19 | 20 | router.put('/setDefaultApiParam', formatParam, controller.setDefaultApiParam) 21 | -------------------------------------------------------------------------------- /app/router/router.search.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.search 4 | let formatParam = router.formatParam 5 | 6 | router.get('/search', formatParam, controller.search) 7 | -------------------------------------------------------------------------------- /app/router/router.sync.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const router = require('./index.js') 3 | const controller = router.controller.sync 4 | let formatParam = router.formatParam 5 | 6 | // 获取 7 | router.get('/clientGetProjList', formatParam, controller.clientGetProjList) 8 | 9 | router.get('/clientGetProjDetail', formatParam, controller.clientGetProjDetail) 10 | 11 | router.get('/clientGetApiListByProject', formatParam, controller.clientGetApiListByProject) 12 | 13 | router.get('/clientGetApiListByIds', formatParam, controller.clientGetApiListByIds) 14 | 15 | // diff 16 | router.get('/clientGetProjDiff', formatParam, controller.clientGetProjDiff) 17 | 18 | router.get('/clientGetApiDiff', formatParam, controller.clientGetApiDiff) 19 | 20 | // 下载 21 | router.put('/clientDownLoadProj', formatParam, controller.clientDownLoadProj) 22 | 23 | router.put('/clientDownLoadProjBase', formatParam, controller.clientDownLoadProjBase) 24 | 25 | router.put('/clientDownLoadApi', formatParam, controller.clientDownLoadApi) 26 | 27 | // 推送 28 | router.put('/clientPushApiListById', formatParam, controller.clientPushApiListById) 29 | 30 | router.put('/clientPushApiById', formatParam, controller.clientPushApiById) 31 | 32 | router.put('/clientPushApiByData', formatParam, controller.clientPushApiByData) 33 | 34 | router.put('/clientPushApiListByData', formatParam, controller.clientPushApiListByData) 35 | 36 | // 服务端 37 | 38 | router.put('/serverReceiveApi', formatParam, controller.serverReceiveApi) 39 | 40 | router.put('/serverReceiveApiList', formatParam, controller.serverReceiveApiList) 41 | 42 | // 获取 43 | router.get('/serverGetProjList', formatParam, controller.serverGetProjList) 44 | 45 | router.get('/serverGetProj', formatParam, controller.serverGetProj) 46 | 47 | router.get('/serverGetApiListByProject', formatParam, controller.serverGetApiListByProject) 48 | 49 | router.get('/serverGetApi', formatParam, controller.serverGetApi) 50 | 51 | // 获取差分 52 | router.put('/serverDiffProj', formatParam, controller.serverDiffProj) 53 | 54 | router.put('/serverDiffApi', formatParam, controller.serverDiffApi) 55 | -------------------------------------------------------------------------------- /app/service/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const loadFileList = require('../util/loadFileList') 4 | 5 | module.exports = loadFileList(path.join(__dirname, './')) 6 | -------------------------------------------------------------------------------- /app/service/project/service.edit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../../database') 3 | const Project = db.project 4 | const ApiBase = db.apiBase 5 | const ApiModel = db.apiModel 6 | 7 | const restart = require('../service.ctrlProc').restart.add 8 | const uid = require('../../util/common').uid() 9 | 10 | const projectGet = require('./service.get') 11 | 12 | module.exports = { 13 | addProject, 14 | editProject, 15 | deleteProject, 16 | } 17 | 18 | async function addProject (params) { 19 | let data 20 | try { 21 | let exist = await projectGet.getExistProject(params, {}) 22 | if (exist.data && exist.data.length) return { code: 1, msg: '项目简称和当前项目冲突', data: exist } 23 | params._uid = uid() 24 | params._mt = +new Date() 25 | data = await Project.insert(params) 26 | } catch (e) { 27 | throw e 28 | } 29 | return { code: 0, data } 30 | } 31 | 32 | async function editProject (id, params) { 33 | let data 34 | try { 35 | let exist = await projectGet.getExistProject(params, { id }) 36 | if (exist.data && exist.data.length) return { code: 1, msg: '项目简称和当前项目冲突', data: exist.data } 37 | params._mt = +new Date() 38 | data = await Project.update({ _id: id }, { $set: params }, { returnUpdatedDocs: true, multi: true, upsert: true }) 39 | data = data[1] 40 | } catch (e) { 41 | throw e 42 | } 43 | 44 | restart({type: 'project', id: id}) 45 | return { code: 0, data } 46 | } 47 | 48 | async function deleteProject (ids = []) { 49 | let data = {} 50 | try { 51 | data.project = await Project.remove({_id: {$in: ids}}) 52 | 53 | let apis = await ApiBase.cfind({project: {$in: ids}}).exec() 54 | let apiIds = apis.map(item => item._id) 55 | 56 | if (!apiIds.length) return 57 | 58 | data.base = await ApiBase.remove({ _id: apiIds }, { multi: true }) 59 | data.model = await ApiModel.remove({ baseid: apiIds }, { multi: true }) 60 | } catch (e) { 61 | throw e 62 | } 63 | 64 | return { code: 0, data } 65 | } 66 | -------------------------------------------------------------------------------- /app/service/project/service.get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../../database') 3 | const Project = db.project 4 | 5 | module.exports = { 6 | getProjectDetailById, 7 | getProjectDetailByQuery, 8 | getProjectByQuery, 9 | getExistProject, 10 | } 11 | 12 | async function getProjectDetailByQuery (query) { 13 | if (!Object.keys(query).length) throw new Error('查询条件不能为空!!!') 14 | try { 15 | let data = await Project.cfindOne(query).exec() 16 | return data 17 | } catch (e) { 18 | 19 | } 20 | } 21 | 22 | async function getProjectDetailById (id) { 23 | try { 24 | return await getProjectDetailByQuery({_id: id}) 25 | } catch (e) { 26 | 27 | } 28 | } 29 | 30 | /** 31 | * 根据查询条件返回API列表 32 | * @param {*} query 33 | * @param {*} param1 34 | */ 35 | async function getProjectByQuery (query, { pageSize, pageNo, order, sortBy }) { 36 | let size = ~~pageSize 37 | let no = ~~pageNo 38 | let skip = ~~(size * no) 39 | 40 | let sortInfo = {} 41 | if (sortBy) { 42 | sortInfo[sortBy] = order 43 | } else { 44 | sortInfo.name = 1 45 | } 46 | 47 | try { 48 | let total = await Project.count(query) 49 | let data = await Project.cfind(query).sort(sortInfo).skip(skip).limit(size).exec() 50 | return { 51 | list: data, 52 | pagination: { 53 | total: total, 54 | pageCnt: Math.ceil(total / size), 55 | pageNo: no, 56 | }, 57 | } 58 | } catch (e) { 59 | throw e 60 | } 61 | } 62 | /** 63 | * 获取该条件下的Project是否已经存在 64 | * @param {*} params 65 | * @param {*} 【id】指定ID 66 | */ 67 | async function getExistProject (params, { id }) { 68 | const query = { 69 | shortcut: params.shortcut, 70 | } 71 | 72 | if (id) { 73 | query._id = { $ne: id } 74 | } 75 | 76 | try { 77 | let data = await Project.cfind(query).exec() 78 | return { data, query } 79 | } catch (e) { 80 | throw e 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/service/service.ctrlProc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../database') 3 | 4 | const serviceProc = require('./service.proc.js') 5 | const getProcById = serviceProc.getProcById 6 | const procList = serviceProc.state.proc 7 | 8 | // 生成项目处理的序列 9 | class ExecQuene { 10 | constructor (func) { 11 | this.exector = func 12 | this.state = 0 13 | this.team = Promise.resolve() 14 | this.add = this.add.bind(this) 15 | } 16 | add () { 17 | this.team.then(async () => { 18 | await this.exector.apply(this, arguments) 19 | }) 20 | } 21 | } 22 | 23 | const reload = new ExecQuene(async function (option) { 24 | let type = option.type 25 | let dbs = option.dbs ? option.dbs : [type] 26 | let info = await getProject(option) 27 | 28 | if (!info) { 29 | return 30 | } 31 | let proc = getProcById(info._id) 32 | if (!proc) return 33 | proc.reloadApis(dbs) 34 | }) 35 | 36 | const restart = new ExecQuene(async function (option) { 37 | try { 38 | let info = await getProject(option) 39 | if (info) serviceProc.addToRestart(info, option).catch(e => console.log(info, option, e)) 40 | } catch (e) { 41 | console.log(e) 42 | } 43 | }) 44 | 45 | async function getProject (option) { 46 | let quene = [ 47 | {name: 'apiModel', key: 'baseid'}, 48 | {name: 'apiBase', key: 'project'}, 49 | {name: 'project', key: '_id'}, 50 | ] 51 | let index = quene.findIndex(q => q.name === option.type) 52 | let doc 53 | let id = option.id 54 | 55 | try { 56 | while (index < quene.length && index >= 0 && id) { 57 | let info = quene[index] 58 | doc = await getDocById(info.name, id).catch(e => { throw e }) 59 | if (!doc) throw new Error(`cannot find doc by id : ${id}, type: ${info.name}`) 60 | id = doc[quene[index].key] 61 | index++ 62 | } 63 | } catch (e) { 64 | console.log(e) 65 | } 66 | 67 | if (procList.find(proc => doc && proc.id === doc._id)) return doc 68 | } 69 | 70 | async function getDocById (collection, id) { 71 | return await db[collection].cfindOne({_id: id}).exec() 72 | } 73 | 74 | module.exports = { 75 | reload, 76 | restart, 77 | } 78 | -------------------------------------------------------------------------------- /app/service/service.log.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | let through2 = require('through2') 3 | 4 | let broadcast = require('./service.ws').broadcast 5 | 6 | // 生成需要的 stream 7 | let errStream = through2(function (chunk, enc, cb) { 8 | process.stdout.write(chunk) 9 | cb(null, chunk) 10 | }) 11 | 12 | // 日志记录函数 13 | function mocker (data) { 14 | if (typeof data === 'object') { 15 | if (data.res && typeof data.res === 'object') { 16 | data.res = JSON.stringify(data.res) 17 | } 18 | broadcast({type: 'log', action: 'ADD_LOGS', logType: data.type, data: data}) 19 | 20 | let type = data.type === 'his' ? 'info' : data.type 21 | if (logger[type]) { 22 | logger[type](data) 23 | } 24 | } 25 | } 26 | 27 | module.exports = function (msg) { 28 | process.stdout.write(msg) 29 | } 30 | 31 | module.exports.errStream = errStream 32 | module.exports.mocker = mocker 33 | -------------------------------------------------------------------------------- /app/service/service.ws.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const WebSocket = require('ws') 3 | let wss 4 | // websocket 通讯 5 | function incoming (client, msg) { 6 | try { 7 | msg = JSON.parse(msg) 8 | } catch (e) { 9 | return 10 | } 11 | } 12 | 13 | function broadcast (data) { 14 | if (typeof data === 'object') data = JSON.stringify(data) 15 | wss.clients.forEach(function (client) { 16 | if (client.readyState === 1) { 17 | client.send(data) 18 | } 19 | }) 20 | } 21 | 22 | module.exports = function (httpServer) { 23 | wss = new WebSocket.Server({ server: httpServer }) 24 | wss.on('connection', function wsConnect (wsClient) { 25 | wsClient.on('message', incoming.bind(null, wsClient)) 26 | }) 27 | } 28 | 29 | module.exports.broadcast = broadcast 30 | -------------------------------------------------------------------------------- /app/service/sync/service.get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const request = require('./service.request').request 4 | const apiGet = require('../api/service.get') 5 | const projectGet = require('../project/service.get') 6 | 7 | module.exports = { 8 | getProjectByUid, 9 | getApiListByUid, 10 | getApiListByProjectUid, 11 | getRemoteData, 12 | } 13 | 14 | async function getRemoteData (url, params) { 15 | try { 16 | return await request.get(url, params) 17 | } catch (e) { 18 | throw e 19 | } 20 | } 21 | 22 | async function getProjectByUid (uid, detail) { 23 | try { 24 | let data = {} 25 | let result = await projectGet.getProjectDetailByQuery({_uid: uid}) 26 | data.proj = result 27 | if (detail) { 28 | const apiQuery = { 29 | pageSize: 10000, 30 | pageNo: 0, 31 | } 32 | let apiData = await apiGet.getApiByProject(result._id, apiQuery, true) 33 | let apiList = apiData.list 34 | 35 | data.api = apiList.map(item => { 36 | let base = Object.assign({}, item, {model: undefined}) 37 | let model = item.model 38 | return {base, model} 39 | }) 40 | } 41 | 42 | return data 43 | } catch (e) { 44 | throw e 45 | } 46 | } 47 | 48 | async function getApiListByProjectUid (uid, option) { 49 | try { 50 | let proj = await projectGet.getProjectDetailByQuery({_uid: uid}) 51 | if (!proj) return 52 | 53 | let result = await apiGet.getApiByProject(proj._id, option, true) 54 | 55 | result.list = result.list.map(item => { 56 | let model = item.model 57 | let base = Object.assign({}, item, {model: undefined}) 58 | return {base, model} 59 | }) 60 | return result 61 | } catch (e) { 62 | throw e 63 | } 64 | } 65 | 66 | async function getApiListByUid (uids, projectUid) { 67 | try { 68 | let proj = await projectGet.getProjectDetailByQuery({ _uid: projectUid }) 69 | if (!proj) return 70 | 71 | if (!Array.isArray(uids)) throw new Error('uid必须是数组') 72 | let query = { 73 | _uid: {$in: uids}, 74 | project: proj._id, 75 | } 76 | const apiQuery = { 77 | pageSize: 10000, 78 | pageNo: 0, 79 | } 80 | let result = await apiGet.getApiByQuery(query, apiQuery, true) 81 | 82 | result = result.list 83 | 84 | let resultData = result.map(item => { 85 | let model = item.model 86 | let base = Object.assign({}, item, {model: undefined}) 87 | return {base, model} 88 | }) 89 | return resultData 90 | } catch (e) { 91 | throw e 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/service/sync/service.push.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const request = require('./service.request').request 4 | const apiGet = require('../api/service.get') 5 | 6 | module.exports = { 7 | pushApiToServerById, 8 | pushApiToServerByIdList, 9 | pushApiToServerByData, 10 | pushApiListToServerByData, 11 | } 12 | 13 | async function pushApiToServerByIdList (apiIds, remoteProjectUid, {force, forceRemove}) { 14 | try { 15 | const apiQuery = { 16 | pageSize: 10000, 17 | pageNo: 0, 18 | } 19 | let apiData = await apiGet.getApiByQuery({_id: {$in: apiIds}}, apiQuery, true) 20 | let list = apiData.list.map(item => { 21 | return { 22 | base: Object.assign({}, item, { model: undefined }), 23 | model: item.model, 24 | } 25 | }) 26 | 27 | return await pushApiListToServerByData(list, remoteProjectUid, { force, forceRemove }) 28 | } catch (e) { 29 | throw e 30 | } 31 | } 32 | 33 | async function pushApiToServerById (apiId, remoteProjectUid, {remoteApiUid, force, forceRemove}) { 34 | try { 35 | let apiData = await apiGet.getApiById(apiId) 36 | if (!apiData) return {code: 1, msg: 'API不存在'} 37 | 38 | let data = { 39 | base: Object.assign({}, apiData, {model: undefined}), 40 | model: apiData.model, 41 | } 42 | 43 | return await pushApiToServerByData(data, remoteProjectUid, { remoteApiUid, force, forceRemove }) 44 | } catch (e) { 45 | throw e 46 | } 47 | } 48 | 49 | async function pushApiToServerByData (apiData, remoteProjectUid, {remoteApiUid, force, forceRemove}) { 50 | try { 51 | const url = '/mock/serverReceiveApi' 52 | const params = { 53 | data: apiData, 54 | project: remoteProjectUid, 55 | apiUid: remoteApiUid, 56 | force, 57 | forceRemove, 58 | } 59 | return await request.put(url, params) 60 | } catch (e) { 61 | throw e 62 | } 63 | } 64 | 65 | async function pushApiListToServerByData (apiList, remoteProjectUid, {force, forceRemove}) { 66 | try { 67 | const url = '/mock/serverReceiveApiList' 68 | const params = { 69 | data: apiList, 70 | project: remoteProjectUid, 71 | force, 72 | forceRemove, 73 | } 74 | return await request.put(url, params) 75 | } catch (e) { 76 | throw e 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/service/sync/service.request.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const db = require('../../database') 3 | const AppBase = db.appBase 4 | const axios = require('axios') 5 | 6 | module.exports = { 7 | getServerInfo, 8 | getRemoteUrl, 9 | request: { 10 | get: axiosGet, 11 | put: axiosPut, 12 | }, 13 | } 14 | 15 | async function getRemoteUrl (url) { 16 | 17 | } 18 | 19 | function getServerInfo () { 20 | return AppBase.cfindOne({}).exec().then(doc => { 21 | doc = doc || {} 22 | let addr = doc.remoteAddress || 'http://localhost:6001' 23 | return addr 24 | }) 25 | .catch(e => { 26 | return 'http://localhost:6001' 27 | }) 28 | } 29 | 30 | function axiosGet (url, params) { 31 | return getServerInfo().then(server => { 32 | let remoteUrl = server + url 33 | return axios.request({url: remoteUrl, params}).then(res => res.data) 34 | }) 35 | } 36 | function axiosPut (url, data) { 37 | return getServerInfo().then(server => { 38 | let remoteUrl = server + url 39 | return axios.request({url: remoteUrl, method: 'PUT', data}).then(res => res.data) 40 | }) 41 | } 42 | 43 | -------------------------------------------------------------------------------- /app/util/combineArray.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = function combineArray (to, from, {toKey = '_id', fromKey = 'id', key = 'key'}) { 3 | if (!Array.isArray(to) || !Array.isArray(from)) return to 4 | to.forEach(function (item) { 5 | item[key] = from.filter((fromItem) => fromItem[fromKey] === item[toKey]) 6 | }) 7 | return to 8 | } 9 | -------------------------------------------------------------------------------- /app/util/definePath.js: -------------------------------------------------------------------------------- 1 | module.exports = function (obj, str, val) { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /app/util/formatCtx.js: -------------------------------------------------------------------------------- 1 | const uaParser = require('ua-parser-js') 2 | const isDev = process.env.NODE_ENV === 'development' 3 | const timer = require('./timer') 4 | 5 | function baseInfo (ctx, option = {}) { 6 | let ua = uaParser(ctx.headers['user-agent']) 7 | 8 | let msg = { 9 | time: timer(), 10 | req: { 11 | method: ctx.method, 12 | path: ctx.path, 13 | url: ctx.url, 14 | href: ctx.request.header['referer'], 15 | }, 16 | ip: ctx.ip, 17 | client: ua, 18 | } 19 | return msg 20 | } 21 | 22 | module.exports = function formatCtx (ctx, message, option = {}) { 23 | let e = option.e 24 | let params = Object.assign({}, ctx.params, ctx.query, ctx.request.body) 25 | 26 | let msg = baseInfo(ctx, option) 27 | msg.reqParsed = params 28 | msg.res = ctx.body 29 | msg.message = message 30 | if (e) { 31 | msg.err = { 32 | msg: e.message, 33 | stack: e.stack, 34 | } 35 | } 36 | 37 | if (!isDev && option.forbidReq) { 38 | const keys = option.forbidReq.split(' ') 39 | keys.forEach(key => { 40 | delete msg.req[key] 41 | }) 42 | } 43 | if (!isDev && option.forbidRes) { 44 | msg = Object.assign({}, msg, {req: undefined}) 45 | } 46 | return msg 47 | } 48 | 49 | module.exports.baseInfo = baseInfo 50 | -------------------------------------------------------------------------------- /app/util/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // const crypto = require('crypto') 3 | let log = require('../service').log 4 | /* 5 | * 设置错误 6 | * */ 7 | /** 8 | * 日志记录。包含内容 9 | * _type: 'out' 10 | * level: 10 日志等级 11 | * time: 时间戳 12 | * data: 内容 13 | * project: project名称 14 | * api: api名称 15 | * apiModel: 分支名称 16 | * projectId: 项目id 17 | * apiId: 接口Id 18 | * apiModelId: 分支id 19 | * req: 请求入参 20 | * reqParse: 转换后入参 21 | * res: 输出参数 22 | * args: process的启动参数 23 | * err: 错误详细信息 24 | * additional: 其他参数 25 | */ 26 | function setError (option) { 27 | let ctx = option.ctx 28 | let next = option.next 29 | let err = option.err 30 | let e = option.e 31 | let code = option.code || -1 32 | 33 | let errObj = { 34 | code: code, 35 | err: err, 36 | } 37 | 38 | let info = { 39 | _type: 'error', 40 | level: 4, 41 | time: +new Date(), 42 | data: err, 43 | req: { 44 | params: ctx.finalParams, 45 | url: ctx.url, 46 | method: ctx.method, 47 | }, 48 | res: errObj, 49 | err: { 50 | msg: String(e), 51 | stack: String(e ? e.stack : ''), 52 | }, 53 | } 54 | 55 | log.childLog(info) 56 | 57 | ctx.body = errObj 58 | return next() 59 | } 60 | 61 | 62 | module.exports = { 63 | setError, 64 | } 65 | -------------------------------------------------------------------------------- /app/util/loadFileList.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | module.exports = function loadFileList (dir, prefix, func) { 5 | let files = fs.readdirSync(dir) 6 | let result = {} 7 | 8 | files.forEach((file) => { 9 | if (path.extname(file) === '.js') { 10 | let name = path.basename(file) 11 | let ctrlName = name.split('.')[1] 12 | if (ctrlName) { 13 | try { 14 | if (func) { 15 | result[ctrlName] = func(path.join(dir, file)) 16 | } else { 17 | result[ctrlName] = require(path.join(dir, file)) 18 | } 19 | } catch (e) { 20 | console.log(e) 21 | console.error(`加载页面出错:${dir}/${file}, ${e.message}`) 22 | } 23 | } 24 | } 25 | }) 26 | return result 27 | } 28 | -------------------------------------------------------------------------------- /app/util/readfiles.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | function getAllFiles (dir, callback) {//获取指定路径下的所有文件名 3 | var filesArr = []; 4 | dir = ///$/.test(dir) ? dir : dir + '/'; 5 | (function dir(dirpath, fn) { 6 | var files = fs.readdirSync(dirpath); 7 | async(files, function (item, next) { 8 | var info = fs.statSync(dirpath + item); 9 | if (info.isDirectory()) { 10 | dir(dirpath + item + '/', function () { 11 | next(); 12 | }); 13 | } else { 14 | filesArr.push(dirpath + item); 15 | callback && callback(dirpath + item); 16 | next(); 17 | } 18 | }, function (err) { 19 | !err && fn && fn(); 20 | }); 21 | })(dir); 22 | return filesArr; 23 | } 24 | 25 | function async (arr, callback1, callback2) { //异步执行任务 26 | if (Object.prototype.toString.call(arr) !== '[object Array]') { 27 | return callback2(new Error('第一个参数必须为数组')); 28 | } 29 | if (arr.length === 0) 30 | return callback2(null); 31 | (function walk(i) { 32 | if (i >= arr.length) { 33 | return callback2(null); 34 | } 35 | callback1(arr[i], function () { 36 | walk(++i); 37 | }); 38 | })(0); 39 | } 40 | 41 | module.exports = getAllFiles -------------------------------------------------------------------------------- /app/util/setGz.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('mz/fs'); 3 | var zlib = require('zlib') 4 | var readFiles = require('./readfiles') 5 | 6 | readFiles('./dist/', async function (file){ 7 | if(/\.gz$/.test(file))return; 8 | var originalFile = fs.createReadStream(file); 9 | var gzFile = fs.WriteStream(file + '.gz') 10 | const output = zlib.createGzip(); 11 | await originalFile.pipe(output).pipe(gzFile); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /app/util/stringify.js: -------------------------------------------------------------------------------- 1 | const isDev = process.env.NODE_ENV === 'development' 2 | const inspect = require('util').inspect 3 | const chalk = require('chalk') 4 | 5 | var prettyObject = isDev ? prettyObjectDev : prettyObjectProd 6 | 7 | function prettyObjectProd (obj) { 8 | if (obj && obj.toJSON) obj = obj.toJSON() 9 | if (typeof obj !== 'object') return String(obj) 10 | if (obj instanceof Error) { 11 | return `${obj.message}\n ${obj.stack}` 12 | } 13 | return JSON.stringify(obj) 14 | } 15 | 16 | function prettyObjectDev (obj) { 17 | if (obj && obj.toJSON) obj = obj.toJSON() 18 | if (typeof obj !== 'object' || !obj || obj instanceof Error || !obj.time) return inspect(obj) 19 | let str = '\n' 20 | for (let key in obj) { 21 | let head = chalk.green(key) 22 | let body = obj[key] 23 | if (body && body.toJSON) body = body.toJSON() 24 | body = inspect(body, {depth: 20, colors: true}) 25 | str += `${head} -> ${body}\n` 26 | } 27 | return str 28 | } 29 | 30 | module.exports = function stringify () { 31 | let str = '' 32 | for (let arg of arguments) { 33 | str += typeof arg === 'object' ? prettyObject(arg) : String(arg) 34 | } 35 | return str 36 | } 37 | 38 | module.exports.stringifyPretty = function stringifyPretty () { 39 | let str = '' 40 | for (let arg of arguments) { 41 | str += typeof arg === 'object' ? prettyObjectDev(arg) : String(arg) 42 | } 43 | return str 44 | } 45 | -------------------------------------------------------------------------------- /app/util/timer.js: -------------------------------------------------------------------------------- 1 | module.exports = function timer (date) { 2 | if (typeof date !== 'object') date = date == null ? new Date() : new Date(date) 3 | if (isNaN(date.getTime())) date = new Date() 4 | date.setMinutes(date.getMinutes() - date.getTimezoneOffset()) 5 | let str = date.toISOString() 6 | return str.slice(0, 10) + ' ' + str.slice(11, 23) 7 | } 8 | 9 | module.exports.formatArrTime = function formatArrTime (arr = [], keys = ['createdAt', 'updatedAt'], len = 16) { 10 | let result = [] 11 | result = arr.map(item => { 12 | if (item.toJSON) item = item.toJSON() 13 | keys.forEach(key => { 14 | if (item[key]) { 15 | item[key] = item[key].slice(0, len) 16 | } 17 | }) 18 | return item 19 | }) 20 | return result 21 | } 22 | -------------------------------------------------------------------------------- /bin/commands/exit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const base = require('./base') 3 | 4 | function killMain () { 5 | return base.sendToServer('killMain', {}) 6 | } 7 | 8 | module.exports = function (commander) { 9 | return base.initAppInfo().then(data => { 10 | if (!data) { 11 | console.log(`mocker服务未启动`) 12 | } else { 13 | killMain().catch(err => { 14 | if (err.code === 'ECONNRESET') { 15 | console.log('主进程已经成功退出') 16 | } else { 17 | throw err 18 | } 19 | }) 20 | } 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /bin/commands/free.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function killPort (port) { 4 | let cmd = process.platform === 'win32' ? 'netstat -ano' : 'ps aux' 5 | let exec = require('child_process').exec 6 | 7 | return new Promise(function (resolve) { 8 | let rd 9 | exec(cmd, function (err, stdout, stderr) { 10 | if (err) { 11 | console.log(err) 12 | resolve(null) 13 | return 14 | } 15 | let lines = stdout.split('\n') 16 | for (let i = 0; i < lines.length; i++) { 17 | let line = lines[i] 18 | let p = line.trim().split(/\s+/) 19 | let address = p[1] 20 | 21 | if (address !== undefined) { 22 | console.log(address.split(':')[1]) 23 | if (address.split(':')[1] == port) { 24 | exec('taskkill /F /pid ' + p[4], function (err, stdout, stderr) { 25 | let msg = err ? '释放指定端口失败!!' : '占用指定端口的程序被成功杀掉!' + p[4] 26 | resolve(msg) 27 | }) 28 | rd = true 29 | } 30 | } 31 | } 32 | if (!rd) resolve('成功杀掉进程') 33 | }) 34 | }) 35 | } 36 | module.exports = function (commander) { 37 | let port = commander.args[0] 38 | if (!port) return console.error('请输入端口号') 39 | killPort(port) 40 | } 41 | -------------------------------------------------------------------------------- /bin/commands/here.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Mocker = require('xmocker') 3 | const stringify = require('../../app/util/stringify').stringifyPretty 4 | function log (debug, msg) { 5 | if (msg.type === 'error' || debug) { 6 | console.log(stringify(msg)) 7 | } 8 | } 9 | 10 | module.exports = function (commander) { 11 | let root = process.cwd() 12 | 13 | let option = { 14 | root, 15 | source: { type: 'json' }, 16 | inject: commander.inject, 17 | port: commander.port || 6003, 18 | proxy404: commander.proxyTo, 19 | proxyMode: 1, 20 | history: commander.history, 21 | } 22 | let mocker = new Mocker(option) 23 | let logger = log.bind(null, commander.debug) 24 | mocker.start(logger).then(function (config) { 25 | console.log('文件服务启动成功, 进程id: ' + mocker.server.pid + ', 端口号:' + config.port) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /bin/commands/list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const base = require('./base') 3 | 4 | function getProjList (name) { 5 | return base.db.project.cfind({}).exec() 6 | } 7 | 8 | function getProjListByNet (option) { 9 | return base.getFromServer('project', option) 10 | } 11 | 12 | module.exports = function list (arg) { 13 | return base.initAppInfo().then(data => { 14 | return data ? getProjListByNet({ pageSize: 1000, pageNo: 0 }) : getProjList() 15 | }).then(data => { 16 | let list = (data.data ? data.data.list : data) || [] 17 | console.log('name \t\t shortcut \t\tstatus') 18 | list.forEach(item => { 19 | console.log(`${item.name} \t\t ${item.shortcut} \t\t ${item.status ? 'running' : 'stopped'}`) 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /bin/commands/restart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const base = require('./base') 3 | function startProjByNet (id, option = {}) { 4 | option.id = id 5 | return base.sendToServer('startProject', option) 6 | } 7 | module.exports = function restart (commander) { 8 | let proj = commander.args[0] 9 | if (!proj) return console.error('请输入要结束的项目名称') 10 | return base.initAppInfo().then(data => { 11 | if (!data) return console.error('主进程尚未启动,请使用start命令') 12 | base.getProjIdByName(proj) 13 | .then((docs) => { 14 | if (docs && docs.length) { 15 | let ids = docs.map(d => d._id).join(',') 16 | startProjByNet(ids, { force: true }) 17 | .then(res => { 18 | console.log(res.message) 19 | }) 20 | } else { 21 | console.log('未找到该项目,请确认输入的名称是否正确') 22 | } 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /bin/commands/start.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const base = require('./base') 3 | 4 | function getProjStateByNet (option) { 5 | return base.getFromServer('state/project', option) 6 | } 7 | 8 | function startProjByNet (id, option = {}) { 9 | option.id = id 10 | return base.sendToServer('startProject', option) 11 | } 12 | 13 | module.exports = function startProject (command) { 14 | let proj = command.args[0] 15 | return base.initAppInfo().then(isStarted => { 16 | if (!isStarted) return base.startServer({ proj }) 17 | if (!proj) return console.error('主进程已启动,如果需要启动项目,请提供项目名') 18 | base.getProjIdByName(proj) 19 | .then((docs) => { 20 | if (docs) { 21 | let ids = docs.map(d => d._id).join(',') 22 | getProjStateByNet({ id: ids }) 23 | .then(data => { 24 | if (data.code) return Promise.resolve(data) 25 | let list = data.data.list 26 | let emptyIds = docs.filter(doc => !list.find(l => l.id === doc._id)) 27 | ids = emptyIds.map(d => d._id).join(',') 28 | if (!ids.length) return Promise.resolve({ message: '指定的项目已经全部启动' }) 29 | return startProjByNet(ids, {}) 30 | }) 31 | .then(res => { 32 | console.log(res.message) 33 | }) 34 | } else { 35 | console.log('未找到该项目,请确认输入的名称是否正确') 36 | } 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /bin/commands/stop.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const base = require('./base') 3 | function stopProjByNet (id) { 4 | return base.sendToServer('stopProject', { id: id, force: true }) 5 | } 6 | module.exports = function kill (commander) { 7 | let proj = commander.args[0] 8 | if (!proj) return console.error('请输入要结束的项目名称') 9 | return base.initAppInfo().then(data => { 10 | if (!data) { 11 | console.log('mocker服务尚未启动') 12 | return 13 | } 14 | base.getProjIdByName(proj) 15 | .then((docs) => { 16 | if (docs) { 17 | let ids = docs.map(d => d._id).join(',') 18 | stopProjByNet(ids) 19 | .then(res => { 20 | console.log(res.message) 21 | }) 22 | } else { 23 | console.log('未找到该项目,请确认输入的名称是否正确') 24 | } 25 | }) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /bin/mocker-exit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/exit') 4 | 5 | program 6 | .parse(process.argv) 7 | 8 | dealer(program) 9 | 10 | -------------------------------------------------------------------------------- /bin/mocker-free.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/free') 4 | 5 | program 6 | .parse(process.argv) 7 | 8 | dealer(program) 9 | -------------------------------------------------------------------------------- /bin/mocker-here.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/here') 4 | 5 | program 6 | .option('-d, --debug', '开启详情记录') 7 | .option('-p, --port ', '端口号') 8 | .option('-t, --proxyTo ', '代理api至指定的服务器') 9 | .option('-i, --inject ', '注入脚本至Html') 10 | .option('-h, --history ', 'history模式') 11 | .parse(process.argv) 12 | 13 | dealer(program) 14 | -------------------------------------------------------------------------------- /bin/mocker-list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/list') 4 | 5 | program 6 | .option('-r, --run', '只列出正在运行的项目') 7 | .parse(process.argv) 8 | 9 | dealer(program) 10 | -------------------------------------------------------------------------------- /bin/mocker-restart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/restart') 4 | 5 | program 6 | .option('-f, --force', '只列出正在运行的项目') 7 | .parse(process.argv) 8 | 9 | dealer(program) 10 | -------------------------------------------------------------------------------- /bin/mocker-start.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/start') 4 | 5 | program 6 | .option('-m, --main', '只启动主进程,不启动任何项目') 7 | .parse(process.argv) 8 | 9 | dealer(program) 10 | -------------------------------------------------------------------------------- /bin/mocker-stop.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const program = require('commander') 3 | const dealer = require('./commands/stop') 4 | 5 | program 6 | .option('-f, --force', '只列出正在运行的项目') 7 | .parse(process.argv) 8 | 9 | dealer(program) 10 | -------------------------------------------------------------------------------- /bin/mocker.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | const program = require('commander') 4 | const pck = require('../package.json') 5 | 6 | program 7 | .version(pck.version) 8 | .command('start ', '启动项目') 9 | .command('stop ', '停止项目') 10 | .command('restart ', '重启项目') 11 | .command('list', '列出项目') 12 | .command('exit', '退出项目') 13 | .command('free ', '杀掉指定端口') 14 | .command('here', '在当前目录启动服务器,更多参数请使用 mocker here -h查看') 15 | .parse(process.argv) 16 | 17 | module.exports = program 18 | 19 | process.on('unhandledRejection', function (e) { 20 | throw e 21 | }) 22 | -------------------------------------------------------------------------------- /doc/guide.md: -------------------------------------------------------------------------------- 1 | ### 页面操作不完全指南 2 | 3 | #### 项目 4 | ##### 项目路径和gulp之间的关系 5 | 项目路径是整个项目的根目录,当未配置时,gulp是不会执行的。配置后, 生成的文件在 项目路径 + gulp配置项中buildPath 下。 6 | 7 | #### 两种情况下API配置 8 | ##### 通过URL判断目标API 9 | 此时,API名称和URL必须相同,API识别路径为空即可 10 | ##### URL固定,通过POST、GET、PUT、DELETE等上传的参数组成的对象进行判断目标API 11 | URL填写固定值即可,在API路径中填写上传参数组成的对象键名,在API名称中填写对应的键值 12 | 如POST-->/api参数: 13 | ~~~javascript 14 | { 15 | func: "someApi", 16 | app: 80, 17 | params: "{\"id\": 3333}" 18 | } 19 | ~~~ 20 | 我们需要根据上传的func进行判断,则填写如下: 21 | ~~~ 22 | api名称: someApi 23 | url: /api 24 | 请求方法: POST 25 | 识别路径: func 26 | ~~~ 27 | 28 | #### 配置返回的数据 29 | ##### 仅需要添加一个数据 30 | ~~~ 31 | 判断条件: 32 | 输入参数模板: {} 33 | 输出参数模板: {} 34 | mock数据: 数据 35 | ~~~ 36 | 37 | ##### 根据上传参数不同返回不同的数据 38 | 如POST-->/api参数: 39 | ~~~javascript 40 | { 41 | func: "someApi", 42 | app: 80, 43 | params: "{\"type\": 0}" 44 | } 45 | ~~~ 46 | 我们需要根据params中type字段不同返回不同的数据 47 | 如type === 0 我们返回 {code: 0, data: {tips: "哈哈,成功了"}} 48 | 其他情况下返回 {code: 0, data: {tips: "这是主干数据"}} 49 | 我们新建一个分支,各项填写如下: 50 | ~~~ 51 | 判断条件: 52 | 输入参数模板: {} 53 | 输出参数模板: {} 54 | mock数据: {code: 0, data: {tips: "这是主干数据"}} 55 | ~~~ 56 | 点击新建分支,添加一个判断数据的分支 57 | ~~~ 58 | 判断条件: params.type === 0 59 | 输入参数模板: {func:{type: 'string'}, params: {type: 'object', child: {type: {type: 'number'}}}} 60 | 输出参数模板: {} 61 | mock数据: {code: 0, data: {tips: "哈哈,成功了"}} 62 | ~~~ 63 | 64 | 判断条件的书写规则是: 65 | 所有从URL上传的参数根据输入参数模板进行格式化后,直接将各参数传给该条件。该判断条件实质上是函数,当脚本中不含有return 时,会自动加上return 66 | return 的值为真,表示符合条件,会返回该api的值。当脚本中含有return 时,直接执行该判断脚本,返回值为真则返回该api的值 67 | 声明新变量或复杂的计算也可写在该脚本中 68 | 69 | 70 | #### 设为当前值、错误、随机、清除的用法 71 | 点击设为当前值后,该分支的值会被设定为api的固定值,不管请求参数如何,均返回该值 72 | 错误值是根据项目的错误信息模板,直接生成对应的错误信息,该API会一直返回该值 73 | 随机是根据输出参数模板生成随机的数据,可使用faker.js中定义的类型进行指定。 74 | 清除会清除上述三种状态 75 | -------------------------------------------------------------------------------- /doc/v1.txt: -------------------------------------------------------------------------------- 1 | 改进要点: 2 | 1. 所有配置在前端全部列出,图形配合JSON串 3 | // 2. 拆分页面至独立文件夹 4 | // 3. 数据库目录存放在 ~ 文件夹下 5 | // 4. 原数据库导入功能 6 | 5. 数据库结构调整,增加各接口间关联功能,包括新增或修改或返回对应值,原各种配置信息修改为config子目录 7 | 6. schema共用,减少输入次数 8 | // 7. 编辑器使用codemirror 9 | 8. mock数据存储添加新表,存储提交类的接口 10 | 9. schema使用标准类型 11 | 10. 自带分页功能 12 | 11. 智能识别Mock对应的schema 13 | // 12. 与fiddler配合实现代理,404都转至指定域名 14 | 13. 错误日志收集 15 | 14. 404转至公用的库或所有的API列表,即extend功能 16 | 15. 数据上传至服务器 17 | 16. 用户系统 18 | 17. api自动生成文档与读取远程文档或本地文档 19 | // 18. 去除随机功能 20 | 19. 抛异常代码404 21 | 20. 自定义快捷消息模板,如code: 401等 22 | 21. 利用代理自动抓取真实server数据,存放于Mock中,并进行相应的提示修改 23 | 22. 数据处理后调用返回函数 24 | 23. 全局公用api 25 | // 24. 快捷捷启动停止退出等 26 | // 25. 延时设置 27 | -- 26. 为项目添加统一前缀 api 28 | 27. 添加eruda方便手机调试,或weinre 29 | 28. 搜索统一 30 | 29. 配合URL链接,生成类似测试的case,用于检测功能是否正常 31 | 32 | api返回数据修改模式 33 | 新建数据表,根据指定api的定位,如 $api['get.getactlist.func'],添加 add 即新数据(参数中传入faker), modify修改指定值,delete删除指定值。每次取数据时从表中获取 34 | api指定关联表 35 | 36 | schema共用模式 37 | 新建schema模板表,模板更新后可选择是否更新到子模板。原表中应该存储模板表中ID 38 | 错误模板共用功能 39 | 新建表 40 | 41 | 42 | 分页 43 | 指定输入的no,size, 输出对应的no, size,定义总页数,复用mock数据中提供的内容,或者使用模板生成数据,暂时使用Mock,使用Mock时添加序号标识 44 | 45 | 数据同步新机制 46 | api仍旧使用uuid 47 | 使同一方法下,同一识别路径,同一名称作为api唯一性判断 48 | 添加识别路径上匹配的字符串,不再使用名称作为判断,可使用中文 49 | 添加api同步时合并功能,项目同步时合并功能 50 | 粒度细分至独立配置项 51 | 52 | 项目同步使用uid, 可单个配置项独立下载 53 | 54 | 55 | 56 | issue 57 | 1. gulp同时只执行一个 58 | 2. 有时修改api数据库没有立即更新 59 | 3. data数据保存为object时,部分obj会报错,需要保存为string类型。 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /doc/v1/api.md: -------------------------------------------------------------------------------- 1 | ## API操作 2 | ### 新增、编辑 3 | API的新增和编辑需要添加API的基础信息和分支。基础信息只包含了判断URL请求是否为当前API。分支则对符合条件的API根据不同的条件进行处理,条件不同,返回不同的数据。 4 | 5 | ### 导入 6 | 点击API列表侧的从其他项目导入可选择其他项目的API进行导入。通常建议使用一个通用的库,而不是相互复制API 7 | 8 | ### 删除 9 | 删除API时会删除API下的分支。 10 | 11 | ## API基础信息参数说明 12 | 13 | ### 名称 14 | - 类型: String 15 | - 默认: '' 16 | - 说明:api的名称,用于检索,与判断api无关系。 17 | 18 | ### 方法 19 | - 类型: 20 | - 默认: POST 21 | - 说明: 当前API指定的方法 22 | 23 | ### URL 24 | - 类型: String 25 | - 默认: /api 26 | - 说明: url路径。规则为:当存在 /:id这种形式,会将提取出来。否则必须url完全相等认为符合当前API,暂不支持正则 27 | 28 | ### 二级路径 29 | - 类型: String 30 | - 默认: func 31 | - 说明: 二级路径是用于当URL相同时,服务端以传入的参数进行区分API的情况。如果是普通API,则此项须为空。 32 | 33 | ### 二级路径字段预期值 34 | - 类型: String 35 | - 默认: '' 36 | - 说明: 二级路径对应的值。当传入参数的二级路径相等时,则认为API符合条件。例如:`二级路径`内容为`func`时,预约值填写的是`test`,请求的URL `/api?func=test`,则当前API为符合条件的API。 37 | 38 | ### 延时设置 39 | - 类型: Number 40 | - 默认: 0 41 | - 说明: 当查找到对应的API分支并进行了处理后,延时多久返回数据。单位为ms。 42 | 43 | ### 描述 44 | - 类型: String 45 | - 默认: '' 46 | - 说明: API的整体描述 47 | 48 | 49 | ## API分支参数说明 50 | 51 | ### 名称 52 | - 类型: String 53 | - 默认: 主干 54 | - 说明: 分支的名称。不能重复!!。 55 | 56 | ### 判断条件 57 | - 类型: String 58 | - 默认: '' 59 | - 示例: 60 | 61 | ```javascript 62 | paramsKey.type == 0 63 | 64 | 或者 65 | paramsKey = JSON.parse(paramsKey) 66 | return paramsKey.type == 0 67 | 68 | ``` 69 | - 说明: 根据输入参数进行判断,返回值为真类型时,认为当前条件符合,不在继续查找;返回假类型数据时认为条件不符合;未填写时,认为当前分支为默认分支,在遍历其他分支未找到时,认为本分支为目标分支。实际上填写的内容是处于一个function之内的内容,没有`return` 关键字则自动添加`return`。所有url中:id形式的参数,从url传过来的search,body中传过来的对象,合在一起形成的对象,将各key传入到入参中。调用为。: 70 | ```javascript 71 | function (...params) 72 | ``` 73 | 74 | - 调用时`this`指向 koa中的`ctx`。 75 | 76 | 77 | ### 输出过滤函数 78 | - 类型: String 79 | - 默认: '' 80 | - 说明: 输出时执行的函数。实现方式与判断条件类型,但传入参数只有两个: params和data。params中URL传入的参数,data是填写的mock数据。返回结果为Object类型时,输入的是函数返回的结果 81 | 82 | ### 输入参数模板 83 | - 类型: Object 84 | - 默认: {} 85 | - 示例 86 | ```javascript 87 | { 88 | "type": "object", 89 | "properties": { 90 | "func": { 91 | "type": "string", 92 | "required": true 93 | }, 94 | "token": { 95 | "type": "string" 96 | }, 97 | "params": { 98 | "type": "object" 99 | } 100 | } 101 | } 102 | ``` 103 | - 说明: 校验输入参数的模板,规则是json-gate类型,具体可见[https://github.com/oferei/json-gate](https://github.com/oferei/json-gate)。 104 | > 注意:本项目对json-gate进行了改写,当输入参数为String类型,而定义的类型为其他,会执行`JSON.parse`命令尝试转换。 105 | 106 | ### 输出参数模板 107 | - 类型: Object 108 | - 默认: {} 109 | - 说明: 与输入参数类似。本参数尚未使用,后续用于生成文档。 110 | 111 | ### mock数据 112 | - 类型: Object 113 | - 默认: {} 114 | - 说明: 要输出的mock数据,对象格式 115 | 116 | -------------------------------------------------------------------------------- /doc/v1/config.md: -------------------------------------------------------------------------------- 1 | ## 修改服务端口 2 | 在配置中修改端口,web页面也会修改访问地址。目前除注入的文件对修改端口有问题,其他都会自动修改。 -------------------------------------------------------------------------------- /doc/v1/guide.md: -------------------------------------------------------------------------------- 1 | ## 安装 2 | 如果不需要使用自带的gulp功能,可以使用全局安装 3 | 4 | - 全局安装 5 | ```bash 6 | npm i -g xmocker-cli 7 | ``` 8 | 9 | - 本地安装 10 | ```bash 11 | git clone https://github.com/wenlonghuo/xmocker-cli.git 12 | cd xmocker-cli 13 | npm link --production 14 | ``` 15 | 16 | ## 启动服务 17 | xmocker注册的全局命令是`mocker`,在命令行中输入: 18 | ```bash 19 | mocker start 20 | ``` 21 | 即启动了mocker服务。然后打开浏览器访问 [http://localhost:6001](http://localhost:6001)。 22 | 23 | ## 新建项目 24 | 25 | - 点击左侧菜单的`项目列表` -> `新建` 26 | 27 | - 填写`项目名称`,`简称`(用于快速启动),`端口号` 28 | 29 | - 点击提交。 30 | 31 | ## 新建API 32 | 33 | - 点击左侧菜单的`项目列表`,点击右侧项目的标题,进入项目详情页 34 | 35 | - 点击API列表栏上的`+新建`,进入新增API页面 36 | 37 | - 在左侧的基础信息中,填写`名称`、`URL`、`描述`,选择`方法`,如果同一URL判断是根据输入参数进行的(如根据输入参数的 `func`判断API名称),则填写`二级路径`和`二级路径字段预期值`。点击提交 38 | 39 | - 在右侧的API分支列表中填写`名称`和`mock数据`(json数据),点击提交 40 | 41 | - 完成了API的创建 42 | 43 | ## 访问 44 | 45 | - 访问 http://localhost:配置的端口号 和相应的URL,即可获取之前填入的`mock数据` 46 | 47 | ## 为项目设置代理服务器 48 | - 在编辑项目信息中,配置`代理`一项。即可将指定路径代理到对应的服务器上 49 | 50 | ```javascript 51 | [ 52 | { 53 | "api": "/official/v1/*", 54 | "target": "http://xxxx.com" 55 | }, 56 | ] 57 | ``` 58 | -------------------------------------------------------------------------------- /doc/v1/log.md: -------------------------------------------------------------------------------- 1 | ## 日志搜索说明 2 | 每个输入框之间是 &&的关系,每个输入框内对于不同的设备之类的是 || 的关系 -------------------------------------------------------------------------------- /doc/v1/sync.md: -------------------------------------------------------------------------------- 1 | ## 同步操作 2 | ### 下载项目 3 | 多人协作时,建议同一个人新建项目及API,其他人下载项目即可。步骤: 4 | - 在配置中填写服务器地址:http://192.168.xx.xx:6001 5 | - 点击同步页面,点下载即可 6 | 7 | ### 下载API 8 | 点击项目下的打开,选中API,下载即可。 9 | 10 | ## 说明 11 | ### 分类说明 12 | 共分为6类。判断是否相同是根据该API的修改时间决定。每个API在创建的时候会生成一个独立的uid,所以不同人创建不必然不同。为同步需要,API创建是同一个人时同步才会生效。其他人建议用同步的方式复制。 13 | - 落后于服务端 14 | - 领先于服务端 15 | - 本机独有 16 | - 服务端独有 17 | - 无变化项目 18 | - 未知项目 19 | 20 | -------------------------------------------------------------------------------- /lower/api-schemas/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _keys = require('babel-runtime/core-js/object/keys'); 4 | 5 | var _keys2 = _interopRequireDefault(_keys); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8 | 9 | var loadFileList = require('../util/loadFileList'); 10 | var schema = {}; 11 | loadFileList(__dirname, 'schema', function (file) { 12 | var obj = require(file); 13 | (0, _keys2.default)(obj).forEach(function (type) { 14 | if (!schema[type]) schema[type] = {}; 15 | (0, _keys2.default)(obj[type]).forEach(function (name) { 16 | schema[type][name] = obj[type][name]; 17 | }); 18 | }); 19 | return obj; 20 | }); 21 | 22 | module.exports = schema; -------------------------------------------------------------------------------- /lower/api-schemas/schema.appBase.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get: {}, 5 | post: {}, 6 | put: {}, 7 | delete: {} 8 | }; 9 | module.exports.get.online = {}; 10 | 11 | module.exports.get.appBase = {}; 12 | 13 | module.exports.get.appStatus = {}; 14 | 15 | module.exports.put.upgradeV0 = {}; 16 | 17 | module.exports.put.appBase = { 18 | type: 'object', 19 | properties: { 20 | remoteAddress: { 21 | type: 'string', 22 | title: '远程访问地址' 23 | }, 24 | managePort: { 25 | type: 'number', 26 | title: '系统管理端口' 27 | } 28 | } 29 | }; -------------------------------------------------------------------------------- /lower/api-schemas/schema.error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get: {}, 5 | post: {}, 6 | put: {}, 7 | delete: {} 8 | }; 9 | 10 | module.exports.post.errorUpload = { 11 | type: 'object', 12 | properties: { 13 | href: { 14 | type: 'string' 15 | }, 16 | ua: { 17 | type: 'string' 18 | }, 19 | message: { 20 | type: 'string' 21 | }, 22 | source: { 23 | type: 'string' 24 | }, 25 | lineno: { 26 | type: 'string' 27 | }, 28 | colno: { 29 | type: 'string' 30 | }, 31 | stack: { 32 | type: 'string' 33 | } 34 | } 35 | }; -------------------------------------------------------------------------------- /lower/api-schemas/schema.lib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get: {}, 5 | post: {}, 6 | put: {}, 7 | delete: {} 8 | }; 9 | 10 | module.exports.get.libDetail = { 11 | type: 'object', 12 | properties: { 13 | id: { 14 | type: 'string', 15 | required: true 16 | } 17 | } 18 | }; 19 | module.exports.get.lib = { 20 | type: 'object', 21 | properties: { 22 | type: { 23 | type: 'string' 24 | }, 25 | projs: { 26 | type: 'array', 27 | title: '指定的项目' 28 | }, 29 | pageSize: { 30 | type: 'number' 31 | }, 32 | pageNo: { 33 | type: 'number' 34 | } 35 | } 36 | }; 37 | 38 | module.exports.get.searchLib = { 39 | type: 'object', 40 | properties: { 41 | words: { 42 | type: 'string', 43 | minLength: 1 44 | }, 45 | type: { 46 | type: 'string', 47 | minLength: 1 48 | }, 49 | pageSize: { 50 | type: 'number', 51 | required: true 52 | }, 53 | pageNo: { 54 | type: 'number', 55 | required: true 56 | } 57 | } 58 | }; 59 | 60 | module.exports.post.lib = { 61 | type: 'object', 62 | properties: { 63 | name: { 64 | type: 'string', 65 | required: true, 66 | minLength: 1, 67 | title: '名称' 68 | }, 69 | type: { 70 | type: 'string', 71 | required: true, 72 | title: '类型' 73 | }, 74 | description: { 75 | type: 'string', 76 | title: '说明' 77 | }, 78 | model: { 79 | type: 'object', 80 | title: '模板' 81 | }, 82 | projs: { 83 | type: 'array', 84 | title: '归属于哪个项目' 85 | } 86 | } 87 | }; 88 | 89 | module.exports.put.lib = { 90 | type: 'object', 91 | properties: { 92 | id: { 93 | type: 'string', 94 | required: true, 95 | minLength: 1 96 | }, 97 | name: { 98 | type: 'string', 99 | required: true, 100 | minLength: 1, 101 | title: '名称' 102 | }, 103 | type: { 104 | type: 'string', 105 | required: true, 106 | title: '类型' 107 | }, 108 | description: { 109 | type: 'string', 110 | title: '说明' 111 | }, 112 | model: { 113 | type: 'object', 114 | title: '模板' 115 | }, 116 | projs: { 117 | type: 'array', 118 | title: '归属于哪个项目' 119 | } 120 | } 121 | }; 122 | 123 | module.exports.delete.lib = { 124 | type: 'object', 125 | properties: { 126 | id: { 127 | type: 'string', 128 | required: true, 129 | minLength: 1 130 | } 131 | } 132 | }; -------------------------------------------------------------------------------- /lower/api-schemas/schema.log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get: {}, 5 | post: {}, 6 | put: {}, 7 | delete: {} 8 | }; 9 | 10 | module.exports.get.log = { 11 | type: 'object', 12 | properties: { 13 | type: { 14 | type: 'string', 15 | minLength: 1 16 | }, 17 | project: { 18 | type: 'string' 19 | }, 20 | api: { 21 | type: 'string' 22 | }, 23 | apiModel: { 24 | type: 'string' 25 | }, 26 | ip: { 27 | type: 'string' 28 | }, 29 | client: { 30 | type: 'object' 31 | }, 32 | pageSize: { 33 | type: 'number', 34 | default: 20, 35 | required: true 36 | }, 37 | pageNo: { 38 | type: 'number', 39 | default: 0, 40 | required: true 41 | } 42 | } 43 | }; -------------------------------------------------------------------------------- /lower/api-schemas/schema.search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get: {}, 5 | post: {}, 6 | put: {}, 7 | delete: {} 8 | }; 9 | 10 | module.exports.get.search = { 11 | type: 'object', 12 | properties: { 13 | type: { 14 | type: 'string' 15 | }, 16 | keyword: { 17 | type: 'string' 18 | }, 19 | pageSize: { 20 | type: 'number', 21 | default: 20, 22 | required: true 23 | }, 24 | pageNo: { 25 | type: 'number', 26 | default: 0, 27 | required: true 28 | } 29 | } 30 | }; -------------------------------------------------------------------------------- /lower/controller/controller.dynData.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /lower/controller/controller.error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _regenerator = require('babel-runtime/regenerator'); 4 | 5 | var _regenerator2 = _interopRequireDefault(_regenerator); 6 | 7 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); 8 | 9 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 10 | 11 | var errorUpload = function () { 12 | var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) { 13 | var finalParams, data; 14 | return _regenerator2.default.wrap(function _callee$(_context) { 15 | while (1) { 16 | switch (_context.prev = _context.next) { 17 | case 0: 18 | finalParams = ctx.finalParams; 19 | _context.prev = 1; 20 | data = formatCtx(ctx, finalParams.message); 21 | 22 | data.err = { 23 | msg: finalParams.message, 24 | source: finalParams.source, 25 | lineno: finalParams.lineno, 26 | stack: finalParams.stack, 27 | colno: finalParams.colno 28 | }; 29 | _context.next = 6; 30 | return db.collectorDB.insert(data); 31 | 32 | case 6: 33 | broadcast({ type: 'log', action: 'ADD_LOGS', logType: 'collector', data: data }); 34 | _context.next = 12; 35 | break; 36 | 37 | case 9: 38 | _context.prev = 9; 39 | _context.t0 = _context['catch'](1); 40 | return _context.abrupt('return', ctx.respond.error('保存错误信息出错', { e: _context.t0 })); 41 | 42 | case 12: 43 | ctx.respond.success('提交成功'); 44 | 45 | case 13: 46 | case 'end': 47 | return _context.stop(); 48 | } 49 | } 50 | }, _callee, this, [[1, 9]]); 51 | })); 52 | 53 | return function errorUpload(_x, _x2) { 54 | return _ref.apply(this, arguments); 55 | }; 56 | }(); 57 | 58 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 59 | 60 | var db = require('../database'); 61 | var formatCtx = require('../util/formatCtx'); 62 | var broadcast = require('../service/service.ws').broadcast; 63 | 64 | module.exports = { 65 | errorUpload: errorUpload 66 | }; -------------------------------------------------------------------------------- /lower/database/v0/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Datastore = require('../promiseNeDb'); 4 | var path = require('path'); 5 | 6 | var apiBase = new Datastore({ filename: path.join(__dirname, '../../../db/api/base'), autoload: true }); 7 | 8 | var apiModel = new Datastore({ filename: path.join(__dirname, '../../../db/api/model'), autoload: true }); 9 | 10 | var appBase = new Datastore({ filename: path.join(__dirname, '../../../db/app/base'), autoload: true }); 11 | 12 | var appProject = new Datastore({ filename: path.join(__dirname, '../../../db/app/project'), autoload: true }); 13 | 14 | module.exports = { 15 | apiBase: apiBase, 16 | apiModel: apiModel, 17 | appBase: appBase, 18 | appProject: appProject 19 | }; -------------------------------------------------------------------------------- /lower/middleware/respond.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _regenerator = require('babel-runtime/regenerator'); 4 | 5 | var _regenerator2 = _interopRequireDefault(_regenerator); 6 | 7 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); 8 | 9 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 10 | 11 | var _assign = require('babel-runtime/core-js/object/assign'); 12 | 13 | var _assign2 = _interopRequireDefault(_assign); 14 | 15 | var _typeof2 = require('babel-runtime/helpers/typeof'); 16 | 17 | var _typeof3 = _interopRequireDefault(_typeof2); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | var version = 2; 22 | 23 | function logError(ctx, code, msg) { 24 | var option = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; 25 | 26 | if ((typeof code === 'undefined' ? 'undefined' : (0, _typeof3.default)(code)) === 'object' && code.code != null) { 27 | ctx.body = (0, _assign2.default)({}, code, { message: code.msg }); 28 | return; 29 | } 30 | if ((typeof msg === 'undefined' ? 'undefined' : (0, _typeof3.default)(msg)) === 'object' || msg == null) { 31 | option = msg || {}; 32 | msg = code; 33 | code = -1; 34 | } 35 | ctx.info.e = option.e; 36 | ctx.body = { 37 | code: code, 38 | message: msg, 39 | data: option.data, 40 | version: version 41 | }; 42 | } 43 | 44 | function success(ctx, msg, data) { 45 | ctx.info.message = msg; 46 | ctx.body = { 47 | code: 0, 48 | message: msg, 49 | data: data, 50 | version: version 51 | }; 52 | } 53 | 54 | module.exports = function () { 55 | return function () { 56 | var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) { 57 | return _regenerator2.default.wrap(function _callee$(_context) { 58 | while (1) { 59 | switch (_context.prev = _context.next) { 60 | case 0: 61 | ctx.info = ctx.info || {}; 62 | ctx.respond = ctx.respond || {}; 63 | ctx.respond.success = success.bind(ctx, ctx); 64 | ctx.respond.error = logError.bind(ctx, ctx); 65 | return _context.abrupt('return', next()); 66 | 67 | case 5: 68 | case 'end': 69 | return _context.stop(); 70 | } 71 | } 72 | }, _callee, this); 73 | })); 74 | 75 | function sendMessage(_x2, _x3) { 76 | return _ref.apply(this, arguments); 77 | } 78 | 79 | return sendMessage; 80 | }(); 81 | }; -------------------------------------------------------------------------------- /lower/plugin/json-gate/json-gate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var validateSchema = require('./valid-schema'), 4 | validateObject = require('./valid-object'), 5 | formatObject = require('./format-object'); 6 | 7 | var Schema = function Schema(schema) { 8 | this.schema = schema; 9 | validateSchema(schema); 10 | 11 | this.validate = function (obj, done) { 12 | validateObject(obj, schema, done); 13 | }; 14 | this.format = function (obj, done) { 15 | formatObject(obj, schema, done); 16 | }; 17 | }; 18 | 19 | module.exports.createSchema = function (schema) { 20 | return new Schema(schema); 21 | }; -------------------------------------------------------------------------------- /lower/router/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var router = require('koa-router')(); 5 | router.prefix('/mock'); 6 | var loadFileList = require('../util/loadFileList'); 7 | var apiSchema = require('../api-schemas'); 8 | 9 | router.formatParam = require('../middleware/formatParam')(apiSchema); 10 | 11 | router.controller = loadFileList(path.join(__dirname, '../controller'), 'controller'); 12 | 13 | module.exports = router; 14 | loadFileList(__dirname, 'router'); -------------------------------------------------------------------------------- /lower/router/router.apiBase.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.apiBase; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/apiDetail', formatParam, controller.getApiDetail); 8 | 9 | router.get('/api', formatParam, controller.getApiList); 10 | 11 | router.get('/apiBase', formatParam, controller.getApiBase); 12 | 13 | router.get('/search/api', formatParam, controller.searchApiBase); 14 | 15 | router.post('/apiBase', formatParam, controller.addApiBase); 16 | 17 | router.put('/apiBase', formatParam, controller.editApiBase); 18 | 19 | router.delete('/api', formatParam, controller.deleteApiBase); 20 | 21 | router.put('/copyApi', formatParam, controller.copyApi); 22 | 23 | router.put('/apiStatus', formatParam, controller.setApiStatus); -------------------------------------------------------------------------------- /lower/router/router.apiModel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.apiModel; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/apiModelList', formatParam, controller.getApiModelList); 8 | 9 | router.get('/apiModel', formatParam, controller.getApiModel); 10 | 11 | router.post('/apiModel', formatParam, controller.addApiModel); 12 | 13 | router.put('/apiModel', formatParam, controller.editApiModel); 14 | 15 | router.delete('/apiModel', formatParam, controller.deleteApiModel); -------------------------------------------------------------------------------- /lower/router/router.appBase.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _regenerator = require('babel-runtime/regenerator'); 4 | 5 | var _regenerator2 = _interopRequireDefault(_regenerator); 6 | 7 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); 8 | 9 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | var router = require('./index.js'); 14 | var controller = router.controller.appBase; 15 | var formatParam = router.formatParam; 16 | var inspect = require('util').inspect; 17 | 18 | router.get('/online', controller.online); 19 | 20 | router.get('/reloadDatabase', controller.reloadDatabase); 21 | 22 | router.get('/appBase', formatParam, controller.getAppBase); 23 | 24 | router.put('/appBase', formatParam, controller.editAppBase); 25 | 26 | router.put('/upgradeV0', formatParam, controller.upgradeFromV0); 27 | 28 | router.put('/killMain', function () { 29 | var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) { 30 | return _regenerator2.default.wrap(function _callee$(_context) { 31 | while (1) { 32 | switch (_context.prev = _context.next) { 33 | case 0: 34 | process.exit(1); 35 | 36 | case 1: 37 | case 'end': 38 | return _context.stop(); 39 | } 40 | } 41 | }, _callee, this); 42 | })); 43 | 44 | return function (_x, _x2) { 45 | return _ref.apply(this, arguments); 46 | }; 47 | }()); 48 | 49 | router.get('/appStatus', formatParam, controller.getAppStatus); 50 | 51 | router.post('/error', function () { 52 | var _ref2 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(ctx, next) { 53 | var time; 54 | return _regenerator2.default.wrap(function _callee2$(_context2) { 55 | while (1) { 56 | switch (_context2.prev = _context2.next) { 57 | case 0: 58 | time = new Date().toLocaleTimeString(); 59 | 60 | console.log('\n[' + time + '] \u7F51\u9875\u51FA\u73B0\u9519\u8BEF\u5566\uFF0C\u4E0B\u9762\u662F\u9519\u8BEF\u4FE1\u606F ~~~'); 61 | console.log(inspect(ctx.request.body, { colors: true, depth: null })); 62 | ctx.body = 'ok'; 63 | 64 | case 4: 65 | case 'end': 66 | return _context2.stop(); 67 | } 68 | } 69 | }, _callee2, this); 70 | })); 71 | 72 | return function (_x3, _x4) { 73 | return _ref2.apply(this, arguments); 74 | }; 75 | }()); -------------------------------------------------------------------------------- /lower/router/router.commonData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.commonData; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/commonData', formatParam, controller.getCommonData); 8 | 9 | router.get('/searchCommonData', formatParam, controller.searchCommonData); 10 | 11 | router.post('/commonData', formatParam, controller.addCommonData); 12 | 13 | router.put('/commonData', formatParam, controller.editCommonData); 14 | 15 | router.delete('/commonData', formatParam, controller.deleteCommonData); -------------------------------------------------------------------------------- /lower/router/router.error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.error; 5 | var formatParam = router.formatParam; 6 | 7 | router.post('/error/upload', formatParam, controller.errorUpload); -------------------------------------------------------------------------------- /lower/router/router.lib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.lib; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/libDetail', formatParam, controller.getLibDetail); 8 | 9 | router.get('/lib', formatParam, controller.getLibList); 10 | 11 | router.post('/lib', formatParam, controller.addLib); 12 | 13 | router.put('/lib', formatParam, controller.editLib); 14 | 15 | router.delete('/lib', formatParam, controller.deleteLib); -------------------------------------------------------------------------------- /lower/router/router.log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.log; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/log', formatParam, controller.searchLog); -------------------------------------------------------------------------------- /lower/router/router.project.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.project; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/project', formatParam, controller.getProject); 8 | 9 | router.get('/state/project', formatParam, controller.getRunningProject); 10 | 11 | router.post('/project', formatParam, controller.addProject); 12 | 13 | router.put('/project', formatParam, controller.editProject); 14 | 15 | router.delete('/project', formatParam, controller.deleteProject); 16 | 17 | router.put('/startProject', formatParam, controller.startProject); 18 | 19 | router.put('/stopProject', formatParam, controller.stopProject); 20 | 21 | router.put('/setDefaultApiParam', formatParam, controller.setDefaultApiParam); -------------------------------------------------------------------------------- /lower/router/router.search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.search; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/search', formatParam, controller.search); -------------------------------------------------------------------------------- /lower/router/router.sync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var router = require('./index.js'); 4 | var controller = router.controller.sync; 5 | var formatParam = router.formatParam; 6 | 7 | router.get('/clientGetProjList', formatParam, controller.clientGetProjList); 8 | 9 | router.get('/clientGetProjDetail', formatParam, controller.clientGetProjDetail); 10 | 11 | router.get('/clientGetApiListByProject', formatParam, controller.clientGetApiListByProject); 12 | 13 | router.get('/clientGetApiListByIds', formatParam, controller.clientGetApiListByIds); 14 | 15 | router.get('/clientGetProjDiff', formatParam, controller.clientGetProjDiff); 16 | 17 | router.get('/clientGetApiDiff', formatParam, controller.clientGetApiDiff); 18 | 19 | router.put('/clientDownLoadProj', formatParam, controller.clientDownLoadProj); 20 | 21 | router.put('/clientDownLoadProjBase', formatParam, controller.clientDownLoadProjBase); 22 | 23 | router.put('/clientDownLoadApi', formatParam, controller.clientDownLoadApi); 24 | 25 | router.put('/clientPushApiListById', formatParam, controller.clientPushApiListById); 26 | 27 | router.put('/clientPushApiById', formatParam, controller.clientPushApiById); 28 | 29 | router.put('/clientPushApiByData', formatParam, controller.clientPushApiByData); 30 | 31 | router.put('/clientPushApiListByData', formatParam, controller.clientPushApiListByData); 32 | 33 | router.put('/serverReceiveApi', formatParam, controller.serverReceiveApi); 34 | 35 | router.put('/serverReceiveApiList', formatParam, controller.serverReceiveApiList); 36 | 37 | router.get('/serverGetProjList', formatParam, controller.serverGetProjList); 38 | 39 | router.get('/serverGetProj', formatParam, controller.serverGetProj); 40 | 41 | router.get('/serverGetApiListByProject', formatParam, controller.serverGetApiListByProject); 42 | 43 | router.get('/serverGetApi', formatParam, controller.serverGetApi); 44 | 45 | router.put('/serverDiffProj', formatParam, controller.serverDiffProj); 46 | 47 | router.put('/serverDiffApi', formatParam, controller.serverDiffApi); -------------------------------------------------------------------------------- /lower/service/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var loadFileList = require('../util/loadFileList'); 5 | 6 | module.exports = loadFileList(path.join(__dirname, './')); -------------------------------------------------------------------------------- /lower/service/service.log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _stringify = require('babel-runtime/core-js/json/stringify'); 4 | 5 | var _stringify2 = _interopRequireDefault(_stringify); 6 | 7 | var _typeof2 = require('babel-runtime/helpers/typeof'); 8 | 9 | var _typeof3 = _interopRequireDefault(_typeof2); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | var through2 = require('through2'); 14 | 15 | var broadcast = require('./service.ws').broadcast; 16 | 17 | var errStream = through2(function (chunk, enc, cb) { 18 | process.stdout.write(chunk); 19 | cb(null, chunk); 20 | }); 21 | 22 | function mocker(data) { 23 | if ((typeof data === 'undefined' ? 'undefined' : (0, _typeof3.default)(data)) === 'object') { 24 | if (data.res && (0, _typeof3.default)(data.res) === 'object') { 25 | data.res = (0, _stringify2.default)(data.res); 26 | } 27 | broadcast({ type: 'log', action: 'ADD_LOGS', logType: data.type, data: data }); 28 | 29 | var type = data.type === 'his' ? 'info' : data.type; 30 | if (logger[type]) { 31 | logger[type](data); 32 | } 33 | } 34 | } 35 | 36 | module.exports = function (msg) { 37 | process.stdout.write(msg); 38 | }; 39 | 40 | module.exports.errStream = errStream; 41 | module.exports.mocker = mocker; -------------------------------------------------------------------------------- /lower/service/service.ws.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _stringify = require('babel-runtime/core-js/json/stringify'); 4 | 5 | var _stringify2 = _interopRequireDefault(_stringify); 6 | 7 | var _typeof2 = require('babel-runtime/helpers/typeof'); 8 | 9 | var _typeof3 = _interopRequireDefault(_typeof2); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | var WebSocket = require('ws'); 14 | var wss = void 0; 15 | 16 | function incoming(client, msg) { 17 | try { 18 | msg = JSON.parse(msg); 19 | } catch (e) { 20 | return; 21 | } 22 | } 23 | 24 | function broadcast(data) { 25 | if ((typeof data === 'undefined' ? 'undefined' : (0, _typeof3.default)(data)) === 'object') data = (0, _stringify2.default)(data); 26 | wss.clients.forEach(function (client) { 27 | if (client.readyState === 1) { 28 | client.send(data); 29 | } 30 | }); 31 | } 32 | 33 | module.exports = function (httpServer) { 34 | wss = new WebSocket.Server({ server: httpServer }); 35 | wss.on('connection', function wsConnect(wsClient) { 36 | wsClient.on('message', incoming.bind(null, wsClient)); 37 | }); 38 | }; 39 | 40 | module.exports.broadcast = broadcast; -------------------------------------------------------------------------------- /lower/service/sync/service.request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _regenerator = require('babel-runtime/regenerator'); 4 | 5 | var _regenerator2 = _interopRequireDefault(_regenerator); 6 | 7 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); 8 | 9 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 10 | 11 | var getRemoteUrl = function () { 12 | var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(url) { 13 | return _regenerator2.default.wrap(function _callee$(_context) { 14 | while (1) { 15 | switch (_context.prev = _context.next) { 16 | case 0: 17 | case 'end': 18 | return _context.stop(); 19 | } 20 | } 21 | }, _callee, this); 22 | })); 23 | 24 | return function getRemoteUrl(_x) { 25 | return _ref.apply(this, arguments); 26 | }; 27 | }(); 28 | 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 30 | 31 | var db = require('../../database'); 32 | var AppBase = db.appBase; 33 | var axios = require('axios'); 34 | 35 | module.exports = { 36 | getServerInfo: getServerInfo, 37 | getRemoteUrl: getRemoteUrl, 38 | request: { 39 | get: axiosGet, 40 | put: axiosPut 41 | } 42 | }; 43 | 44 | function getServerInfo() { 45 | return AppBase.cfindOne({}).exec().then(function (doc) { 46 | doc = doc || {}; 47 | var addr = doc.remoteAddress || 'http://localhost:6001'; 48 | return addr; 49 | }).catch(function (e) { 50 | return 'http://localhost:6001'; 51 | }); 52 | } 53 | 54 | function axiosGet(url, params) { 55 | return getServerInfo().then(function (server) { 56 | var remoteUrl = server + url; 57 | return axios.request({ url: remoteUrl, params: params }).then(function (res) { 58 | return res.data; 59 | }); 60 | }); 61 | } 62 | function axiosPut(url, data) { 63 | return getServerInfo().then(function (server) { 64 | var remoteUrl = server + url; 65 | return axios.request({ url: remoteUrl, method: 'PUT', data: data }).then(function (res) { 66 | return res.data; 67 | }); 68 | }); 69 | } -------------------------------------------------------------------------------- /lower/util/combineArray.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function combineArray(to, from, _ref) { 4 | var _ref$toKey = _ref.toKey, 5 | toKey = _ref$toKey === undefined ? '_id' : _ref$toKey, 6 | _ref$fromKey = _ref.fromKey, 7 | fromKey = _ref$fromKey === undefined ? 'id' : _ref$fromKey, 8 | _ref$key = _ref.key, 9 | key = _ref$key === undefined ? 'key' : _ref$key; 10 | 11 | if (!Array.isArray(to) || !Array.isArray(from)) return to; 12 | to.forEach(function (item) { 13 | item[key] = from.filter(function (fromItem) { 14 | return fromItem[fromKey] === item[toKey]; 15 | }); 16 | }); 17 | return to; 18 | }; -------------------------------------------------------------------------------- /lower/util/definePath.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function (obj, str, val) {}; -------------------------------------------------------------------------------- /lower/util/formatCtx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _assign = require('babel-runtime/core-js/object/assign'); 4 | 5 | var _assign2 = _interopRequireDefault(_assign); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8 | 9 | var uaParser = require('ua-parser-js'); 10 | var isDev = process.env.NODE_ENV === 'development'; 11 | var timer = require('./timer'); 12 | 13 | function baseInfo(ctx) { 14 | var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 15 | 16 | var ua = uaParser(ctx.headers['user-agent']); 17 | 18 | var msg = { 19 | time: timer(), 20 | req: { 21 | method: ctx.method, 22 | path: ctx.path, 23 | url: ctx.url, 24 | href: ctx.request.header['referer'] 25 | }, 26 | ip: ctx.ip, 27 | client: ua 28 | }; 29 | return msg; 30 | } 31 | 32 | module.exports = function formatCtx(ctx, message) { 33 | var option = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 34 | 35 | var e = option.e; 36 | var params = (0, _assign2.default)({}, ctx.params, ctx.query, ctx.request.body); 37 | 38 | var msg = baseInfo(ctx, option); 39 | msg.reqParsed = params; 40 | msg.res = ctx.body; 41 | msg.message = message; 42 | if (e) { 43 | msg.err = { 44 | msg: e.message, 45 | stack: e.stack 46 | }; 47 | } 48 | 49 | if (!isDev && option.forbidReq) { 50 | var keys = option.forbidReq.split(' '); 51 | keys.forEach(function (key) { 52 | delete msg.req[key]; 53 | }); 54 | } 55 | if (!isDev && option.forbidRes) { 56 | msg = (0, _assign2.default)({}, msg, { req: undefined }); 57 | } 58 | return msg; 59 | }; 60 | 61 | module.exports.baseInfo = baseInfo; -------------------------------------------------------------------------------- /lower/util/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var log = require('../service').log; 4 | 5 | function setError(option) { 6 | var ctx = option.ctx; 7 | var next = option.next; 8 | var err = option.err; 9 | var e = option.e; 10 | var code = option.code || -1; 11 | 12 | var errObj = { 13 | code: code, 14 | err: err 15 | }; 16 | 17 | var info = { 18 | _type: 'error', 19 | level: 4, 20 | time: +new Date(), 21 | data: err, 22 | req: { 23 | params: ctx.finalParams, 24 | url: ctx.url, 25 | method: ctx.method 26 | }, 27 | res: errObj, 28 | err: { 29 | msg: String(e), 30 | stack: String(e ? e.stack : '') 31 | } 32 | }; 33 | 34 | log.childLog(info); 35 | 36 | ctx.body = errObj; 37 | return next(); 38 | } 39 | 40 | module.exports = { 41 | setError: setError 42 | }; -------------------------------------------------------------------------------- /lower/util/loadFileList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | module.exports = function loadFileList(dir, prefix, func) { 7 | var files = fs.readdirSync(dir); 8 | var result = {}; 9 | 10 | files.forEach(function (file) { 11 | if (path.extname(file) === '.js') { 12 | var name = path.basename(file); 13 | var ctrlName = name.split('.')[1]; 14 | if (ctrlName) { 15 | try { 16 | if (func) { 17 | result[ctrlName] = func(path.join(dir, file)); 18 | } else { 19 | result[ctrlName] = require(path.join(dir, file)); 20 | } 21 | } catch (e) { 22 | console.log(e); 23 | console.error('\u52A0\u8F7D\u9875\u9762\u51FA\u9519\uFF1A' + dir + '/' + file + ', ' + e.message); 24 | } 25 | } 26 | } 27 | }); 28 | return result; 29 | }; -------------------------------------------------------------------------------- /lower/util/readfiles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | function getAllFiles(dir, callback) { 5 | var filesArr = []; 6 | dir = function dir(dirpath, fn) { 7 | var files = fs.readdirSync(dirpath); 8 | async(files, function (item, next) { 9 | var info = fs.statSync(dirpath + item); 10 | if (info.isDirectory()) { 11 | dir(dirpath + item + '/', function () { 12 | next(); 13 | }); 14 | } else { 15 | filesArr.push(dirpath + item); 16 | callback && callback(dirpath + item); 17 | next(); 18 | } 19 | }, function (err) { 20 | !err && fn && fn(); 21 | }); 22 | }(dir); 23 | return filesArr; 24 | } 25 | 26 | function async(arr, callback1, callback2) { 27 | if (Object.prototype.toString.call(arr) !== '[object Array]') { 28 | return callback2(new Error('第一个参数必须为数组')); 29 | } 30 | if (arr.length === 0) return callback2(null); 31 | (function walk(i) { 32 | if (i >= arr.length) { 33 | return callback2(null); 34 | } 35 | callback1(arr[i], function () { 36 | walk(++i); 37 | }); 38 | })(0); 39 | } 40 | 41 | module.exports = getAllFiles; -------------------------------------------------------------------------------- /lower/util/setGz.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _regenerator = require('babel-runtime/regenerator'); 4 | 5 | var _regenerator2 = _interopRequireDefault(_regenerator); 6 | 7 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); 8 | 9 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | var fs = require('mz/fs'); 14 | var zlib = require('zlib'); 15 | var readFiles = require('./readfiles'); 16 | 17 | readFiles('./dist/', function () { 18 | var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(file) { 19 | var originalFile, gzFile, output; 20 | return _regenerator2.default.wrap(function _callee$(_context) { 21 | while (1) { 22 | switch (_context.prev = _context.next) { 23 | case 0: 24 | if (!/\.gz$/.test(file)) { 25 | _context.next = 2; 26 | break; 27 | } 28 | 29 | return _context.abrupt('return'); 30 | 31 | case 2: 32 | originalFile = fs.createReadStream(file); 33 | gzFile = fs.WriteStream(file + '.gz'); 34 | output = zlib.createGzip(); 35 | _context.next = 7; 36 | return originalFile.pipe(output).pipe(gzFile); 37 | 38 | case 7: 39 | case 'end': 40 | return _context.stop(); 41 | } 42 | } 43 | }, _callee, this); 44 | })); 45 | 46 | return function (_x) { 47 | return _ref.apply(this, arguments); 48 | }; 49 | }()); -------------------------------------------------------------------------------- /lower/util/timer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _typeof2 = require('babel-runtime/helpers/typeof'); 4 | 5 | var _typeof3 = _interopRequireDefault(_typeof2); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8 | 9 | module.exports = function timer(date) { 10 | if ((typeof date === 'undefined' ? 'undefined' : (0, _typeof3.default)(date)) !== 'object') date = date == null ? new Date() : new Date(date); 11 | if (isNaN(date.getTime())) date = new Date(); 12 | date.setMinutes(date.getMinutes() - date.getTimezoneOffset()); 13 | var str = date.toISOString(); 14 | return str.slice(0, 10) + ' ' + str.slice(11, 23); 15 | }; 16 | 17 | module.exports.formatArrTime = function formatArrTime() { 18 | var arr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 19 | var keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['createdAt', 'updatedAt']; 20 | var len = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 16; 21 | 22 | var result = []; 23 | result = arr.map(function (item) { 24 | if (item.toJSON) item = item.toJSON(); 25 | keys.forEach(function (key) { 26 | if (item[key]) { 27 | item[key] = item[key].slice(0, len); 28 | } 29 | }); 30 | return item; 31 | }); 32 | return result; 33 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xmocker-cli", 3 | "version": "1.2.10", 4 | "description": "a server to provide api data for fe", 5 | "repository": "https://github.com/wenlonghuo/fe-mock-server", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "node ./app", 9 | "server": "node ./app --isServer", 10 | "dev": "cross-env NODE_ENV=development nodemon ./app --watch app", 11 | "build": "rimraf ./lower & babel ./app -d ./lower" 12 | }, 13 | "bin": { 14 | "mocker": "./bin/mocker.js" 15 | }, 16 | "keywords": [ 17 | "mock" 18 | ], 19 | "author": "jsyaowenlong@126.com", 20 | "license": "MIT", 21 | "dependencies": { 22 | "axios": "^0.16.2", 23 | "babel-runtime": "^6.25.0", 24 | "chalk": "^2.1.0", 25 | "commander": "^2.9.0", 26 | "debug": "^2.6.0", 27 | "fkill": "^5.0.0", 28 | "koa": "^2.0.0", 29 | "koa-bodyparser": "^3.2.0", 30 | "koa-router": "^7.0.1", 31 | "lodash": "^4.17.4", 32 | "minimist": "^1.2.0", 33 | "mz": "^2.6.0", 34 | "nedb-promise": "^2.0.1", 35 | "resolve-path": "^1.3.3", 36 | "superagent": "^3.5.2", 37 | "through2": "^2.0.3", 38 | "ua-parser-js": "^0.7.12", 39 | "ws": "^1.1.1", 40 | "xmocker": "^1.1.5" 41 | }, 42 | "devDependencies": { 43 | "babel-cli": "^6.24.1", 44 | "babel-eslint": "^7.0.0", 45 | "babel-plugin-transform-runtime": "^6.0.0", 46 | "babel-preset-es2015": "^6.0.0", 47 | "babel-preset-stage-2": "^6.0.0", 48 | "cross-env": "^5.0.5", 49 | "eslint": "^3.7.1", 50 | "eslint-config-standard": "^6.1.0", 51 | "eslint-friendly-formatter": "^2.0.5", 52 | "eslint-loader": "^1.5.0", 53 | "eslint-plugin-html": "^1.3.0", 54 | "eslint-plugin-promise": "^3.4.0", 55 | "eslint-plugin-standard": "^2.0.1", 56 | "nodemon": "^1.11.0", 57 | "rimraf": "^2.6.1" 58 | }, 59 | "engines": { 60 | "node": ">= 6.0.0", 61 | "npm": ">= 3.0.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tool/gulp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mockServer", 3 | "version": "1.2.0", 4 | "description": "provide server for html debugging", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "npm run server", 8 | "all": "node index.js --c", 9 | "fast": "node index.js --devgulpname dev --c", 10 | "gulp": "node_modules/.bin/gulp devb --c ", 11 | "gulpf": "node_modules/.bin/gulp dev --c ", 12 | "server": "node ./lib/nodemon.js --c ", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": "yaowenlong@91taogu.com", 16 | "license": "MIT", 17 | "dependencies": { 18 | "browserify": "^13.1.0", 19 | "del": "^2.2.2", 20 | "glob": "^7.1.1", 21 | "gulp": "github:gulpjs/gulp#4.0", 22 | "gulp-autoprefixer": "github:wenlonghuo/gulp-autoprefixer", 23 | "gulp-buffer": "0.0.2", 24 | "gulp-clean-css": "^3.4.0", 25 | "gulp-notify": "^3.0.0", 26 | "gulp-plumber": "^1.1.0", 27 | "gulp-rename": "^1.2.2", 28 | "gulp-rev": "^7.1.2", 29 | "gulp-rev-collector": "^1.0.5", 30 | "gulp-tap": "^0.1.3", 31 | "gulp-util": "^3.0.7", 32 | "minimist": "^1.2.0", 33 | "stream-combiner2": "^1.1.1", 34 | "superagent": "^3.5.2", 35 | "vinyl-buffer": "^1.0.0", 36 | "vinyl-source-stream": "^1.1.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tool/gulp/setGulpOption.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const fs = require('fs') 4 | const join = path.join 5 | var defaultOption = { 6 | root: '../', 7 | buildPath: '/build/', 8 | js: '/{script,general}/', 9 | css: '/{general,style}/', 10 | html: '/{general,html}/', 11 | image: '/{general,image}/', 12 | } 13 | 14 | var ext = { 15 | js: '/*.js', 16 | css: '/*.css', 17 | html: '/*.html', 18 | image: '/*.{jpg,png,gif,webp}', 19 | } 20 | var sourceList = ['html', 'js', 'css', 'image'] 21 | var specialReg = /\/!\((\w*)\)\$\/?$/ 22 | 23 | function formatPath (option) { 24 | var root = option.root 25 | keysToArr(option, sourceList) 26 | sourceList.forEach((name) => { 27 | option[name] = arrRealPath(root, option[name], ext[name]) 28 | }) 29 | 30 | option.buildPath = join(root, option.buildPath) 31 | return option 32 | } 33 | // 所有键值转数组 34 | function keysToArr (obj, list) { 35 | list.forEach((key) => { 36 | obj[key] = toArr(obj[key]) 37 | }) 38 | } 39 | // 转数组 40 | function toArr (str) { 41 | if (typeof str !== 'string' && typeof str !== 'object') return [] 42 | var arr = Array.isArray(str) ? str : str.split(',') 43 | return arr 44 | } 45 | // 转为实体路径 46 | function arrRealPath (base, arr, extStr) { 47 | if (!Array.isArray(arr)) arr = [arr] 48 | var result = [] 49 | arr.forEach((p) => { 50 | if (specialReg.test(p)) { 51 | var contentName = p.match(specialReg) 52 | contentName = contentName && contentName[1] 53 | // result.push(addExt(join(base, p), '/**' + extStr)) 54 | p = p.replace(specialReg, '') 55 | 56 | result.push(addExt(join(base, p), extStr)) 57 | 58 | var list = fs.readdirSync(join(base, p)) 59 | list.forEach((content) => { 60 | if (!path.extname(content) && content !== contentName) { 61 | var pathStr = addExt(join(base, p, content), '/**' + extStr) 62 | result.push(pathStr) 63 | } 64 | }) 65 | } else { 66 | result.push(addExt(join(base, p), '/**' + extStr)) 67 | } 68 | }) 69 | return result 70 | } 71 | 72 | function addExt (str, extStr) { 73 | return (str.replace(/[/\\]$/, '') + extStr).replace(/\\/g, '/') 74 | } 75 | 76 | module.exports = function (option) { 77 | let buildOption = Object.assign(defaultOption, option.gulp) 78 | buildOption.root = option.path.trim() 79 | 80 | buildOption = formatPath(buildOption, ext) 81 | return buildOption 82 | } 83 | -------------------------------------------------------------------------------- /web/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@vue/app" 4 | ] 5 | } -------------------------------------------------------------------------------- /web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "root": true, 3 | "extends": [ 4 | "plugin:vue/essential", 5 | "@vue/standard" 6 | ], 7 | 'rules': { 8 | "comma-dangle": [0, "always-multiline"], 9 | // allow paren-less arrow functions 10 | 'arrow-parens': 0, 11 | // allow async-await 12 | 'generator-star-spacing': 0, 13 | 'no-new-func': 0, 14 | "no-template-curly-in-string": 0, 15 | // allow debugger during development 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 17 | }, 18 | "globals": { 19 | "WebSocket": true 20 | } 21 | } -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | -------------------------------------------------------------------------------- /web/.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "autoprefixer": {} 4 | } 5 | } -------------------------------------------------------------------------------- /web/dist/css/app.12a6e1c2.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/css/app.12a6e1c2.css.gz -------------------------------------------------------------------------------- /web/dist/fonts/ionicons.143146fa.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/fonts/ionicons.143146fa.woff2 -------------------------------------------------------------------------------- /web/dist/fonts/ionicons.99ac3308.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/fonts/ionicons.99ac3308.woff -------------------------------------------------------------------------------- /web/dist/fonts/ionicons.d535a25a.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/fonts/ionicons.d535a25a.ttf -------------------------------------------------------------------------------- /web/dist/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /web/dist/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /web/dist/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /web/dist/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /web/dist/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /web/dist/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /web/dist/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /web/dist/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /web/dist/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /web/dist/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /web/dist/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /web/dist/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /web/dist/js/0.342a6d4d.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/js/0.342a6d4d.js.gz -------------------------------------------------------------------------------- /web/dist/js/1.83aeb450.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/js/1.83aeb450.js.gz -------------------------------------------------------------------------------- /web/dist/js/2.da1cb8f8.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/js/2.da1cb8f8.js.gz -------------------------------------------------------------------------------- /web/dist/js/app.3543d77b.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/js/app.3543d77b.js.gz -------------------------------------------------------------------------------- /web/dist/js/vendor.7efaf87e.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/js/vendor.7efaf87e.js.gz -------------------------------------------------------------------------------- /web/dist/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "views", 3 | "short_name": "views", 4 | "icons": [ 5 | { 6 | "src": "/img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87" 20 | } 21 | -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/base/worker/workerMain.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/monaco-editor/min/vs/base/worker/workerMain.js.gz -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/bat/bat.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.0.4(d2db3faa76b741bf4ee822c403fc355c913bc46d) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/bat/bat",["require","exports"],function(e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.conf={comments:{lineComment:"REM"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],folding:{markers:{start:new RegExp("^\\s*(::\\s*|REM\\s+)#region"),end:new RegExp("^\\s*(::\\s*|REM\\s+)#endregion")}}},s.language={defaultToken:"",ignoreCase:!0,tokenPostfix:".bat",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"}],keywords:/call|defined|echo|errorlevel|exist|for|goto|if|pause|set|shift|start|title|not|pushd|popd/,symbols:/[=>=","<",">","=",";",":",",",".","..","..."],symbols:/[=>]/,"@brackets"],[/[a-zA-Z@#]\w*/,{cases:{"@keywords":"keyword","@default":"identifier"}}],[/[<>=\\+\\-\\*\\/\\^\\|\\~,]|and\\b|or\\b|not\\b]/,"operator"]],whitespace:[[/\s+/,"white"]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],numbers:[[/0[xX][0-9a-fA-F]*(_?[0-9a-fA-F])*/,"number.hex"],[/@decimal((\.@decpart)?([eE][\-+]?@decpart)?)[fF]*/,{cases:{"(\\d)*":"number",$0:"number.float"}}]],strings:[[/'$/,"string.escape","@popall"],[/'/,"string.escape","@stringBody"],[/"$/,"string.escape","@popall"],[/"/,"string.escape","@dblStringBody"]],stringBody:[[/\\./,"string"],[/'/,"string.escape","@popall"],[/.(?=.*')/,"string"],[/.*\\$/,"string"],[/.*$/,"string","@popall"]],dblStringBody:[[/\\./,"string"],[/"/,"string.escape","@popall"],[/.(?=.*")/,"string"],[/.*\\$/,"string"],[/.*$/,"string","@popall"]]}}}); -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/pgsql/pgsql.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/monaco-editor/min/vs/basic-languages/pgsql/pgsql.js.gz -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/redshift/redshift.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/monaco-editor/min/vs/basic-languages/redshift/redshift.js.gz -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/sb/sb.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.0.4(d2db3faa76b741bf4ee822c403fc355c913bc46d) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/sb/sb",["require","exports"],function(e,o){"use strict";Object.defineProperty(o,"__esModule",{value:!0}),o.conf={comments:{lineComment:"'"},brackets:[["(",")"],["[","]"],["If","EndIf"],["While","EndWhile"],["For","EndFor"],["Sub","EndSub"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]}]},o.language={defaultToken:"",tokenPostfix:".sb",ignoreCase:!0,brackets:[{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"keyword.tag-if",open:"If",close:"EndIf"},{token:"keyword.tag-while",open:"While",close:"EndWhile"},{token:"keyword.tag-for",open:"For",close:"EndFor"},{token:"keyword.tag-sub",open:"Sub",close:"EndSub"}],keywords:["Else","ElseIf","EndFor","EndIf","EndSub","EndWhile","For","Goto","If","Step","Sub","Then","To","While"],tagwords:["If","Sub","While","For"],operators:[">","<","<>","<=",">=","And","Or","+","-","*","/","="],identifier:/[a-zA-Z_][\w]*/,symbols:/[=><:+\-*\/%\.,]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[{include:"@whitespace"},[/(@identifier)(?=[.])/,"type"],[/@identifier/,{cases:{"@keywords":{token:"keyword.$0"},"@operators":"operator","@default":"variable.name"}}],[/([.])(@identifier)/,{cases:{$2:["delimiter","type.member"],"@default":""}}],[/\d*\.\d+/,"number.float"],[/\d+/,"number"],[/[()\[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"operator","@default":"delimiter"}}],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"]],whitespace:[[/[ \t\r\n]+/,""],[/(\').*$/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"C?/,"string","@pop"]]}}}); -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/solidity/solidity.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/monaco-editor/min/vs/basic-languages/solidity/solidity.js.gz -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/sql/sql.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenlonghuo/xmocker-cli/b3b314bf197a0c0cf7ed454ceacef52dcb405e51/web/dist/monaco-editor/min/vs/basic-languages/sql/sql.js.gz -------------------------------------------------------------------------------- /web/dist/monaco-editor/min/vs/basic-languages/xml/xml.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.0.4(d2db3faa76b741bf4ee822c403fc355c913bc46d) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/xml/xml",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{blockComment:["\x3c!--","--\x3e"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}]},t.language={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[//,{token:"comment",next:"@pop"}],[/ 24 | 25 | 26 | -------------------------------------------------------------------------------- /web/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "views", 3 | "short_name": "views", 4 | "icons": [ 5 | { 6 | "src": "/img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87" 20 | } 21 | -------------------------------------------------------------------------------- /web/public/monaco-editor/min/vs/basic-languages/bat/bat.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.0.4(d2db3faa76b741bf4ee822c403fc355c913bc46d) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/bat/bat",["require","exports"],function(e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.conf={comments:{lineComment:"REM"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],folding:{markers:{start:new RegExp("^\\s*(::\\s*|REM\\s+)#region"),end:new RegExp("^\\s*(::\\s*|REM\\s+)#endregion")}}},s.language={defaultToken:"",ignoreCase:!0,tokenPostfix:".bat",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"}],keywords:/call|defined|echo|errorlevel|exist|for|goto|if|pause|set|shift|start|title|not|pushd|popd/,symbols:/[=>=","<",">","=",";",":",",",".","..","..."],symbols:/[=>]/,"@brackets"],[/[a-zA-Z@#]\w*/,{cases:{"@keywords":"keyword","@default":"identifier"}}],[/[<>=\\+\\-\\*\\/\\^\\|\\~,]|and\\b|or\\b|not\\b]/,"operator"]],whitespace:[[/\s+/,"white"]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],numbers:[[/0[xX][0-9a-fA-F]*(_?[0-9a-fA-F])*/,"number.hex"],[/@decimal((\.@decpart)?([eE][\-+]?@decpart)?)[fF]*/,{cases:{"(\\d)*":"number",$0:"number.float"}}]],strings:[[/'$/,"string.escape","@popall"],[/'/,"string.escape","@stringBody"],[/"$/,"string.escape","@popall"],[/"/,"string.escape","@dblStringBody"]],stringBody:[[/\\./,"string"],[/'/,"string.escape","@popall"],[/.(?=.*')/,"string"],[/.*\\$/,"string"],[/.*$/,"string","@popall"]],dblStringBody:[[/\\./,"string"],[/"/,"string.escape","@popall"],[/.(?=.*")/,"string"],[/.*\\$/,"string"],[/.*$/,"string","@popall"]]}}}); -------------------------------------------------------------------------------- /web/public/monaco-editor/min/vs/basic-languages/sb/sb.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.0.4(d2db3faa76b741bf4ee822c403fc355c913bc46d) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/sb/sb",["require","exports"],function(e,o){"use strict";Object.defineProperty(o,"__esModule",{value:!0}),o.conf={comments:{lineComment:"'"},brackets:[["(",")"],["[","]"],["If","EndIf"],["While","EndWhile"],["For","EndFor"],["Sub","EndSub"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]}]},o.language={defaultToken:"",tokenPostfix:".sb",ignoreCase:!0,brackets:[{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"keyword.tag-if",open:"If",close:"EndIf"},{token:"keyword.tag-while",open:"While",close:"EndWhile"},{token:"keyword.tag-for",open:"For",close:"EndFor"},{token:"keyword.tag-sub",open:"Sub",close:"EndSub"}],keywords:["Else","ElseIf","EndFor","EndIf","EndSub","EndWhile","For","Goto","If","Step","Sub","Then","To","While"],tagwords:["If","Sub","While","For"],operators:[">","<","<>","<=",">=","And","Or","+","-","*","/","="],identifier:/[a-zA-Z_][\w]*/,symbols:/[=><:+\-*\/%\.,]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[{include:"@whitespace"},[/(@identifier)(?=[.])/,"type"],[/@identifier/,{cases:{"@keywords":{token:"keyword.$0"},"@operators":"operator","@default":"variable.name"}}],[/([.])(@identifier)/,{cases:{$2:["delimiter","type.member"],"@default":""}}],[/\d*\.\d+/,"number.float"],[/\d+/,"number"],[/[()\[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"operator","@default":"delimiter"}}],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"]],whitespace:[[/[ \t\r\n]+/,""],[/(\').*$/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"C?/,"string","@pop"]]}}}); -------------------------------------------------------------------------------- /web/public/monaco-editor/min/vs/basic-languages/xml/xml.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 1.0.4(d2db3faa76b741bf4ee822c403fc355c913bc46d) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/xml/xml",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{blockComment:["\x3c!--","--\x3e"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}]},t.language={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[//,{token:"comment",next:"@pop"}],[/