├── .babelrc ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── assets └── uploads │ └── .gitkeep ├── build └── dev-server.js ├── ca-cert.pem ├── ca-key.pem ├── ca-req.csr ├── docs ├── .nojekyll ├── README.md ├── _coverpage.md ├── _navbar.md ├── _sidebar.md ├── buildmock.md ├── configuration.md ├── images │ ├── 5.jpg │ ├── dcloud1.jpg │ ├── dcloud10.jpg │ ├── dcloud11.jpg │ ├── dcloud2.jpg │ ├── dcloud3.jpg │ ├── dcloud4.jpg │ ├── dcloud5.jpg │ ├── dcloud6.jpg │ ├── dcloud7.jpg │ ├── dcloud8.jpg │ ├── dcloud9.jpg │ ├── favicon.png │ └── rbac.jpg ├── index.html ├── notonlymock.md ├── permission.md ├── permissionapi.md ├── quickstart.md └── template.md ├── gulpfile.js ├── migration ├── book.js └── index.js ├── nunjucks.tmLanguage ├── package.json ├── publicKey.pub ├── screenshot ├── 1.png ├── 2.jpg ├── 3.jpg ├── 4.jpg └── 5.jpg ├── src ├── app.js ├── config.js ├── controllers │ ├── auth.js │ ├── index.js │ ├── interface.js │ ├── menu.js │ ├── requestlog.js │ ├── role.js │ ├── route.js │ ├── system.js │ └── user.js ├── db │ ├── db.json │ ├── db_backup.json │ └── request_log_db.json ├── im │ ├── closeMiddleware.js │ ├── connectMiddleware.js │ ├── index.js │ ├── messageMiddleware.js │ ├── messageRouteMiddleware.js │ ├── remoteEmitMiddleware.js │ ├── roomInfoMiddleware.js │ └── routes │ │ ├── index.js │ │ ├── message.js │ │ └── room.js ├── lib │ ├── EasySocket.js │ ├── compose.js │ ├── proxy.js │ └── responseTemplate.js ├── middleware │ ├── ErrorRoutesCatch.js │ ├── ParseUserInfo.js │ ├── PermissionCheck.js │ ├── Proxy.js │ └── RequestLog.js ├── models │ ├── baseModel.js │ ├── index.js │ └── requestLogModel.js ├── routes │ ├── index.js │ └── main.js ├── services │ ├── interfaceService.js │ ├── memuService.js │ ├── requestLogService.js │ ├── roleService.js │ ├── routeService.js │ ├── systemService.js │ ├── tokenService.js │ └── userService.js └── tool │ └── Common.js ├── templates ├── config │ ├── config.js │ ├── index.js │ └── model.js ├── generate.js └── server │ ├── controller.njk │ ├── db.njk │ ├── model.njk │ ├── quickAdd │ ├── controller.njk │ └── route.njk │ ├── route.njk │ └── service.njk └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ], 5 | "plugins": [ 6 | "@babel/plugin-transform-runtime" 7 | ] 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | test/unit/coverage 6 | test/e2e/reports 7 | selenium-debug.log 8 | .idea/ 9 | codeGenerate/dist/ 10 | yarn.lock 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM daocloud.io/node 2 | WORKDIR /app 3 | COPY . /app/ 4 | RUN npm install 5 | RUN npm run build 6 | RUN cp -r src/db dist/ 7 | CMD ["npm","run","production"] 8 | 9 | EXPOSE 3000 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 若邪 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | ## 关于lazy mock 8 | > 一个快速生成后端模拟数据的懒人工具 9 | 10 | ## 是什么 11 | 12 | lazy mock 是一个使用`koa2`构建的,`lowdb`持久化数据到JSON文件的快速生成后端模拟数据的工具。只需要简单的配置就可以实现和json-server一样的功能,但是比json-server更加灵活,后期可配置性更强,完全可以模拟真实后端业务逻辑。 13 | 14 | lazy mock默认包含了`jwt`实现的登录与登出,实现了基于`RBAC`模型的通用权限控制逻辑。 15 | 16 | ## 安装 17 | 18 | ``` bash 19 | $ npm install -g lazy-mock 20 | ``` 21 | 22 | ## 使用 23 | 24 | ``` bash 25 | $ lazy-mock init 26 | ``` 27 | 28 | 例子: 29 | 30 | ``` bash 31 | $ lazy-mock init d2-admin-pm my-project 32 | ``` 33 | 34 | 在 my-project 目录下执行 npm install 35 | 36 | ## 目前支持模板 37 | * [rbac](https://github.com/lazy-mock-templates/rbac) --- 包含 RBAC 权限控制模型 38 | * [d2-admin-pm](https://github.com/lazy-mock-templates/d2-admin-pm) --- 包含 [d2-admin-pm](https://github.com/wjkang/d2-admin-pm) 的 curd 模板 39 | 40 | >支持模板开发及自定义,详细看文档 41 | 42 | ## 特性 43 | - 轻松对接`mock.js`,`faker.js`等假数据生成工具 44 | - 不需要数据库,直接持久化数据到JSON文件 45 | - 相比json-server单JSON文件,支持一个实体一个JSON文件 46 | - 默认包含了`jwt`实现的登录与登出,基于`RBAC`模型的权限控制 47 | - 使用 `async/await` 处理异步问题 48 | - `MVC`代码分层结构 49 | - 内置简单代码生成器 50 | 51 | 52 | 53 | [文档](https://wjkang.github.io/lazy-mock) 54 | 55 | ## Stargazers over time 56 | 57 | [![Stargazers over time](https://starcharts.herokuapp.com/wjkang/lazy-mock.svg)](https://starcharts.herokuapp.com/wjkang/lazy-mock) 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /assets/uploads/.gitkeep: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('@babel/register') 2 | require('@babel/polyfill') 3 | require('../src/app') 4 | -------------------------------------------------------------------------------- /ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICDDCCAXUCFGXtwq5k0yQ1BdRHeBAO5ofUXO9IMA0GCSqGSIb3DQEBCwUAMEUx 3 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl 4 | cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTkwOTE3MTExMTEyWhcNMjkwOTE0MTEx 5 | MTEyWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE 6 | CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GN 7 | ADCBiQKBgQC9bQDsdARAap/0ZSEpQWpDrepnmO6gRQf86lWdBiISGWgq/bOFdH3A 8 | qq+Pf43xxlZMHGrS2qk3vpw2J19KXYmxP0BZo0CHXTc62VHH3j0HXEwq2M00UAba 9 | vpiCMdPiugnCx4Z7Vr8T+oNSPizjFAaFqJUtWtd4crV1I6wmCSF1cQIDAQABMA0G 10 | CSqGSIb3DQEBCwUAA4GBAGdhWZevVKz3Hvivev7cni5mMHcfnqZjilVhaGxMh4WN 11 | apdYf6Ur1A+We5goKEIGMNpzizrjBaCcposib35fdjTUhKKfYmycYQWmriMhPKKF 12 | Ojgh7QZYAlKidG65qkFmku8ez0KUzMtP03oSN3Se1tedPBuHCfGC4sIoVMn7REcg 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /ca-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXwIBAAKBgQC9bQDsdARAap/0ZSEpQWpDrepnmO6gRQf86lWdBiISGWgq/bOF 3 | dH3Aqq+Pf43xxlZMHGrS2qk3vpw2J19KXYmxP0BZo0CHXTc62VHH3j0HXEwq2M00 4 | UAbavpiCMdPiugnCx4Z7Vr8T+oNSPizjFAaFqJUtWtd4crV1I6wmCSF1cQIDAQAB 5 | AoGBAIqBmZtLeZBgZnUdPRIdcsXp9OurN1CZKS4VamRWh7MUQMaumwWKGCk4pQYY 6 | DoIqtA8S+EkU+YZ5KV+vik2l93cQhRzp37TI9C2/j7STYutMC7ac2qaT+0OzyDEu 7 | tiG5XN+rQzjFiAQfojbvmJauu2txzO8KtFE96ZKDsrig0RrBAkEA56uNLvVaN+2d 8 | xNuWZSNM9TSwu8FN0w5nvdt8g+jQsRkKdye39MhbsvwOTF+Yxi5s5NnTsEev7tUU 9 | XPCi4xaRtwJBANFRtr+zjKlMvYpukFWsaWCO31tEIAVLZ7mk+hpu9Q+zTNPRBdYJ 10 | IYtyoXIEpSAwVr/XNftnX/dhyT4HWVUAkhcCQQCeQZl7Z42OwRpSXPLa+gdbRfgo 11 | +j7Qm3mQv3vKnGLbZ9C3XwGSDMBff0HBOFijoRkwKAEs3Xu4egSkDJoo6MT3AkEA 12 | vIYORGZgX/MgG1gtYxxf5HmJrdeTx3D8wPVX1QruaO+iWHw/92BN+ByMT/bjCjS/ 13 | TRV2JzIZ+uL1r4pK6QYUjQJBALxKE67kfWmYwbjaE+0M161dLW4LoOuW3ck+mWhG 14 | fe0o0mxoSScNeoHo8v0yG/PPEEq/sLPOu62PBeUE1PtbrRw= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /ca-req.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh 3 | MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB 4 | AQUAA4GNADCBiQKBgQC9bQDsdARAap/0ZSEpQWpDrepnmO6gRQf86lWdBiISGWgq 5 | /bOFdH3Aqq+Pf43xxlZMHGrS2qk3vpw2J19KXYmxP0BZo0CHXTc62VHH3j0HXEwq 6 | 2M00UAbavpiCMdPiugnCx4Z7Vr8T+oNSPizjFAaFqJUtWtd4crV1I6wmCSF1cQID 7 | AQABoAAwDQYJKoZIhvcNAQELBQADgYEAfYptIQjk0rd0gzjVNcYCPGx27k3Vx+6y 8 | gKdcuPLA6WIdUX86jUDFDiaGnS/td44cvPUV3+BDPtHNtW5gLF72DmSSQtUF4Txt 9 | lwhjUu3nXQGo4aXF3CUikH2YvnrcgYcmkN8n29jwjqPqkexRMDq+69qzuMVMSDjO 10 | Uv4LtZHh7t4= 11 | -----END CERTIFICATE REQUEST----- 12 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/.nojekyll -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## 关于lazy mock 2 | > 一个快速生成后端模拟数据的懒人工具 3 | 4 | ## 是什么 5 | 6 | lazy mock 是一个使用`koa2`构建的,`lowdb`持久化数据到JSON文件的快速生成后端模拟数据的工具。只需要简单的配置就可以实现和json-server一样的功能,但是比json-server更加灵活,后期可配置性更强,完全可以模拟真实后端业务逻辑。 7 | 8 | lazy mock默认包含了`jwt`实现的登录与登出,实现了基于`RBAC`模型的通用权限控制逻辑。 9 | 10 | 查看[快速开始](quickstart.md)了解详情。 11 | 12 | ## 特性 13 | - 轻松对接`mock.js`,`faker.js`等假数据生成工具 14 | - 不需要数据库,直接持久化数据到JSON文件 15 | - 相比json-server单JSON文件,支持一个实体一个JSON文件 16 | - 默认包含了`jwt`实现的登录与登出,基于`RBAC`模型的权限控制 17 | - 使用 `async/await` 处理异步问题 18 | - `MVC`代码分层结构 19 | - 内置简单代码生成器 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](https://raw.githubusercontent.com/wjkang/lazy-mock/master/screenshot/1.png ':size=200') 2 | 3 | # lazy mock 4 | 5 | > 一个快速生成后端模拟数据的懒人工具 6 | 7 | [GitHub](https://github.com/wjkang/lazy-mock) 8 | [Get Started](#关于lazy-mock) -------------------------------------------------------------------------------- /docs/_navbar.md: -------------------------------------------------------------------------------- 1 | * 使用案例 2 | 3 | * [3YAdmin](https://github.com/wjkang/3YAdmin) 4 | * [vue-quasar-admin](https://github.com/wjkang/vue-quasar-admin) 5 | * [d2-admin-pm](https://github.com/wjkang/d2-admin-pm) -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * 入门 2 | 3 | * [快速开始](quickstart.md) 4 | * [构建你自己的mock服务](buildmock.md) 5 | 6 | * 使用权限控制 7 | 8 | * [权限模型](permission.md) 9 | * [权限接口](permissionapi.md) 10 | 11 | * 定制 12 | * [模板定制](template.md) 13 | * [配置](configuration.md) 14 | 15 | * 更多 16 | 17 | * [不仅仅mock](notonlymock.md) -------------------------------------------------------------------------------- /docs/buildmock.md: -------------------------------------------------------------------------------- 1 | ## 配置实体 2 | 修改 `templates/config/model.js` ,比如 3 | ```js 4 | var shortid = require('shortid') 5 | var Mock = require('mockjs') 6 | var Random = Mock.Random 7 | 8 | //必须包含字段id 9 | export default { 10 | name: "book", 11 | Name: "Book", 12 | properties: [ 13 | { 14 | key: "id", 15 | title: "id" 16 | }, 17 | { 18 | key: "name", 19 | title: "书名" 20 | }, 21 | { 22 | key: "author", 23 | title: "作者" 24 | }, 25 | { 26 | key: "press", 27 | title: "出版社" 28 | } 29 | ], 30 | buildMockData: function () {//不需要生成设为false 31 | let data = [] 32 | for (let i = 0; i < 100; i++) { 33 | data.push({ 34 | id: shortid.generate(), 35 | name: Random.cword(5, 7), 36 | author: Random.cname(), 37 | press: Random.cword(5, 7) 38 | }) 39 | } 40 | return data 41 | } 42 | } 43 | ``` 44 | 45 | ## 生成代码 46 | 47 | 内部使用`nunjucks`写了个简单的代码生成器,读取配置信息直接生成`CURD`代码 48 | ``` 49 | npm run code 50 | ``` 51 | !> 如果程序运行过程中执行`npm run code`,程序会自动重启,可直接调用新增的接口 52 | 53 | ## 调用接口 54 | 55 | 接口默认需要授权访问,请求头需要带上token。 56 | 57 | >token 登录成功后取到 58 | 59 | 以`axios`为例(后面请求接口也是使用`axios`) 60 | ```js 61 | import axios from 'axios'; 62 | 63 | const request = axios.create({ 64 | baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : 'productBaseUrl', // api的base_url 65 | timeout: 20000 // request timeout 66 | }) 67 | 68 | request.interceptors.request.use(config => { 69 | config.headers['Authorization'] = 'Bearer ' + 登陆后获取到的token// 让每个请求携带token 70 | return config 71 | }, error => { 72 | Promise.reject(error) 73 | }) 74 | ``` 75 | 76 | ### Insert 77 | 78 | ```js 79 | request({ 80 | url: '/book/save', 81 | method: 'post', 82 | data: { 83 | name: "javascript", 84 | author: "xxoo", 85 | press: "xxoo出版社", 86 | } 87 | }).then(res => { 88 | console.log(res.data) 89 | }) 90 | ``` 91 | !> 新增不用传入id 92 | 93 | ### Delete 94 | 95 | ```js 96 | request({ 97 | url: '/book/del', 98 | method: 'delete', 99 | params: { 100 | id:'dsdsd23e23e' 101 | } 102 | }) 103 | ``` 104 | 105 | ```js 106 | request({ 107 | url: '/book/batchdel', 108 | method: 'delete', 109 | params: { 110 | ids:"['sd23edese343d3','433krker346lkrtr']" 111 | }) 112 | ``` 113 | !> 注意参数的格式 114 | 115 | ### Update 116 | ```js 117 | request({ 118 | url: '/book/save', 119 | method: 'post', 120 | data: { 121 | id: "1", 122 | name: "javascript", 123 | author: "xxoo", 124 | press: "xxoo出版社", 125 | } 126 | }).then(res => { 127 | console.log(res.data) 128 | }) 129 | ``` 130 | !>更新需要传入id 131 | 132 | ### Query 133 | 134 | ```js 135 | request({ 136 | url: '/book/dsdsd23e23e', 137 | method: 'get' 138 | }) 139 | ``` 140 | 141 | ```js 142 | request({ 143 | url: '/book/paged', 144 | method: 'get', 145 | params: { 146 | pageIndex:1, 147 | pageSize:10, 148 | sortBy:'name', 149 | descending:'true', 150 | id:'', 151 | name:'', 152 | author:'', 153 | press:'' 154 | }) 155 | ``` 156 | >所有参数都不是必须 157 | 158 | >默认升序,倒序则将`descending`设为`'true'` 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | ## 修改应用监听端口 2 | 修改`src/config.js`文件 3 | 4 | ## 生成mock数据 5 | 配置`templates/config/model.js` 6 | 7 | ```js 8 | buildMockData: function () {//不需要生成设为false 9 | let data = [] 10 | for (let i = 0; i < 100; i++) { 11 | data.push({ 12 | id: shortid.generate(), 13 | name: Random.cword(5, 7), 14 | author: Random.cname(), 15 | press: Random.cword(5, 7) 16 | }) 17 | } 18 | return data 19 | } 20 | 21 | ``` 22 | 生成代码的时候会执行`buildMockData`,将生成的mock数据填充到对应db.json文件。 23 | 24 | 默认使用[mockjs](https://github.com/nuysoft/Mock),更多规则可查看其[文档](https://github.com/nuysoft/Mock)。也可以自行安装[faker.js](https://github.com/Marak/faker.js)使用 25 | 26 | ## 修改接口名称 27 | 28 | * 修改模板 29 | 30 | 修改`templates/server/route.njk`代码模板,后续生成代码就会以新的模板来生成。 31 | 32 | * 直接修改 33 | 34 | 找到对应的路由文件(`src/routes`下,路由文件名称会以实体名称生成),直接修改即可。 35 | 36 | ## 授权配置 37 | 38 | 默认需要授权访问,请求的时候请求头需要带上 token 39 | 40 | * 完全去掉授权限制 41 | 42 | `src/app.js`找到下行代码 43 | ```js 44 | .use(jwt({ secret: publicKey }).unless({ path: [/^\/public|\/auth\/login|\/assets/] })) 45 | ``` 46 | 直接去掉即可。 47 | >完全去掉授权限制 权限控制功能也会失效 48 | 49 | * 部分接口去掉授权显示 50 | 51 | 还是上面那行代码,根据规则修改 52 | ```js 53 | { path: [/^\/public|\/auth\/login|\/assets/] } 54 | ``` 55 | 56 | ## 修改数据返回格式 57 | 58 | 修改`src/lib/responseTemplate.js` 59 | 60 | ```js 61 | export let businessError = (ctx,msg) => { 62 | ctx.body = { 63 | statusCode: 500, 64 | msg: msg, 65 | data: null 66 | } 67 | } 68 | 69 | export let success = (ctx,data) => { 70 | ctx.body = { 71 | statusCode: 200, 72 | msg: '', 73 | data: data 74 | } 75 | } 76 | ``` 77 | 78 | ## 配置接口访问权限 79 | 使用已经实现的RBAC权限控制,可以实现接口的访问权限控制。 80 | 81 | 假如某个接口只允许特定的角色或具备特定权限的人访问,需要修改对应接口的路由配置,比如 82 | ```js 83 | .get('/function/pagedlist', PermissionCheck({ permission: ["function_view"], role: ["test"] }), controllers.function.getFunctionPagedList) 84 | ``` 85 | 使用中间件`PermissionCheck`做权限校验,如果当前用户没有登录,或者不具备function_view权限并且不属于test角色,访问此接口的时候会返回相应的错误信息。 86 | 87 | >permission为功能编码,role为角色编码,如果用户属于admin则不做校验 88 | 89 | 90 | ## 添加业务逻辑 91 | 92 | 如果简单的增删改查不能满足你的要求。你可以自行修改代码,添加你需要的功能。 93 | 94 | 首先需要了解一下整个应用的结构及调用过程。 95 | 96 | * **`src\routes`** 97 | 98 | 生成代码时会在该文件夹下生成相应的路由文件,比如`bookRoute.js`。 99 | 100 | 路由配置里,每个接口被访问的时候,会调用对应controller的方法 101 | ```js 102 | .post('/auth/login', controllers.auth.login) 103 | ``` 104 | 比如登陆的时候,调用`auth`控制器的`login`方法 105 | 106 | * **`src\controllers`** 107 | 108 | 生成代码时会在该文件夹下生成相应的控制器文件,比如`book.js`。 109 | 110 | 控制器里会引入相应的实体的服务,也可引入多个实体的服务 111 | ```js 112 | import menuService from '../services/memuService' 113 | import roleService from '../services/roleService' 114 | ``` 115 | 调用服务里的方法来完成某些功能需求,比如 116 | ```js 117 | export let getMenuFunctions = async (ctx) => { 118 | let menuId = ctx.query.menuId 119 | let roleId = ctx.query.roleId 120 | let [menuFunctions, roleFunctions] = 121 | await Promise.all([menuService.getMenuFunctions(menuId), roleService.getRoleFunctions(roleId)]) 122 | return responseTemplate.success(ctx, { 123 | menuFunctions: menuFunctions, 124 | roleFunctions: roleFunctions 125 | }) 126 | } 127 | ``` 128 | 129 | * **`src\services`** 130 | 131 | 生成代码时会在该文件夹下生成相应的服务文件,比如`bookService.js`。 132 | 服务里会引入相应的model,也可以引入其它服务 133 | ```js 134 | import model from '../models/baseModel' 135 | import roleService from './roleService' 136 | import functionService from './functionService' 137 | ``` 138 | 139 | * **`src\models`** 140 | 141 | 生成代码时会在该文件夹下生成相应的model,比如`bookModel.js`。 142 | model里就是使用`lowdb`读取相应的db.json文件,提供获取`lowdb`实例的方法 143 | ```js 144 | import path from 'path' 145 | 146 | const low = require('lowdb') 147 | const lodashId = require('lodash-id') 148 | const FileAsync = require('lowdb/adapters/FileAsync') 149 | const dbFile = path.join(__dirname, '../db/book_db.json') 150 | const adapter = new FileAsync(dbFile) 151 | let instance = undefined 152 | module.exports = { 153 | init: function (context) { 154 | return new Promise((resolve, reject) => { 155 | if (instance === undefined) { 156 | low(adapter).then(db => { 157 | db._.mixin(lodashId) 158 | instance = db; 159 | resolve(db.get(context)) 160 | }) 161 | } else { 162 | resolve(instance.get(context)) 163 | } 164 | }) 165 | }, 166 | read: () => { 167 | return new Promise((resolve, reject) => { 168 | if (instance === undefined) { 169 | resolve() 170 | } 171 | else { 172 | instance.read().then(() => { 173 | resolve() 174 | }) 175 | } 176 | }) 177 | } 178 | 179 | } 180 | 181 | ``` 182 | 183 | * **`src\db`** 184 | 185 | 生成代码时会在该文件夹下生成相应的`JSON`文件,比如`book_db.json`。 186 | 187 | json文件的作用就是持久化数据。 188 | 189 | 到此,相信你已经知道如何去修改或添加代码来实现你要的功能。 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/5.jpg -------------------------------------------------------------------------------- /docs/images/dcloud1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud1.jpg -------------------------------------------------------------------------------- /docs/images/dcloud10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud10.jpg -------------------------------------------------------------------------------- /docs/images/dcloud11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud11.jpg -------------------------------------------------------------------------------- /docs/images/dcloud2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud2.jpg -------------------------------------------------------------------------------- /docs/images/dcloud3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud3.jpg -------------------------------------------------------------------------------- /docs/images/dcloud4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud4.jpg -------------------------------------------------------------------------------- /docs/images/dcloud5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud5.jpg -------------------------------------------------------------------------------- /docs/images/dcloud6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud6.jpg -------------------------------------------------------------------------------- /docs/images/dcloud7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud7.jpg -------------------------------------------------------------------------------- /docs/images/dcloud8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud8.jpg -------------------------------------------------------------------------------- /docs/images/dcloud9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/dcloud9.jpg -------------------------------------------------------------------------------- /docs/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/favicon.png -------------------------------------------------------------------------------- /docs/images/rbac.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/docs/images/rbac.jpg -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | lazy mock 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/notonlymock.md: -------------------------------------------------------------------------------- 1 | 借助现有的权限管理和持久化数据的功能,`lazy mock`可以作为demo的后端服务支持,甚至可以做为一个博客的后台系统部署到服务器上。 2 | 3 | ## 部署 4 | 5 | 以[daocloud](https://dashboard.daocloud.io)+docker部署为例 6 | 7 | ### 创建项目 8 | 9 | `daocloud`支持多个流行git仓库,也可以使用你自己的git仓库地址 10 | 11 | ![](images/dcloud1.jpg) 12 | 13 | ### 流程定义 14 | 15 | 编辑那刚才创建的项目,将默认测试任务删除 16 | 17 | ![](images/dcloud2.jpg) 18 | 19 | ### 构建镜像 20 | 21 | 项目右边才做菜单,选择“手动构建”,然后选择要构建的分支 22 | 23 | ![](images/dcloud3.jpg) 24 | 25 | 成功后到镜像仓库就可以看到新构建的镜像 26 | 27 | ![](images/dcloud4.jpg) 28 | 29 | ### 部署应用 30 | 31 | 点击“部署最新版本” 32 | 33 | ![](images/dcloud5.jpg) 34 | 35 | 填写应用名称,选择镜像版本以及主机(如何配置主机,可以看`daocloud`的文档,那里会更加详细) 36 | 37 | 点击下一步,继续配置 38 | 39 | ![](images/dcloud6.jpg) 40 | 41 | 然后点部署即可 42 | 43 | 最后还要将项目`src/db`下所有文件拷贝到上面配置的云主机的路径下(`/apps/3YAdmin/db`) 44 | 45 | 以下是我成功部署的两个应用 46 | 47 | ![](images/dcloud7.jpg) 48 | 49 | ### 持续集成 50 | 51 | 回到流程定义界面,设置“默认构建任务”,设置触发条件 52 | 53 | ![](images/dcloud8.jpg) 54 | 55 | 点击“发布阶段”的“添加并行任务” 56 | 57 | ![](images/dcloud9.jpg) 58 | 59 | 选择发布到自有主机,然后选择之前创建的应用 60 | 61 | ![](images/dcloud10.jpg) 62 | 63 | 64 | ![](images/dcloud11.jpg) 65 | 66 | 最后点击创建任务。以后每当相应的分支提交了代码,会自动构建镜像,并且以最新镜像部署应用 67 | 68 | >需要手动删除云主机上的旧的镜像,避免占用磁盘空间 69 | 70 | 71 | ## 其它接口 72 | 73 | 以生产环境的方式部署后,用户的访问记录会记录在`request_log_db.json`文件中。 74 | 75 | 查看的接口是 76 | ```js 77 | request({ 78 | url: '/requestlog/pagedlist', 79 | method: 'get', 80 | params: { 81 | pageIndex: 1, 82 | pageSize: 10, 83 | sortBy: 'createdDate', 84 | descending: true 85 | } 86 | }) 87 | ``` 88 | 89 | 如果是将应用作为demo后端服务部署,可能有时候需要将应用的数据重置为最开始部署的样子。对应的接口为 90 | ```js 91 | request({ 92 | url: '/resetdb', 93 | method: 'post' 94 | }) 95 | ``` 96 | 其实就是将`db_backup.json`的内容覆盖到`db.json`里。如果其他的实体的db.json文件也需如此,可按照代码自行添加 97 | 98 | -------------------------------------------------------------------------------- /docs/permission.md: -------------------------------------------------------------------------------- 1 | # 权限模型 2 | 3 | lazy mock基于RBAC模型实现了权限控制。 4 | 5 | ![RBAC](images/rbac.jpg) -------------------------------------------------------------------------------- /docs/permissionapi.md: -------------------------------------------------------------------------------- 1 | ## 授权 2 | 3 | ### 登录 4 | 5 | #### Request 6 | 7 | ```js 8 | request({ 9 | url: '/auth/login', 10 | method: 'post', 11 | data: qs.stringify({ 12 | username: 'admin', 13 | password: '123' 14 | }) 15 | }) 16 | ``` 17 | >使用了`qs`的`stringify`,与直接传入js对象的区别是:使用前者,axios会将请求头`content-type`设为`application/x-www-form-urlencoded`,避免浏览器发起`options`请求;后者使用默认的`application/json`。后端`koa`接收的时候,都是通过`ctx.request.body`接收。 18 | 19 | #### Response 20 | 21 | ```js 22 | { 23 | "statusCode": 200, 24 | "msg": "", 25 | "data": { 26 | "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxIiwiaWF0IjoxNTMyMzE2NzIzLCJleHAiOjE1MzI5MjE1MjN9.fCWajolT_ttl_2UGHSw16_lRUNFwxlU6Tl30pt33kaY" 27 | } 28 | } 29 | ``` 30 | 31 | #### Error 32 | ```js 33 | { 34 | "statusCode": 500, 35 | "msg": "账号或密码错误!", 36 | "data": null 37 | } 38 | ``` 39 | 40 | ### 登出 41 | 42 | #### Request 43 | ```js 44 | request({ 45 | url: '/auth/logout', 46 | method: 'post' 47 | }) 48 | ``` 49 | #### Response 50 | ```js 51 | { 52 | "statusCode": 200, 53 | "msg": "", 54 | "data": null 55 | } 56 | ``` 57 | 58 | ## 用户 59 | 60 | ### 获取用户信息 61 | 62 | #### Request 63 | ```js 64 | request({ 65 | url: '/user/info', 66 | method: 'get' 67 | }) 68 | ``` 69 | 70 | #### Response 71 | ```js 72 | { 73 | "statusCode": 200, 74 | "msg": "", 75 | "data": { 76 | "userName": "admin", 77 | "userRole": [ 78 | "role_test", 79 | "role_website_admin" 80 | ], 81 | "userPermission": [ 82 | "post_edit", 83 | "post_view", 84 | "post_del", 85 | "menu_view", 86 | "role_view", 87 | "role_permission_view", 88 | "role_user_view", 89 | "user_role_view", 90 | "user_view", 91 | "department_view", 92 | "position_view" 93 | ], 94 | "isAdmin": 1, 95 | "avatarUrl": "https://api.adorable.io/avatars/85/abott@adorable.png" 96 | } 97 | } 98 | ``` 99 | > 包含用户角色权限信息 100 | 101 | ### 新增或更新用户 102 | #### Request 103 | ```js 104 | request({ 105 | url: '/user/save', 106 | method: 'post', 107 | data: { 108 | id: "1", 109 | name: "admin", 110 | email: "123@qq.com", 111 | phone: "18290024784", 112 | trueName: "张三" 113 | } 114 | }) 115 | ``` 116 | !>不设置id则为新增,否则为更新 117 | 118 | #### Response 119 | ```js 120 | { 121 | "statusCode": 200, 122 | "msg": "", 123 | "data": null 124 | } 125 | ``` 126 | 127 | ### 删除用户 128 | #### Request 129 | ```js 130 | request({ 131 | url: '/user/del', 132 | method: 'delete', 133 | params: { 134 | id: "1" 135 | } 136 | }) 137 | ``` 138 | ```js 139 | request({ 140 | url: '/user/batchdel', 141 | method: 'delete', 142 | params: { 143 | ids:"['1','2']" 144 | }) 145 | ``` 146 | #### Response 147 | ```js 148 | { 149 | "statusCode": 200, 150 | "msg": "", 151 | "data": null 152 | } 153 | ``` 154 | #### Error 155 | ```js 156 | { 157 | "statusCode": 500, 158 | "msg": "不能删除管理员账号", 159 | "data": null 160 | } 161 | ``` 162 | ### 用户列表 163 | #### Request 164 | ```js 165 | request({ 166 | url: '/user/pagedlist', 167 | method: 'get', 168 | params: { 169 | pageIndex: 1, 170 | pageSize: 10, 171 | sortBy: 'name', 172 | descending: true, 173 | filter: { 174 | name: 'admin', 175 | email: '', 176 | } 177 | } 178 | }) 179 | ``` 180 | #### Response 181 | ```js 182 | { 183 | "statusCode": 200, 184 | "msg": "", 185 | "data": { 186 | "totalCount": 1, 187 | "rows": [ 188 | { 189 | "id": "1", 190 | "name": "admin", 191 | "password": "123", 192 | "email": "123@qq.com", 193 | "phone": "18290024784", 194 | "trueName": "张三" 195 | } 196 | ] 197 | } 198 | } 199 | ``` 200 | 201 | ## 角色 202 | ### 新增或更新角色 203 | #### Request 204 | ```js 205 | request({ 206 | url: '/role/save', 207 | method: 'post', 208 | data: { 209 | id: "1", 210 | name: "网站管理员", 211 | code: "role_website_admin", 212 | description: "xxoo" 213 | } 214 | }) 215 | ``` 216 | !>不设置id则为新增,否则为更新 217 | #### Response 218 | ```js 219 | { 220 | "statusCode": 200, 221 | "msg": "", 222 | "data": null 223 | } 224 | ``` 225 | 226 | ### 删除角色 227 | #### Request 228 | ```js 229 | request({ 230 | url: '/role/del', 231 | method: 'delete', 232 | params: { 233 | id: "1" 234 | } 235 | }) 236 | ``` 237 | ```js 238 | request({ 239 | url: '/role/batchdel', 240 | method: 'delete', 241 | params: { 242 | ids:"['1','2']" 243 | }) 244 | ``` 245 | #### Response 246 | ```js 247 | { 248 | "statusCode": 200, 249 | "msg": "", 250 | "data": null 251 | } 252 | ``` 253 | 254 | ### 角色列表 255 | #### Request 256 | ```js 257 | request({ 258 | url: '/role/pagedlist', 259 | method: 'get', 260 | params: { 261 | pageIndex: 1, 262 | pageSize: 10, 263 | sortBy: 'name', 264 | descending: true, 265 | filter: { 266 | name: '', 267 | code: '', 268 | } 269 | } 270 | }) 271 | ``` 272 | #### Response 273 | ```js 274 | { 275 | "statusCode": 200, 276 | "msg": "", 277 | "data": { 278 | "totalCount": 2, 279 | "rows": [ 280 | { 281 | "name": "测试", 282 | "code": "role_test", 283 | "description": "具备全部数据查看权限,没有相关系统设置的操作权限", 284 | "id": "40af8f42-3b18-410c-9fc2-aba8158e92d7" 285 | }, 286 | { 287 | "name": "网站模块管理员", 288 | "code": "role_website_admin", 289 | "description": "网站模块管理员", 290 | "id": "9fc587ff-3543-4f58-93ab-15f64d3d19e5" 291 | } 292 | ] 293 | } 294 | } 295 | ``` 296 | 297 | ## 功能 298 | ### 新增或更新功能 299 | #### Request 300 | ```js 301 | request({ 302 | url: '/function/save', 303 | method: 'post', 304 | data: { 305 | id: "2817154d-2df0-4875-ac26-5c3dd27061ad", 306 | name: "2-文章编辑", 307 | code: "post_edit", 308 | description: "文章编辑", 309 | moduleId: 3, 310 | module: "文章管理" 311 | } 312 | }) 313 | ``` 314 | !>不设置id则为新增,否则为更新 315 | >`moduleId`为菜单id,`module`为菜单名称,方便查询 316 | 317 | #### Response 318 | ```js 319 | { 320 | "statusCode": 200, 321 | "msg": "", 322 | "data": null 323 | } 324 | ``` 325 | 326 | ### 删除功能 327 | #### Request 328 | ```js 329 | request({ 330 | url: '/function/del', 331 | method: 'delete', 332 | params: { 333 | id: "1" 334 | } 335 | }) 336 | ``` 337 | ```js 338 | request({ 339 | url: '/function/batchdel', 340 | method: 'delete', 341 | params: { 342 | ids:"['1','2']" 343 | }) 344 | ``` 345 | #### Response 346 | ```js 347 | { 348 | "statusCode": 200, 349 | "msg": "", 350 | "data": null 351 | } 352 | ``` 353 | 354 | ### 功能列表 355 | #### Request 356 | ```js 357 | request({ 358 | url: '/function/pagedlist', 359 | method: 'get', 360 | params: { 361 | pageIndex: 1, 362 | pageSize: 10, 363 | sortBy: 'name', 364 | descending: true, 365 | filter: { 366 | name: '', 367 | code: '', 368 | module: '文章管理' 369 | } 370 | } 371 | }) 372 | ``` 373 | #### Response 374 | ```js 375 | { 376 | "statusCode": 200, 377 | "msg": "", 378 | "data": { 379 | "totalCount": 3, 380 | "rows": [ 381 | { 382 | "name": "1-文章列表", 383 | "moduleId": 3, 384 | "module": "文章管理", 385 | "code": "post_view", 386 | "description": "文章列表", 387 | "id": "17684882-c610-4007-b357-e783631c059f" 388 | }, 389 | { 390 | "name": "2-文章编辑", 391 | "code": "post_edit", 392 | "description": "文章编辑", 393 | "moduleId": 3, 394 | "module": "文章管理", 395 | "id": "2817154d-2df0-4875-ac26-5c3dd27061ad" 396 | }, 397 | { 398 | "name": "3-文章删除", 399 | "code": "post_del", 400 | "description": "文章删除", 401 | "moduleId": 3, 402 | "module": "文章管理", 403 | "id": "8ab3b7b6-921a-4f8f-9bad-2fd106c870e1" 404 | } 405 | ] 406 | } 407 | } 408 | ``` 409 | 410 | ## 角色用户 411 | ### 关联角色用户 412 | #### Request 413 | ```js 414 | request({ 415 | url: '/user/editroleuser', 416 | method: 'post', 417 | data: { 418 | roleId: '', 419 | userId: '', 420 | action: 1 421 | } 422 | }) 423 | ``` 424 | !>`action`为1则添加关联,2则删除关联 425 | #### Reponse 426 | ```js 427 | { 428 | "statusCode": 200, 429 | "msg": "", 430 | "data": null 431 | } 432 | ``` 433 | 434 | ## 角色权限 435 | 436 | ### 保存角色权限 437 | #### Request 438 | ```js 439 | request({ 440 | url: '/role/savepermission', 441 | method: 'post', 442 | data: { 443 | roleId: '2817154d-2df0-4875-ac26-5c3dd27061ad', 444 | permissions: ['40af8f42-3b18-410c-9fc2-aba8158e92d7'] 445 | } 446 | }) 447 | ``` 448 | >`permissions`为功能id列表,表明当前角色有权限访问这些功能或资源 449 | 450 | #### Response 451 | ```js 452 | { 453 | "statusCode": 200, 454 | "msg": "", 455 | "data": null 456 | } 457 | ``` 458 | 459 | ## 菜单 460 | ### 添加或更新菜单 461 | #### Request 462 | ```js 463 | request({ 464 | url: '/menu/savemenu', 465 | method: 'post', 466 | data: { 467 | id: 3, 468 | parentId: 2 469 | icon: "settings", 470 | title: "文章管理", 471 | name: "article", 472 | leftMemu: true, 473 | functionCode: "article_view", 474 | sort: 2, 475 | isLock: false 476 | } 477 | }) 478 | ``` 479 | !>不设置id则为新增,否则为更新。parentId为0则为顶级菜单 480 | 481 | #### Response 482 | ```js 483 | { 484 | "statusCode": 200, 485 | "msg": "", 486 | "data": null 487 | } 488 | ``` 489 | 490 | ### 菜单列表 491 | #### Request 492 | ```js 493 | request({ 494 | url: '/menu', 495 | method: 'get' 496 | }) 497 | ``` 498 | #### Response 499 | ```js 500 | { 501 | "statusCode": 200, 502 | "msg": "", 503 | "data": [ 504 | { 505 | "id": 4, 506 | "parentId": 0, 507 | "path": "", 508 | "icon": "appstore", 509 | "title": "系统", 510 | "name": "系统", 511 | "leftMemu": true, 512 | "functionCode": "", 513 | "sort": 1, 514 | "children": [ 515 | { 516 | "id": 5, 517 | "parentId": 4, 518 | "path": "/system", 519 | "icon": "setting", 520 | "title": "系统设置", 521 | "name": "系统设置", 522 | "leftMemu": true, 523 | "functionCode": "", 524 | "children": [ 525 | { 526 | "id": 6, 527 | "parentId": 5, 528 | "path": "menu", 529 | "icon": "chrome", 530 | "title": "菜单管理", 531 | "name": "menu", 532 | "leftMemu": true, 533 | "functionCode": "menu_view", 534 | "children": [ 535 | 536 | ] 537 | } 538 | ] 539 | }, 540 | { 541 | "id": 7, 542 | "parentId": 4, 543 | "path": "/permission", 544 | "icon": "key", 545 | "title": "权限管理", 546 | "name": "权限管理", 547 | "leftMemu": true, 548 | "functionCode": "", 549 | "children": [ 550 | { 551 | "id": 8, 552 | "parentId": 7, 553 | "path": "function", 554 | "icon": "solution", 555 | "title": "功能管理", 556 | "name": "function", 557 | "leftMemu": true, 558 | "functionCode": "function_view", 559 | "children": [ 560 | 561 | ] 562 | }, 563 | { 564 | "id": 20, 565 | "parentId": 7, 566 | "path": "role", 567 | "icon": "idcard", 568 | "title": "角色管理", 569 | "name": "role", 570 | "leftMemu": true, 571 | "functionCode": "role_view", 572 | "children": [ 573 | 574 | ] 575 | }, 576 | { 577 | "id": 9, 578 | "parentId": 7, 579 | "path": "rolepermission", 580 | "icon": "calculator", 581 | "title": "角色权限管理", 582 | "name": "rolepermission", 583 | "leftMemu": true, 584 | "functionCode": "role_permission_view", 585 | "children": [ 586 | 587 | ] 588 | }, 589 | { 590 | "id": 10, 591 | "parentId": 7, 592 | "path": "roleuser", 593 | "icon": "android", 594 | "title": "角色用户管理", 595 | "name": "roleuser", 596 | "leftMemu": true, 597 | "functionCode": "role_user_view", 598 | "children": [ 599 | 600 | ] 601 | }, 602 | { 603 | "id": 11, 604 | "parentId": 7, 605 | "path": "userrole", 606 | "icon": "dropbox", 607 | "title": "用户角色管理", 608 | "name": "userrole", 609 | "leftMemu": true, 610 | "functionCode": "user_role_view", 611 | "children": [ 612 | 613 | ] 614 | } 615 | ] 616 | } 617 | ] 618 | } 619 | ] 620 | } 621 | ``` 622 | 623 | ### 获取授权菜单 624 | #### Request 625 | ```js 626 | request({ 627 | url: '/menu/getaccessmenu', 628 | method: 'get' 629 | }) 630 | ``` 631 | #### Response 632 | >如[菜单列表](#菜单列表)接口返回格式 633 | 634 | ### 菜单功能权限 635 | #### Request 636 | ```js 637 | request({ 638 | url: '/menu/menufunctions', 639 | method: 'get', 640 | params: { 641 | menuId:0, 642 | roleId:'1' 643 | } 644 | }) 645 | ``` 646 | #### Response 647 | ```js 648 | { 649 | "statusCode": 200, 650 | "msg": "", 651 | "data": { 652 | "menuFunctions": [ 653 | { 654 | "id": 6, 655 | "parentId": 5, 656 | "path": "menu", 657 | "icon": "chrome", 658 | "title": "菜单管理", 659 | "name": "menu", 660 | "leftMemu": true, 661 | "functionCode": "menu_view", 662 | "functions": [ 663 | { 664 | "name": "1-菜单列表", 665 | "code": "menu_view", 666 | "description": "查看菜单列表", 667 | "moduleId": 6, 668 | "module": "菜单管理", 669 | "id": "6f3ef7c1-53fb-47b6-8800-0adff147c295" 670 | }, 671 | { 672 | "name": "2-菜单编辑", 673 | "code": "menu_edit", 674 | "description": "菜单编辑", 675 | "moduleId": 6, 676 | "module": "菜单管理", 677 | "id": "f8297890-5a35-4704-b901-6b25074beb43" 678 | } 679 | ] 680 | }, 681 | ... 682 | ], 683 | "roleFunctions": [ 684 | { 685 | "roleId": "40af8f42-3b18-410c-9fc2-aba8158e92d7", 686 | "functionId": "2de3ee27-a382-4bbb-8c8f-fa6f73c99d1d", 687 | "moduleId": 20, 688 | "id": "6b0365da-54d2-49e6-bb28-e129d1bbce1a" 689 | }, 690 | ... 691 | ] 692 | } 693 | } 694 | ``` 695 | >`menuFunctions`为菜单列表,`menuFunctions.functions`菜单下相应的功能或资源 696 | 697 | >`roleFunctions`为对应角色具备的权限列表 698 | 699 | >此接口用在角色权限编辑 -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | ## 安装 2 | 3 | ``` bash 4 | $ npm install -g lazy-mock 5 | ``` 6 | 7 | ## 使用 8 | 9 | ``` bash 10 | $ lazy-mock init rbac lazy-mock-rbac 11 | ``` 12 | 13 | ## 安装依赖 14 | 进入 lazy-mock-rbac 文件夹,通过 `npm install` 安装依赖 15 | ```bash 16 | npm install 17 | ``` 18 | 19 | ## 启动程序 20 | 通过 `gulp-nodemon` ,程序运行过程,修改受监控的文件时,程序会自动重启。 21 | ```bash 22 | npm run start 23 | ``` 24 | 25 | ## 登录 26 | 使用 postman 模拟登陆,拿到 token 27 | ![image](https://raw.githubusercontent.com/wjkang/lazy-mock/master/screenshot/2.jpg) 28 | 29 | >内置账号:admin,密码:123 30 | 31 | >用户,权限管理等信息保存在 `src/db/db.json` 文件中,后面增加实体会生成独立的json文件 -------------------------------------------------------------------------------- /docs/template.md: -------------------------------------------------------------------------------- 1 | ### 目前支持的模板 2 | * [rbac](https://github.com/lazy-mock-templates/rbac) - 包含 RBAC 权限控制模型 3 | * [d2-admin-pm](https://github.com/lazy-mock-templates/d2-admin-pm) - 包含 [d2-admin-pm](https://github.com/wjkang/d2-admin-pm) 的 curd 模板 4 | 5 | ### 开发模板 6 | 7 | 参照 [rbac](https://github.com/lazy-mock-templates/rbac) 模板 8 | 9 | 自定义模板的使用: 10 | ``` bash 11 | $ lazy-mock init username/repo my-project 12 | ``` -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('@babel/register') 2 | require('@babel/polyfill') 3 | const gulp = require('gulp') 4 | const nodemon = require('gulp-nodemon') 5 | const rename = require('gulp-rename') 6 | const nunjucksRender = require('gulp-nunjucks-render') 7 | const codeGenerate = require('./templates/generate') 8 | const migrate = require('./migration') 9 | 10 | var jsScript = 'node' 11 | if (process.env.npm_config_argv !== undefined && process.env.npm_config_argv.indexOf('debug') > 0) { 12 | jsScript = 'node debug' 13 | } 14 | gulp.task('nodemon', function () { 15 | return nodemon({ 16 | script: 'build/dev-server.js', 17 | execMap: { 18 | js: jsScript 19 | }, 20 | verbose: true, 21 | ignore: ['build/*.js', 'dist/*.js', 'nodemon.json', '.git', 'node_modules/**/node_modules', 'gulpfile.js', 'src/db', 'codeGenerate'], 22 | env: { 23 | NODE_ENV: 'development' 24 | }, 25 | ext: 'js json' 26 | }) 27 | }) 28 | 29 | const ServerFullPath = require('./package.json').ServerFullPath; 30 | const FrontendFullPath = require('./package.json').FrontendFullPath; 31 | const nunjucksRenderConfig = { 32 | path: 'templates/server', 33 | envOptions: { 34 | tags: { 35 | blockStart: '<%', 36 | blockEnd: '%>', 37 | variableStart: '<$', 38 | variableEnd: '$>', 39 | commentStart: '<#', 40 | commentEnd: '#>' 41 | }, 42 | }, 43 | ext: '.js', 44 | ServerFullPath, 45 | FrontendFullPath 46 | } 47 | 48 | gulp.task('code', function() { 49 | require('events').EventEmitter.defaultMaxListeners = 0 50 | return codeGenerate.run(gulp, nunjucksRender, rename, nunjucksRenderConfig) 51 | }) 52 | 53 | gulp.task('migrate', async function(cb) { 54 | let options = process.argv[4] 55 | if (options) { 56 | options = JSON.parse(options) 57 | } 58 | let modules = [] 59 | for (let module of options) { 60 | let s = module.split(':') 61 | modules.push({ 62 | entity: s[0], 63 | keys: s[1] ? s[1].split(',') : [] 64 | }) 65 | } 66 | await migrate(modules, cb) 67 | }) 68 | 69 | gulp.task('add', async function(cb) { 70 | let options = process.argv[4].split('|') 71 | if (options.length === 1) { 72 | options[1] = 'new_api' 73 | } 74 | if (options.length === 2) { 75 | options[2] = options[1] 76 | } 77 | await codeGenerate.quickAdd(options, nunjucksRenderConfig) 78 | }) 79 | -------------------------------------------------------------------------------- /migration/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | server: { 3 | api: { 4 | target: '/src/routes/bookRoute.js', 5 | migrate: [ 6 | { 7 | from: '/book/proxy', 8 | to: '/book/proxy123' 9 | }, 10 | { 11 | from: '/book/paged', 12 | to: '/book/paged' 13 | }, 14 | { 15 | from: '/book/:id', 16 | to: '/book/:id' 17 | }, 18 | { 19 | from: '/book/del', 20 | to: '/book/del' 21 | }, 22 | { 23 | from: '/book/batchdel', 24 | to: '/book/batchdel' 25 | }, 26 | { 27 | from: '/book/save', 28 | to: '/book/save' 29 | } 30 | ], 31 | prefix: '', 32 | suffix: '' 33 | }, 34 | controller: { 35 | target: '/src/controllers/book.js', 36 | migrate: [ 37 | { 38 | from: 'getBook', 39 | to: 'getBook' 40 | }, 41 | { 42 | from: 'getBookPagedList', 43 | to: 'getBookPagedList' 44 | }, 45 | { 46 | from: 'delBook', 47 | to: 'delBook' 48 | }, 49 | { 50 | from: 'delBooks', 51 | to: 'delBooks' 52 | }, 53 | { 54 | from: 'saveBook', 55 | to: 'saveBook' 56 | }, 57 | { 58 | from: 'bookProxy', 59 | to: 'bookProxy' 60 | } 61 | ], 62 | prefix: 'export let ', 63 | suffix: ' = async' 64 | } 65 | }, 66 | frontEnd: { 67 | test: { 68 | target: '/wewew.txt', 69 | migrate: [ 70 | { 71 | from: 'df', 72 | to: 'jjjjjjjjjjjj' 73 | } 74 | ], 75 | prefix: '', 76 | suffix: '' 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /migration/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | 3 | let requireDirectory = require('require-directory') 4 | let modules = requireDirectory(module) 5 | const ServerFullPath = require('../package.json').ServerFullPath 6 | const FrontEndFullPath = require('../package.json').FrontendFullPath 7 | module.exports = async function migrate(migrateModules, cb) { 8 | for (let migrateModule of migrateModules) { 9 | let needMigrate = modules[migrateModule.entity] 10 | if (!needMigrate) { 11 | continue 12 | } 13 | // server 14 | if (migrateModule.keys.length > 0) { 15 | for (let key of migrateModule.keys) { 16 | if (key !== 'frontEnd' && key !== 'front') { 17 | await migrateItem(needMigrate.server[key], ServerFullPath) 18 | } 19 | } 20 | } else { 21 | for (let key in needMigrate.server) { 22 | await migrateItem(needMigrate.server[key], ServerFullPath) 23 | } 24 | } 25 | // front 26 | for (let key in needMigrate.frontEnd) { 27 | await migrateItem(needMigrate.frontEnd[key], FrontEndFullPath) 28 | } 29 | } 30 | cb() 31 | } 32 | async function migrateItem(item, rootPath) { 33 | if (!item) { 34 | return 35 | } 36 | const fileFullPath = rootPath + item.target 37 | let content = await fs.readFile(fileFullPath, 'utf-8') 38 | for (let detail of item.migrate) { 39 | let from = item.prefix + detail.from + item.suffix 40 | let to = item.prefix + detail.to + item.suffix 41 | content = content.replace(new RegExp(from, 'ig'), to) 42 | } 43 | await fs.writeFile(fileFullPath, content) 44 | } 45 | -------------------------------------------------------------------------------- /nunjucks.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | fileTypes 8 | 9 | nunjucks 10 | nunjs 11 | nj 12 | njk 13 | html 14 | htm 15 | template 16 | tmpl 17 | tpl 18 | 19 | 20 | name 21 | Nunjucks 22 | 23 | patterns 24 | 25 | 26 | 27 | begin 28 | <% comment %> 29 | end 30 | <% endcomment %> 31 | name 32 | comment.block.nunjucks 33 | 34 | 35 | 36 | begin 37 | <# 38 | end 39 | #> 40 | name 41 | comment.line.number-sign.nunjucks 42 | 43 | 44 | 45 | begin 46 | <\$ 47 | captures 48 | 49 | 0 50 | 51 | name 52 | entity.tag.tagbraces.nunjucks 53 | 54 | 55 | end 56 | \$> 57 | name 58 | storage.type.variable.nunjucks 59 | patterns 60 | 61 | 62 | include 63 | #template_filter 64 | 65 | 66 | 67 | 68 | 69 | begin 70 | <%-|<% 71 | captures 72 | 73 | 0 74 | 75 | name 76 | entity.tag.tagbraces.nunjucks 77 | 78 | 79 | end 80 | -%>|%> 81 | name 82 | storage.type.templatetag.nunjucks 83 | patterns 84 | 85 | 86 | include 87 | #template_tag 88 | 89 | 90 | include 91 | #template_filter 92 | 93 | 94 | 95 | 96 | 97 | begin 98 | (<)([a-zA-Z0-9:]++)(?=[^>]*></\2>) 99 | beginCaptures 100 | 101 | 1 102 | 103 | name 104 | punctuation.definition.tag.begin.html 105 | 106 | 2 107 | 108 | name 109 | entity.name.tag.html 110 | 111 | 112 | end 113 | (>)(<)(/)(\2)(>) 114 | endCaptures 115 | 116 | 1 117 | 118 | name 119 | punctuation.definition.tag.end.html 120 | 121 | 2 122 | 123 | name 124 | punctuation.definition.tag.begin.html meta.scope.between-tag-pair.html 125 | 126 | 3 127 | 128 | name 129 | punctuation.definition.tag.begin.html 130 | 131 | 4 132 | 133 | name 134 | entity.name.tag.html 135 | 136 | 5 137 | 138 | name 139 | punctuation.definition.tag.end.html 140 | 141 | 142 | name 143 | meta.tag.any.html 144 | patterns 145 | 146 | 147 | include 148 | #tag-stuff 149 | 150 | 151 | 152 | 153 | 154 | begin 155 | (<\?)(xml) 156 | captures 157 | 158 | 1 159 | 160 | name 161 | punctuation.definition.tag.html 162 | 163 | 2 164 | 165 | name 166 | entity.name.tag.xml.html 167 | 168 | 169 | end 170 | (\?>) 171 | name 172 | meta.tag.preprocessor.xml.html 173 | patterns 174 | 175 | 176 | include 177 | #tag-generic-attribute 178 | 179 | 180 | include 181 | #string-double-quoted 182 | 183 | 184 | include 185 | #string-single-quoted 186 | 187 | 188 | 189 | 190 | 191 | begin 192 | <!-- 193 | captures 194 | 195 | 0 196 | 197 | name 198 | punctuation.definition.comment.html 199 | 200 | 201 | end 202 | --\s*> 203 | name 204 | comment.block.html 205 | patterns 206 | 207 | 208 | match 209 | -- 210 | name 211 | invalid.illegal.bad-comments-or-CDATA.html 212 | 213 | 214 | include 215 | #embedded-code 216 | 217 | 218 | 219 | 220 | 221 | begin 222 | <! 223 | captures 224 | 225 | 0 226 | 227 | name 228 | punctuation.definition.tag.html 229 | 230 | 231 | end 232 | > 233 | name 234 | meta.tag.sgml.html 235 | patterns 236 | 237 | 238 | begin 239 | (?i:DOCTYPE) 240 | captures 241 | 242 | 1 243 | 244 | name 245 | entity.name.tag.doctype.html 246 | 247 | 248 | end 249 | (?=>) 250 | name 251 | meta.tag.sgml.doctype.html 252 | patterns 253 | 254 | 255 | match 256 | "[^">]*" 257 | name 258 | string.quoted.double.doctype.identifiers-and-DTDs.html 259 | 260 | 261 | 262 | 263 | begin 264 | \[CDATA\[ 265 | end 266 | ]](?=>) 267 | name 268 | constant.other.inline-data.html 269 | 270 | 271 | match 272 | (\s*)(?!--|>)\S(\s*) 273 | name 274 | invalid.illegal.bad-comments-or-CDATA.html 275 | 276 | 277 | 278 | 279 | 280 | include 281 | #embedded-code 282 | 283 | 284 | begin 285 | (?:^\s+)?(<)((?i:style))\b(?![^>]*/>) 286 | captures 287 | 288 | 1 289 | 290 | name 291 | punctuation.definition.tag.begin.html 292 | 293 | 2 294 | 295 | name 296 | entity.name.tag.style.html 297 | 298 | 3 299 | 300 | name 301 | punctuation.definition.tag.html 302 | 303 | 304 | end 305 | (</)((?i:style))(>)(?:\s*\n)? 306 | name 307 | source.css.embedded.html 308 | patterns 309 | 310 | 311 | include 312 | #tag-stuff 313 | 314 | 315 | begin 316 | (>) 317 | beginCaptures 318 | 319 | 1 320 | 321 | name 322 | punctuation.definition.tag.end.html 323 | 324 | 325 | end 326 | (?=</(?i:style)) 327 | patterns 328 | 329 | 330 | begin 331 | <% comment %> 332 | end 333 | <% endcomment %> 334 | name 335 | comment.block.nunjucks 336 | 337 | 338 | begin 339 | <# 340 | end 341 | #> 342 | name 343 | comment.line.number-sign.nunjucks 344 | 345 | 346 | begin 347 | <\$ 348 | captures 349 | 350 | 0 351 | 352 | name 353 | entity.tag.tagbraces.nunjucks 354 | 355 | 356 | end 357 | \$> 358 | name 359 | storage.type.variable.nunjucks 360 | patterns 361 | 362 | 363 | include 364 | #template_filter 365 | 366 | 367 | 368 | 369 | begin 370 | <% 371 | captures 372 | 373 | 0 374 | 375 | name 376 | entity.tag.tagbraces.nunjucks 377 | 378 | 379 | end 380 | %> 381 | name 382 | storage.type.templatetag.nunjucks 383 | patterns 384 | 385 | 386 | include 387 | #template_tag 388 | 389 | 390 | include 391 | #template_filter 392 | 393 | 394 | 395 | 396 | include 397 | #embedded-code 398 | 399 | 400 | include 401 | source.css.nunjucks 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | begin 410 | (?:^\s+)?(<)((?i:script))\b(?![^>]*/>) 411 | beginCaptures 412 | 413 | 1 414 | 415 | name 416 | punctuation.definition.tag.begin.html 417 | 418 | 2 419 | 420 | name 421 | entity.name.tag.script.html 422 | 423 | 424 | end 425 | (?<=</(script|SCRIPT))(>)(?:\s*\n)? 426 | endCaptures 427 | 428 | 2 429 | 430 | name 431 | punctuation.definition.tag.html 432 | 433 | 434 | name 435 | source.js.embedded.html 436 | patterns 437 | 438 | 439 | include 440 | #tag-stuff 441 | 442 | 443 | begin 444 | (?<!</(?:script|SCRIPT))(>) 445 | captures 446 | 447 | 1 448 | 449 | name 450 | punctuation.definition.tag.end.html 451 | 452 | 2 453 | 454 | name 455 | entity.name.tag.script.html 456 | 457 | 458 | end 459 | (</)((?i:script)) 460 | patterns 461 | 462 | 463 | begin 464 | <% comment %> 465 | end 466 | <% endcomment %> 467 | name 468 | comment.block.nunjucks 469 | 470 | 471 | begin 472 | <# 473 | end 474 | #> 475 | name 476 | comment.line.number-sign.nunjucks 477 | 478 | 479 | begin 480 | <\$ 481 | captures 482 | 483 | 0 484 | 485 | name 486 | entity.tag.tagbraces.nunjucks 487 | 488 | 489 | end 490 | \$> 491 | name 492 | storage.type.variable.nunjucks 493 | patterns 494 | 495 | 496 | include 497 | #template_filter 498 | 499 | 500 | 501 | 502 | begin 503 | <% 504 | captures 505 | 506 | 0 507 | 508 | name 509 | entity.tag.tagbraces.nunjucks 510 | 511 | 512 | end 513 | %> 514 | name 515 | storage.type.templatetag.nunjucks 516 | patterns 517 | 518 | 519 | include 520 | #template_tag 521 | 522 | 523 | include 524 | #template_filter 525 | 526 | 527 | 528 | 529 | captures 530 | 531 | 1 532 | 533 | name 534 | punctuation.definition.comment.js 535 | 536 | 537 | match 538 | (//).*?((?=</script)|$\n?) 539 | name 540 | comment.line.double-slash.js 541 | 542 | 543 | begin 544 | /\* 545 | captures 546 | 547 | 0 548 | 549 | name 550 | punctuation.definition.comment.js 551 | 552 | 553 | end 554 | \*/|(?=</script) 555 | name 556 | comment.block.js 557 | 558 | 559 | include 560 | #php 561 | 562 | 563 | include 564 | source.js 565 | 566 | 567 | 568 | 569 | 570 | 571 | begin 572 | (</?)((?i:body|head|html)\b) 573 | captures 574 | 575 | 1 576 | 577 | name 578 | punctuation.definition.tag.begin.html 579 | 580 | 2 581 | 582 | name 583 | entity.name.tag.structure.any.html 584 | 585 | 586 | end 587 | (>) 588 | endCaptures 589 | 590 | 1 591 | 592 | name 593 | punctuation.definition.tag.end.html 594 | 595 | 596 | name 597 | meta.tag.structure.any.html 598 | patterns 599 | 600 | 601 | include 602 | #tag-stuff 603 | 604 | 605 | 606 | 607 | begin 608 | (</?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\b) 609 | beginCaptures 610 | 611 | 1 612 | 613 | name 614 | punctuation.definition.tag.begin.html 615 | 616 | 2 617 | 618 | name 619 | entity.name.tag.block.any.html 620 | 621 | 622 | end 623 | (>) 624 | endCaptures 625 | 626 | 1 627 | 628 | name 629 | punctuation.definition.tag.end.html 630 | 631 | 632 | name 633 | meta.tag.block.any.html 634 | patterns 635 | 636 | 637 | include 638 | #tag-stuff 639 | 640 | 641 | 642 | 643 | begin 644 | (</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\b) 645 | beginCaptures 646 | 647 | 1 648 | 649 | name 650 | punctuation.definition.tag.begin.html 651 | 652 | 2 653 | 654 | name 655 | entity.name.tag.inline.any.html 656 | 657 | 658 | end 659 | ((?: ?/)?>) 660 | endCaptures 661 | 662 | 1 663 | 664 | name 665 | punctuation.definition.tag.end.html 666 | 667 | 668 | name 669 | meta.tag.inline.any.html 670 | patterns 671 | 672 | 673 | include 674 | #tag-stuff 675 | 676 | 677 | 678 | 679 | begin 680 | (</?)([a-zA-Z0-9:]+) 681 | beginCaptures 682 | 683 | 1 684 | 685 | name 686 | punctuation.definition.tag.begin.html 687 | 688 | 2 689 | 690 | name 691 | entity.name.tag.other.html 692 | 693 | 694 | end 695 | (>) 696 | endCaptures 697 | 698 | 1 699 | 700 | name 701 | punctuation.definition.tag.end.html 702 | 703 | 704 | name 705 | meta.tag.other.html 706 | patterns 707 | 708 | 709 | include 710 | #tag-stuff 711 | 712 | 713 | 714 | 715 | include 716 | #entities 717 | 718 | 719 | match 720 | <> 721 | name 722 | invalid.illegal.incomplete.html 723 | 724 | 725 | match 726 | < 727 | name 728 | invalid.illegal.bad-angle-bracket.html 729 | 730 | 731 | repository 732 | 733 | template_filter 734 | 735 | patterns 736 | 737 | 738 | match 739 | (add|addslashes|capfirst|center|cut|date|default|default_if_none|dictsort|dictsortreversed|divisibleby|escape|escapejs|filesizeformat|first|fix_ampersands|floatformat|force_escape|get_digit|iriencode|join|last|length|length_is|linebreaks|linebreaksbr|linenumbers|ljust|lower|make_list|phone2numeric|pluralize|pprint|random|removetags|rjust|safe|safeseq|slice|slugify|stringformat|striptags|time|timesince|timeutil|title|truncatewords|truncatewords_html|unordered_list|upper|urlencode|urlize|urlizetrunc|wordcount|wordwrap|yesno|apnumber|intcomma|intword|naturalday|ordinal|STATIC_PREFIX)\b 740 | name 741 | keyword.control.filter.nunjucks 742 | 743 | 744 | begin 745 | :"|" 746 | end 747 | " 748 | name 749 | storage.type.attr.nunjucks 750 | 751 | 752 | begin 753 | :\'|\' 754 | end 755 | \' 756 | name 757 | storage.type.attr.nunjucks 758 | 759 | 760 | match 761 | \| 762 | name 763 | string.unquoted.filter-pipe.nunjucks 764 | 765 | 766 | match 767 | [a-zA-Z0-9_.]+ 768 | name 769 | string.unquoted.tag-string.nunjucks 770 | 771 | 772 | 773 | template_tag 774 | 775 | patterns 776 | 777 | 778 | match 779 | \b(autoescape|endautoescape|block|endblock|blocktrans|endblocktrans|trans|plural|debug|extends|filter|firstof|for|empty|endfor|if|elif|else|endif|include|ifchanged|endifchanged|ifequal|endifequal|ifnotequal|endifnotequal|load|from|low|regroup|ssi|spaceless|endspaceless|templatetag|widthratio|with|endwith|csrf_token|cycle|url|lorem|thumbnail|endthumbnail|get_static_prefix)\b 780 | name 781 | keyword.control.tag-name.nunjucks 782 | 783 | 784 | match 785 | \b(and|or|not|in|by|as)\b 786 | name 787 | keyword.operator.nunjucks 788 | 789 | 790 | 791 | embedded-code 792 | 793 | patterns 794 | 795 | 796 | include 797 | #ruby 798 | 799 | 800 | include 801 | #php 802 | 803 | 804 | include 805 | #python 806 | 807 | 808 | 809 | entities 810 | 811 | patterns 812 | 813 | 814 | captures 815 | 816 | 1 817 | 818 | name 819 | punctuation.definition.entity.html 820 | 821 | 3 822 | 823 | name 824 | punctuation.definition.entity.html 825 | 826 | 827 | match 828 | (&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;) 829 | name 830 | constant.character.entity.html 831 | 832 | 833 | match 834 | & 835 | name 836 | invalid.illegal.bad-ampersand.html 837 | 838 | 839 | 840 | php 841 | 842 | begin 843 | (?=(^\s*)?<\?) 844 | end 845 | (?!(^\s*)?<\?) 846 | patterns 847 | 848 | 849 | include 850 | source.php 851 | 852 | 853 | 854 | python 855 | 856 | begin 857 | (?:^\s*)<\?python(?!.*\?>) 858 | end 859 | \?>(?:\s*$\n)? 860 | name 861 | source.python.embedded.html 862 | patterns 863 | 864 | 865 | include 866 | source.python 867 | 868 | 869 | 870 | ruby 871 | 872 | patterns 873 | 874 | 875 | begin 876 | <%+# 877 | captures 878 | 879 | 0 880 | 881 | name 882 | punctuation.definition.comment.erb 883 | 884 | 885 | end 886 | %> 887 | name 888 | comment.block.erb 889 | 890 | 891 | begin 892 | <%+(?!>)=? 893 | captures 894 | 895 | 0 896 | 897 | name 898 | punctuation.section.embedded.ruby 899 | 900 | 901 | end 902 | -?%> 903 | name 904 | source.ruby.embedded.html 905 | patterns 906 | 907 | 908 | captures 909 | 910 | 1 911 | 912 | name 913 | punctuation.definition.comment.ruby 914 | 915 | 916 | match 917 | (#).*?(?=-?%>) 918 | name 919 | comment.line.number-sign.ruby 920 | 921 | 922 | include 923 | source.ruby 924 | 925 | 926 | 927 | 928 | begin 929 | <\?r(?!>)=? 930 | captures 931 | 932 | 0 933 | 934 | name 935 | punctuation.section.embedded.ruby.nitro 936 | 937 | 938 | end 939 | -?\?> 940 | name 941 | source.ruby.nitro.embedded.html 942 | patterns 943 | 944 | 945 | captures 946 | 947 | 1 948 | 949 | name 950 | punctuation.definition.comment.ruby.nitro 951 | 952 | 953 | match 954 | (#).*?(?=-?\?>) 955 | name 956 | comment.line.number-sign.ruby.nitro 957 | 958 | 959 | include 960 | source.ruby 961 | 962 | 963 | 964 | 965 | 966 | 968 | string-double-quoted 969 | 970 | begin 971 | " 972 | beginCaptures 973 | 974 | 0 975 | 976 | name 977 | punctuation.definition.string.begin.html 978 | 979 | 980 | end 981 | " 982 | endCaptures 983 | 984 | 0 985 | 986 | name 987 | punctuation.definition.string.end.html 988 | 989 | 990 | name 991 | string.quoted.double.html 992 | patterns 993 | 994 | 995 | include 996 | #embedded-code 997 | 998 | 999 | include 1000 | #entities 1001 | 1002 | 1003 | 1004 | string-single-quoted 1005 | 1006 | begin 1007 | ' 1008 | beginCaptures 1009 | 1010 | 0 1011 | 1012 | name 1013 | punctuation.definition.string.begin.html 1014 | 1015 | 1016 | end 1017 | ' 1018 | endCaptures 1019 | 1020 | 0 1021 | 1022 | name 1023 | punctuation.definition.string.end.html 1024 | 1025 | 1026 | name 1027 | string.quoted.single.html 1028 | patterns 1029 | 1030 | 1031 | include 1032 | #embedded-code 1033 | 1034 | 1035 | include 1036 | #entities 1037 | 1038 | 1039 | 1040 | tag-generic-attribute 1041 | 1042 | match 1043 | \b([a-zA-Z\-:]+) 1044 | name 1045 | entity.other.attribute-name.html 1046 | 1047 | tag-id-attribute 1048 | 1049 | begin 1050 | \b(id)\b\s*(=) 1051 | captures 1052 | 1053 | 1 1054 | 1055 | name 1056 | entity.other.attribute-name.id.html 1057 | 1058 | 2 1059 | 1060 | name 1061 | punctuation.separator.key-value.html 1062 | 1063 | 1064 | end 1065 | (?<='|") 1066 | name 1067 | meta.attribute-with-value.id.html 1068 | patterns 1069 | 1070 | 1071 | begin 1072 | " 1073 | beginCaptures 1074 | 1075 | 0 1076 | 1077 | name 1078 | punctuation.definition.string.begin.html 1079 | 1080 | 1081 | contentName 1082 | meta.toc-list.id.html 1083 | end 1084 | " 1085 | endCaptures 1086 | 1087 | 0 1088 | 1089 | name 1090 | punctuation.definition.string.end.html 1091 | 1092 | 1093 | name 1094 | string.quoted.double.html 1095 | patterns 1096 | 1097 | 1098 | include 1099 | #embedded-code 1100 | 1101 | 1102 | include 1103 | #entities 1104 | 1105 | 1106 | 1107 | 1108 | begin 1109 | ' 1110 | beginCaptures 1111 | 1112 | 0 1113 | 1114 | name 1115 | punctuation.definition.string.begin.html 1116 | 1117 | 1118 | contentName 1119 | meta.toc-list.id.html 1120 | end 1121 | ' 1122 | endCaptures 1123 | 1124 | 0 1125 | 1126 | name 1127 | punctuation.definition.string.end.html 1128 | 1129 | 1130 | name 1131 | string.quoted.single.html 1132 | patterns 1133 | 1134 | 1135 | include 1136 | #embedded-code 1137 | 1138 | 1139 | include 1140 | #entities 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | tag-stuff 1147 | 1148 | patterns 1149 | 1150 | 1151 | include 1152 | #tag-id-attribute 1153 | 1154 | 1155 | include 1156 | #tag-generic-attribute 1157 | 1158 | 1159 | include 1160 | #string-double-quoted 1161 | 1162 | 1163 | include 1164 | #string-single-quoted 1165 | 1166 | 1167 | include 1168 | #embedded-code 1169 | 1170 | 1171 | 1172 | 1173 | scopeName 1174 | text.html.nunjucks 1175 | uuid 1176 | 0AAEDD0E-56AD-4B71-95C8-2FF271DE5B19 1177 | 1178 | 1179 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lazy-mock", 3 | "version": "1.0.0", 4 | "description": "lazy-mock", 5 | "author": "若邪", 6 | "scripts": { 7 | "start": "gulp nodemon", 8 | "dev": "gulp", 9 | "build": "babel src -d dist", 10 | "code": "gulp code", 11 | "migrate": "gulp migrate --options [\\\"book:api,controller\\\"] ", 12 | "add": "gulp add --options \"book|/book/new|bookNew\" ", 13 | "production": "cross-env NODE_ENV=production node dist/app.js", 14 | "doc": "docsify serve docs" 15 | }, 16 | "dependencies": { 17 | "jsonwebtoken": "^8.5.1", 18 | "koa": "^2.8.1", 19 | "koa-body": "^2.5.0", 20 | "koa-compose": "^4.1.0", 21 | "koa-jwt": "^3.6.0", 22 | "koa-router": "^7.4.0", 23 | "koa-static2": "^0.1.8", 24 | "koa2-cors": "^2.0.6", 25 | "lodash": "^4.17.15", 26 | "lodash-id": "^0.14.0", 27 | "lowdb": "^1.0.0", 28 | "matchit": "^1.0.8", 29 | "require-directory": "^2.1.1", 30 | "shortid": "^2.2.13", 31 | "ws": "^6.0.0", 32 | "superagent": "^5.1.0", 33 | "@babel/runtime": "^7.6.0" 34 | }, 35 | "devDependencies": { 36 | "@babel/cli": "^7.6.0", 37 | "@babel/core": "^7.6.0", 38 | "@babel/plugin-transform-runtime": "^7.6.0", 39 | "@babel/polyfill": "^7.6.0", 40 | "@babel/preset-env": "^7.6.0", 41 | "@babel/register": "^7.6.0", 42 | "cross-env": "^5.2.1", 43 | "gulp": "^4.0.2", 44 | "gulp-nodemon": "^2.4.2", 45 | "gulp-nunjucks-render": "^2.2.3", 46 | "gulp-rename": "^1.4.0", 47 | "mockjs": "^1.0.1-beta3", 48 | "nunjucks": "^3.2.0", 49 | "fs-extra": "^8.1.0" 50 | }, 51 | "engines": { 52 | "node": ">= 7.8.0", 53 | "npm": ">= 4.2.0" 54 | }, 55 | "ServerFullPath": "G:/GitHubProject/lazy-mock", 56 | "FrontendFullPath": "G:/GitHubProject/d2-admin-pm" 57 | } 58 | -------------------------------------------------------------------------------- /publicKey.pub: -------------------------------------------------------------------------------- 1 | wqdjkwl1e21FQlk1j2 2 | -------------------------------------------------------------------------------- /screenshot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/screenshot/1.png -------------------------------------------------------------------------------- /screenshot/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/screenshot/2.jpg -------------------------------------------------------------------------------- /screenshot/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/screenshot/3.jpg -------------------------------------------------------------------------------- /screenshot/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/screenshot/4.jpg -------------------------------------------------------------------------------- /screenshot/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjkang/lazy-mock/5e69439096f4da08df23efd623ad3e82c3e18526/screenshot/5.jpg -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import Koa2 from 'koa' 2 | import KoaBody from 'koa-body' 3 | import KoaStatic from 'koa-static2' 4 | import cors from 'koa2-cors' 5 | import { System as SystemConfig } from './config' 6 | import path from 'path' 7 | import Routes from './routes/index' 8 | import ErrorRoutesCatch from './middleware/ErrorRoutesCatch' 9 | import ParseUserInfo from './middleware/ParseUserInfo' 10 | import RequestLog from './middleware/RequestLog' 11 | import jwt from 'koa-jwt' 12 | import fs from 'fs' 13 | import im from './im' 14 | import EasySocket from './lib/EasySocket' 15 | import imRoutes from './im/routes' 16 | const https = require('https') 17 | 18 | const app = new Koa2() 19 | const env = process.env.NODE_ENV || 'development' // Current mode 20 | 21 | const publicKey = fs.readFileSync(path.join(__dirname, '../publicKey.pub')) 22 | 23 | app.use(cors()) 24 | if (env === 'development') { 25 | // logger 26 | app.use((ctx, next) => { 27 | const start = new Date() 28 | return next().then(() => { 29 | const ms = new Date() - start 30 | console.log( 31 | `${ctx.ip} ${ctx.method} ${decodeURI(ctx.url)} - ${ms}ms` 32 | ) 33 | }) 34 | }) 35 | } 36 | app.use(ErrorRoutesCatch()) 37 | .use(KoaStatic('assets', path.resolve(__dirname, '../assets'))) // Static resource 38 | .use( 39 | jwt({ secret: publicKey }).unless({ 40 | path: [/^\/public|\/auth\/login|\/resetdb|\/assets/] 41 | }) 42 | ) 43 | .use(ParseUserInfo()) 44 | if (env === 'production') { 45 | app.use(RequestLog()) 46 | } 47 | app.use( 48 | KoaBody({ 49 | multipart: true, 50 | strict: false, 51 | formidable: { 52 | uploadDir: path.join(__dirname, '../assets/uploads/tmp') 53 | }, 54 | jsonLimit: '10mb', 55 | formLimit: '10mb', 56 | textLimit: '10mb' 57 | }) 58 | ) 59 | 60 | Object.keys(Routes).forEach(function(key) { 61 | app.use(Routes[key].routes()).use(Routes[key].allowedMethods()) 62 | }) 63 | 64 | // https 65 | // .createServer(options, app.callback()) 66 | // .listen(SystemConfig.API_SERVER_PORT, () => { 67 | // console.log( 68 | // 'Now start API server on port ' + 69 | // SystemConfig.API_SERVER_PORT + 70 | // '...' 71 | // ) 72 | // }) 73 | app.listen(SystemConfig.API_SERVER_PORT) 74 | console.log( 75 | 'Now start API server on port ' + SystemConfig.API_SERVER_PORT + '...' 76 | ) 77 | 78 | const imMergeRoutes = {} 79 | Object.keys(imRoutes).forEach(function(key) { 80 | Object.assign(imMergeRoutes, imRoutes[key].default) 81 | }) 82 | const easySocket = new EasySocket() 83 | easySocket 84 | .connectionUse(im.connectMiddleware()) 85 | .closeUse(im.closeMiddleware()) 86 | //.messageUse(im.roomInfoMiddleware()) 87 | //.messageUse(im.messageMiddleware()) //与messageRouteMiddleware为两种方案 88 | .messageUse(im.messageRouteMiddleware(imMergeRoutes)) 89 | .remoteEmitUse(im.remoteEmitMiddleware()) 90 | .listen(SystemConfig.WS_CONFIG) 91 | 92 | easySocket.on('chat message', function(data) { 93 | //触发执行remoteEmit中间件(如果有) 94 | easySocket.emit('chat message', data) 95 | }) 96 | easySocket.on('user login', function(data) { 97 | //触发执行remoteEmit中间件(如果有) 98 | easySocket.emit('user login', data) 99 | }) 100 | 101 | console.log( 102 | 'Now start WebSocket server on port ' + SystemConfig.WS_CONFIG.port + '...' 103 | ) 104 | 105 | export default app 106 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | 2 | // 系统配置 3 | export let System = { 4 | API_SERVER_PORT: '3000', // API服务器监听的端口号 5 | WS_CONFIG: { 6 | port: 3001, 7 | perMessageDeflate: { 8 | zlibDeflateOptions: { // See zlib defaults. 9 | chunkSize: 1024, 10 | memLevel: 7, 11 | level: 3, 12 | }, 13 | zlibInflateOptions: { 14 | chunkSize: 10 * 1024 15 | }, 16 | // Other options settable: 17 | clientNoContextTakeover: true, // Defaults to negotiated value. 18 | serverNoContextTakeover: true, // Defaults to negotiated value. 19 | //clientMaxWindowBits: 10, // Defaults to negotiated value. 20 | serverMaxWindowBits: 10, // Defaults to negotiated value. 21 | // Below options specified as default values. 22 | concurrencyLimit: 10, // Limits zlib concurrency for perf. 23 | threshold: 1024, // Size (in bytes) below which messages 24 | // should not be compressed. 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/controllers/auth.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken' 2 | import fs from 'fs' 3 | import path from 'path' 4 | import * as responseTemplate from '../lib/responseTemplate' 5 | import userService from '../services/userService' 6 | import tokenService from '../services/tokenService' 7 | 8 | const publicKey = fs.readFileSync(path.join(__dirname, '../../publicKey.pub')) 9 | 10 | export let login = async (ctx) => { 11 | let name = ctx.request.body.username 12 | let pwd = ctx.request.body.password 13 | if (!name || !pwd) { 14 | return responseTemplate.businessError(ctx, '请输入账号密码!') 15 | } 16 | let user = await userService.getUserByNameAndPwd(name, pwd) 17 | if (!user) { 18 | return responseTemplate.businessError(ctx, '账号或密码错误!') 19 | } 20 | let token = jwt.sign({ 21 | userId: user.id // 你要保存到token的数据 22 | }, publicKey, { expiresIn: '7d' }) 23 | //await tokenService.add(token) 24 | return responseTemplate.success(ctx, { 25 | id: user.id, 26 | name: user.trueName, 27 | accessToken: token 28 | }) 29 | } 30 | export let logout = async (ctx) => { 31 | let user = ctx.user; 32 | if (!user || !user.token) { 33 | return responseTemplate.success(ctx, null) 34 | } 35 | await tokenService.remove(user.token) 36 | return responseTemplate.success(ctx, null) 37 | } 38 | -------------------------------------------------------------------------------- /src/controllers/index.js: -------------------------------------------------------------------------------- 1 | let requireDirectory = require('require-directory') 2 | module.exports = requireDirectory(module) 3 | -------------------------------------------------------------------------------- /src/controllers/interface.js: -------------------------------------------------------------------------------- 1 | import interfaceService from '../services/interfaceService' 2 | import * as responseTemplate from '../lib/responseTemplate' 3 | 4 | export let getInterface = async (ctx) => { 5 | let id = ctx.params.id 6 | console.log(id) 7 | let entity = await interfaceService.getInterface(id) 8 | if (!entity) { 9 | return responseTemplate.businessError(ctx, "接口不存在!") 10 | } 11 | return responseTemplate.success(ctx, entity) 12 | } 13 | export let getInterfacePagedList = async (ctx, next) => { 14 | let pageIndex = ctx.query.pageIndex 15 | let pageSize = ctx.query.pageSize 16 | let sortBy = ctx.query.sortBy 17 | let descending = ctx.query.descending 18 | let filter = { 19 | id: ctx.query.id, 20 | name: ctx.query.name, 21 | path: ctx.query.path, 22 | method: ctx.query.method, 23 | isLocked: ctx.query.isLocked, 24 | description: ctx.query.description, 25 | functionId: ctx.query.functionId 26 | } 27 | let pagedList = await interfaceService.getInterfacePagedList(pageIndex, pageSize, sortBy, descending, filter) 28 | return responseTemplate.success(ctx, pagedList) 29 | } 30 | export let delInterface = async (ctx) => { 31 | let id = ctx.query.id 32 | await interfaceService.delInterface(id) 33 | return responseTemplate.success(ctx, null) 34 | } 35 | 36 | export let delInterfaces = async (ctx) => { 37 | let ids = JSON.parse(ctx.query.ids) 38 | for (let id of ids) { 39 | await interfaceService.delInterface(id) 40 | } 41 | return responseTemplate.success(ctx, null) 42 | } 43 | 44 | export let saveInterface = async (ctx) => { 45 | let entity = ctx.request.body 46 | if (entity.name == "") { 47 | return responseTemplate.businessError(ctx, "名称不能为空!") 48 | } 49 | if (entity.path == "") { 50 | return responseTemplate.businessError(ctx, "接口地址不能为空!") 51 | } 52 | if (entity.method == "") { 53 | return responseTemplate.businessError(ctx, "接口方法不能为空!") 54 | } 55 | let result = await interfaceService.saveInterface(entity) 56 | if (!result.success) { 57 | return responseTemplate.businessError(ctx, result.msg) 58 | } 59 | return responseTemplate.success(ctx, null) 60 | } 61 | export let relateInterface = async (ctx) => { 62 | let functionInterface = ctx.request.body 63 | await interfaceService.relate(functionInterface) 64 | return responseTemplate.success(ctx, null) 65 | } 66 | -------------------------------------------------------------------------------- /src/controllers/menu.js: -------------------------------------------------------------------------------- 1 | import menuService from '../services/memuService' 2 | import roleService from '../services/roleService' 3 | import * as responseTemplate from '../lib/responseTemplate' 4 | 5 | export let getMenuList = async (ctx) => { 6 | let menuList = await menuService.getMenuList() 7 | return responseTemplate.success(ctx, menuList) 8 | } 9 | 10 | export let getMenu = async (ctx) => { 11 | let id = ctx.params.id 12 | let menu = await menuService.getMenu(id) 13 | if (!menu) { 14 | return responseTemplate.businessError(ctx, "菜单不存在") 15 | } 16 | return responseTemplate.success(ctx, menu) 17 | } 18 | export let saveMenu = async (ctx) => { 19 | let menu = ctx.request.body; 20 | if (menu.title == "") { 21 | return responseTemplate.businessError(ctx, "标题不能为空!") 22 | } 23 | if (!menu.type) { 24 | return responseTemplate.businessError(ctx, "请选择类型!") 25 | } 26 | if (menu.type == 1 && menu.path == "") { 27 | return responseTemplate.businessError(ctx, "路径不能为空!") 28 | } 29 | let result = await menuService.saveMenu(menu) 30 | if (!result.success) { 31 | return responseTemplate.businessError(ctx, result.msg) 32 | } 33 | return responseTemplate.success(ctx, null) 34 | } 35 | export let delMenu = async (ctx) => { 36 | let id = ctx.params.id 37 | let result = await menuService.delMenu(id) 38 | if (!result.success) { 39 | return responseTemplate.businessError(ctx, result.msg) 40 | } 41 | return responseTemplate.success(ctx, null) 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/controllers/requestlog.js: -------------------------------------------------------------------------------- 1 | import requestLogService from '../services/requestLogService' 2 | import * as responseTemplate from '../lib/responseTemplate' 3 | 4 | export let getRequestLogPagedList = async (ctx) => { 5 | let pageIndex = ctx.query.pageIndex 6 | let pageSize = ctx.query.pageSize 7 | let sortBy = ctx.query.sortBy 8 | let descending = ctx.query.descending 9 | let pagedList = await requestLogService.getRequestLogPagedList(pageIndex, pageSize, sortBy, descending) 10 | return responseTemplate.success(ctx, pagedList) 11 | } -------------------------------------------------------------------------------- /src/controllers/role.js: -------------------------------------------------------------------------------- 1 | import roleService from '../services/roleService' 2 | import menuService from '../services/memuService' 3 | import * as responseTemplate from '../lib/responseTemplate' 4 | export let getRole = async (ctx) => { 5 | let id = ctx.params.id 6 | let role = await roleService.getRole(id) 7 | if (!role) { 8 | return responseTemplate.businessError(ctx, "角色不存在") 9 | } 10 | return responseTemplate.success(ctx, role) 11 | } 12 | export let getRolePagedList = async (ctx) => { 13 | let pageIndex = ctx.query.pageIndex 14 | let pageSize = ctx.query.pageSize 15 | let sortBy = ctx.query.sortBy 16 | let descending = ctx.query.descending 17 | let filter = JSON.parse(ctx.query.filter) 18 | let pagedList = await roleService.getRolePagedList(pageIndex, pageSize, sortBy, descending, filter) 19 | return responseTemplate.success(ctx, pagedList) 20 | } 21 | export let delRole = async (ctx) => { 22 | let id = ctx.query.id 23 | await roleService.delRole(id) 24 | return responseTemplate.success(ctx, null) 25 | } 26 | 27 | export let delRoles = async (ctx) => { 28 | let ids = JSON.parse(ctx.query.ids) 29 | for (let id of ids) { 30 | await roleService.delRole(id) 31 | } 32 | return responseTemplate.success(ctx, null) 33 | } 34 | 35 | export let saveRole = async (ctx) => { 36 | let func = ctx.request.body 37 | if (func.name == "") { 38 | return responseTemplate.businessError(ctx, "名称不能为空!") 39 | } 40 | if (func.code == "") { 41 | return responseTemplate.businessError(ctx, "编码不能为空!") 42 | } 43 | let result = await roleService.saveRole(func) 44 | if (!result.success) { 45 | return responseTemplate.businessError(ctx, result.msg) 46 | } 47 | return responseTemplate.success(ctx, null) 48 | } 49 | export let getRolePermissions = async (ctx) => { 50 | let roleId = ctx.params.roleId; 51 | let rolePermissions = await roleService.getRoleFunctions(roleId); 52 | return responseTemplate.success(ctx, rolePermissions) 53 | } 54 | export let savePermission = async (ctx) => { 55 | let data = ctx.request.body; 56 | await roleService.savePermission(data.roleId, data.permissions) 57 | return responseTemplate.success(ctx, null) 58 | } -------------------------------------------------------------------------------- /src/controllers/route.js: -------------------------------------------------------------------------------- 1 | import routeService from '../services/routeService' 2 | import * as responseTemplate from '../lib/responseTemplate' 3 | 4 | export let getRoute = async (ctx) => { 5 | let id = ctx.params.id 6 | let route = await routeService.getRoute(id) 7 | if (!route) { 8 | return responseTemplate.businessError(ctx, "route不存在!") 9 | } 10 | return responseTemplate.success(ctx, route) 11 | } 12 | export let getRouteList = async (ctx) => { 13 | let routeList = await routeService.getRouteList() 14 | return responseTemplate.success(ctx, routeList) 15 | } 16 | export let getRoutePagedList = async (ctx, next) => { 17 | let pageIndex = ctx.query.pageIndex 18 | let pageSize = ctx.query.pageSize 19 | let sortBy = ctx.query.sortBy 20 | let descending = ctx.query.descending 21 | let filter = { 22 | id: ctx.query.id, 23 | parentId: ctx.query.parentId, 24 | name: ctx.query.name, 25 | path: ctx.query.path, 26 | title: ctx.query.title, 27 | component: ctx.query.component, 28 | componentPath: ctx.query.componentPath, 29 | cache: ctx.query.cache, 30 | isLock: ctx.query.isLock, 31 | sort: ctx.query.sort, 32 | } 33 | let pagedList = await routeService.getRoutePagedList(pageIndex, pageSize, sortBy, descending, filter) 34 | responseTemplate.success(ctx, pagedList) 35 | return next() 36 | } 37 | export let delRoute = async (ctx) => { 38 | let id = ctx.params.id 39 | await routeService.delRoute(id) 40 | return responseTemplate.success(ctx, null) 41 | } 42 | 43 | export let delRoutes = async (ctx) => { 44 | let ids = JSON.parse(ctx.query.ids) 45 | for (let id of ids) { 46 | await routeService.delRoute(id) 47 | } 48 | return responseTemplate.success(ctx, null) 49 | } 50 | 51 | export let saveRoute = async (ctx) => { 52 | let entity = ctx.request.body 53 | if (!entity.name) { 54 | return responseTemplate.businessError(ctx, "name不能为空!") 55 | } 56 | if (!entity.path) { 57 | return responseTemplate.businessError(ctx, "path不能为空!") 58 | } 59 | if (!entity.title) { 60 | return responseTemplate.businessError(ctx, "标题不能为空!") 61 | } 62 | if (!entity.component && !entity.componentPath) { 63 | return responseTemplate.businessError(ctx, "组件与组件路径不能同时为空!") 64 | } 65 | let result = await routeService.saveRoute(entity) 66 | if (!result.success) { 67 | return responseTemplate.businessError(ctx, result.msg) 68 | } 69 | return responseTemplate.success(ctx, null) 70 | } 71 | -------------------------------------------------------------------------------- /src/controllers/system.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | import path from 'path' 3 | import systemService from '../services/systemService' 4 | import * as responseTemplate from '../lib/responseTemplate' 5 | 6 | export let resetDb = async (ctx) => { 7 | try { 8 | await fs.copyFileSync(path.join(__dirname, '../db/db_backup.json'), path.join(__dirname, '../db/db.json')); 9 | await systemService.resetDb() 10 | return responseTemplate.success(ctx, "初始化成功") 11 | } catch (e) { 12 | console.log(e) 13 | return responseTemplate.businessError(ctx, "初始化失败") 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/controllers/user.js: -------------------------------------------------------------------------------- 1 | import userService from '../services/userService' 2 | import menuService from '../services/memuService' 3 | import routeService from '../services/routeService' 4 | import interfaceService from '../services/interfaceService' 5 | import * as responseTemplate from '../lib/responseTemplate' 6 | let formatAccessMenus = (menus) => { 7 | let f = (item, children) => { 8 | item.children = [] 9 | for (let child of children) { 10 | let itemChild = { 11 | title: child.title, 12 | path: child.path, 13 | icon: child.icon 14 | } 15 | if (child.children && child.children.length > 0) { 16 | f(itemChild, child.children) 17 | } 18 | item.children.push(itemChild) 19 | } 20 | } 21 | let list = [] 22 | for (let menu of menus) { 23 | let item = { 24 | title: menu.title, 25 | path: menu.path, 26 | icon: menu.icon 27 | } 28 | if (menu.children && menu.children.length > 0) { 29 | f(item, menu.children) 30 | } 31 | list.push(item) 32 | } 33 | return list 34 | } 35 | let formatAccessRoutes = (routes) => { 36 | let f = (item, children) => { 37 | item.children = [] 38 | for (let child of children) { 39 | let itemChild = { 40 | name: child.name, 41 | path: child.path, 42 | component: child.component, 43 | componentPath: child.componentPath, 44 | meta: { 45 | title: child.title, 46 | cache: child.cache 47 | } 48 | } 49 | if (child.children && child.children.length > 0) { 50 | f(itemChild, child.children) 51 | } 52 | item.children.push(itemChild) 53 | } 54 | } 55 | let list = [] 56 | for (let route of routes) { 57 | let item = { 58 | name: route.name, 59 | path: route.path, 60 | component: route.component, 61 | componentPath: route.componentPath, 62 | meta: { 63 | title: route.title, 64 | cache: route.cache 65 | } 66 | } 67 | if (route.children && route.children.length > 0) { 68 | f(item, route.children) 69 | } 70 | list.push(item) 71 | } 72 | return list 73 | } 74 | export let getUser = async (ctx) => { 75 | let id = ctx.params.id 76 | let user = await userService.getUserById(id) 77 | if (!user) { 78 | return responseTemplate.businessError(ctx, "用户不存在") 79 | } 80 | return responseTemplate.success(ctx, user) 81 | } 82 | export let getUserInfo = async (ctx) => { 83 | let user = ctx.user; 84 | if (!user || !user.userId) { 85 | return responseTemplate.businessError(ctx, '获取用户信息失败!') 86 | } 87 | let [userInfo, 88 | userRole, 89 | permissions, 90 | accessMenus, 91 | accessRoutes, 92 | accessInterfaces, 93 | isAdmin 94 | ] = await Promise.all([ 95 | userService.getUserById(user.userId), 96 | userService.getUserRole(user.userId), 97 | userService.getUserPermission(user.userId), 98 | menuService.getAccessMenuList(user.userId), 99 | routeService.getAccessRouteList(user.userId), 100 | interfaceService.getAccessInterfaceList(user.userId), 101 | userService.isAdmin(user.userId) 102 | ]) 103 | if (!userInfo) { 104 | return responseTemplate.businessError(ctx, '获取用户信息失败!') 105 | } 106 | return responseTemplate.success(ctx, { 107 | userName: userInfo.name, 108 | userRoles: userRole, 109 | userPermissions: permissions, 110 | accessMenus: formatAccessMenus(accessMenus), 111 | accessRoutes: formatAccessRoutes(accessRoutes), 112 | accessInterfaces: accessInterfaces.map(s => { return { path: s.path, method: s.method } }), 113 | isAdmin: isAdmin ? 1 : 0, 114 | avatarUrl: 'https://api.adorable.io/avatars/85/abott@adorable.png' 115 | }) 116 | } 117 | 118 | export let getUserPagedList = async (ctx) => { 119 | let pageIndex = ctx.query.pageIndex 120 | let pageSize = ctx.query.pageSize 121 | let sortBy = ctx.query.sortBy 122 | let descending = ctx.query.descending 123 | let filter = JSON.parse(ctx.query.filter) 124 | let pagedList = await userService.getUserPagedList(pageIndex, pageSize, sortBy, descending, filter) 125 | return responseTemplate.success(ctx, pagedList) 126 | } 127 | 128 | export let delUser = async (ctx) => { 129 | let id = ctx.query.id 130 | console.log(id) 131 | let result = await userService.delUser(id) 132 | if (!result.success) { 133 | return responseTemplate.businessError(ctx, result.msg) 134 | } 135 | return responseTemplate.success(ctx, null) 136 | } 137 | 138 | export let delUsers = async (ctx) => { 139 | let ids = JSON.parse(ctx.query.ids) 140 | for (let id of ids) { 141 | await userService.delUser(id) 142 | } 143 | return responseTemplate.success(ctx, null) 144 | } 145 | 146 | export let saveUser = async (ctx) => { 147 | let func = ctx.request.body 148 | if (func.name == "") { 149 | return responseTemplate.businessError(ctx, "账号不能为空!") 150 | } 151 | let result = await userService.saveUser(func) 152 | if (!result.success) { 153 | return responseTemplate.businessError(ctx, result.msg) 154 | } 155 | return responseTemplate.success(ctx, null) 156 | } 157 | 158 | export let editRoleUser = async (ctx) => { 159 | let roleUser = ctx.request.body 160 | await userService.editRoleUser(roleUser) 161 | return responseTemplate.success(ctx, null) 162 | } -------------------------------------------------------------------------------- /src/db/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": [ 3 | { 4 | "id": "1", 5 | "name": "admin", 6 | "password": "123", 7 | "email": "123@qq.com", 8 | "phone": "110", 9 | "trueName": "超级管理员" 10 | }, 11 | { 12 | "name": "MenuManager", 13 | "trueName": "菜单管理员", 14 | "password": "123456", 15 | "id": "54a3488f-3c94-489e-bcd0-3f2631991f5e", 16 | "phone": "110", 17 | "email": "12345@qq.com" 18 | }, 19 | { 20 | "name": "no", 21 | "trueName": "没有权限的用户", 22 | "password": "123456", 23 | "id": "92e6b921-d820-4ea6-9b57-95b1378c961e", 24 | "phone": "110", 25 | "email": "66666@qq.com" 26 | } 27 | ], 28 | "admin": [ 29 | "1" 30 | ], 31 | "menu": [ 32 | { 33 | "id": "2bf39892-f047-44b1-88d3-ccd7ea5c8c45", 34 | "parentId": 0, 35 | "title": "系统", 36 | "path": "/system", 37 | "icon": "cogs", 38 | "sort": "1", 39 | "type": 1 40 | }, 41 | { 42 | "id": "52500348-50f0-4327-a898-9e4b9893f1a9", 43 | "parentId": "2bf39892-f047-44b1-88d3-ccd7ea5c8c45", 44 | "title": "系统设置", 45 | "icon": "cogs", 46 | "sort": "1", 47 | "type": 1 48 | }, 49 | { 50 | "id": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 51 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 52 | "title": "菜单管理", 53 | "path": "/system/menu", 54 | "icon": "th-list", 55 | "sort": "1", 56 | "type": 1, 57 | "permission": "p_menu_menu" 58 | }, 59 | { 60 | "id": "34943e8f-5e57-4038-bd2f-6c13e3c5f168", 61 | "parentId": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 62 | "title": "2-查询菜单", 63 | "sort": "2", 64 | "type": 2, 65 | "permission": "p_menu_view" 66 | }, 67 | { 68 | "id": "3423218d-886c-4fb7-944d-42afe4c03076", 69 | "parentId": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 70 | "title": "3-修改菜单", 71 | "sort": "3", 72 | "type": 2, 73 | "permission": "p_menu_edit" 74 | }, 75 | { 76 | "id": "c2578794-328c-4f0f-bbba-289ebdace7f1", 77 | "parentId": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 78 | "title": "1-菜单权限", 79 | "type": 2, 80 | "sort": "1", 81 | "permission": "p_menu_menu" 82 | }, 83 | { 84 | "id": "6db38bfe-9ad0-4db4-a9be-28082741ef20", 85 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 86 | "path": "/system/route", 87 | "title": "路由管理", 88 | "type": 1, 89 | "permission": "p_route_menu", 90 | "sort": "2", 91 | "icon": "share-alt-square" 92 | }, 93 | { 94 | "id": "2b453777-ddb0-4806-8ab0-4ec56167cdb3", 95 | "parentId": "6db38bfe-9ad0-4db4-a9be-28082741ef20", 96 | "title": "1-菜单权限", 97 | "type": 2, 98 | "permission": "p_route_menu" 99 | }, 100 | { 101 | "id": "1a74ac59-001c-4914-874e-6dbefe33415c", 102 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 103 | "title": "角色管理", 104 | "path": "/system/role", 105 | "type": 1, 106 | "sort": "3", 107 | "icon": "users", 108 | "permission": "p_role_menu" 109 | }, 110 | { 111 | "id": "d0d47bda-ab26-4909-a7f2-74bc2591a357", 112 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 113 | "title": "用户管理", 114 | "path": "/system/user", 115 | "sort": "4", 116 | "type": 1, 117 | "icon": "user", 118 | "permission": "p_user_menu" 119 | }, 120 | { 121 | "id": "85bfdc38-fca4-4703-ad22-2cba711fc42a", 122 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 123 | "title": "接口管理", 124 | "path": "/system/interface", 125 | "type": 1, 126 | "sort": "5", 127 | "icon": "paper-plane", 128 | "permission": "p_interface_menu" 129 | }, 130 | { 131 | "id": "5e5b0453-cb24-4a8f-89ef-360922cecba5", 132 | "parentId": "1a74ac59-001c-4914-874e-6dbefe33415c", 133 | "title": "1-菜单权限", 134 | "type": 2, 135 | "permission": "p_role_menu" 136 | }, 137 | { 138 | "id": "97273b77-bda3-47ad-bd93-3c904fb8fb10", 139 | "parentId": "d0d47bda-ab26-4909-a7f2-74bc2591a357", 140 | "title": "1-菜单权限", 141 | "type": 2, 142 | "permission": "p_user_menu" 143 | }, 144 | { 145 | "id": "21352cb0-870b-40f0-acc9-362846fd1506", 146 | "parentId": "85bfdc38-fca4-4703-ad22-2cba711fc42a", 147 | "title": "1-菜单权限", 148 | "type": 2, 149 | "permission": "p_interface_menu" 150 | }, 151 | { 152 | "id": "18c7a1e3-3c69-4242-afd7-6f54bfdce271", 153 | "parentId": "2bf39892-f047-44b1-88d3-ccd7ea5c8c45", 154 | "title": "组织架构", 155 | "sort": "2", 156 | "type": 1, 157 | "icon": "pie-chart" 158 | }, 159 | { 160 | "id": "dc5667cf-85a8-446e-8e69-354a684a762a", 161 | "parentId": "18c7a1e3-3c69-4242-afd7-6f54bfdce271", 162 | "title": "部门管理", 163 | "type": 1, 164 | "isLock": false, 165 | "sort": "1", 166 | "icon": "html5" 167 | }, 168 | { 169 | "id": "0fda8037-61df-4a1c-8f9b-18f287d51049", 170 | "parentId": "18c7a1e3-3c69-4242-afd7-6f54bfdce271", 171 | "title": "职位管理", 172 | "sort": "2", 173 | "type": 1, 174 | "icon": "opencart" 175 | }, 176 | { 177 | "id": "6a0c4206-a78c-4459-bd36-3c8972b7bb53", 178 | "parentId": "dc5667cf-85a8-446e-8e69-354a684a762a", 179 | "title": "1-菜单权限", 180 | "sort": "1", 181 | "type": 2 182 | }, 183 | { 184 | "id": "ec47ad63-fb2e-4988-90ec-77fe4e398839", 185 | "parentId": "0fda8037-61df-4a1c-8f9b-18f287d51049", 186 | "title": "1-菜单权限", 187 | "sort": "1", 188 | "type": 2 189 | } 190 | ], 191 | "route": [ 192 | { 193 | "id": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 194 | "parentId": 0, 195 | "name": "System", 196 | "title": "系统设置", 197 | "path": "/system", 198 | "permission": "", 199 | "component": "layoutHeaderAside", 200 | "componentPath": "layout/header-aside/layout", 201 | "sort": "1", 202 | "isLock": false, 203 | "cache": true 204 | }, 205 | { 206 | "id": "aa4c13f7-f3eb-4d1e-aac2-c2b2f7f8d296", 207 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 208 | "name": "MenuPage", 209 | "title": "菜单管理", 210 | "path": "/system/menu", 211 | "component": "menu", 212 | "componentPath": "pages/sys/menu/index", 213 | "sort": "2", 214 | "cache": true 215 | }, 216 | { 217 | "id": "618e2e9c-8afe-4c64-b486-a4b08c752a81", 218 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 219 | "title": "路由管理", 220 | "name": "RoutePage", 221 | "path": "/system/route", 222 | "component": "route", 223 | "componentPath": "pages/sys/route/index", 224 | "sort": "3", 225 | "cache": true 226 | }, 227 | { 228 | "id": "67a76a8f-c765-4015-bebd-03edc3170e63", 229 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 230 | "name": "RolePage", 231 | "title": "角色管理", 232 | "path": "/system/role", 233 | "component": "role", 234 | "sort": "3", 235 | "cache": true, 236 | "componentPath": "pages/sys/role/index" 237 | }, 238 | { 239 | "id": "58c5bc63-dfdc-413c-9bd6-b827ef06203e", 240 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 241 | "componentPath": "pages/sys/user/index", 242 | "sort": "4", 243 | "cache": true, 244 | "component": "user", 245 | "path": "/system/user", 246 | "title": "用户管理", 247 | "name": "UserPage" 248 | }, 249 | { 250 | "id": "d8808450-aa31-4a4a-817d-e49c8eee983b", 251 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 252 | "name": "InterfacePage", 253 | "title": "接口管理", 254 | "path": "/system/interface", 255 | "component": "interface", 256 | "sort": "5" 257 | } 258 | ], 259 | "interface": [ 260 | { 261 | "name": "用户登录", 262 | "path": "/auth/login", 263 | "method": "post", 264 | "description": "用户登录", 265 | "id": "4662e940-ebae-4424-8a74-2c67ce408d3e" 266 | }, 267 | { 268 | "name": "获取菜单", 269 | "path": "/menu/:id", 270 | "method": "get", 271 | "description": "根据id获取菜单", 272 | "id": "c6567a47-0382-4eb9-852b-3db2c8be9251" 273 | }, 274 | { 275 | "name": "获取菜单列表", 276 | "path": "/menu", 277 | "method": "get", 278 | "description": "获取菜单列表", 279 | "id": "23c892ed-76c6-4bdb-9240-4618b3fc4399" 280 | }, 281 | { 282 | "name": "提交菜单", 283 | "path": "/menu/save", 284 | "method": "post", 285 | "description": "新增或更新菜单", 286 | "id": "8dba087e-ba42-4220-a036-0de4adc0f585" 287 | }, 288 | { 289 | "name": "删除菜单", 290 | "path": "/menu/:id", 291 | "method": "delete", 292 | "description": "根据id删除菜单", 293 | "id": "6be0b030-1d42-4230-aed0-5cc7ca8e53df" 294 | }, 295 | { 296 | "name": "获取路由列表", 297 | "path": "/route", 298 | "method": "get", 299 | "description": "获取路由列表", 300 | "id": "f42e8101-af1e-4717-82aa-a9d620b65021" 301 | }, 302 | { 303 | "name": "获取路由", 304 | "path": "/route/:id", 305 | "method": "get", 306 | "description": "根据id获取路由", 307 | "id": "53b58c16-b9fb-4ea0-bb65-0bb4cfd96293" 308 | }, 309 | { 310 | "name": "提交路由", 311 | "path": "/route/save", 312 | "method": "post", 313 | "description": "新增或更新路由", 314 | "id": "ffef90da-dab5-4177-81ae-1a4d8e70b0c8" 315 | }, 316 | { 317 | "name": "删除路由", 318 | "path": "/route/:id", 319 | "method": "delete", 320 | "description": "根本id删除路由", 321 | "id": "cee27c96-c02c-409d-b446-1de2e55e2bf6" 322 | }, 323 | { 324 | "name": "获取角色", 325 | "path": "/role/:id", 326 | "method": "get", 327 | "description": "根据id获取角色", 328 | "id": "02ce362f-1f04-4386-99c1-ef7a1bcf71fe" 329 | }, 330 | { 331 | "name": "分页获取角色", 332 | "path": "/role/pagedlist", 333 | "method": "get", 334 | "description": "分页的方式获取角色数据,支持排序", 335 | "id": "90a1d0c6-22e5-4743-b39c-9f6e643b75d4" 336 | }, 337 | { 338 | "name": "删除角色", 339 | "path": "/role/del", 340 | "method": "delete", 341 | "description": "根据id删除角色", 342 | "id": "873aaf0d-6f67-4c74-8dde-7dba8c524921" 343 | }, 344 | { 345 | "name": "批量删除角色", 346 | "path": "/role/batchdel", 347 | "method": "delete", 348 | "description": "批量删除角色", 349 | "id": "0c229c5e-30d7-42c3-841a-73c4ba210a06" 350 | }, 351 | { 352 | "name": "提交角色", 353 | "path": "/role/save", 354 | "method": "post", 355 | "description": "新增或更新角色", 356 | "id": "3d0a2d40-89d1-4416-8354-05849276dccd" 357 | }, 358 | { 359 | "name": "获取角色功能权限", 360 | "path": "/role/getpermissions/:roleid", 361 | "method": "get", 362 | "description": "获取角色功能权限", 363 | "id": "6eb4cf01-45d1-432c-83ac-d9eb72c0c88e" 364 | }, 365 | { 366 | "name": "保存角色功能权限", 367 | "path": "/role/savepermission", 368 | "method": "post", 369 | "description": "保存角色功能权限", 370 | "id": "07ff0d6a-78a6-4cd3-ba76-b4959a0c641a" 371 | }, 372 | { 373 | "name": "获取用户", 374 | "path": "/user/:id", 375 | "method": "get", 376 | "description": "获取用户", 377 | "id": "020a75ef-ea43-4435-9a16-cb09fb79dc8b" 378 | }, 379 | { 380 | "name": "分页获取用户", 381 | "path": "/user/pagedlist", 382 | "method": "get", 383 | "description": "分页获取用户", 384 | "id": "93c064e8-baf4-4f03-80ef-fa1230911213" 385 | }, 386 | { 387 | "name": "提交用户", 388 | "path": "/user/save", 389 | "method": "post", 390 | "description": "新增或更新用户", 391 | "id": "31373f0b-0050-42d4-80b9-48dc3735f213" 392 | }, 393 | { 394 | "name": "删除用户", 395 | "path": "/user/del", 396 | "method": "delete", 397 | "description": "删除用户", 398 | "id": "80eb294a-9919-4341-b1c0-ef94462bfa9f" 399 | }, 400 | { 401 | "name": "批量删除用户", 402 | "path": "/user/batchdel", 403 | "method": "delete", 404 | "description": "批量删除用户", 405 | "id": "6cf44f79-726c-4655-843a-b05d8ed5ebd5" 406 | }, 407 | { 408 | "name": "关联角色用户", 409 | "path": "/user/editroleuser", 410 | "method": "post", 411 | "description": "关联或取消关联", 412 | "id": "61cf07fb-bd28-46f0-88d1-b96d3d7d0556" 413 | }, 414 | { 415 | "name": "获取用户权限信息", 416 | "path": "/user/info", 417 | "method": "get", 418 | "description": "获取用户权限信息", 419 | "id": "b49d6801-a037-46f2-bdc9-e175d64e6027" 420 | }, 421 | { 422 | "name": "获取接口", 423 | "path": "/interface/:id", 424 | "method": "get", 425 | "description": "根据id获取接口", 426 | "id": "87d9908b-2876-4f50-ad80-7fd4fef34456" 427 | }, 428 | { 429 | "name": "分页获取接口", 430 | "path": "/interface/paged", 431 | "method": "get", 432 | "description": "分页获取接口", 433 | "id": "72ce9b95-8b49-42f5-96f9-488f42f84da4" 434 | }, 435 | { 436 | "name": "提交接口", 437 | "path": "/interface/save", 438 | "method": "post", 439 | "description": "新增或更新接口", 440 | "id": "dbf8208c-0c93-4f6d-8292-2c4821e2ee29" 441 | }, 442 | { 443 | "name": "删除接口", 444 | "path": "/interface/del/:id", 445 | "method": "delete", 446 | "description": "删除接口", 447 | "id": "1a9bcd3d-2b2e-4fb4-8b67-13321d744b40" 448 | }, 449 | { 450 | "name": "批量删除接口", 451 | "path": "/interface/batchdel/:ids", 452 | "method": "delete", 453 | "description": "批量删除接口", 454 | "id": "deb494eb-8417-4dcb-a7b9-7e0a3378930c" 455 | }, 456 | { 457 | "name": "关联接口", 458 | "path": "/interface/relate", 459 | "method": "post", 460 | "description": "关联或取消关联", 461 | "id": "a77d58dc-69c1-4fb4-ab96-2e613f49d425" 462 | } 463 | ], 464 | "functionInterface": [ 465 | { 466 | "functionId": "34943e8f-5e57-4038-bd2f-6c13e3c5f168", 467 | "interfaceId": "c6567a47-0382-4eb9-852b-3db2c8be9251" 468 | }, 469 | { 470 | "functionId": "3423218d-886c-4fb7-944d-42afe4c03076", 471 | "interfaceId": "8dba087e-ba42-4220-a036-0de4adc0f585" 472 | }, 473 | { 474 | "functionId": "c2578794-328c-4f0f-bbba-289ebdace7f1", 475 | "interfaceId": "23c892ed-76c6-4bdb-9240-4618b3fc4399" 476 | }, 477 | { 478 | "functionId": "3423218d-886c-4fb7-944d-42afe4c03076", 479 | "interfaceId": "72ce9b95-8b49-42f5-96f9-488f42f84da4" 480 | } 481 | ], 482 | "role": [ 483 | { 484 | "name": "菜单管理员", 485 | "code": "R_MENUADMIN", 486 | "description": "菜单管理员", 487 | "id": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 488 | "isAdd": 2 489 | } 490 | ], 491 | "roleUser": [ 492 | { 493 | "userId": "54a3488f-3c94-489e-bcd0-3f2631991f5e", 494 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959" 495 | } 496 | ], 497 | "permission": [ 498 | { 499 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 500 | "functionId": "c2578794-328c-4f0f-bbba-289ebdace7f1", 501 | "id": "6500ee1f-eaec-4c56-ad60-06b3367d45ba" 502 | }, 503 | { 504 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 505 | "functionId": "34943e8f-5e57-4038-bd2f-6c13e3c5f168", 506 | "id": "6c9ef830-a5f8-45e2-927b-d096e81052db" 507 | }, 508 | { 509 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 510 | "functionId": "3423218d-886c-4fb7-944d-42afe4c03076", 511 | "id": "968204ac-97c3-4f2c-bb3c-3ea1aa89731e" 512 | } 513 | ], 514 | "token": [] 515 | } -------------------------------------------------------------------------------- /src/db/db_backup.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": [ 3 | { 4 | "id": "1", 5 | "name": "admin", 6 | "password": "123", 7 | "email": "123@qq.com", 8 | "phone": "110", 9 | "trueName": "超级管理员" 10 | }, 11 | { 12 | "name": "MenuManager", 13 | "trueName": "菜单管理员", 14 | "password": "123456", 15 | "id": "54a3488f-3c94-489e-bcd0-3f2631991f5e", 16 | "phone": "110", 17 | "email": "12345@qq.com" 18 | }, 19 | { 20 | "name": "no", 21 | "trueName": "没有权限的用户", 22 | "password": "123456", 23 | "id": "92e6b921-d820-4ea6-9b57-95b1378c961e", 24 | "phone": "110", 25 | "email": "66666@qq.com" 26 | } 27 | ], 28 | "admin": [ 29 | "1" 30 | ], 31 | "menu": [ 32 | { 33 | "id": "2bf39892-f047-44b1-88d3-ccd7ea5c8c45", 34 | "parentId": 0, 35 | "title": "系统", 36 | "path": "/system", 37 | "icon": "cogs", 38 | "sort": "1", 39 | "type": 1 40 | }, 41 | { 42 | "id": "52500348-50f0-4327-a898-9e4b9893f1a9", 43 | "parentId": "2bf39892-f047-44b1-88d3-ccd7ea5c8c45", 44 | "title": "系统设置", 45 | "icon": "cogs", 46 | "sort": "1", 47 | "type": 1 48 | }, 49 | { 50 | "id": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 51 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 52 | "title": "菜单管理", 53 | "path": "/system/menu", 54 | "icon": "th-list", 55 | "sort": "1", 56 | "type": 1, 57 | "permission": "p_menu_menu" 58 | }, 59 | { 60 | "id": "34943e8f-5e57-4038-bd2f-6c13e3c5f168", 61 | "parentId": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 62 | "title": "2-查询菜单", 63 | "sort": "2", 64 | "type": 2, 65 | "permission": "p_menu_view" 66 | }, 67 | { 68 | "id": "3423218d-886c-4fb7-944d-42afe4c03076", 69 | "parentId": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 70 | "title": "3-修改菜单", 71 | "sort": "3", 72 | "type": 2, 73 | "permission": "p_menu_edit" 74 | }, 75 | { 76 | "id": "c2578794-328c-4f0f-bbba-289ebdace7f1", 77 | "parentId": "1f3ea5ba-0d67-4710-b75c-52a267296b85", 78 | "title": "1-菜单权限", 79 | "type": 2, 80 | "sort": "1", 81 | "permission": "p_menu_menu" 82 | }, 83 | { 84 | "id": "6db38bfe-9ad0-4db4-a9be-28082741ef20", 85 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 86 | "path": "/system/route", 87 | "title": "路由管理", 88 | "type": 1, 89 | "permission": "p_route_menu", 90 | "sort": "2", 91 | "icon": "share-alt-square" 92 | }, 93 | { 94 | "id": "2b453777-ddb0-4806-8ab0-4ec56167cdb3", 95 | "parentId": "6db38bfe-9ad0-4db4-a9be-28082741ef20", 96 | "title": "1-菜单权限", 97 | "type": 2, 98 | "permission": "p_route_menu" 99 | }, 100 | { 101 | "id": "1a74ac59-001c-4914-874e-6dbefe33415c", 102 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 103 | "title": "角色管理", 104 | "path": "/system/role", 105 | "type": 1, 106 | "sort": "3", 107 | "icon": "users", 108 | "permission": "p_role_menu" 109 | }, 110 | { 111 | "id": "d0d47bda-ab26-4909-a7f2-74bc2591a357", 112 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 113 | "title": "用户管理", 114 | "path": "/system/user", 115 | "sort": "4", 116 | "type": 1, 117 | "icon": "user", 118 | "permission": "p_user_menu" 119 | }, 120 | { 121 | "id": "85bfdc38-fca4-4703-ad22-2cba711fc42a", 122 | "parentId": "52500348-50f0-4327-a898-9e4b9893f1a9", 123 | "title": "接口管理", 124 | "path": "/system/interface", 125 | "type": 1, 126 | "sort": "5", 127 | "icon": "paper-plane", 128 | "permission": "p_interface_menu" 129 | }, 130 | { 131 | "id": "5e5b0453-cb24-4a8f-89ef-360922cecba5", 132 | "parentId": "1a74ac59-001c-4914-874e-6dbefe33415c", 133 | "title": "1-菜单权限", 134 | "type": 2, 135 | "permission": "p_role_menu" 136 | }, 137 | { 138 | "id": "97273b77-bda3-47ad-bd93-3c904fb8fb10", 139 | "parentId": "d0d47bda-ab26-4909-a7f2-74bc2591a357", 140 | "title": "1-菜单权限", 141 | "type": 2, 142 | "permission": "p_user_menu" 143 | }, 144 | { 145 | "id": "21352cb0-870b-40f0-acc9-362846fd1506", 146 | "parentId": "85bfdc38-fca4-4703-ad22-2cba711fc42a", 147 | "title": "1-菜单权限", 148 | "type": 2, 149 | "permission": "p_interface_menu" 150 | }, 151 | { 152 | "id": "18c7a1e3-3c69-4242-afd7-6f54bfdce271", 153 | "parentId": "2bf39892-f047-44b1-88d3-ccd7ea5c8c45", 154 | "title": "组织架构", 155 | "sort": "2", 156 | "type": 1, 157 | "icon": "pie-chart" 158 | }, 159 | { 160 | "id": "dc5667cf-85a8-446e-8e69-354a684a762a", 161 | "parentId": "18c7a1e3-3c69-4242-afd7-6f54bfdce271", 162 | "title": "部门管理", 163 | "type": 1, 164 | "isLock": false, 165 | "sort": "1", 166 | "icon": "html5" 167 | }, 168 | { 169 | "id": "0fda8037-61df-4a1c-8f9b-18f287d51049", 170 | "parentId": "18c7a1e3-3c69-4242-afd7-6f54bfdce271", 171 | "title": "职位管理", 172 | "sort": "2", 173 | "type": 1, 174 | "icon": "opencart" 175 | }, 176 | { 177 | "id": "6a0c4206-a78c-4459-bd36-3c8972b7bb53", 178 | "parentId": "dc5667cf-85a8-446e-8e69-354a684a762a", 179 | "title": "1-菜单权限", 180 | "sort": "1", 181 | "type": 2 182 | }, 183 | { 184 | "id": "ec47ad63-fb2e-4988-90ec-77fe4e398839", 185 | "parentId": "0fda8037-61df-4a1c-8f9b-18f287d51049", 186 | "title": "1-菜单权限", 187 | "sort": "1", 188 | "type": 2 189 | } 190 | ], 191 | "route": [ 192 | { 193 | "id": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 194 | "parentId": 0, 195 | "name": "System", 196 | "title": "系统设置", 197 | "path": "/system", 198 | "permission": "", 199 | "component": "layoutHeaderAside", 200 | "componentPath": "layout/header-aside/layout", 201 | "sort": "1", 202 | "isLock": false, 203 | "cache": true 204 | }, 205 | { 206 | "id": "aa4c13f7-f3eb-4d1e-aac2-c2b2f7f8d296", 207 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 208 | "name": "MenuPage", 209 | "title": "菜单管理", 210 | "path": "/system/menu", 211 | "component": "menu", 212 | "componentPath": "pages/sys/menu/index", 213 | "sort": "2", 214 | "cache": true 215 | }, 216 | { 217 | "id": "618e2e9c-8afe-4c64-b486-a4b08c752a81", 218 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 219 | "title": "路由管理", 220 | "name": "RoutePage", 221 | "path": "/system/route", 222 | "component": "route", 223 | "componentPath": "pages/sys/route/index", 224 | "sort": "3", 225 | "cache": true 226 | }, 227 | { 228 | "id": "67a76a8f-c765-4015-bebd-03edc3170e63", 229 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 230 | "name": "RolePage", 231 | "title": "角色管理", 232 | "path": "/system/role", 233 | "component": "role", 234 | "sort": "3", 235 | "cache": true, 236 | "componentPath": "pages/sys/role/index" 237 | }, 238 | { 239 | "id": "58c5bc63-dfdc-413c-9bd6-b827ef06203e", 240 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 241 | "componentPath": "pages/sys/user/index", 242 | "sort": "4", 243 | "cache": true, 244 | "component": "user", 245 | "path": "/system/user", 246 | "title": "用户管理", 247 | "name": "UserPage" 248 | }, 249 | { 250 | "id": "d8808450-aa31-4a4a-817d-e49c8eee983b", 251 | "parentId": "3af738f0-0792-4f34-aae9-3339d0ab12a7", 252 | "name": "InterfacePage", 253 | "title": "接口管理", 254 | "path": "/system/interface", 255 | "component": "interface", 256 | "sort": "5" 257 | } 258 | ], 259 | "interface": [ 260 | { 261 | "name": "用户登录", 262 | "path": "/auth/login", 263 | "method": "post", 264 | "description": "用户登录", 265 | "id": "4662e940-ebae-4424-8a74-2c67ce408d3e" 266 | }, 267 | { 268 | "name": "获取菜单", 269 | "path": "/menu/:id", 270 | "method": "get", 271 | "description": "根据id获取菜单", 272 | "id": "c6567a47-0382-4eb9-852b-3db2c8be9251" 273 | }, 274 | { 275 | "name": "获取菜单列表", 276 | "path": "/menu", 277 | "method": "get", 278 | "description": "获取菜单列表", 279 | "id": "23c892ed-76c6-4bdb-9240-4618b3fc4399" 280 | }, 281 | { 282 | "name": "提交菜单", 283 | "path": "/menu/save", 284 | "method": "post", 285 | "description": "新增或更新菜单", 286 | "id": "8dba087e-ba42-4220-a036-0de4adc0f585" 287 | }, 288 | { 289 | "name": "删除菜单", 290 | "path": "/menu/:id", 291 | "method": "delete", 292 | "description": "根据id删除菜单", 293 | "id": "6be0b030-1d42-4230-aed0-5cc7ca8e53df" 294 | }, 295 | { 296 | "name": "获取路由列表", 297 | "path": "/route", 298 | "method": "get", 299 | "description": "获取路由列表", 300 | "id": "f42e8101-af1e-4717-82aa-a9d620b65021" 301 | }, 302 | { 303 | "name": "获取路由", 304 | "path": "/route/:id", 305 | "method": "get", 306 | "description": "根据id获取路由", 307 | "id": "53b58c16-b9fb-4ea0-bb65-0bb4cfd96293" 308 | }, 309 | { 310 | "name": "提交路由", 311 | "path": "/route/save", 312 | "method": "post", 313 | "description": "新增或更新路由", 314 | "id": "ffef90da-dab5-4177-81ae-1a4d8e70b0c8" 315 | }, 316 | { 317 | "name": "删除路由", 318 | "path": "/route/:id", 319 | "method": "delete", 320 | "description": "根本id删除路由", 321 | "id": "cee27c96-c02c-409d-b446-1de2e55e2bf6" 322 | }, 323 | { 324 | "name": "获取角色", 325 | "path": "/role/:id", 326 | "method": "get", 327 | "description": "根据id获取角色", 328 | "id": "02ce362f-1f04-4386-99c1-ef7a1bcf71fe" 329 | }, 330 | { 331 | "name": "分页获取角色", 332 | "path": "/role/pagedlist", 333 | "method": "get", 334 | "description": "分页的方式获取角色数据,支持排序", 335 | "id": "90a1d0c6-22e5-4743-b39c-9f6e643b75d4" 336 | }, 337 | { 338 | "name": "删除角色", 339 | "path": "/role/del", 340 | "method": "delete", 341 | "description": "根据id删除角色", 342 | "id": "873aaf0d-6f67-4c74-8dde-7dba8c524921" 343 | }, 344 | { 345 | "name": "批量删除角色", 346 | "path": "/role/batchdel", 347 | "method": "delete", 348 | "description": "批量删除角色", 349 | "id": "0c229c5e-30d7-42c3-841a-73c4ba210a06" 350 | }, 351 | { 352 | "name": "提交角色", 353 | "path": "/role/save", 354 | "method": "post", 355 | "description": "新增或更新角色", 356 | "id": "3d0a2d40-89d1-4416-8354-05849276dccd" 357 | }, 358 | { 359 | "name": "获取角色功能权限", 360 | "path": "/role/getpermissions/:roleid", 361 | "method": "get", 362 | "description": "获取角色功能权限", 363 | "id": "6eb4cf01-45d1-432c-83ac-d9eb72c0c88e" 364 | }, 365 | { 366 | "name": "保存角色功能权限", 367 | "path": "/role/savepermission", 368 | "method": "post", 369 | "description": "保存角色功能权限", 370 | "id": "07ff0d6a-78a6-4cd3-ba76-b4959a0c641a" 371 | }, 372 | { 373 | "name": "获取用户", 374 | "path": "/user/:id", 375 | "method": "get", 376 | "description": "获取用户", 377 | "id": "020a75ef-ea43-4435-9a16-cb09fb79dc8b" 378 | }, 379 | { 380 | "name": "分页获取用户", 381 | "path": "/user/pagedlist", 382 | "method": "get", 383 | "description": "分页获取用户", 384 | "id": "93c064e8-baf4-4f03-80ef-fa1230911213" 385 | }, 386 | { 387 | "name": "提交用户", 388 | "path": "/user/save", 389 | "method": "post", 390 | "description": "新增或更新用户", 391 | "id": "31373f0b-0050-42d4-80b9-48dc3735f213" 392 | }, 393 | { 394 | "name": "删除用户", 395 | "path": "/user/del", 396 | "method": "delete", 397 | "description": "删除用户", 398 | "id": "80eb294a-9919-4341-b1c0-ef94462bfa9f" 399 | }, 400 | { 401 | "name": "批量删除用户", 402 | "path": "/user/batchdel", 403 | "method": "delete", 404 | "description": "批量删除用户", 405 | "id": "6cf44f79-726c-4655-843a-b05d8ed5ebd5" 406 | }, 407 | { 408 | "name": "关联角色用户", 409 | "path": "/user/editroleuser", 410 | "method": "post", 411 | "description": "关联或取消关联", 412 | "id": "61cf07fb-bd28-46f0-88d1-b96d3d7d0556" 413 | }, 414 | { 415 | "name": "获取用户权限信息", 416 | "path": "/user/info", 417 | "method": "get", 418 | "description": "获取用户权限信息", 419 | "id": "b49d6801-a037-46f2-bdc9-e175d64e6027" 420 | }, 421 | { 422 | "name": "获取接口", 423 | "path": "/interface/:id", 424 | "method": "get", 425 | "description": "根据id获取接口", 426 | "id": "87d9908b-2876-4f50-ad80-7fd4fef34456" 427 | }, 428 | { 429 | "name": "分页获取接口", 430 | "path": "/interface/paged", 431 | "method": "get", 432 | "description": "分页获取接口", 433 | "id": "72ce9b95-8b49-42f5-96f9-488f42f84da4" 434 | }, 435 | { 436 | "name": "提交接口", 437 | "path": "/interface/save", 438 | "method": "post", 439 | "description": "新增或更新接口", 440 | "id": "dbf8208c-0c93-4f6d-8292-2c4821e2ee29" 441 | }, 442 | { 443 | "name": "删除接口", 444 | "path": "/interface/del/:id", 445 | "method": "delete", 446 | "description": "删除接口", 447 | "id": "1a9bcd3d-2b2e-4fb4-8b67-13321d744b40" 448 | }, 449 | { 450 | "name": "批量删除接口", 451 | "path": "/interface/batchdel/:ids", 452 | "method": "delete", 453 | "description": "批量删除接口", 454 | "id": "deb494eb-8417-4dcb-a7b9-7e0a3378930c" 455 | }, 456 | { 457 | "name": "关联接口", 458 | "path": "/interface/relate", 459 | "method": "post", 460 | "description": "关联或取消关联", 461 | "id": "a77d58dc-69c1-4fb4-ab96-2e613f49d425" 462 | } 463 | ], 464 | "functionInterface": [ 465 | { 466 | "functionId": "34943e8f-5e57-4038-bd2f-6c13e3c5f168", 467 | "interfaceId": "c6567a47-0382-4eb9-852b-3db2c8be9251" 468 | }, 469 | { 470 | "functionId": "3423218d-886c-4fb7-944d-42afe4c03076", 471 | "interfaceId": "8dba087e-ba42-4220-a036-0de4adc0f585" 472 | }, 473 | { 474 | "functionId": "c2578794-328c-4f0f-bbba-289ebdace7f1", 475 | "interfaceId": "23c892ed-76c6-4bdb-9240-4618b3fc4399" 476 | }, 477 | { 478 | "functionId": "3423218d-886c-4fb7-944d-42afe4c03076", 479 | "interfaceId": "72ce9b95-8b49-42f5-96f9-488f42f84da4" 480 | } 481 | ], 482 | "role": [ 483 | { 484 | "name": "菜单管理员", 485 | "code": "R_MENUADMIN", 486 | "description": "菜单管理员", 487 | "id": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 488 | "isAdd": 2 489 | } 490 | ], 491 | "roleUser": [ 492 | { 493 | "userId": "54a3488f-3c94-489e-bcd0-3f2631991f5e", 494 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959" 495 | } 496 | ], 497 | "permission": [ 498 | { 499 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 500 | "functionId": "c2578794-328c-4f0f-bbba-289ebdace7f1", 501 | "id": "6500ee1f-eaec-4c56-ad60-06b3367d45ba" 502 | }, 503 | { 504 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 505 | "functionId": "34943e8f-5e57-4038-bd2f-6c13e3c5f168", 506 | "id": "6c9ef830-a5f8-45e2-927b-d096e81052db" 507 | }, 508 | { 509 | "roleId": "6db7bf60-1cbc-404b-9894-4dd6f4a02959", 510 | "functionId": "3423218d-886c-4fb7-944d-42afe4c03076", 511 | "id": "968204ac-97c3-4f2c-bb3c-3ea1aa89731e" 512 | } 513 | ], 514 | "token": [] 515 | } -------------------------------------------------------------------------------- /src/db/request_log_db.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestLog": [ 3 | ] 4 | } -------------------------------------------------------------------------------- /src/im/closeMiddleware.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return function (context, next) { 3 | let server = context.server; 4 | let client = context.client; 5 | let code = context.code; 6 | let reason = context.message; 7 | server.clients.delete(client.clientId); 8 | if (code === 1003 && (reason === 'invalid clientId' || reason === 'exists clientId')) { 9 | console.log("'invalid clientId' or 'exists clientId' closed") 10 | } else { 11 | console.log(client.clientId + " closed"); 12 | if (server.userMap) { 13 | server.userMap.delete(client.shortid); 14 | server.emit("user logout", { 15 | id: client.shortid, 16 | name: client.clientId 17 | }); 18 | } 19 | } 20 | next(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/im/connectMiddleware.js: -------------------------------------------------------------------------------- 1 | import url from 'url'; 2 | var shortid = require('shortid'); 3 | 4 | function makeEvent(event) { 5 | return JSON.stringify({ 6 | type: 'event', 7 | event: event.event, 8 | args: event.args 9 | }) 10 | } 11 | 12 | export default () => { 13 | return function (context, next) { 14 | let req = context.req; 15 | let client = context.client; 16 | let server = context.server; 17 | let location = url.parse(req.url, true); 18 | let clientId = location.query.token || location.query.user; 19 | if (!clientId) { 20 | client.end(makeEvent({ 21 | event: 'loginError', 22 | args: 'invalid token or user' 23 | })) 24 | client.close(1003, "invalid clientId"); 25 | } 26 | if (server.clients.has(clientId)) { 27 | client.send(makeEvent({ 28 | event: 'loginError', 29 | args: 'user online' 30 | })) 31 | client.close(1003, "exists clientId"); 32 | return; 33 | } 34 | let sid = shortid.generate(); 35 | let user = { 36 | id: sid, 37 | name: clientId 38 | }; 39 | client.clientId = clientId; 40 | client.shortid = sid; 41 | server.clients.set(clientId, client); 42 | if (!server.userMap) { 43 | server.userMap = new Map(); 44 | } 45 | if (!server.roomMap) { 46 | server.roomMap = new Map(); 47 | } 48 | server.userMap.set(sid, user); 49 | server.emit('user login', { 50 | id: sid, 51 | name: clientId 52 | }, true); 53 | client.send(makeEvent({ 54 | event: 'loginSuccess', 55 | args: { 56 | user: user, 57 | userList: [...server.userMap.values()], 58 | roomList: [...server.roomMap.values()] 59 | } 60 | })); 61 | console.log(client.clientId + " Connected"); 62 | next(); 63 | } 64 | } -------------------------------------------------------------------------------- /src/im/index.js: -------------------------------------------------------------------------------- 1 | import connectMiddleware from './connectMiddleware'; 2 | import closeMiddleware from './closeMiddleware'; 3 | import messageMiddleware from './messageMiddleware'; 4 | import remoteEmitMiddleware from './remoteEmitMiddleware'; 5 | import roomInfoMiddleware from './roomInfoMiddleware'; 6 | import messageRouteMiddleware from './messageRouteMiddleware'; 7 | export default { 8 | connectMiddleware, 9 | closeMiddleware, 10 | messageMiddleware, 11 | remoteEmitMiddleware, 12 | roomInfoMiddleware, 13 | messageRouteMiddleware 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/im/messageMiddleware.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return (context, next) => { 3 | if (context.req.type === 'event') { 4 | //context.server.emit(context.req.event, context.req.args, true); 5 | context.server.emit(context.req.event, context.req.args); 6 | } 7 | next(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/im/messageRouteMiddleware.js: -------------------------------------------------------------------------------- 1 | export default (routes) => { 2 | return async (context, next) => { 3 | if (context.req.type === 'event') { 4 | if (routes[context.req.event]) { 5 | await routes[context.req.event](context); 6 | } else { 7 | context.server.emit(context.req.event, context.req.args); 8 | } 9 | } 10 | next(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/im/remoteEmitMiddleware.js: -------------------------------------------------------------------------------- 1 | // emit才会调用此中间件 2 | export default () => { 3 | return (context, next) => { 4 | let server = context.server; 5 | let event = context.event; 6 | let msgType = event.args.type;//broadcast=0,private chat=1,room chat=2, default 0 7 | if (msgType > 0) { 8 | if (msgType == 1) { 9 | let to = event.args.to.user; 10 | let from = event.args.from; 11 | for (let client of server.clients.values()) { 12 | if (to.name == client.clientId || from.name == client.clientId) { 13 | client.send(makeEventMessage(event)); 14 | } 15 | } 16 | } 17 | else if (msgType == 2) { 18 | let to = event.args.to.room; 19 | let room = server.roomMap.get(to.id); 20 | for (let client of server.clients.values()) { 21 | if (room.userList.some((user) => user.name == client.clientId)) { 22 | client.send(makeEventMessage(event)); 23 | } 24 | } 25 | } 26 | } 27 | else { 28 | for (let client of server.clients.values()) { 29 | client.send(makeEventMessage(event)); 30 | } 31 | } 32 | next(); 33 | } 34 | } 35 | function makeEventMessage(event) { 36 | return JSON.stringify({ 37 | type: 'event', 38 | event: event.event, 39 | args: event.args 40 | }) 41 | } -------------------------------------------------------------------------------- /src/im/roomInfoMiddleware.js: -------------------------------------------------------------------------------- 1 | var shortid = require('shortid'); 2 | export default () => { 3 | return (context, next) => { 4 | if (context.req.type === 'event' && context.req.event === 'addRoom') { 5 | if (!context.server.roomMap) { 6 | context.server.roomMap = new Map(); 7 | } 8 | let roomId = shortid.generate(); 9 | context.req.args.id = roomId; 10 | context.server.roomMap.set(roomId, { 11 | id:roomId, 12 | name: context.req.args.name, 13 | userList: [] 14 | }); 15 | } 16 | if (context.req.type === 'event' && context.req.event === 'enterRoom') { 17 | let room = context.server.roomMap.get(context.req.args.room.id); 18 | room.userList.push({ ...context.req.args.user }); 19 | } 20 | 21 | if (context.req.type === 'event' && context.req.event === 'leaveRoom') { 22 | let room = context.server.roomMap.get(context.req.args.room.id); 23 | room.userList.splice(room.userList.findIndex((user) => user.id == context.req.args.user.id), 1); 24 | } 25 | 26 | next(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/im/routes/index.js: -------------------------------------------------------------------------------- 1 | let requireDirectory = require('require-directory') 2 | module.exports = requireDirectory(module) -------------------------------------------------------------------------------- /src/im/routes/message.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } -------------------------------------------------------------------------------- /src/im/routes/room.js: -------------------------------------------------------------------------------- 1 | var shortid = require('shortid'); 2 | export default { 3 | addRoom: function (context) { 4 | if (!context.server.roomMap) { 5 | context.server.roomMap = new Map(); 6 | } 7 | let roomId = shortid.generate(); 8 | context.req.args.id = roomId; 9 | context.server.roomMap.set(roomId, { 10 | id: roomId, 11 | name: context.req.args.name, 12 | userList: [] 13 | }); 14 | context.server.emit("addRoom", context.req.args); 15 | console.log("addRoom") 16 | }, 17 | enterRoom: function (context) { 18 | let room = context.server.roomMap.get(context.req.args.room.id); 19 | room.userList.push({ ...context.req.args.user }); 20 | context.server.emit("enterRoom", context.req.args); 21 | console.log("enterRoom") 22 | }, 23 | leaveRoom: function (context) { 24 | let room = context.server.roomMap.get(context.req.args.room.id); 25 | room.userList.splice(room.userList.findIndex((user) => user.id == context.req.args.user.id), 1); 26 | context.server.emit("enterRoom", context.req.args); 27 | console.log("enterRoom") 28 | } 29 | } -------------------------------------------------------------------------------- /src/lib/EasySocket.js: -------------------------------------------------------------------------------- 1 | import compose from './compose'; 2 | const WebSocket = require('ws'); 3 | var EventEmitter = require('events').EventEmitter; 4 | export default class EasySocket extends EventEmitter { 5 | constructor() { 6 | super(); 7 | this.clients = new Map(); 8 | this.connectionMiddleware = []; 9 | this.closeMiddleware = []; 10 | this.messageMiddleware = []; 11 | this.errorMiddleware = []; 12 | this.remoteEmitMiddleware = []; 13 | 14 | this.connectionFn = Promise.resolve(); 15 | this.closeFn = Promise.resolve(); 16 | this.messageFn = Promise.resolve(); 17 | this.errorFn = Promise.resolve(); 18 | this.remoteEmitFn = Promise.resolve(); 19 | } 20 | connectionUse(fn, runtime) { 21 | this.connectionMiddleware.push(fn); 22 | if (runtime) { 23 | this.connectionFn = compose(this.connectionMiddleware); 24 | } 25 | return this; 26 | } 27 | closeUse(fn, runtime) { 28 | this.closeMiddleware.push(fn); 29 | if (runtime) { 30 | this.closeFn = compose(this.closeMiddleware); 31 | } 32 | return this; 33 | } 34 | messageUse(fn, runtime) { 35 | this.messageMiddleware.push(fn); 36 | if (runtime) { 37 | this.messageFn = compose(this.messageMiddleware); 38 | } 39 | return this; 40 | } 41 | errorUse(fn, runtime) { 42 | this.errorMiddleware.push(fn); 43 | if (runtime) { 44 | this.errorFn = compose(this.errorMiddleware); 45 | } 46 | return this; 47 | } 48 | remoteEmitUse(fn, runtime) { 49 | this.remoteEmitMiddleware.push(fn); 50 | if (runtime) { 51 | this.remoteEmitFn = compose(this.remoteEmitMiddleware); 52 | } 53 | return this; 54 | } 55 | listen(config) { 56 | this.socket = new WebSocket.Server(config); 57 | this.connectionFn = compose(this.connectionMiddleware); 58 | this.messageFn = compose(this.messageMiddleware); 59 | this.closeFn = compose(this.closeMiddleware); 60 | this.errorFn = compose(this.errorMiddleware); 61 | this.remoteEmitFn = compose(this.remoteEmitMiddleware); 62 | 63 | this.socket.on('connection', (client, req) => { 64 | let context = { server: this, client, req }; 65 | this.connectionFn(context).catch(error => { console.log(error) }); 66 | 67 | client.on('message', (message) => { 68 | let req; 69 | try { 70 | req = JSON.parse(message); 71 | } catch (error) { 72 | req = message; 73 | } 74 | let messageContext = { server: this, client, req } 75 | this.messageFn(messageContext).catch(error => { console.log(error) }) 76 | }); 77 | 78 | client.on('close', (code, message) => { 79 | let closeContext = { server: this, client, code, message }; 80 | this.closeFn(closeContext).catch(error => { console.log(error) }) 81 | }); 82 | 83 | client.on('error', (error) => { 84 | let errorContext = { server: this, client, error }; 85 | this.errorFn(errorContext).catch(error => { console.log(error) }) 86 | }); 87 | }) 88 | } 89 | emit(event, args, isLocal = false) { 90 | let arr = [event, args]; 91 | if (isLocal) { 92 | super.emit.apply(this, arr); 93 | return this; 94 | } 95 | let evt = { 96 | event: event, 97 | args: args 98 | } 99 | let remoteEmitContext = { server: this, event: evt }; 100 | this.remoteEmitFn(remoteEmitContext).catch(error => { console.log(error) }) 101 | return this; 102 | } 103 | } -------------------------------------------------------------------------------- /src/lib/compose.js: -------------------------------------------------------------------------------- 1 | /** 2 | * copy from https://github.com/koajs/compose 3 | */ 4 | export default function compose(middleware) { 5 | if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') 6 | for (const fn of middleware) { 7 | if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') 8 | } 9 | 10 | /** 11 | * @param {Object} context 12 | * @return {Promise} 13 | * @api public 14 | */ 15 | 16 | return function (context, next) { 17 | // last called middleware # 18 | let index = -1 19 | return dispatch(0) 20 | function dispatch(i) { 21 | if (i <= index) return Promise.reject(new Error('next() called multiple times')) 22 | index = i 23 | let fn = middleware[i] 24 | if (i === middleware.length) fn = next 25 | if (!fn) return Promise.resolve() 26 | try { 27 | return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); 28 | } catch (err) { 29 | return Promise.reject(err) 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/lib/proxy.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent') 2 | const url = require('url') 3 | 4 | export default async function(ctx, host, referer = 'http://www.baidu.com') { 5 | let hostObj = url.parse(host) 6 | let result = {} 7 | try { 8 | if (ctx.request.method.toUpperCase() === 'PUT' || ctx.request.method.toUpperCase() === 'POST') { 9 | result = await request(ctx.request.method, host + ctx.request.url) 10 | .set({ 11 | ...ctx.request.header, 12 | Referer: referer, 13 | Host: hostObj.hostname 14 | }) 15 | .send({ ...ctx.request.body }) 16 | } else { 17 | result = await request(ctx.request.method, host + ctx.request.url).set({ 18 | ...ctx.request.header, 19 | Referer: referer, 20 | Host: hostObj.hostname 21 | }) 22 | } 23 | } catch (ex) { 24 | result.body = ex.response.text 25 | } 26 | return result 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/responseTemplate.js: -------------------------------------------------------------------------------- 1 | export let businessError = (ctx,msg) => { 2 | ctx.body = { 3 | statusCode: 500, 4 | msg: msg, 5 | data: null 6 | } 7 | } 8 | 9 | export let success = (ctx,data) => { 10 | ctx.body = { 11 | statusCode: 200, 12 | msg: '', 13 | data: data 14 | } 15 | } -------------------------------------------------------------------------------- /src/middleware/ErrorRoutesCatch.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return function (ctx, next) { 3 | return next().catch((err) => { 4 | switch (err.status) { 5 | case 401: 6 | ctx.status = 401 7 | ctx.body = { 8 | status: 401, 9 | result: { 10 | err: 'Authentication Error', 11 | errInfo: 'Protected resource, use Authorization header to get access.' 12 | } 13 | } 14 | break 15 | default: 16 | throw err 17 | } 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/middleware/ParseUserInfo.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken' 2 | import fs from 'fs' 3 | import path from 'path' 4 | const publicKey = fs.readFileSync(path.join(__dirname, '../../publicKey.pub')) 5 | 6 | module.exports = function () { 7 | return function (ctx, next) { 8 | if (ctx.request.header.authorization) { 9 | let token = ctx.header.authorization.split(' ')[1] 10 | if (token&&token!=='undefined') { 11 | let decoded = jwt.verify(token, publicKey) 12 | if (decoded.userId) { 13 | ctx.user = { 14 | token: token, 15 | userId: decoded.userId 16 | } 17 | } 18 | } 19 | } 20 | return next() 21 | } 22 | } -------------------------------------------------------------------------------- /src/middleware/PermissionCheck.js: -------------------------------------------------------------------------------- 1 | import userService from '../services/userService' 2 | import interfaceService from '../services/interfaceService' 3 | import * as responseTemplate from '../lib/responseTemplate' 4 | import { match, parse } from 'matchit'; 5 | 6 | module.exports = (permission = [], role = [], apiCheck = true) => { 7 | return async function (ctx, next) { 8 | if (!ctx.user || !ctx.user.userId) { 9 | return responseTemplate.businessError(ctx, "没有访问权限") 10 | } 11 | if (permission.length == 0 && role.length == 0 && !apiCheck) { 12 | return next() 13 | } 14 | let isAdmin = await userService.isAdmin(ctx.user.userId) 15 | if (isAdmin) { 16 | return next() 17 | } 18 | let roles = await userService.getUserRole(ctx.user.userId) 19 | let r = roles.filter(s => { 20 | return role.indexOf(s) > -1 21 | }) 22 | if (r && r.length > 0) { 23 | return next() 24 | } 25 | let userPermisssions = await userService.getUserPermission(ctx.user.userId) 26 | let p = userPermisssions.filter(s => { 27 | return permission.indexOf(s) > -1 28 | }) 29 | if (p && p.length > 0) { 30 | return next() 31 | } 32 | let userAccessInterfaces = await interfaceService.getAccessInterfaceList(ctx.user.userId) 33 | userAccessInterfaces = userAccessInterfaces.filter(s => s.method.toUpperCase() === ctx.request.method.toUpperCase()).map(s => parse(s.path)) 34 | let matched = match(ctx.request.url.split("?")[0], userAccessInterfaces) 35 | if (matched.length > 0) { 36 | return next() 37 | } 38 | return responseTemplate.businessError(ctx, "没有访问权限") 39 | } 40 | }; -------------------------------------------------------------------------------- /src/middleware/Proxy.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent') 2 | const url = require('url') 3 | module.exports = (host, referer = 'http://www.baidu.com') => { 4 | let hostObj = url.parse(host) 5 | return async function(ctx, next) { 6 | let result = {} 7 | try { 8 | if (ctx.request.method.toUpperCase() === 'PUT' || ctx.request.method.toUpperCase() === 'POST') { 9 | result = await request(ctx.request.method, host + ctx.request.url) 10 | .set({ 11 | ...ctx.request.header, 12 | Referer: referer, 13 | Host: hostObj.hostname 14 | }) 15 | .send({ ...ctx.request.body }) 16 | } else { 17 | result = await request(ctx.request.method, host + ctx.request.url).set({ 18 | ...ctx.request.header, 19 | Referer: referer, 20 | Host: hostObj.hostname 21 | }) 22 | } 23 | ctx.body = result.type.indexOf('html') > -1 ? result.text : result.body 24 | } catch (ex) { 25 | ctx.status = ex.status 26 | ctx.body = ex.response.text 27 | } 28 | console.log('proxy') 29 | return 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/middleware/RequestLog.js: -------------------------------------------------------------------------------- 1 | import requestLogService from '../services/requestLogService' 2 | module.exports = function () { 3 | return (ctx, next) => { 4 | if (ctx.url.indexOf("/requestlog/pagedlist") > -1) { 5 | return next() 6 | } 7 | const start = new Date() 8 | return next().then(async () => { 9 | const ms = new Date() - start 10 | let userId = ctx.user && ctx.user.userId ? ctx.user.userId : '' 11 | let ip = ctx.ip.split(":").pop() 12 | let log = { 13 | ip: ip, 14 | method: ctx.method, 15 | request: ctx.url.split("?")[0], 16 | time: ms, 17 | createdBy: userId, 18 | createdDate: start.getTime() 19 | } 20 | await requestLogService.addLog(log) 21 | }) 22 | } 23 | } -------------------------------------------------------------------------------- /src/models/baseModel.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | const low = require('lowdb') 4 | const lodashId = require('lodash-id') 5 | const FileAsync = require('lowdb/adapters/FileAsync') 6 | const dbFile = path.join(__dirname, '../db/db.json') 7 | const adapter = new FileAsync(dbFile) 8 | let instance = undefined 9 | module.exports = { 10 | init: function (context) { 11 | return new Promise((resolve, reject) => { 12 | if (instance === undefined) { 13 | low(adapter).then(db => { 14 | db._.mixin(lodashId) 15 | instance = db; 16 | resolve(db.get(context)) 17 | }) 18 | } else { 19 | resolve(instance.get(context)) 20 | } 21 | }) 22 | }, 23 | read: () => { 24 | return new Promise((resolve, reject) => { 25 | if (instance === undefined) { 26 | resolve() 27 | } 28 | else { 29 | instance.read().then(() => { 30 | resolve() 31 | }) 32 | } 33 | }) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/models/index.js: -------------------------------------------------------------------------------- 1 | let requireDirectory = require('require-directory') 2 | module.exports = requireDirectory(module) 3 | -------------------------------------------------------------------------------- /src/models/requestLogModel.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | const low = require('lowdb') 4 | const lodashId = require('lodash-id') 5 | const FileAsync = require('lowdb/adapters/FileAsync') 6 | const dbFile = path.join(__dirname, '../db/request_log_db.json') 7 | const adapter = new FileAsync(dbFile) 8 | let instance = undefined 9 | module.exports = { 10 | init: function (context) { 11 | return new Promise((resolve, reject) => { 12 | if (instance === undefined) { 13 | low(adapter).then(db => { 14 | db._.mixin(lodashId) 15 | instance = db; 16 | resolve(db.get(context)) 17 | }) 18 | } else { 19 | resolve(instance.get(context)) 20 | } 21 | }) 22 | }, 23 | read: () => { 24 | return new Promise((resolve, reject) => { 25 | if (instance === undefined) { 26 | resolve() 27 | } 28 | else { 29 | instance.read().then(() => { 30 | resolve() 31 | }) 32 | } 33 | }) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | let requireDirectory = require('require-directory') 2 | module.exports = requireDirectory(module) 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/routes/main.js: -------------------------------------------------------------------------------- 1 | import KoaRouter from 'koa-router' 2 | import controllers from '../controllers/index.js' 3 | import PermissionCheck from '../middleware/PermissionCheck' 4 | 5 | const router = new KoaRouter() 6 | 7 | router 8 | .get('/public/get', function (ctx, next) { 9 | ctx.body = '允许访问!' 10 | }) // 以/public开头则不用经过权限认证 11 | .post('/auth/login', controllers.auth.login) 12 | .post('/auth/logout', controllers.auth.logout) 13 | 14 | .get('/menu', PermissionCheck(), controllers.menu.getMenuList) 15 | .get('/menu/:id', PermissionCheck(), controllers.menu.getMenu) 16 | .post('/menu/save', PermissionCheck(), controllers.menu.saveMenu) 17 | .del('/menu/:id', PermissionCheck(), controllers.menu.delMenu) 18 | 19 | .get('/route', PermissionCheck(), controllers.route.getRouteList) 20 | .get('/route/paged', PermissionCheck(), controllers.route.getRoutePagedList) 21 | .get('/route/:id', PermissionCheck(), controllers.route.getRoute) 22 | .del('/route/:id', PermissionCheck(), controllers.route.delRoute) 23 | .del('/route/batchdel', PermissionCheck(), controllers.route.delRoutes) 24 | .post('/route/save', PermissionCheck(), controllers.route.saveRoute) 25 | 26 | .get('/role/pagedlist', PermissionCheck(), controllers.role.getRolePagedList) 27 | .get('/role/:id', PermissionCheck(), controllers.role.getRole) 28 | .get('/role/getpermissions/:roleId', PermissionCheck(), controllers.role.getRolePermissions) 29 | .del('/role/del', PermissionCheck(), controllers.role.delRole) 30 | .del('/role/batchdel', PermissionCheck(), controllers.role.delRoles) 31 | .post('/role/save', PermissionCheck(), controllers.role.saveRole) 32 | .post('/role/savepermission', PermissionCheck(), controllers.role.savePermission) 33 | 34 | .get('/user/pagedlist', PermissionCheck(), controllers.user.getUserPagedList) 35 | .get('/user/info', controllers.user.getUserInfo) 36 | .get('/user/:id', PermissionCheck(), controllers.user.getUser) 37 | .del('/user/del', PermissionCheck(), controllers.user.delUser) 38 | .del('/user/batchdel', PermissionCheck(), controllers.user.delUsers) 39 | .post('/user/save', PermissionCheck(), controllers.user.saveUser) 40 | .post('/user/editroleuser', PermissionCheck(), controllers.user.editRoleUser) 41 | 42 | .get('/interface/paged', PermissionCheck(), controllers.interface.getInterfacePagedList) 43 | .get('/interface/:id', PermissionCheck(), controllers.interface.getInterface) 44 | .del('/interface/del', PermissionCheck(), controllers.interface.delInterface) 45 | .del('/interface/batchdel', PermissionCheck(), controllers.interface.delInterfaces) 46 | .post('/interface/save', PermissionCheck(), controllers.interface.saveInterface) 47 | .post('/interface/relate', PermissionCheck(), controllers.interface.relateInterface) 48 | 49 | .get('/requestlog/pagedlist', controllers.requestlog.getRequestLogPagedList) 50 | .post('/resetdb', controllers.system.resetDb) 51 | 52 | 53 | module.exports = router 54 | -------------------------------------------------------------------------------- /src/services/interfaceService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/baseModel' 2 | import userService from '../services/userService' 3 | import _ from 'lodash' 4 | const context = 'interface' 5 | const functionInterfaceContext = 'functionInterface' 6 | module.exports = { 7 | getInterface: async (id) => { 8 | let db = await model.init(context) 9 | let entity = db.find({ id: id }).value() 10 | return entity 11 | }, 12 | getInterfacePagedList: async (pageIndex, pageSize, sortBy, descending, filter) => { 13 | let db = await model.init(context) 14 | let interfaceList = db.value() 15 | let resultList = JSON.parse(JSON.stringify(interfaceList)) 16 | if (filter.id) { 17 | resultList = _.filter(resultList, (o) => { 18 | return o.id.indexOf(filter.id) > -1 19 | }); 20 | } 21 | if (filter.name) { 22 | resultList = _.filter(resultList, (o) => { 23 | return o.name.indexOf(filter.name) > -1 24 | }); 25 | } 26 | if (filter.path) { 27 | resultList = _.filter(resultList, (o) => { 28 | return o.path.indexOf(filter.path) > -1 29 | }); 30 | } 31 | if (filter.method) { 32 | resultList = _.filter(resultList, (o) => { 33 | return o.method.indexOf(filter.method) > -1 34 | }); 35 | } 36 | if (filter.isLocked) { 37 | resultList = _.filter(resultList, (o) => { 38 | return o.isLocked.indexOf(filter.isLocked) > -1 39 | }); 40 | } 41 | if (filter.description) { 42 | resultList = _.filter(resultList, (o) => { 43 | return o.description.indexOf(filter.description) > -1 44 | }); 45 | } 46 | if (filter.functionId) { 47 | let functionInterfaceDb = await model.init(functionInterfaceContext) 48 | let functionInterfaceList = functionInterfaceDb.filter({ functionId: filter.functionId }).value() 49 | let interfaceIdList = functionInterfaceList.map(s => { 50 | return s.interfaceId 51 | }) 52 | resultList = _.map(resultList, (item) => { 53 | if (interfaceIdList.indexOf(item.id) > -1) { 54 | item.isAdd = 1 55 | } else { 56 | item.isAdd = 2 57 | } 58 | return item 59 | }) 60 | sortBy = "isAdd" 61 | } 62 | let totalCount = resultList.length 63 | if (sortBy) { 64 | resultList = _.sortBy(resultList, [sortBy]) 65 | if (descending === 'true') { 66 | resultList = resultList.reverse() 67 | } 68 | } 69 | if (!pageIndex || pageIndex <= 0) { 70 | pageIndex = 1 71 | } 72 | if (pageSize) { 73 | let start = (pageIndex - 1) * pageSize 74 | let end = pageIndex * pageSize 75 | resultList = _.slice(resultList, start, end) 76 | } 77 | return { 78 | totalCount: totalCount, 79 | rows: resultList 80 | } 81 | 82 | }, 83 | delInterface: async (id) => { 84 | let db = await model.init(context) 85 | await db.remove({ id: id }).write() 86 | }, 87 | saveInterface: async (entity) => { 88 | let db = await model.init(context) 89 | let exist = db.find({ path: entity.path, method: entity.method }).value() 90 | if (exist && exist.id != entity.id) { 91 | return { 92 | success: false, 93 | msg: "路径与方法组合必须唯一" 94 | } 95 | } 96 | if (entity.id) { 97 | await db.find({ id: entity.id }) 98 | .assign(entity) 99 | .write() 100 | } else { 101 | await db.insert(entity).write() 102 | } 103 | return { 104 | success: true, 105 | msg: "" 106 | } 107 | }, 108 | relate: async (functionInterface) => { 109 | let functionInterfaceDb = await model.init(functionInterfaceContext) 110 | if (functionInterface.action == 1) { 111 | await functionInterfaceDb.push({ functionId: functionInterface.functionId, interfaceId: functionInterface.interfaceId }).write() 112 | } else { 113 | await functionInterfaceDb.remove({ functionId: functionInterface.functionId, interfaceId: functionInterface.interfaceId }).write() 114 | } 115 | }, 116 | getAccessInterfaceList: async (userId) => { 117 | let functionInterfaceDb = await model.init(functionInterfaceContext) 118 | let db = await model.init(context) 119 | let userFunctions = await userService.getUserFunctions(userId) 120 | let userFunctionIds = userFunctions.map(s => s.id) 121 | let functionInterfaceList = functionInterfaceDb.value() 122 | let userFunctionInterfaceList = functionInterfaceList.filter(s => { 123 | return userFunctionIds.indexOf(s.functionId) > -1 124 | }) 125 | let userInterfaceIdList = userFunctionInterfaceList.map(s => s.interfaceId) 126 | let interfaceIdList = db.value() 127 | let userInterfaceList = interfaceIdList.filter(s => { 128 | return userInterfaceIdList.indexOf(s.id) > -1 129 | }) 130 | return userInterfaceList 131 | } 132 | } -------------------------------------------------------------------------------- /src/services/memuService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/baseModel' 2 | import userService from './userService' 3 | import _ from 'lodash' 4 | const context = 'menu' 5 | let buildMenu = (parentMenu, menuList) => { 6 | parentMenu.children = [] 7 | let children = menuList.filter((item) => { 8 | return item.parentId == parentMenu.id 9 | }) 10 | for (let menu of children) { 11 | buildMenu(menu, menuList) 12 | } 13 | parentMenu.children.push(...children) 14 | } 15 | let buildAccessMenu = (parentMenu, menuList, userPermission) => { 16 | parentMenu.children = [] 17 | let children = menuList.filter((item) => { 18 | return item.parentId == parentMenu.id && (!item.permission || userPermission.indexOf(item.permission) > -1) 19 | }) 20 | //父级没有权限访问,子级也不能访问 21 | for (let menu of children) { 22 | buildAccessMenu(menu, menuList, userPermission) 23 | } 24 | parentMenu.children.push(...children) 25 | } 26 | let checkAccssMenu = (accessMenuList, menuList) => { 27 | for (let item of accessMenuList) { 28 | if (item.children) { 29 | checkAccssMenu(item.children, menuList) 30 | } 31 | } 32 | _.remove(accessMenuList, (item) => { 33 | return item.children.length == 0 && menuList.some(s => { 34 | return s.parentId == item.id 35 | }) 36 | }); 37 | } 38 | let menuService = { 39 | getMenu: async (id) => { 40 | let db = await model.init(context) 41 | let menu = db.find({ id: id }).value() 42 | if (!menu) { 43 | menu = db.find({ id: parseInt(id) }).value() 44 | } 45 | return menu 46 | }, 47 | getMenuList: async () => { 48 | let db = await model.init(context) 49 | let menuList = JSON.parse(JSON.stringify(db.value())) 50 | menuList = _.sortBy(menuList, ["sort"]) 51 | let parentMenuList = menuList.filter((item) => { 52 | return item.parentId === 0 53 | }) 54 | for (let menu of parentMenuList) { 55 | buildMenu(menu, menuList) 56 | } 57 | return parentMenuList 58 | }, 59 | getAccessMenuList: async (userId) => { 60 | let db = await model.init(context) 61 | let menuList = JSON.parse(JSON.stringify(db.filter({ type: 1 }).value())) 62 | menuList = _.sortBy(menuList, ["sort"]) 63 | let parentMenuList = menuList.filter((item) => { 64 | return item.parentId == 0 && !item.isLock 65 | }) 66 | let isAdmin = await userService.isAdmin(userId) 67 | let userPermission = await userService.getUserPermission(userId) 68 | if (isAdmin) { 69 | for (let menu of parentMenuList) { 70 | buildMenu(menu, menuList) 71 | } 72 | } else { 73 | for (let menu of parentMenuList) { 74 | buildAccessMenu(menu, menuList, userPermission) 75 | } 76 | } 77 | checkAccssMenu(parentMenuList, menuList) 78 | return parentMenuList 79 | }, 80 | saveMenu: async (menu) => { 81 | let db = await model.init(context) 82 | if (menu.id) { 83 | await db.find({ id: menu.id }) 84 | .assign(menu) 85 | .write() 86 | } else { 87 | await db.insert(menu).write() 88 | } 89 | return { 90 | success: true, 91 | msg: "" 92 | } 93 | }, 94 | delMenu: async (menuId) => { 95 | let db = await model.init(context) 96 | let child = db.find({ parentId: menuId }).value() 97 | if (child) { 98 | return { 99 | success: false, 100 | msg: "请先删除子菜单" 101 | } 102 | } 103 | await db.remove({ id: menuId }).write() 104 | return { 105 | success: true, 106 | msg: "" 107 | } 108 | } 109 | } 110 | module.exports = menuService -------------------------------------------------------------------------------- /src/services/requestLogService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/requestLogModel' 2 | import userService from './userService' 3 | import _ from 'lodash' 4 | const context = 'requestLog' 5 | 6 | let requestLogService = { 7 | addLog: async (log) => { 8 | let db = await model.init(context) 9 | await db.insert(log).write() 10 | }, 11 | getRequestLogPagedList: async (pageIndex, pageSize, sortBy, descending) => { 12 | let db = await model.init(context) 13 | let list = db.value() 14 | let resultList = list 15 | let totalCount = resultList.length 16 | if (sortBy) { 17 | resultList = _.sortBy(resultList, [sortBy]) 18 | if (descending === 'true') { 19 | resultList = resultList.reverse() 20 | } 21 | } 22 | else { 23 | resultList = _.sortBy(resultList, ["createdDate"]) 24 | if (descending === 'true') { 25 | resultList = resultList.reverse() 26 | } 27 | } 28 | let start = (pageIndex - 1) * pageSize 29 | let end = pageIndex * pageSize 30 | resultList = _.slice(resultList, start, end) 31 | let userList = await userService.getUserList() 32 | for (let item of resultList) { 33 | let user = userList.filter(s => { 34 | return s.id == item.createdBy 35 | }) 36 | if (user.length > 0) { 37 | item.createdByName = user[0].name 38 | } else { 39 | item.createdByName = item.createdBy 40 | } 41 | } 42 | return { 43 | totalCount: totalCount, 44 | rows: resultList 45 | } 46 | 47 | } 48 | } 49 | module.exports = requestLogService -------------------------------------------------------------------------------- /src/services/roleService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/baseModel' 2 | import _ from 'lodash' 3 | const context = 'role' 4 | const permissionContext = "permission" 5 | const roleUserContext = 'roleUser' 6 | module.exports = { 7 | getRole: async (id) => { 8 | let db = await model.init(context) 9 | let role = db.find({ id: id }).value() 10 | return role 11 | }, 12 | getRolePagedList: async (pageIndex, pageSize, sortBy, descending, filter) => { 13 | let db = await model.init(context) 14 | let roleList = db.value() 15 | let resultList = roleList 16 | if (filter.code) { 17 | resultList = _.filter(resultList, (o) => { 18 | return o.code.indexOf(filter.code) > -1 19 | }); 20 | } 21 | if (filter.name) { 22 | resultList = _.filter(resultList, (o) => { 23 | return o.name.indexOf(filter.name) > -1 24 | }); 25 | } 26 | if (filter.userId) { 27 | let roleUserDb = await model.init(roleUserContext) 28 | let roleUserList = roleUserDb.filter({ userId: filter.userId }).value() 29 | roleUserList = roleUserList.map(s => { 30 | return s.roleId 31 | }) 32 | resultList = _.map(resultList, (item) => { 33 | if (roleUserList.indexOf(item.id) > -1) { 34 | item.isAdd = 1 35 | } else { 36 | item.isAdd = 2 37 | } 38 | return item 39 | }) 40 | sortBy = "isAdd" 41 | } 42 | let totalCount = resultList.length 43 | if (sortBy) { 44 | resultList = _.sortBy(resultList, [sortBy]) 45 | if (descending === 'true') { 46 | resultList = resultList.reverse() 47 | } 48 | } 49 | let start = (pageIndex - 1) * pageSize 50 | let end = pageIndex * pageSize 51 | resultList = _.slice(resultList, start, end) 52 | 53 | return { 54 | totalCount: totalCount, 55 | rows: resultList 56 | } 57 | 58 | }, 59 | delRole: async (id) => { 60 | let db = await model.init(context) 61 | await db.remove({ id: id }).write() 62 | }, 63 | saveRole: async (role) => { 64 | let db = await model.init(context) 65 | let exist = db.find({ code: role.code }).value() 66 | if (exist && exist.id != role.id) { 67 | return { 68 | success: false, 69 | msg: "角色编码已经存在" 70 | } 71 | } 72 | let exist1 = db.find({ name: role.name }).value() 73 | if (exist1 && exist1.id != role.id) { 74 | return { 75 | success: false, 76 | msg: "角色名称已经存在" 77 | } 78 | } 79 | if (role.id) { 80 | 81 | await db.find({ id: role.id }) 82 | .assign(role) 83 | .write() 84 | } else { 85 | await db.insert(role).write() 86 | } 87 | return { 88 | success: true, 89 | msg: "" 90 | } 91 | }, 92 | getRoleFunctions: async (roleId) => { 93 | let db = await model.init(permissionContext) 94 | let list = db.value() 95 | let roleFunctions = list.filter(s => { 96 | return s.roleId == roleId 97 | }) 98 | return roleFunctions 99 | }, 100 | getRoleFuntionsByRoleIds: async (roleIds) => { 101 | let db = await model.init(permissionContext) 102 | let list = db.value() 103 | let roleFunctions = list.filter(s => { 104 | return roleIds.indexOf(s.roleId) > -1 105 | }) 106 | return roleFunctions 107 | }, 108 | savePermission: async (roleId, permissions) => { 109 | let db = await model.init(permissionContext) 110 | await await db.remove({ roleId: roleId }).write() 111 | for (let permission of permissions) { 112 | await db.insert({ 113 | roleId: roleId, 114 | functionId: permission, 115 | }).write() 116 | } 117 | }, 118 | getRoleListByIdList: async (idList) => { 119 | let db = await model.init(context) 120 | let roleList = db.value() 121 | let result = roleList.filter(s => { 122 | return idList.indexOf(s.id) > -1 123 | }) 124 | return result 125 | } 126 | } -------------------------------------------------------------------------------- /src/services/routeService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/baseModel' 2 | import userService from './userService' 3 | import _ from 'lodash' 4 | const context = 'route' 5 | let buildChildren = (parent, list) => { 6 | parent.children = [] 7 | let children = list.filter((item) => { 8 | return item.parentId == parent.id 9 | }) 10 | for (let item of children) { 11 | buildChildren(item, list) 12 | } 13 | parent.children.push(...children) 14 | } 15 | module.exports = { 16 | getRoute: async (id) => { 17 | let db = await model.init(context) 18 | let route = db.find({ id: id }).value() 19 | return route 20 | }, 21 | getRouteList: async () => { 22 | let db = await model.init(context) 23 | let list = JSON.parse(JSON.stringify(db.value())) 24 | list = _.sortBy(list, ["sort"]) 25 | let parentList = list.filter((item) => { 26 | return !item.parentId 27 | }) 28 | for (let item of parentList) { 29 | buildChildren(item, list) 30 | } 31 | return parentList 32 | }, 33 | getRoutePagedList: async (pageIndex, pageSize, sortBy, descending, filter) => { 34 | let db = await model.init(context) 35 | let routeList = db.value() 36 | let resultList = routeList 37 | 38 | if (filter.id) { 39 | resultList = _.filter(resultList, (o) => { 40 | return o.id.indexOf(filter.id) > -1 41 | }); 42 | } 43 | 44 | if (filter.parentId) { 45 | resultList = _.filter(resultList, (o) => { 46 | return o.parentId.indexOf(filter.parentId) > -1 47 | }); 48 | } 49 | 50 | if (filter.name) { 51 | resultList = _.filter(resultList, (o) => { 52 | return o.name.indexOf(filter.name) > -1 53 | }); 54 | } 55 | 56 | if (filter.path) { 57 | resultList = _.filter(resultList, (o) => { 58 | return o.path.indexOf(filter.path) > -1 59 | }); 60 | } 61 | 62 | if (filter.title) { 63 | resultList = _.filter(resultList, (o) => { 64 | return o.title.indexOf(filter.title) > -1 65 | }); 66 | } 67 | 68 | if (filter.component) { 69 | resultList = _.filter(resultList, (o) => { 70 | return o.component.indexOf(filter.component) > -1 71 | }); 72 | } 73 | 74 | if (filter.componentPath) { 75 | resultList = _.filter(resultList, (o) => { 76 | return o.componentPath.indexOf(filter.componentPath) > -1 77 | }); 78 | } 79 | 80 | if (filter.cache) { 81 | resultList = _.filter(resultList, (o) => { 82 | return o.cache.indexOf(filter.cache) > -1 83 | }); 84 | } 85 | 86 | if (filter.isLock) { 87 | resultList = _.filter(resultList, (o) => { 88 | return o.isLock.indexOf(filter.isLock) > -1 89 | }); 90 | } 91 | 92 | if (filter.sort) { 93 | resultList = _.filter(resultList, (o) => { 94 | return o.sort.indexOf(filter.sort) > -1 95 | }); 96 | } 97 | 98 | let totalCount = resultList.length 99 | if (sortBy) { 100 | resultList = _.sortBy(resultList, [sortBy]) 101 | if (descending === 'true') { 102 | resultList = resultList.reverse() 103 | } 104 | } 105 | if (!pageIndex || pageIndex <= 0) { 106 | pageIndex = 1 107 | } 108 | if (pageSize) { 109 | let start = (pageIndex - 1) * pageSize 110 | let end = pageIndex * pageSize 111 | resultList = _.slice(resultList, start, end) 112 | } 113 | 114 | return { 115 | totalCount: totalCount, 116 | rows: resultList 117 | } 118 | 119 | }, 120 | delRoute: async (id) => { 121 | let db = await model.init(context) 122 | let child = db.find({ parentId: id }).value() 123 | if (child) { 124 | return { 125 | success: false, 126 | msg: "请先删除子路由" 127 | } 128 | } 129 | await db.remove({ id: id }).write() 130 | return { 131 | success: true, 132 | msg: "" 133 | } 134 | }, 135 | saveRoute: async (route) => { 136 | let db = await model.init(context) 137 | let exist = db.find({ name: route.name }).value() 138 | if (exist && exist.id != route.id) { 139 | return { 140 | success: false, 141 | msg: "name已经存在" 142 | } 143 | } 144 | if (route.id) { 145 | await db.find({ id: route.id }) 146 | .assign(route) 147 | .write() 148 | } else { 149 | await db.insert(route).write() 150 | } 151 | return { 152 | success: true, 153 | msg: "" 154 | } 155 | }, 156 | getAccessRouteList: async (userId) => { 157 | let db = await model.init(context) 158 | let routeList = JSON.parse(JSON.stringify(db.value())) 159 | routeList = _.sortBy(routeList, ["sort"]) 160 | let parentRouteList = routeList.filter((item) => { 161 | return item.parentId == 0 && !item.isLock 162 | }) 163 | let isAdmin = await userService.isAdmin(userId) 164 | let userPermission = await userService.getUserPermission(userId) 165 | if (isAdmin) { 166 | for (let route of parentRouteList) { 167 | buildRoute(route, routeList) 168 | } 169 | } else { 170 | for (let route of parentRouteList) { 171 | buildAccessRoute(route, routeList, userPermission) 172 | } 173 | } 174 | checkAccssRoute(parentRouteList, routeList) 175 | return parentRouteList 176 | }, 177 | } 178 | let buildRoute = (parentRoute, routeList) => { 179 | parentRoute.children = [] 180 | let children = routeList.filter((item) => { 181 | return item.parentId == parentRoute.id 182 | }) 183 | for (let route of children) { 184 | buildRoute(route, routeList) 185 | } 186 | parentRoute.children.push(...children) 187 | } 188 | let buildAccessRoute = (parentRoute, routeList, userPermission) => { 189 | parentRoute.children = [] 190 | let children = routeList.filter((item) => { 191 | return item.parentId == parentRoute.id && (!item.permission || userPermission.indexOf(item.permission) > -1) 192 | }) 193 | //父级没有权限访问,子级也不能访问 194 | for (let route of children) { 195 | buildAccessRoute(route, routeList, userPermission) 196 | } 197 | parentRoute.children.push(...children) 198 | } 199 | let checkAccssRoute = (accessRouteList, routeList) => { 200 | for (let item of accessRouteList) { 201 | if (item.children) { 202 | checkAccssRoute(item.children, routeList) 203 | } 204 | } 205 | _.remove(accessRouteList, (item) => { 206 | return item.children.length == 0 && routeList.some(s => { 207 | return s.parentId == item.id 208 | }) 209 | }); 210 | } -------------------------------------------------------------------------------- /src/services/systemService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/baseModel' 2 | 3 | module.exports = { 4 | resetDb:async()=>{ 5 | await model.read() 6 | } 7 | } -------------------------------------------------------------------------------- /src/services/tokenService.js: -------------------------------------------------------------------------------- 1 | import model from '../models/baseModel' 2 | import { exists } from 'fs'; 3 | const context = 'token' 4 | module.exports = { 5 | add: async (token) => { 6 | let db = await model.init(context) 7 | await db.push({ value: token }).write() 8 | }, 9 | exist: async (token) => { 10 | let db = await model.init(context) 11 | let exist = db.find({ value: token }) 12 | .value() 13 | if (exist) { 14 | return true 15 | } else { 16 | return false 17 | } 18 | }, 19 | remove: async (token) => { 20 | let db = await model.init(context) 21 | await db.remove({ value: token }) 22 | .write() 23 | } 24 | } -------------------------------------------------------------------------------- /src/services/userService.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | import model from '../models/baseModel' 3 | import roleService from './roleService' 4 | const context = 'user' 5 | const adminContext = 'admin' 6 | const roleUserContext = 'roleUser' 7 | const menuContext = 'menu' 8 | module.exports = { 9 | getUserByNameAndPwd: async (name, pwd) => { 10 | let db = await model.init(context) 11 | let user = db.find({ name: name, password: pwd }).value() 12 | return user 13 | }, 14 | getUserById: async (id) => { 15 | let db = await model.init(context) 16 | let user = db.find({ id: id }).value() 17 | return user 18 | }, 19 | getUserList: async () => { 20 | let db = await model.init(context) 21 | let list = db.value() 22 | return list 23 | }, 24 | getUserPagedList: async (pageIndex, pageSize, sortBy, descending, filter) => { 25 | let db = await model.init(context) 26 | let roleList = db.value() 27 | let resultList = JSON.parse(JSON.stringify(roleList)) 28 | if (filter.name) { 29 | resultList = _.filter(resultList, (o) => { 30 | return o.name.indexOf(filter.name) > -1 || o.trueName.indexOf(filter.name) > -1 31 | }); 32 | } 33 | if (filter.email) { 34 | resultList = _.filter(resultList, (o) => { 35 | return o.email.indexOf(filter.email) > -1 36 | }); 37 | } 38 | if (filter.roleId) { 39 | let roleUserDb = await model.init(roleUserContext) 40 | let roleUserList = roleUserDb.filter({ roleId: filter.roleId }).value() 41 | roleUserList = roleUserList.map(s => { 42 | return s.userId 43 | }) 44 | resultList = _.map(resultList, (item) => { 45 | if (roleUserList.indexOf(item.id) > -1) { 46 | item.isAdd = 1 47 | } else { 48 | item.isAdd = 2 49 | } 50 | return item 51 | }) 52 | sortBy = "isAdd" 53 | } 54 | let totalCount = resultList.length 55 | if (sortBy) { 56 | resultList = _.sortBy(resultList, [sortBy]) 57 | if (descending === 'true') { 58 | resultList = resultList.reverse() 59 | } 60 | } 61 | let start = (pageIndex - 1) * pageSize 62 | let end = pageIndex * pageSize 63 | resultList = _.slice(resultList, start, end) 64 | 65 | return { 66 | totalCount: totalCount, 67 | rows: resultList 68 | } 69 | 70 | }, 71 | delUser: async (id) => { 72 | let db = await model.init(context) 73 | let adminDb = await model.init(adminContext) 74 | let admin = adminDb.value() 75 | if (admin.indexOf(id) > -1) { 76 | return { 77 | success: false, 78 | msg: "不能删除管理员账号" 79 | } 80 | } 81 | await db.remove({ id: id }).write() 82 | return { 83 | success: true, 84 | msg: "" 85 | } 86 | }, 87 | saveUser: async (user) => { 88 | let db = await model.init(context) 89 | let exist = db.find({ name: user.name }).value() 90 | if (exist && exist.id != user.id) { 91 | return { 92 | success: false, 93 | msg: "账号名称已经存在" 94 | } 95 | } 96 | let exist1 = db.find({ email: user.email }).value() 97 | if (exist1 && exist1.id != user.id) { 98 | return { 99 | success: false, 100 | msg: "用户邮箱已经存在" 101 | } 102 | } 103 | if (user.id) { 104 | await db.find({ id: user.id }) 105 | .assign(user) 106 | .write() 107 | } else { 108 | user.password = "123456" 109 | await db.insert(user).write() 110 | } 111 | return { 112 | success: true, 113 | msg: "" 114 | } 115 | }, 116 | changePassWord: async (user) => { 117 | 118 | }, 119 | editRoleUser: async (roleUser) => { 120 | let roleUserDb = await model.init(roleUserContext) 121 | if (roleUser.action == 1) { 122 | await roleUserDb.push({ userId: roleUser.userId, roleId: roleUser.roleId }).write() 123 | } else { 124 | await roleUserDb.remove({ userId: roleUser.userId, roleId: roleUser.roleId }).write() 125 | } 126 | }, 127 | getUserRole: async (userId) => { 128 | let roleUserDb = await model.init(roleUserContext) 129 | let roleUserList = roleUserDb.filter({ userId: userId }).value() 130 | let roleIdList = roleUserList.map(s => { 131 | return s.roleId 132 | }) 133 | let roleList = await roleService.getRoleListByIdList(roleIdList) 134 | let roleCodeList = roleList.map(s => { 135 | return s.code 136 | }) 137 | return roleCodeList 138 | }, 139 | getUserPermission: async (userId) => { 140 | let roleUserDb = await model.init(roleUserContext) 141 | let roleUserList = roleUserDb.filter({ userId: userId }).value() 142 | let roleIdList = roleUserList.map(s => { 143 | return s.roleId 144 | }) 145 | let roleFunctions = await roleService.getRoleFuntionsByRoleIds(roleIdList) 146 | let functionIds = roleFunctions.map(s => { 147 | return s.functionId 148 | }) 149 | let menudb = await model.init(menuContext) 150 | let menuList = menudb.value() 151 | menuList = menuList.filter(s => { 152 | return functionIds.indexOf(s.id) > -1 153 | }) 154 | let functionCodeList = menuList.map(s => { 155 | return s.permission 156 | }) 157 | functionCodeList = functionCodeList.filter(s => s) 158 | return functionCodeList 159 | }, 160 | getUserFunctions: async (userId) => { 161 | let roleUserDb = await model.init(roleUserContext) 162 | let roleUserList = roleUserDb.filter({ userId: userId }).value() 163 | let roleIdList = roleUserList.map(s => { 164 | return s.roleId 165 | }) 166 | let roleFunctions = await roleService.getRoleFuntionsByRoleIds(roleIdList) 167 | let functionIds = roleFunctions.map(s => { 168 | return s.functionId 169 | }) 170 | let menudb = await model.init(menuContext) 171 | let menuList = menudb.value() 172 | let functionList = menuList.filter(s => { 173 | return functionIds.indexOf(s.id) > -1 174 | }) 175 | return functionList; 176 | }, 177 | isAdmin: async (userId) => { 178 | let adminDb = await model.init(adminContext) 179 | let admin = adminDb.value() 180 | if (admin.indexOf(userId) > -1) { 181 | return true 182 | } 183 | return false 184 | } 185 | } -------------------------------------------------------------------------------- /src/tool/Common.js: -------------------------------------------------------------------------------- 1 | import { 2 | SystemConfig 3 | } from '../config' 4 | 5 | // 截取字符串,多余的部分用...代替 6 | export let setString = (str, len) => { 7 | let StrLen = 0 8 | let s = '' 9 | for (let i = 0; i < str.length; i++) { 10 | if (str.charCodeAt(i) > 128) { 11 | StrLen += 2 12 | } else { 13 | StrLen++ 14 | } 15 | s += str.charAt(i) 16 | if (StrLen >= len) { 17 | return s + '...' 18 | } 19 | } 20 | return s 21 | } 22 | 23 | // 格式化设置 24 | export let OptionFormat = (GetOptions) => { 25 | let options = '{' 26 | for (let n = 0; n < GetOptions.length; n++) { 27 | options = options + '\'' + GetOptions[n].option_name + '\':\'' + GetOptions[n].option_value + '\'' 28 | if (n < GetOptions.length - 1) { 29 | options = options + ',' 30 | } 31 | } 32 | return JSON.parse(options + '}') 33 | } 34 | 35 | // 替换SQL字符串中的前缀 36 | export let SqlFormat = (str) => { 37 | if (SystemConfig.mysql_prefix !== 'api_') { 38 | str = str.replace(/api_/g, SystemConfig.mysql_prefix) 39 | } 40 | return str 41 | } 42 | 43 | // 数组去重 44 | export let HovercUnique = (arr) => { 45 | let n = {} 46 | let r = [] 47 | for (var i = 0; i < arr.length; i++) { 48 | if (!n[arr[i]]) { 49 | n[arr[i]] = true 50 | r.push(arr[i]) 51 | } 52 | } 53 | return r 54 | } 55 | 56 | // 获取json长度 57 | export let getJsonLength = (jsonData) => { 58 | var arr = [] 59 | for (var item in jsonData) { 60 | arr.push(jsonData[item]) 61 | } 62 | return arr.length 63 | } 64 | -------------------------------------------------------------------------------- /templates/config/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | //server 3 | RouteRelativePath: '/src/routes/', 4 | ControllerRelativePath: '/src/controllers/', 5 | ServiceRelativePath: '/src/services/', 6 | ModelRelativePath: '/src/models/', 7 | DBRelativePath: '/src/db/' 8 | } -------------------------------------------------------------------------------- /templates/config/index.js: -------------------------------------------------------------------------------- 1 | import model from './model'; 2 | import config from './config'; 3 | export default { 4 | model, 5 | config 6 | } -------------------------------------------------------------------------------- /templates/config/model.js: -------------------------------------------------------------------------------- 1 | 2 | var shortid = require('shortid') 3 | var Mock = require('mockjs') 4 | var Random = Mock.Random 5 | 6 | //必须包含字段id 7 | export default { 8 | name: "book", 9 | Name: "Book", 10 | properties: [ 11 | { 12 | key: "id", 13 | title: "id" 14 | }, 15 | { 16 | key: "name", 17 | title: "书名" 18 | }, 19 | { 20 | key: "author", 21 | title: "作者" 22 | }, 23 | { 24 | key: "press", 25 | title: "出版社" 26 | } 27 | ], 28 | buildMockData: function () {//不需要生成设为false 29 | let data = [] 30 | for (let i = 0; i < 100; i++) { 31 | data.push({ 32 | id: shortid.generate(), 33 | name: Random.cword(5, 7), 34 | author: Random.cname(), 35 | press: Random.cword(5, 7) 36 | }) 37 | } 38 | return data 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /templates/generate.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const CodeGenerateConfig = require('./config').default 3 | const Model = CodeGenerateConfig.model 4 | 5 | module.exports = { 6 | run: function(gulp, nunjucksRender, rename, nunjucksRenderConfig) { 7 | nunjucksRenderConfig.data = { 8 | model: CodeGenerateConfig.model, 9 | config: CodeGenerateConfig.config 10 | } 11 | const ServerProjectRootPath = nunjucksRenderConfig.ServerFullPath 12 | //server 13 | const serverTemplatePath = 'templates/server/' 14 | gulp.src(`${serverTemplatePath}controller.njk`) 15 | .pipe(nunjucksRender(nunjucksRenderConfig)) 16 | .pipe(rename(Model.name + '.js')) 17 | .pipe( 18 | gulp.dest( 19 | ServerProjectRootPath + 20 | CodeGenerateConfig.config.ControllerRelativePath 21 | ) 22 | ) 23 | 24 | gulp.src(`${serverTemplatePath}service.njk`) 25 | .pipe(nunjucksRender(nunjucksRenderConfig)) 26 | .pipe(rename(Model.name + 'Service.js')) 27 | .pipe( 28 | gulp.dest( 29 | ServerProjectRootPath + 30 | CodeGenerateConfig.config.ServiceRelativePath 31 | ) 32 | ) 33 | 34 | gulp.src(`${serverTemplatePath}model.njk`) 35 | .pipe(nunjucksRender(nunjucksRenderConfig)) 36 | .pipe(rename(Model.name + 'Model.js')) 37 | .pipe( 38 | gulp.dest( 39 | ServerProjectRootPath + 40 | CodeGenerateConfig.config.ModelRelativePath 41 | ) 42 | ) 43 | 44 | gulp.src(`${serverTemplatePath}db.njk`) 45 | .pipe(nunjucksRender(nunjucksRenderConfig)) 46 | .pipe(rename(Model.name + '_db.json')) 47 | .pipe( 48 | gulp.dest( 49 | ServerProjectRootPath + 50 | CodeGenerateConfig.config.DBRelativePath 51 | ) 52 | ) 53 | 54 | return gulp 55 | .src(`${serverTemplatePath}route.njk`) 56 | .pipe(nunjucksRender(nunjucksRenderConfig)) 57 | .pipe(rename(Model.name + 'Route.js')) 58 | .pipe( 59 | gulp.dest( 60 | ServerProjectRootPath + 61 | CodeGenerateConfig.config.RouteRelativePath 62 | ) 63 | ) 64 | }, 65 | quickAdd: async function(options, nunjucksRenderConfig) { 66 | const ServerProjectRootPath = nunjucksRenderConfig.ServerFullPath 67 | const serverTemplatePath = 'templates/server/quickAdd/' 68 | const existFileFullPath = 69 | ServerProjectRootPath + 70 | CodeGenerateConfig.config.ControllerRelativePath + 71 | options[0] + 72 | '.js' 73 | let controllerTemplate = await fs.readFile( 74 | `${ServerProjectRootPath}/${serverTemplatePath}controller.njk`, 75 | 'utf-8' 76 | ) 77 | let routeTemplate = await fs.readFile( 78 | `${ServerProjectRootPath}/${serverTemplatePath}route.njk`, 79 | 'utf-8' 80 | ) 81 | if (await fs.exists(existFileFullPath)) { 82 | console.log('exist entity') 83 | 84 | await fs.appendFile( 85 | ServerProjectRootPath + 86 | CodeGenerateConfig.config.ControllerRelativePath + 87 | options[0] + 88 | '.js', 89 | controllerTemplate.replace(/<\$ name \$>/g, options[2]) 90 | ) 91 | let routeContent = await fs.readFile( 92 | ServerProjectRootPath + 93 | CodeGenerateConfig.config.RouteRelativePath + 94 | options[0] + 95 | 'Route.js', 96 | 'utf-8' 97 | ) 98 | let routeContentLines = routeContent.split('\n') 99 | routeContentLines.splice( 100 | -2, 101 | 0, 102 | ` .get('${options[1]}', controllers.${options[0]}.${ 103 | options[2] 104 | })\r` 105 | ) 106 | await fs.writeFile( 107 | ServerProjectRootPath + 108 | CodeGenerateConfig.config.RouteRelativePath + 109 | options[0] + 110 | 'Route.js', 111 | routeContentLines.join('\n') 112 | ) 113 | } else { 114 | await fs.writeFile( 115 | ServerProjectRootPath + 116 | CodeGenerateConfig.config.ControllerRelativePath + 117 | options[0] + 118 | '.js', 119 | controllerTemplate.replace(/<\$ name \$>/g, options[2]) 120 | ) 121 | 122 | await fs.writeFile( 123 | ServerProjectRootPath + 124 | CodeGenerateConfig.config.RouteRelativePath + 125 | options[0] + 126 | 'Route.js', 127 | routeTemplate 128 | .replace(/<\$ api \$>/g, options[1]) 129 | .replace(/<\$ entity \$>/g, options[0]) 130 | .replace(/<\$ method \$>/g, options[2]) 131 | ) 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /templates/server/controller.njk: -------------------------------------------------------------------------------- 1 | import <$ model.name $>Service from '../services/<$ model.name $>Service' 2 | import * as responseTemplate from '../lib/responseTemplate' 3 | 4 | export let get<$ model.Name $> = async (ctx) => { 5 | let id = ctx.params.id 6 | let <$ model.name $> = await <$ model.name $>Service.get<$ model.Name $>(id) 7 | if (!<$ model.name $>) { 8 | return responseTemplate.businessError(ctx, "<$ model.name $>不存在!") 9 | } 10 | return responseTemplate.success(ctx, <$ model.name $>) 11 | } 12 | export let get<$ model.Name $>PagedList = async (ctx, next) => { 13 | let pageIndex = ctx.query.pageIndex 14 | let pageSize = ctx.query.pageSize 15 | let sortBy = ctx.query.sortBy 16 | let descending = ctx.query.descending 17 | let filter = { 18 | <% for property in model.properties -%> 19 | <$ property.key $>: ctx.query.<$ property.key -$>, 20 | <% endfor %> 21 | } 22 | let pagedList = await <$ model.name $>Service.get<$ model.Name $>PagedList(pageIndex, pageSize, sortBy, descending, filter) 23 | return responseTemplate.success(ctx, pagedList) 24 | } 25 | export let del<$ model.Name $> = async (ctx) => { 26 | let id = ctx.query.id 27 | await <$ model.name $>Service.del<$ model.Name $>(id) 28 | return responseTemplate.success(ctx, null) 29 | } 30 | 31 | export let del<$ model.Name $>s = async (ctx) => { 32 | let ids = JSON.parse(ctx.query.ids) 33 | for (let id of ids) { 34 | await <$ model.name $>Service.del<$ model.Name $>(id) 35 | } 36 | return responseTemplate.success(ctx, null) 37 | } 38 | 39 | export let save<$ model.Name $> = async (ctx) => { 40 | let entity = ctx.request.body 41 | <% for property in model.properties -%> 42 | <% if property.key!='id' -%> 43 | if (entity.<$ property.key $> == "") { 44 | return responseTemplate.businessError(ctx, "<$ property.title $>不能为空!") 45 | } 46 | <% endif -%> 47 | <% endfor -%> 48 | let result = await <$ model.name $>Service.save<$ model.Name $>(entity) 49 | if (!result.success) { 50 | return responseTemplate.businessError(ctx, result.msg) 51 | } 52 | return responseTemplate.success(ctx, null) 53 | } 54 | -------------------------------------------------------------------------------- /templates/server/db.njk: -------------------------------------------------------------------------------- 1 | { 2 | "<$ model.name $>":<% if model.buildMockData %><$ model.buildMockData()|dump|safe $><% else %>[]<% endif %> 3 | } -------------------------------------------------------------------------------- /templates/server/model.njk: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | const low = require('lowdb') 4 | const lodashId = require('lodash-id') 5 | const FileAsync = require('lowdb/adapters/FileAsync') 6 | const dbFile = path.join(__dirname, '../db/<$ model.name $>_db.json') 7 | const adapter = new FileAsync(dbFile) 8 | let instance = undefined 9 | module.exports = { 10 | init: function (context) { 11 | return new Promise((resolve, reject) => { 12 | if (instance === undefined) { 13 | low(adapter).then(db => { 14 | db._.mixin(lodashId) 15 | instance = db; 16 | resolve(db.get(context)) 17 | }) 18 | } else { 19 | resolve(instance.get(context)) 20 | } 21 | }) 22 | }, 23 | read: () => { 24 | return new Promise((resolve, reject) => { 25 | if (instance === undefined) { 26 | resolve() 27 | } 28 | else { 29 | instance.read().then(() => { 30 | resolve() 31 | }) 32 | } 33 | }) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /templates/server/quickAdd/controller.njk: -------------------------------------------------------------------------------- 1 | export let <$ name $> = async ctx => { 2 | ctx.body = { 3 | code: 200, 4 | msg: '', 5 | result: null 6 | } 7 | } -------------------------------------------------------------------------------- /templates/server/quickAdd/route.njk: -------------------------------------------------------------------------------- 1 | import KoaRouter from 'koa-router' 2 | import controllers from '../controllers/index.js' 3 | import proxy from '../middleware/Proxy' 4 | 5 | const router = new KoaRouter() 6 | router 7 | .get('<$ api $>', controllers.<$ entity $>.<$ method $>) 8 | module.exports = router -------------------------------------------------------------------------------- /templates/server/route.njk: -------------------------------------------------------------------------------- 1 | import KoaRouter from 'koa-router' 2 | import controllers from '../controllers/index.js' 3 | import PermissionCheck from '../middleware/PermissionCheck' 4 | 5 | const router = new KoaRouter() 6 | router 7 | .get('/<$ model.name $>/paged', controllers.<$model.name $>.get<$ model.Name $>PagedList) 8 | .get('/<$ model.name $>/:id', controllers.<$ model.name $>.get<$ model.Name $>) 9 | .del('/<$ model.name $>/del', controllers.<$ model.name $>.del<$ model.Name $>) 10 | .del('/<$ model.name $>/batchdel', controllers.<$ model.name $>.del<$ model.Name $>s) 11 | .post('/<$ model.name $>/save', controllers.<$ model.name $>.save<$ model.Name $>) 12 | 13 | module.exports = router -------------------------------------------------------------------------------- /templates/server/service.njk: -------------------------------------------------------------------------------- 1 | import model from '../models/<$ model.name $>Model' 2 | import _ from 'lodash' 3 | const context = '<$ model.name $>' 4 | module.exports = { 5 | get<$ model.Name $>: async (id) => { 6 | let db = await model.init(context) 7 | let <$ model.name $> = db.find({ id: id }).value() 8 | return <$ model.name $> 9 | }, 10 | get<$ model.Name $>PagedList: async (pageIndex, pageSize, sortBy, descending, filter) => { 11 | let db = await model.init(context) 12 | let <$ model.name $>List = db.value() 13 | let resultList = <$ model.name $>List 14 | <% for property in model.properties -%> 15 | if (filter.<$ property.key $>) { 16 | resultList = _.filter(resultList, (o) => { 17 | return o.<$ property.key $>.indexOf(filter.<$ property.key $>) > -1 18 | }); 19 | } 20 | <% endfor %> 21 | let totalCount = resultList.length 22 | if (sortBy) { 23 | resultList = _.sortBy(resultList, [sortBy]) 24 | if (descending === 'true') { 25 | resultList = resultList.reverse() 26 | } 27 | } 28 | if(!pageIndex||pageIndex<=0){ 29 | pageIndex=1 30 | } 31 | if(pageSize){ 32 | let start = (pageIndex - 1) * pageSize 33 | let end = pageIndex * pageSize 34 | resultList = _.slice(resultList, start, end) 35 | } 36 | 37 | return { 38 | totalCount: totalCount, 39 | rows: resultList 40 | } 41 | 42 | }, 43 | del<$ model.Name $>: async (id) => { 44 | let db = await model.init(context) 45 | await db.remove({ id: id }).write() 46 | }, 47 | save<$ model.Name $>: async (<$ model.name $>) => { 48 | let db = await model.init(context) 49 | if (<$ model.name $>.id) { 50 | await db.find({ id: <$ model.name $>.id }) 51 | .assign(<$ model.name $>) 52 | .write() 53 | } else { 54 | await db.insert(<$ model.name $>).write() 55 | } 56 | return { 57 | success: true, 58 | msg: "" 59 | } 60 | } 61 | } --------------------------------------------------------------------------------