├── .DS_Store ├── .gitignore ├── .prettierrc ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── data ├── export.dicts.json ├── export.groups.json ├── export.menus.json ├── export.roles.json ├── export.settings.json └── export.users.json ├── dist ├── swagger.json └── swagger.yaml ├── docs └── client.http ├── index.js ├── jest.json ├── karma.conf.js ├── log4js.debug.json ├── log4js.json ├── nestx.code-workspace ├── nodemon-debug.json ├── nodemon.json ├── ormconfig.json ├── package.json ├── src ├── .DS_Store ├── app.controller.spec.ts ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── auth │ ├── auth.controller.ts │ ├── auth.module.ts │ ├── auth.service.ts │ ├── dto │ │ ├── Login.dto.ts │ │ └── Register.dto.ts │ ├── guards │ │ └── jwt-auth.guard.ts │ ├── interfaces │ │ └── jwt-payload.interface.ts │ └── jwt.strategy.ts ├── cms │ ├── cms.module.ts │ ├── cms.provider.ts │ ├── interface │ │ └── photo.interface.ts │ ├── photo.controller.ts │ ├── photo.service.ts │ └── schemas │ │ ├── article.schema.ts │ │ ├── category.schema.ts │ │ ├── comment.schema.ts │ │ ├── media.schema.ts │ │ └── page.schema.ts ├── commerce │ ├── commerce.module.ts │ ├── dto │ │ └── create-product.dto.ts │ ├── interfaces │ │ └── product.interface.ts │ ├── products.controller.ts │ └── products.service.ts ├── common │ ├── decorators │ │ └── roles.decorator.ts │ ├── filters │ │ └── http-exception.filter.ts │ ├── guards │ │ └── roles.guard.ts │ ├── interceptors │ │ ├── exception.interceptor.ts │ │ ├── logging.interceptor.ts │ │ ├── timeout.interceptor.ts │ │ └── transform.interceptor.ts │ ├── interfaces │ │ └── result.interface.ts │ ├── middlewares │ │ └── logger.middleware.ts │ ├── pipes │ │ ├── parse-int.pipe.ts │ │ └── validation.pipe.ts │ └── services │ │ ├── controller.service.ts │ │ └── repository.service.ts ├── config │ ├── config.module.ts │ ├── config.service.ts │ ├── enums.ts │ └── index.ts ├── core │ ├── controllers │ │ ├── dicts.controller.ts │ │ ├── dicts.service.ts │ │ ├── index.ts │ │ ├── logs.controller.ts │ │ ├── logs.service.ts │ │ ├── menus.controller.ts │ │ ├── menus.service.ts │ │ ├── users.controller.ts │ │ └── users.service.ts │ ├── core.module.ts │ ├── crypto │ │ ├── crypto.module.ts │ │ └── crypto.service.ts │ ├── dto │ │ ├── auth.dto.ts │ │ ├── common.dto.ts │ │ ├── dict.dto.ts │ │ ├── group.dto.ts │ │ ├── index.ts │ │ ├── menu.dto.ts │ │ └── user.dto.ts │ ├── interfaces │ │ ├── dict.interface.ts │ │ ├── group.interface.ts │ │ ├── index.ts │ │ ├── log.interface.ts │ │ ├── menu.interface.ts │ │ ├── role.interface.ts │ │ ├── setting.interface.ts │ │ └── user.interface.ts │ ├── mongoose.service.ts │ └── schemas │ │ ├── dict.schema.ts │ │ ├── group.schema.ts │ │ ├── index.ts │ │ ├── log.schema.ts │ │ ├── menu.schema.ts │ │ ├── role.schema.ts │ │ ├── setting.schema.ts │ │ └── user.schema.ts ├── events.gateway.ts ├── main.ts ├── scripts │ ├── connector.ts │ ├── data.export.all.ts │ ├── data.export.ts │ ├── data.import.ts │ ├── data.install.ts │ ├── database.ts │ └── generate.ts ├── swagger │ ├── constants.ts │ └── index.ts └── utils │ ├── logger.ts │ ├── secrets.ts │ └── utils.ts ├── swagger.config.json ├── tsconfig.build.json ├── tsconfig.json ├── tsfmt.json └── tslint.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vellengs/nestx-server/3baba62018dabf9d498a4abf878428e09ebb2cae/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | package-lock.json* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "attach", 7 | "name": "Node: Nodemon", 8 | "processId": "${command:PickProcess}", 9 | "restart": true, 10 | "protocol": "inspector" 11 | }, 12 | { 13 | "type": "node", 14 | "request": "attach", 15 | "name": "Attach by Process ID", 16 | "processId": "${command:PickProcess}" 17 | }, 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Node start", 22 | "program": "${workspaceFolder}/dist/main.js" 23 | }, 24 | { 25 | "type": "node", 26 | "request": "launch", 27 | "name": "Jest All", 28 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 29 | "args": ["--runInBand"], 30 | "console": "integratedTerminal", 31 | "internalConsoleOptions": "neverOpen" 32 | }, 33 | { 34 | "type": "node", 35 | "request": "launch", 36 | "name": "Jest Current File", 37 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 38 | "args": ["${relativeFile}"], 39 | "console": "integratedTerminal", 40 | "internalConsoleOptions": "neverOpen" 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 vellengs 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 | # nestx server 3 | Server side for nestx 4 | ### Quick start 5 | 6 | ```bash 7 | 8 | # install the repo with npm 9 | $ npm install 10 | 11 | # start the server 12 | $ npm start 13 | 14 | ``` 15 | 16 | ### Other commands 17 | 18 | ```bash 19 | 20 | # development 21 | $ npm run start 22 | 23 | # watch mode 24 | $ npm run start:dev 25 | 26 | # production mode 27 | $ npm run start:prod 28 | 29 | ``` 30 | 31 | ### Test 32 | 33 | ``` 34 | # unit tests 35 | $ npm run test 36 | 37 | # e2e tests 38 | $ npm run test:e2e 39 | 40 | # test coverage 41 | $ npm run test:cov 42 | ``` 43 | -------------------------------------------------------------------------------- /data/export.dicts.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "_id": "59f058a7696cf532c5172ead", 3 | "category": "category", 4 | "name": "category", 5 | "translate": "字典分类" 6 | }] -------------------------------------------------------------------------------- /data/export.groups.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "_id": "5b1e89da653fef1f92c4b955", 3 | "name": "总公司", 4 | "paths": [], 5 | "description": "总公司分组" 6 | }, { 7 | "_id": "5b2766a3177e2c274909cb6d", 8 | "name": "第一分公司", 9 | "parent": "5b1e89da653fef1f92c4b955", 10 | "paths": [], 11 | "director": "5af409755283214fb9c2ccd0", 12 | "description": "dfsdfsdf" 13 | }] -------------------------------------------------------------------------------- /data/export.menus.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "_id": "5b0d00ed41b2399582a10b09", 3 | "name": "首页", 4 | "paths": [], 5 | "order": 100, 6 | "isMenu": true, 7 | "link": "/dashboard", 8 | "slug": "home", 9 | "icon": "icon-home", 10 | "permissions": [] 11 | }, { 12 | "_id": "5b0d013341b2399582a10b0a", 13 | "name": "内容管理", 14 | "paths": [], 15 | "order": 100, 16 | "isMenu": true, 17 | "link": "course", 18 | "slug": "course", 19 | "icon": "icon-screen-desktop", 20 | "permissions": [] 21 | }, { 22 | "_id": "5b0d07b841b2399582a10b0b", 23 | "name": "系统管理", 24 | "paths": [], 25 | "order": 100, 26 | "isMenu": true, 27 | "link": "order", 28 | "slug": "order", 29 | "icon": "icon-settings", 30 | "permissions": [] 31 | }, { 32 | "_id": "5b194e0d139f72b0e7a28580", 33 | "name": "设置管理", 34 | "parent": "5b0d07b841b2399582a10b0b", 35 | "paths": [], 36 | "order": 100, 37 | "isMenu": true, 38 | "link": "/system/settings", 39 | "slug": "settings", 40 | "permissions": [] 41 | }, { 42 | "_id": "5b194e24139f72b0e7a28581", 43 | "name": "帐号管理", 44 | "parent": "5b0d07b841b2399582a10b0b", 45 | "paths": [], 46 | "order": 100, 47 | "isMenu": true, 48 | "link": "/system/accounts", 49 | "slug": "accounts", 50 | "permissions": ["5b1a2da699f6ac12569afb0e", "5b1a2da699f6ac12569afb0f", "5b1a2da699f6ac12569afb10"] 51 | }, { 52 | "_id": "5b194e39139f72b0e7a28582", 53 | "name": "角色管理", 54 | "parent": "5b0d07b841b2399582a10b0b", 55 | "paths": [], 56 | "order": 100, 57 | "isMenu": true, 58 | "link": "/system/roles", 59 | "slug": "roles", 60 | "permissions": [] 61 | }, { 62 | "_id": "5b194e4e139f72b0e7a28583", 63 | "name": "字典管理", 64 | "parent": "5b0d07b841b2399582a10b0b", 65 | "paths": [], 66 | "order": 100, 67 | "isMenu": true, 68 | "link": "/system/dicts", 69 | "slug": "dicts", 70 | "permissions": [] 71 | }, { 72 | "_id": "5b194e63139f72b0e7a28584", 73 | "name": "日志查询", 74 | "parent": "5b0d07b841b2399582a10b0b", 75 | "paths": [], 76 | "order": 100, 77 | "isMenu": true, 78 | "link": "/system/logs", 79 | "slug": "logs", 80 | "permissions": [] 81 | }, { 82 | "_id": "5b194e7e139f72b0e7a28585", 83 | "name": "页面管理", 84 | "parent": "5b0d013341b2399582a10b0a", 85 | "paths": [], 86 | "order": 100, 87 | "isMenu": true, 88 | "link": "/cms/pages", 89 | "slug": "pages", 90 | "permissions": ["5b1a2da699f6ac12569afb0f", "5b1a2da699f6ac12569afb10"] 91 | }, { 92 | "_id": "5b1a2da699f6ac12569afb0e", 93 | "name": "是否允许添加帐号", 94 | "paths": [], 95 | "order": 100, 96 | "isMenu": false, 97 | "link": "CanAddAccount", 98 | "slug": "CanAddAccount", 99 | "permissions": [] 100 | }, { 101 | "_id": "5b1a2da699f6ac12569afb0f", 102 | "name": "是否允许编辑帐号", 103 | "paths": [], 104 | "order": 100, 105 | "isMenu": false, 106 | "link": "CanEditAccount", 107 | "slug": "CanEditAccount", 108 | "permissions": [] 109 | }, { 110 | "_id": "5b1a2da699f6ac12569afb10", 111 | "name": "是否允许删除帐号", 112 | "paths": [], 113 | "order": 100, 114 | "isMenu": false, 115 | "link": "CanRemoveAccount", 116 | "slug": "CanRemoveAccount", 117 | "permissions": [] 118 | }, { 119 | "_id": "5b25f774a408a579f0d89f94", 120 | "name": "菜单管理", 121 | "parent": "5b0d07b841b2399582a10b0b", 122 | "paths": [], 123 | "order": 100, 124 | "isMenu": true, 125 | "link": "/system/menus", 126 | "slug": "menus", 127 | "permissions": [] 128 | }, { 129 | "_id": "5b25fd480231387d74c8c253", 130 | "name": "接口配置", 131 | "parent": "5b0d07b841b2399582a10b0b", 132 | "paths": [], 133 | "order": 100, 134 | "isMenu": true, 135 | "link": "/system/permission", 136 | "slug": "permission", 137 | "permissions": [] 138 | }] -------------------------------------------------------------------------------- /data/export.roles.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "_id": "5b1e8b2559989d1ffc06ac7d", 3 | "name": "系统管理员", 4 | "description": "管理系统", 5 | "permissions": ["5b0d07b841b2399582a10b0b", "5b194e0d139f72b0e7a28580", "5b194e24139f72b0e7a28581", "5b1a2da699f6ac12569afb0e", "5b1a2da699f6ac12569afb0f", "5b1a2da699f6ac12569afb10", "5b194e39139f72b0e7a28582", "5b194e4e139f72b0e7a28583", "5b194e63139f72b0e7a28584", "5b0d013341b2399582a10b0a", "5b194e7e139f72b0e7a28585", "5b0d00ed41b2399582a10b09"] 6 | }, { 7 | "_id": "5b1e8b3e59989d1ffc06ac7e", 8 | "name": "业务员主管", 9 | "description": "管理业务数据", 10 | "permissions": ["5b0d07b841b2399582a10b0b", "5b194e24139f72b0e7a28581", "5b1a2da699f6ac12569afb0e", "5b1a2da699f6ac12569afb0f", "5b1a2da699f6ac12569afb10", "5b194e4e139f72b0e7a28583", "5b194e63139f72b0e7a28584", "5b0d013341b2399582a10b0a", "5b194e7e139f72b0e7a28585"] 11 | }] -------------------------------------------------------------------------------- /data/export.settings.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "_id": "5b0d006899f6ac12569afb05", 3 | "name": "main", 4 | "key": "name", 5 | "value": "Typerx" 6 | }, { 7 | "_id": "5b0d00a099f6ac12569afb06", 8 | "name": "main", 9 | "key": "logo", 10 | "value": "/uploads/6dd2eea21bdde9c69d4aabc898764d7f" 11 | }] -------------------------------------------------------------------------------- /data/export.users.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "_id": "5af409755283214fb9c2ccd0", 3 | "username": "admin", 4 | "nick": "viking", 5 | "password": "$2a$10$EmEaDrSJ.gs6PsWJYjZKqOXD0M2JZkGIeqC9Tu3fbMW/bHXTsfB5y", 6 | "avatar": "https://avatars2.githubusercontent.com/u/5743338?s=400&u=ac196285a689b6dd65ab67f0be85eb787b07b428&v=4", 7 | "type": "222", 8 | "email": "velleng1s@qq.com", 9 | "mobile": "13012345671", 10 | "roles": ["5b1173e519e6cf0d3bdb0018", "5b1227fa9b6a1c20600b662a", "5b1227fa9b6a1c20600b662a", "5b1227fa9b6a1c20600b662a"], 11 | "groups": ["5b0cfa81b7d02a8a4bf5d126", "5b13df0b6a129d64f589e105"], 12 | "isDisable": false, 13 | "isAdmin": true, 14 | "isApproved": true, 15 | "expired": "2028-06-30T02:54:07.000Z" 16 | }, { 17 | "_id": "5b31eba49a674f606f7c5849", 18 | "username": "tester", 19 | "password": "$2a$10$xg95hppMP9i3KWKVztJyZOo8hCP8Zojs4vsrVkfhLkFDLp1SwfIx2", 20 | "mobile": "13099948844", 21 | "roles": ["5b1e8b2559989d1ffc06ac7d", "5b1e8b3e59989d1ffc06ac7e"], 22 | "groups": ["5b1e89da653fef1f92c4b955", "5b2766a3177e2c274909cb6d"], 23 | "isDisable": false, 24 | "isAdmin": false, 25 | "isApproved": true 26 | }] -------------------------------------------------------------------------------- /dist/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "LoginDto": { 4 | "description": "", 5 | "properties": { 6 | "username": { 7 | "type": "string", 8 | "description": "" 9 | }, 10 | "password": { 11 | "type": "string", 12 | "description": "" 13 | } 14 | }, 15 | "type": "object", 16 | "required": [ 17 | "username", 18 | "password" 19 | ] 20 | }, 21 | "RegisterDto": { 22 | "description": "", 23 | "properties": { 24 | "username": { 25 | "type": "string", 26 | "description": "" 27 | }, 28 | "password": { 29 | "type": "string", 30 | "description": "" 31 | }, 32 | "mobile": { 33 | "type": "string", 34 | "description": "" 35 | } 36 | }, 37 | "type": "object", 38 | "required": [ 39 | "username", 40 | "password", 41 | "mobile" 42 | ] 43 | }, 44 | "CreateDictReq": { 45 | "description": "", 46 | "properties": { 47 | "category": { 48 | "type": "string", 49 | "description": "" 50 | }, 51 | "name": { 52 | "type": "string", 53 | "description": "" 54 | }, 55 | "translate": { 56 | "type": "string", 57 | "description": "" 58 | }, 59 | "expand": { 60 | "type": "object", 61 | "description": "" 62 | } 63 | }, 64 | "type": "object", 65 | "required": [ 66 | "category", 67 | "name", 68 | "translate", 69 | "expand" 70 | ] 71 | }, 72 | "Dict": { 73 | "description": "", 74 | "properties": { 75 | "_id": { 76 | "type": "string", 77 | "description": "" 78 | }, 79 | "name": { 80 | "type": "string", 81 | "description": "" 82 | }, 83 | "category": { 84 | "type": "string", 85 | "description": "" 86 | }, 87 | "translate": { 88 | "type": "string", 89 | "description": "" 90 | }, 91 | "expand": { 92 | "type": "object", 93 | "description": "" 94 | } 95 | }, 96 | "type": "object", 97 | "required": [ 98 | "_id", 99 | "name", 100 | "category", 101 | "translate", 102 | "expand" 103 | ] 104 | }, 105 | "EditDictReq": { 106 | "description": "", 107 | "properties": { 108 | "id": { 109 | "type": "string", 110 | "description": "" 111 | }, 112 | "category": { 113 | "type": "string", 114 | "description": "" 115 | }, 116 | "name": { 117 | "type": "string", 118 | "description": "" 119 | }, 120 | "translate": { 121 | "type": "string", 122 | "description": "" 123 | }, 124 | "expand": { 125 | "type": "object", 126 | "description": "" 127 | } 128 | }, 129 | "type": "object", 130 | "required": [ 131 | "id", 132 | "category", 133 | "name", 134 | "translate", 135 | "expand" 136 | ] 137 | }, 138 | "KeyValueDto": { 139 | "description": "", 140 | "properties": { 141 | "label": { 142 | "type": "string", 143 | "description": "" 144 | }, 145 | "value": { 146 | "type": "string", 147 | "description": "" 148 | } 149 | }, 150 | "type": "object", 151 | "required": [ 152 | "label", 153 | "value" 154 | ] 155 | }, 156 | "Query": { 157 | "description": "", 158 | "properties": { 159 | "size": { 160 | "type": "number", 161 | "format": "double", 162 | "description": "" 163 | }, 164 | "index": { 165 | "type": "number", 166 | "format": "double", 167 | "description": "" 168 | } 169 | }, 170 | "type": "object", 171 | "required": [ 172 | "size", 173 | "index" 174 | ] 175 | }, 176 | "ResultList$Dict": { 177 | "description": "", 178 | "properties": { 179 | "list": { 180 | "type": "array", 181 | "items": { 182 | "$ref": "#/definitions/Generic~T" 183 | }, 184 | "description": "" 185 | }, 186 | "count": { 187 | "type": "number", 188 | "format": "double", 189 | "description": "" 190 | }, 191 | "query": { 192 | "$ref": "#/definitions/Query" 193 | } 194 | }, 195 | "type": "object", 196 | "required": [ 197 | "list" 198 | ] 199 | }, 200 | "User": { 201 | "description": "", 202 | "properties": { 203 | "_id": { 204 | "type": "string", 205 | "description": "" 206 | }, 207 | "username": { 208 | "type": "string", 209 | "description": "" 210 | }, 211 | "password": { 212 | "type": "string", 213 | "description": "" 214 | }, 215 | "name": { 216 | "type": "string", 217 | "description": "" 218 | }, 219 | "keyword": { 220 | "type": "string", 221 | "description": "" 222 | }, 223 | "avatar": { 224 | "type": "string", 225 | "description": "" 226 | }, 227 | "type": { 228 | "type": "string", 229 | "description": "" 230 | }, 231 | "groups": { 232 | "type": "array", 233 | "items": { 234 | "type": "string" 235 | }, 236 | "description": "" 237 | }, 238 | "roles": { 239 | "type": "array", 240 | "items": { 241 | "type": "string" 242 | }, 243 | "description": "" 244 | }, 245 | "email": { 246 | "type": "string", 247 | "description": "" 248 | }, 249 | "mobile": { 250 | "type": "string", 251 | "description": "" 252 | }, 253 | "profile": { 254 | "type": "object", 255 | "description": "" 256 | }, 257 | "isDisable": { 258 | "type": "boolean", 259 | "description": "" 260 | }, 261 | "isAdmin": { 262 | "type": "boolean", 263 | "description": "" 264 | }, 265 | "isApproved": { 266 | "type": "boolean", 267 | "description": "" 268 | }, 269 | "secret": { 270 | "type": "string", 271 | "description": "" 272 | }, 273 | "expired": { 274 | "type": "string", 275 | "format": "date-time", 276 | "description": "" 277 | } 278 | }, 279 | "type": "object", 280 | "required": [ 281 | "_id", 282 | "username", 283 | "password", 284 | "name", 285 | "keyword", 286 | "avatar", 287 | "type", 288 | "groups", 289 | "roles", 290 | "email", 291 | "mobile", 292 | "profile", 293 | "isDisable", 294 | "isAdmin", 295 | "isApproved", 296 | "secret", 297 | "expired" 298 | ] 299 | }, 300 | "CreateUserReq": { 301 | "description": "", 302 | "properties": { 303 | "username": { 304 | "type": "string", 305 | "description": "" 306 | }, 307 | "password": { 308 | "type": "string", 309 | "description": "" 310 | } 311 | }, 312 | "type": "object", 313 | "required": [ 314 | "username", 315 | "password" 316 | ] 317 | }, 318 | "EditUserReq": { 319 | "description": "", 320 | "properties": { 321 | "name": { 322 | "type": "string", 323 | "description": "" 324 | }, 325 | "mobile": { 326 | "type": "number", 327 | "format": "double", 328 | "description": "" 329 | }, 330 | "email": { 331 | "type": "string", 332 | "description": "" 333 | }, 334 | "company": { 335 | "type": "string", 336 | "description": "" 337 | }, 338 | "siteUrl": { 339 | "type": "string", 340 | "description": "" 341 | }, 342 | "address": { 343 | "type": "string", 344 | "description": "" 345 | } 346 | }, 347 | "type": "object", 348 | "required": [ 349 | "name", 350 | "mobile" 351 | ] 352 | }, 353 | "T": { 354 | "description": "", 355 | "properties": {}, 356 | "type": "object" 357 | }, 358 | "ResultList$User": { 359 | "description": "", 360 | "properties": { 361 | "list": { 362 | "type": "array", 363 | "items": { 364 | "$ref": "#/definitions/T" 365 | }, 366 | "description": "" 367 | }, 368 | "count": { 369 | "type": "number", 370 | "format": "double", 371 | "description": "" 372 | }, 373 | "query": { 374 | "$ref": "#/definitions/Query" 375 | } 376 | }, 377 | "type": "object", 378 | "required": [ 379 | "list" 380 | ] 381 | }, 382 | "CreateMenuRes": { 383 | "description": "", 384 | "properties": { 385 | "name": { 386 | "type": "string", 387 | "description": "" 388 | }, 389 | "slug": { 390 | "type": "string", 391 | "description": "" 392 | }, 393 | "group": { 394 | "type": "boolean", 395 | "description": "" 396 | }, 397 | "link": { 398 | "type": "string", 399 | "description": "" 400 | }, 401 | "order": { 402 | "type": "number", 403 | "format": "double", 404 | "description": "" 405 | }, 406 | "externalLink": { 407 | "type": "string", 408 | "description": "" 409 | }, 410 | "blank": { 411 | "type": "boolean", 412 | "description": "" 413 | }, 414 | "icon": { 415 | "type": "string", 416 | "description": "" 417 | }, 418 | "badge": { 419 | "type": "string", 420 | "description": "" 421 | }, 422 | "badgeDot": { 423 | "type": "string", 424 | "description": "" 425 | }, 426 | "badgeStatus": { 427 | "type": "string", 428 | "description": "" 429 | }, 430 | "enable": { 431 | "type": "boolean", 432 | "description": "" 433 | }, 434 | "expanded": { 435 | "type": "boolean", 436 | "description": "" 437 | }, 438 | "acl": { 439 | "type": "string", 440 | "description": "" 441 | }, 442 | "paths": { 443 | "type": "array", 444 | "items": { 445 | "type": "object" 446 | }, 447 | "description": "" 448 | }, 449 | "parent": { 450 | "type": "string", 451 | "description": "" 452 | }, 453 | "permissions": { 454 | "type": "array", 455 | "items": { 456 | "type": "object" 457 | }, 458 | "description": "" 459 | }, 460 | "isMenu": { 461 | "type": "boolean", 462 | "description": "" 463 | } 464 | }, 465 | "type": "object", 466 | "required": [ 467 | "name", 468 | "slug", 469 | "group", 470 | "link", 471 | "order", 472 | "externalLink", 473 | "blank", 474 | "icon", 475 | "badge", 476 | "badgeDot", 477 | "badgeStatus", 478 | "enable", 479 | "expanded", 480 | "acl", 481 | "paths", 482 | "parent", 483 | "isMenu" 484 | ] 485 | }, 486 | "Menu": { 487 | "description": "", 488 | "properties": { 489 | "_id": { 490 | "type": "string", 491 | "description": "" 492 | }, 493 | "name": { 494 | "type": "string", 495 | "description": "" 496 | }, 497 | "slug": { 498 | "type": "string", 499 | "description": "" 500 | }, 501 | "group": { 502 | "type": "boolean", 503 | "description": "" 504 | }, 505 | "link": { 506 | "type": "string", 507 | "description": "" 508 | }, 509 | "order": { 510 | "type": "number", 511 | "format": "double", 512 | "description": "" 513 | }, 514 | "externalLink": { 515 | "type": "string", 516 | "description": "" 517 | }, 518 | "blank": { 519 | "type": "boolean", 520 | "description": "" 521 | }, 522 | "icon": { 523 | "type": "string", 524 | "description": "" 525 | }, 526 | "badge": { 527 | "type": "string", 528 | "description": "" 529 | }, 530 | "badgeDot": { 531 | "type": "string", 532 | "description": "" 533 | }, 534 | "badgeStatus": { 535 | "type": "string", 536 | "description": "" 537 | }, 538 | "enable": { 539 | "type": "boolean", 540 | "description": "" 541 | }, 542 | "expanded": { 543 | "type": "boolean", 544 | "description": "" 545 | }, 546 | "acl": { 547 | "type": "string", 548 | "description": "" 549 | }, 550 | "paths": { 551 | "type": "array", 552 | "items": { 553 | "type": "object" 554 | }, 555 | "description": "" 556 | }, 557 | "parent": { 558 | "type": "object", 559 | "description": "" 560 | }, 561 | "permissions": { 562 | "type": "array", 563 | "items": { 564 | "type": "object" 565 | }, 566 | "description": "" 567 | }, 568 | "isMenu": { 569 | "type": "boolean", 570 | "description": "" 571 | } 572 | }, 573 | "type": "object", 574 | "required": [ 575 | "_id", 576 | "name", 577 | "slug", 578 | "group", 579 | "link", 580 | "order", 581 | "externalLink", 582 | "blank", 583 | "icon", 584 | "badge", 585 | "badgeDot", 586 | "badgeStatus", 587 | "enable", 588 | "expanded", 589 | "acl", 590 | "paths", 591 | "parent", 592 | "isMenu" 593 | ] 594 | }, 595 | "EditMenuRes": { 596 | "description": "", 597 | "properties": { 598 | "name": { 599 | "type": "string", 600 | "description": "" 601 | }, 602 | "slug": { 603 | "type": "string", 604 | "description": "" 605 | }, 606 | "group": { 607 | "type": "boolean", 608 | "description": "" 609 | }, 610 | "link": { 611 | "type": "string", 612 | "description": "" 613 | }, 614 | "order": { 615 | "type": "number", 616 | "format": "double", 617 | "description": "" 618 | }, 619 | "externalLink": { 620 | "type": "string", 621 | "description": "" 622 | }, 623 | "blank": { 624 | "type": "boolean", 625 | "description": "" 626 | }, 627 | "icon": { 628 | "type": "string", 629 | "description": "" 630 | }, 631 | "badge": { 632 | "type": "string", 633 | "description": "" 634 | }, 635 | "badgeDot": { 636 | "type": "string", 637 | "description": "" 638 | }, 639 | "badgeStatus": { 640 | "type": "string", 641 | "description": "" 642 | }, 643 | "enable": { 644 | "type": "boolean", 645 | "description": "" 646 | }, 647 | "expanded": { 648 | "type": "boolean", 649 | "description": "" 650 | }, 651 | "acl": { 652 | "type": "string", 653 | "description": "" 654 | }, 655 | "paths": { 656 | "type": "array", 657 | "items": { 658 | "type": "object" 659 | }, 660 | "description": "" 661 | }, 662 | "parent": { 663 | "type": "string", 664 | "description": "" 665 | }, 666 | "permissions": { 667 | "type": "array", 668 | "items": { 669 | "type": "object" 670 | }, 671 | "description": "" 672 | }, 673 | "isMenu": { 674 | "type": "boolean", 675 | "description": "" 676 | } 677 | }, 678 | "type": "object", 679 | "required": [ 680 | "name", 681 | "slug", 682 | "group", 683 | "link", 684 | "order", 685 | "externalLink", 686 | "blank", 687 | "icon", 688 | "badge", 689 | "badgeDot", 690 | "badgeStatus", 691 | "enable", 692 | "expanded", 693 | "acl", 694 | "paths", 695 | "parent", 696 | "isMenu" 697 | ] 698 | }, 699 | "ResultList$Menu": { 700 | "description": "", 701 | "properties": { 702 | "list": { 703 | "type": "array", 704 | "items": { 705 | "$ref": "#/definitions/T" 706 | }, 707 | "description": "" 708 | }, 709 | "count": { 710 | "type": "number", 711 | "format": "double", 712 | "description": "" 713 | }, 714 | "query": { 715 | "$ref": "#/definitions/Query" 716 | } 717 | }, 718 | "type": "object", 719 | "required": [ 720 | "list" 721 | ] 722 | }, 723 | "ResultList$Log": { 724 | "description": "", 725 | "properties": { 726 | "list": { 727 | "type": "array", 728 | "items": { 729 | "$ref": "#/definitions/T" 730 | }, 731 | "description": "" 732 | }, 733 | "count": { 734 | "type": "number", 735 | "format": "double", 736 | "description": "" 737 | }, 738 | "query": { 739 | "$ref": "#/definitions/Query" 740 | } 741 | }, 742 | "type": "object", 743 | "required": [ 744 | "list" 745 | ] 746 | }, 747 | "Log": { 748 | "description": "", 749 | "properties": { 750 | "id": { 751 | "type": "string", 752 | "description": "" 753 | }, 754 | "name": { 755 | "type": "string", 756 | "description": "" 757 | }, 758 | "operator": { 759 | "type": "string", 760 | "description": "" 761 | }, 762 | "operatorIp": { 763 | "type": "string", 764 | "description": "" 765 | }, 766 | "operation": { 767 | "type": "string", 768 | "description": "" 769 | }, 770 | "comment": { 771 | "type": "string", 772 | "description": "" 773 | }, 774 | "createdAt": { 775 | "type": "string", 776 | "format": "date-time", 777 | "description": "" 778 | } 779 | }, 780 | "type": "object", 781 | "required": [ 782 | "id", 783 | "name", 784 | "operator", 785 | "operatorIp", 786 | "operation", 787 | "comment", 788 | "createdAt" 789 | ] 790 | }, 791 | "CreateProductDto": { 792 | "description": "", 793 | "properties": { 794 | "name": { 795 | "type": "string", 796 | "description": "" 797 | }, 798 | "title": { 799 | "type": "string", 800 | "description": "" 801 | } 802 | }, 803 | "type": "object", 804 | "required": [ 805 | "name", 806 | "title" 807 | ] 808 | }, 809 | "Product": { 810 | "description": "", 811 | "properties": { 812 | "name": { 813 | "type": "string", 814 | "description": "" 815 | }, 816 | "title": { 817 | "type": "string", 818 | "description": "" 819 | } 820 | }, 821 | "type": "object", 822 | "required": [ 823 | "name", 824 | "title" 825 | ] 826 | } 827 | }, 828 | "info": { 829 | "description": "项目标准接口", 830 | "license": { 831 | "name": "MIT" 832 | }, 833 | "title": "rest api interface", 834 | "version": "0.0.1" 835 | }, 836 | "paths": { 837 | "/": { 838 | "get": { 839 | "operationId": "AppRoot", 840 | "produces": [ 841 | "text/html" 842 | ], 843 | "responses": { 844 | "200": { 845 | "description": "Ok", 846 | "schema": { 847 | "type": "string" 848 | } 849 | } 850 | }, 851 | "description": "", 852 | "parameters": [] 853 | } 854 | }, 855 | "/auth/login": { 856 | "post": { 857 | "operationId": "AuthLogin", 858 | "produces": [ 859 | "text/html" 860 | ], 861 | "responses": { 862 | "200": { 863 | "description": "Ok", 864 | "schema": { 865 | "type": "string", 866 | "enum": [ 867 | "", 868 | "" 869 | ] 870 | } 871 | } 872 | }, 873 | "description": "", 874 | "consumes": [ 875 | "application/json" 876 | ], 877 | "parameters": [ 878 | { 879 | "description": "", 880 | "in": "body", 881 | "name": "payload", 882 | "required": true, 883 | "schema": { 884 | "$ref": "#/definitions/LoginDto" 885 | } 886 | } 887 | ] 888 | } 889 | }, 890 | "/auth/register": { 891 | "post": { 892 | "operationId": "AuthRegister", 893 | "produces": [ 894 | "text/html" 895 | ], 896 | "responses": { 897 | "200": { 898 | "description": "Ok", 899 | "schema": { 900 | "type": "string", 901 | "enum": [ 902 | "", 903 | "" 904 | ] 905 | } 906 | } 907 | }, 908 | "description": "", 909 | "consumes": [ 910 | "application/json" 911 | ], 912 | "parameters": [ 913 | { 914 | "description": "", 915 | "in": "body", 916 | "name": "payload", 917 | "required": true, 918 | "schema": { 919 | "$ref": "#/definitions/RegisterDto" 920 | } 921 | } 922 | ] 923 | } 924 | }, 925 | "/dicts/": { 926 | "post": { 927 | "operationId": "DictsCreate", 928 | "produces": [ 929 | "text/html" 930 | ], 931 | "responses": { 932 | "204": { 933 | "description": "No content" 934 | } 935 | }, 936 | "description": "", 937 | "consumes": [ 938 | "application/json" 939 | ], 940 | "parameters": [ 941 | { 942 | "description": "", 943 | "in": "body", 944 | "name": "entry", 945 | "required": true, 946 | "schema": { 947 | "$ref": "#/definitions/CreateDictReq" 948 | } 949 | } 950 | ] 951 | }, 952 | "put": { 953 | "operationId": "DictsUpdate", 954 | "produces": [ 955 | "application/json" 956 | ], 957 | "responses": { 958 | "200": { 959 | "description": "Ok", 960 | "schema": { 961 | "$ref": "#/definitions/Dict" 962 | } 963 | } 964 | }, 965 | "description": "", 966 | "consumes": [ 967 | "application/json" 968 | ], 969 | "parameters": [ 970 | { 971 | "description": "", 972 | "in": "body", 973 | "name": "entry", 974 | "required": true, 975 | "schema": { 976 | "$ref": "#/definitions/EditDictReq" 977 | } 978 | } 979 | ] 980 | } 981 | }, 982 | "/dicts/search": { 983 | "get": { 984 | "operationId": "DictsSearch", 985 | "produces": [ 986 | "application/json" 987 | ], 988 | "responses": { 989 | "200": { 990 | "description": "Ok", 991 | "schema": { 992 | "type": "array", 993 | "items": { 994 | "$ref": "#/definitions/KeyValueDto" 995 | } 996 | } 997 | } 998 | }, 999 | "description": "", 1000 | "parameters": [ 1001 | { 1002 | "description": "", 1003 | "in": "query", 1004 | "name": "keyword", 1005 | "required": false, 1006 | "type": "string" 1007 | }, 1008 | { 1009 | "description": "", 1010 | "in": "query", 1011 | "name": "value", 1012 | "required": false, 1013 | "type": "string" 1014 | } 1015 | ] 1016 | } 1017 | }, 1018 | "/dicts/query": { 1019 | "get": { 1020 | "operationId": "DictsQuery", 1021 | "produces": [ 1022 | "application/json" 1023 | ], 1024 | "responses": { 1025 | "200": { 1026 | "description": "Ok", 1027 | "schema": { 1028 | "$ref": "#/definitions/ResultList$Dict" 1029 | } 1030 | } 1031 | }, 1032 | "description": "", 1033 | "parameters": [ 1034 | { 1035 | "description": "", 1036 | "in": "query", 1037 | "name": "keyword", 1038 | "required": false, 1039 | "type": "string" 1040 | }, 1041 | { 1042 | "description": "", 1043 | "in": "query", 1044 | "name": "index", 1045 | "required": false, 1046 | "type": "number", 1047 | "format": "double", 1048 | "default": 1 1049 | }, 1050 | { 1051 | "description": "", 1052 | "in": "query", 1053 | "name": "size", 1054 | "required": false, 1055 | "type": "number", 1056 | "format": "double", 1057 | "default": 10 1058 | } 1059 | ] 1060 | } 1061 | }, 1062 | "/dicts/{id}": { 1063 | "get": { 1064 | "operationId": "DictsFindOne", 1065 | "produces": [ 1066 | "application/json" 1067 | ], 1068 | "responses": { 1069 | "200": { 1070 | "description": "Ok", 1071 | "schema": { 1072 | "$ref": "#/definitions/Dict" 1073 | } 1074 | } 1075 | }, 1076 | "description": "", 1077 | "parameters": [ 1078 | { 1079 | "description": "", 1080 | "in": "path", 1081 | "name": "id", 1082 | "required": true, 1083 | "type": "string" 1084 | } 1085 | ] 1086 | } 1087 | }, 1088 | "/users/profile": { 1089 | "get": { 1090 | "operationId": "UsersProfile", 1091 | "produces": [ 1092 | "application/json" 1093 | ], 1094 | "responses": { 1095 | "200": { 1096 | "description": "Ok", 1097 | "schema": { 1098 | "$ref": "#/definitions/User" 1099 | } 1100 | } 1101 | }, 1102 | "description": "", 1103 | "parameters": [] 1104 | } 1105 | }, 1106 | "/users/": { 1107 | "post": { 1108 | "operationId": "UsersCreate", 1109 | "produces": [ 1110 | "text/html" 1111 | ], 1112 | "responses": { 1113 | "204": { 1114 | "description": "No content" 1115 | } 1116 | }, 1117 | "description": "", 1118 | "consumes": [ 1119 | "application/json" 1120 | ], 1121 | "parameters": [ 1122 | { 1123 | "description": "", 1124 | "in": "body", 1125 | "name": "user", 1126 | "required": true, 1127 | "schema": { 1128 | "$ref": "#/definitions/CreateUserReq" 1129 | } 1130 | } 1131 | ] 1132 | }, 1133 | "put": { 1134 | "operationId": "UsersUpdate", 1135 | "produces": [ 1136 | "application/json" 1137 | ], 1138 | "responses": { 1139 | "200": { 1140 | "description": "Ok", 1141 | "schema": { 1142 | "$ref": "#/definitions/User" 1143 | } 1144 | } 1145 | }, 1146 | "description": "", 1147 | "consumes": [ 1148 | "application/json" 1149 | ], 1150 | "parameters": [ 1151 | { 1152 | "description": "", 1153 | "in": "body", 1154 | "name": "user", 1155 | "required": true, 1156 | "schema": { 1157 | "$ref": "#/definitions/EditUserReq" 1158 | } 1159 | } 1160 | ] 1161 | } 1162 | }, 1163 | "/users/search": { 1164 | "get": { 1165 | "operationId": "UsersSearch", 1166 | "produces": [ 1167 | "application/json" 1168 | ], 1169 | "responses": { 1170 | "200": { 1171 | "description": "Ok", 1172 | "schema": { 1173 | "type": "array", 1174 | "items": { 1175 | "$ref": "#/definitions/KeyValueDto" 1176 | } 1177 | } 1178 | } 1179 | }, 1180 | "description": "", 1181 | "parameters": [ 1182 | { 1183 | "description": "", 1184 | "in": "query", 1185 | "name": "keyword", 1186 | "required": false, 1187 | "type": "string" 1188 | }, 1189 | { 1190 | "description": "", 1191 | "in": "query", 1192 | "name": "value", 1193 | "required": false, 1194 | "type": "string" 1195 | } 1196 | ] 1197 | } 1198 | }, 1199 | "/users/query": { 1200 | "get": { 1201 | "operationId": "UsersQuery", 1202 | "produces": [ 1203 | "application/json" 1204 | ], 1205 | "responses": { 1206 | "200": { 1207 | "description": "Ok", 1208 | "schema": { 1209 | "$ref": "#/definitions/ResultList$User" 1210 | } 1211 | } 1212 | }, 1213 | "description": "", 1214 | "parameters": [ 1215 | { 1216 | "description": "", 1217 | "in": "query", 1218 | "name": "keyword", 1219 | "required": false, 1220 | "type": "string" 1221 | }, 1222 | { 1223 | "description": "", 1224 | "in": "query", 1225 | "name": "index", 1226 | "required": false, 1227 | "type": "number", 1228 | "format": "double", 1229 | "default": 1 1230 | }, 1231 | { 1232 | "description": "", 1233 | "in": "query", 1234 | "name": "size", 1235 | "required": false, 1236 | "type": "number", 1237 | "format": "double", 1238 | "default": 10 1239 | } 1240 | ] 1241 | } 1242 | }, 1243 | "/users/{id}": { 1244 | "get": { 1245 | "operationId": "UsersFindOne", 1246 | "produces": [ 1247 | "application/json" 1248 | ], 1249 | "responses": { 1250 | "200": { 1251 | "description": "Ok", 1252 | "schema": { 1253 | "$ref": "#/definitions/User" 1254 | } 1255 | } 1256 | }, 1257 | "description": "", 1258 | "parameters": [ 1259 | { 1260 | "description": "", 1261 | "in": "path", 1262 | "name": "id", 1263 | "required": true, 1264 | "type": "string" 1265 | } 1266 | ] 1267 | } 1268 | }, 1269 | "/menus/": { 1270 | "post": { 1271 | "operationId": "MenusCreate", 1272 | "produces": [ 1273 | "text/html" 1274 | ], 1275 | "responses": { 1276 | "204": { 1277 | "description": "No content" 1278 | } 1279 | }, 1280 | "description": "", 1281 | "consumes": [ 1282 | "application/json" 1283 | ], 1284 | "parameters": [ 1285 | { 1286 | "description": "", 1287 | "in": "body", 1288 | "name": "entry", 1289 | "required": true, 1290 | "schema": { 1291 | "$ref": "#/definitions/CreateMenuRes" 1292 | } 1293 | } 1294 | ] 1295 | }, 1296 | "put": { 1297 | "operationId": "MenusUpdate", 1298 | "produces": [ 1299 | "application/json" 1300 | ], 1301 | "responses": { 1302 | "200": { 1303 | "description": "Ok", 1304 | "schema": { 1305 | "$ref": "#/definitions/Menu" 1306 | } 1307 | } 1308 | }, 1309 | "description": "", 1310 | "consumes": [ 1311 | "application/json" 1312 | ], 1313 | "parameters": [ 1314 | { 1315 | "description": "", 1316 | "in": "body", 1317 | "name": "entry", 1318 | "required": true, 1319 | "schema": { 1320 | "$ref": "#/definitions/EditMenuRes" 1321 | } 1322 | } 1323 | ] 1324 | } 1325 | }, 1326 | "/menus/search": { 1327 | "get": { 1328 | "operationId": "MenusSearch", 1329 | "produces": [ 1330 | "application/json" 1331 | ], 1332 | "responses": { 1333 | "200": { 1334 | "description": "Ok", 1335 | "schema": { 1336 | "type": "array", 1337 | "items": { 1338 | "$ref": "#/definitions/KeyValueDto" 1339 | } 1340 | } 1341 | } 1342 | }, 1343 | "description": "", 1344 | "parameters": [ 1345 | { 1346 | "description": "", 1347 | "in": "query", 1348 | "name": "keyword", 1349 | "required": false, 1350 | "type": "string" 1351 | }, 1352 | { 1353 | "description": "", 1354 | "in": "query", 1355 | "name": "value", 1356 | "required": false, 1357 | "type": "string" 1358 | } 1359 | ] 1360 | } 1361 | }, 1362 | "/menus/query": { 1363 | "get": { 1364 | "operationId": "MenusQuery", 1365 | "produces": [ 1366 | "application/json" 1367 | ], 1368 | "responses": { 1369 | "200": { 1370 | "description": "Ok", 1371 | "schema": { 1372 | "$ref": "#/definitions/ResultList$Menu" 1373 | } 1374 | } 1375 | }, 1376 | "description": "", 1377 | "parameters": [ 1378 | { 1379 | "description": "", 1380 | "in": "query", 1381 | "name": "keyword", 1382 | "required": false, 1383 | "type": "string" 1384 | }, 1385 | { 1386 | "description": "", 1387 | "in": "query", 1388 | "name": "index", 1389 | "required": false, 1390 | "type": "number", 1391 | "format": "double", 1392 | "default": 1 1393 | }, 1394 | { 1395 | "description": "", 1396 | "in": "query", 1397 | "name": "size", 1398 | "required": false, 1399 | "type": "number", 1400 | "format": "double", 1401 | "default": 10 1402 | } 1403 | ] 1404 | } 1405 | }, 1406 | "/menus/{id}": { 1407 | "get": { 1408 | "operationId": "MenusFindOne", 1409 | "produces": [ 1410 | "application/json" 1411 | ], 1412 | "responses": { 1413 | "200": { 1414 | "description": "Ok", 1415 | "schema": { 1416 | "$ref": "#/definitions/Menu" 1417 | } 1418 | } 1419 | }, 1420 | "description": "", 1421 | "parameters": [ 1422 | { 1423 | "description": "", 1424 | "in": "path", 1425 | "name": "id", 1426 | "required": true, 1427 | "type": "string" 1428 | } 1429 | ] 1430 | } 1431 | }, 1432 | "/logs/search": { 1433 | "get": { 1434 | "operationId": "LogsSearch", 1435 | "produces": [ 1436 | "application/json" 1437 | ], 1438 | "responses": { 1439 | "200": { 1440 | "description": "Ok", 1441 | "schema": { 1442 | "type": "array", 1443 | "items": { 1444 | "$ref": "#/definitions/KeyValueDto" 1445 | } 1446 | } 1447 | } 1448 | }, 1449 | "description": "", 1450 | "parameters": [ 1451 | { 1452 | "description": "", 1453 | "in": "query", 1454 | "name": "keyword", 1455 | "required": false, 1456 | "type": "string" 1457 | }, 1458 | { 1459 | "description": "", 1460 | "in": "query", 1461 | "name": "value", 1462 | "required": false, 1463 | "type": "string" 1464 | } 1465 | ] 1466 | } 1467 | }, 1468 | "/logs/query": { 1469 | "get": { 1470 | "operationId": "LogsQuery", 1471 | "produces": [ 1472 | "application/json" 1473 | ], 1474 | "responses": { 1475 | "200": { 1476 | "description": "Ok", 1477 | "schema": { 1478 | "$ref": "#/definitions/ResultList$Log" 1479 | } 1480 | } 1481 | }, 1482 | "description": "", 1483 | "parameters": [ 1484 | { 1485 | "description": "", 1486 | "in": "query", 1487 | "name": "keyword", 1488 | "required": false, 1489 | "type": "string" 1490 | }, 1491 | { 1492 | "description": "", 1493 | "in": "query", 1494 | "name": "index", 1495 | "required": false, 1496 | "type": "number", 1497 | "format": "double", 1498 | "default": 1 1499 | }, 1500 | { 1501 | "description": "", 1502 | "in": "query", 1503 | "name": "size", 1504 | "required": false, 1505 | "type": "number", 1506 | "format": "double", 1507 | "default": 10 1508 | } 1509 | ] 1510 | } 1511 | }, 1512 | "/logs/{id}": { 1513 | "get": { 1514 | "operationId": "LogsFindOne", 1515 | "produces": [ 1516 | "application/json" 1517 | ], 1518 | "responses": { 1519 | "200": { 1520 | "description": "Ok", 1521 | "schema": { 1522 | "$ref": "#/definitions/Log" 1523 | } 1524 | } 1525 | }, 1526 | "description": "", 1527 | "parameters": [ 1528 | { 1529 | "description": "", 1530 | "in": "path", 1531 | "name": "id", 1532 | "required": true, 1533 | "type": "string" 1534 | } 1535 | ] 1536 | } 1537 | }, 1538 | "/cats/": { 1539 | "post": { 1540 | "operationId": "ProductsCreate", 1541 | "produces": [ 1542 | "text/html" 1543 | ], 1544 | "responses": { 1545 | "204": { 1546 | "description": "No content" 1547 | } 1548 | }, 1549 | "description": "", 1550 | "consumes": [ 1551 | "application/json" 1552 | ], 1553 | "parameters": [ 1554 | { 1555 | "description": "", 1556 | "in": "body", 1557 | "name": "createCatDto", 1558 | "required": true, 1559 | "schema": { 1560 | "$ref": "#/definitions/CreateProductDto" 1561 | } 1562 | } 1563 | ] 1564 | }, 1565 | "get": { 1566 | "operationId": "ProductsFindAll", 1567 | "produces": [ 1568 | "application/json" 1569 | ], 1570 | "responses": { 1571 | "200": { 1572 | "description": "Ok", 1573 | "schema": { 1574 | "type": "array", 1575 | "items": { 1576 | "$ref": "#/definitions/Product" 1577 | } 1578 | } 1579 | } 1580 | }, 1581 | "description": "", 1582 | "parameters": [] 1583 | } 1584 | }, 1585 | "/cats/{id}": { 1586 | "get": { 1587 | "operationId": "ProductsFindOne", 1588 | "produces": [ 1589 | "text/html" 1590 | ], 1591 | "responses": { 1592 | "204": { 1593 | "description": "No content" 1594 | } 1595 | }, 1596 | "description": "", 1597 | "parameters": [ 1598 | { 1599 | "description": "", 1600 | "in": "path", 1601 | "name": "id", 1602 | "required": true, 1603 | "type": "number", 1604 | "format": "double" 1605 | } 1606 | ] 1607 | } 1608 | } 1609 | }, 1610 | "swagger": "2.0", 1611 | "securityDefinitions": {}, 1612 | "produces": [ 1613 | "application/json" 1614 | ] 1615 | } -------------------------------------------------------------------------------- /dist/swagger.yaml: -------------------------------------------------------------------------------- 1 | basePath: null 2 | definitions: 3 | LoginDto: 4 | description: "" 5 | properties: 6 | username: 7 | type: string 8 | description: "" 9 | password: 10 | type: string 11 | description: "" 12 | type: object 13 | required: 14 | - username 15 | - password 16 | RegisterDto: 17 | description: "" 18 | properties: 19 | username: 20 | type: string 21 | description: "" 22 | password: 23 | type: string 24 | description: "" 25 | mobile: 26 | type: string 27 | description: "" 28 | type: object 29 | required: 30 | - username 31 | - password 32 | - mobile 33 | CreateDictReq: 34 | description: "" 35 | properties: 36 | category: 37 | type: string 38 | description: "" 39 | name: 40 | type: string 41 | description: "" 42 | translate: 43 | type: string 44 | description: "" 45 | expand: 46 | type: object 47 | description: "" 48 | type: object 49 | required: 50 | - category 51 | - name 52 | - translate 53 | - expand 54 | Dict: 55 | description: "" 56 | properties: 57 | _id: 58 | type: string 59 | description: "" 60 | name: 61 | type: string 62 | description: "" 63 | category: 64 | type: string 65 | description: "" 66 | translate: 67 | type: string 68 | description: "" 69 | expand: 70 | type: object 71 | description: "" 72 | type: object 73 | required: 74 | - _id 75 | - name 76 | - category 77 | - translate 78 | - expand 79 | EditDictReq: 80 | description: "" 81 | properties: 82 | id: 83 | type: string 84 | description: "" 85 | category: 86 | type: string 87 | description: "" 88 | name: 89 | type: string 90 | description: "" 91 | translate: 92 | type: string 93 | description: "" 94 | expand: 95 | type: object 96 | description: "" 97 | type: object 98 | required: 99 | - id 100 | - category 101 | - name 102 | - translate 103 | - expand 104 | KeyValueDto: 105 | description: "" 106 | properties: 107 | label: 108 | type: string 109 | description: "" 110 | value: 111 | type: string 112 | description: "" 113 | type: object 114 | required: 115 | - label 116 | - value 117 | Query: 118 | description: "" 119 | properties: 120 | size: 121 | type: number 122 | format: double 123 | description: "" 124 | index: 125 | type: number 126 | format: double 127 | description: "" 128 | type: object 129 | required: 130 | - size 131 | - index 132 | ResultList$Dict: 133 | description: "" 134 | properties: 135 | list: 136 | type: array 137 | items: 138 | $ref: '#/definitions/Generic~T' 139 | description: "" 140 | count: 141 | type: number 142 | format: double 143 | description: "" 144 | query: 145 | $ref: '#/definitions/Query' 146 | type: object 147 | required: 148 | - list 149 | User: 150 | description: "" 151 | properties: 152 | _id: 153 | type: string 154 | description: "" 155 | username: 156 | type: string 157 | description: "" 158 | password: 159 | type: string 160 | description: "" 161 | name: 162 | type: string 163 | description: "" 164 | keyword: 165 | type: string 166 | description: "" 167 | avatar: 168 | type: string 169 | description: "" 170 | type: 171 | type: string 172 | description: "" 173 | groups: 174 | type: array 175 | items: 176 | type: string 177 | description: "" 178 | roles: 179 | type: array 180 | items: 181 | type: string 182 | description: "" 183 | email: 184 | type: string 185 | description: "" 186 | mobile: 187 | type: string 188 | description: "" 189 | profile: 190 | type: object 191 | description: "" 192 | isDisable: 193 | type: boolean 194 | description: "" 195 | isAdmin: 196 | type: boolean 197 | description: "" 198 | isApproved: 199 | type: boolean 200 | description: "" 201 | secret: 202 | type: string 203 | description: "" 204 | expired: 205 | type: string 206 | format: date-time 207 | description: "" 208 | type: object 209 | required: 210 | - _id 211 | - username 212 | - password 213 | - name 214 | - keyword 215 | - avatar 216 | - type 217 | - groups 218 | - roles 219 | - email 220 | - mobile 221 | - profile 222 | - isDisable 223 | - isAdmin 224 | - isApproved 225 | - secret 226 | - expired 227 | CreateUserReq: 228 | description: "" 229 | properties: 230 | username: 231 | type: string 232 | description: "" 233 | password: 234 | type: string 235 | description: "" 236 | type: object 237 | required: 238 | - username 239 | - password 240 | EditUserReq: 241 | description: "" 242 | properties: 243 | name: 244 | type: string 245 | description: "" 246 | mobile: 247 | type: number 248 | format: double 249 | description: "" 250 | email: 251 | type: string 252 | description: "" 253 | company: 254 | type: string 255 | description: "" 256 | siteUrl: 257 | type: string 258 | description: "" 259 | address: 260 | type: string 261 | description: "" 262 | type: object 263 | required: 264 | - name 265 | - mobile 266 | T: 267 | description: "" 268 | properties: {} 269 | type: object 270 | ResultList$User: 271 | description: "" 272 | properties: 273 | list: 274 | type: array 275 | items: 276 | $ref: '#/definitions/T' 277 | description: "" 278 | count: 279 | type: number 280 | format: double 281 | description: "" 282 | query: 283 | $ref: '#/definitions/Query' 284 | type: object 285 | required: 286 | - list 287 | CreateMenuRes: 288 | description: "" 289 | properties: 290 | name: 291 | type: string 292 | description: "" 293 | slug: 294 | type: string 295 | description: "" 296 | group: 297 | type: boolean 298 | description: "" 299 | link: 300 | type: string 301 | description: "" 302 | order: 303 | type: number 304 | format: double 305 | description: "" 306 | externalLink: 307 | type: string 308 | description: "" 309 | blank: 310 | type: boolean 311 | description: "" 312 | icon: 313 | type: string 314 | description: "" 315 | badge: 316 | type: string 317 | description: "" 318 | badgeDot: 319 | type: string 320 | description: "" 321 | badgeStatus: 322 | type: string 323 | description: "" 324 | enable: 325 | type: boolean 326 | description: "" 327 | expanded: 328 | type: boolean 329 | description: "" 330 | acl: 331 | type: string 332 | description: "" 333 | paths: 334 | type: array 335 | items: 336 | type: object 337 | description: "" 338 | parent: 339 | type: string 340 | description: "" 341 | permissions: 342 | type: array 343 | items: 344 | type: object 345 | description: "" 346 | isMenu: 347 | type: boolean 348 | description: "" 349 | type: object 350 | required: 351 | - name 352 | - slug 353 | - group 354 | - link 355 | - order 356 | - externalLink 357 | - blank 358 | - icon 359 | - badge 360 | - badgeDot 361 | - badgeStatus 362 | - enable 363 | - expanded 364 | - acl 365 | - paths 366 | - parent 367 | - isMenu 368 | Menu: 369 | description: "" 370 | properties: 371 | _id: 372 | type: string 373 | description: "" 374 | name: 375 | type: string 376 | description: "" 377 | slug: 378 | type: string 379 | description: "" 380 | group: 381 | type: boolean 382 | description: "" 383 | link: 384 | type: string 385 | description: "" 386 | order: 387 | type: number 388 | format: double 389 | description: "" 390 | externalLink: 391 | type: string 392 | description: "" 393 | blank: 394 | type: boolean 395 | description: "" 396 | icon: 397 | type: string 398 | description: "" 399 | badge: 400 | type: string 401 | description: "" 402 | badgeDot: 403 | type: string 404 | description: "" 405 | badgeStatus: 406 | type: string 407 | description: "" 408 | enable: 409 | type: boolean 410 | description: "" 411 | expanded: 412 | type: boolean 413 | description: "" 414 | acl: 415 | type: string 416 | description: "" 417 | paths: 418 | type: array 419 | items: 420 | type: object 421 | description: "" 422 | parent: 423 | type: object 424 | description: "" 425 | permissions: 426 | type: array 427 | items: 428 | type: object 429 | description: "" 430 | isMenu: 431 | type: boolean 432 | description: "" 433 | type: object 434 | required: 435 | - _id 436 | - name 437 | - slug 438 | - group 439 | - link 440 | - order 441 | - externalLink 442 | - blank 443 | - icon 444 | - badge 445 | - badgeDot 446 | - badgeStatus 447 | - enable 448 | - expanded 449 | - acl 450 | - paths 451 | - parent 452 | - isMenu 453 | EditMenuRes: 454 | description: "" 455 | properties: 456 | name: 457 | type: string 458 | description: "" 459 | slug: 460 | type: string 461 | description: "" 462 | group: 463 | type: boolean 464 | description: "" 465 | link: 466 | type: string 467 | description: "" 468 | order: 469 | type: number 470 | format: double 471 | description: "" 472 | externalLink: 473 | type: string 474 | description: "" 475 | blank: 476 | type: boolean 477 | description: "" 478 | icon: 479 | type: string 480 | description: "" 481 | badge: 482 | type: string 483 | description: "" 484 | badgeDot: 485 | type: string 486 | description: "" 487 | badgeStatus: 488 | type: string 489 | description: "" 490 | enable: 491 | type: boolean 492 | description: "" 493 | expanded: 494 | type: boolean 495 | description: "" 496 | acl: 497 | type: string 498 | description: "" 499 | paths: 500 | type: array 501 | items: 502 | type: object 503 | description: "" 504 | parent: 505 | type: string 506 | description: "" 507 | permissions: 508 | type: array 509 | items: 510 | type: object 511 | description: "" 512 | isMenu: 513 | type: boolean 514 | description: "" 515 | type: object 516 | required: 517 | - name 518 | - slug 519 | - group 520 | - link 521 | - order 522 | - externalLink 523 | - blank 524 | - icon 525 | - badge 526 | - badgeDot 527 | - badgeStatus 528 | - enable 529 | - expanded 530 | - acl 531 | - paths 532 | - parent 533 | - isMenu 534 | ResultList$Menu: 535 | description: "" 536 | properties: 537 | list: 538 | type: array 539 | items: 540 | $ref: '#/definitions/T' 541 | description: "" 542 | count: 543 | type: number 544 | format: double 545 | description: "" 546 | query: 547 | $ref: '#/definitions/Query' 548 | type: object 549 | required: 550 | - list 551 | ResultList$Log: 552 | description: "" 553 | properties: 554 | list: 555 | type: array 556 | items: 557 | $ref: '#/definitions/T' 558 | description: "" 559 | count: 560 | type: number 561 | format: double 562 | description: "" 563 | query: 564 | $ref: '#/definitions/Query' 565 | type: object 566 | required: 567 | - list 568 | Log: 569 | description: "" 570 | properties: 571 | id: 572 | type: string 573 | description: "" 574 | name: 575 | type: string 576 | description: "" 577 | operator: 578 | type: string 579 | description: "" 580 | operatorIp: 581 | type: string 582 | description: "" 583 | operation: 584 | type: string 585 | description: "" 586 | comment: 587 | type: string 588 | description: "" 589 | createdAt: 590 | type: string 591 | format: date-time 592 | description: "" 593 | type: object 594 | required: 595 | - id 596 | - name 597 | - operator 598 | - operatorIp 599 | - operation 600 | - comment 601 | - createdAt 602 | CreateProductDto: 603 | description: "" 604 | properties: 605 | name: 606 | type: string 607 | description: "" 608 | title: 609 | type: string 610 | description: "" 611 | type: object 612 | required: 613 | - name 614 | - title 615 | Product: 616 | description: "" 617 | properties: 618 | name: 619 | type: string 620 | description: "" 621 | title: 622 | type: string 623 | description: "" 624 | type: object 625 | required: 626 | - name 627 | - title 628 | info: 629 | description: 项目标准接口 630 | license: 631 | name: MIT 632 | title: 'rest api interface' 633 | version: 0.0.1 634 | paths: 635 | /: 636 | get: 637 | operationId: AppRoot 638 | produces: 639 | - text/html 640 | responses: 641 | '200': 642 | description: Ok 643 | schema: 644 | type: string 645 | description: "" 646 | parameters: [] 647 | /auth/login: 648 | post: 649 | operationId: AuthLogin 650 | produces: 651 | - text/html 652 | responses: 653 | '200': 654 | description: Ok 655 | schema: 656 | type: string 657 | enum: 658 | - "" 659 | - "" 660 | description: "" 661 | consumes: 662 | - application/json 663 | parameters: 664 | - 665 | description: "" 666 | in: body 667 | name: payload 668 | required: true 669 | schema: 670 | $ref: '#/definitions/LoginDto' 671 | /auth/register: 672 | post: 673 | operationId: AuthRegister 674 | produces: 675 | - text/html 676 | responses: 677 | '200': 678 | description: Ok 679 | schema: 680 | type: string 681 | enum: 682 | - "" 683 | - "" 684 | description: "" 685 | consumes: 686 | - application/json 687 | parameters: 688 | - 689 | description: "" 690 | in: body 691 | name: payload 692 | required: true 693 | schema: 694 | $ref: '#/definitions/RegisterDto' 695 | /dicts/: 696 | post: 697 | operationId: DictsCreate 698 | produces: 699 | - text/html 700 | responses: 701 | '204': 702 | description: 'No content' 703 | description: "" 704 | consumes: 705 | - application/json 706 | parameters: 707 | - 708 | description: "" 709 | in: body 710 | name: entry 711 | required: true 712 | schema: 713 | $ref: '#/definitions/CreateDictReq' 714 | put: 715 | operationId: DictsUpdate 716 | produces: 717 | - application/json 718 | responses: 719 | '200': 720 | description: Ok 721 | schema: 722 | $ref: '#/definitions/Dict' 723 | description: "" 724 | consumes: 725 | - application/json 726 | parameters: 727 | - 728 | description: "" 729 | in: body 730 | name: entry 731 | required: true 732 | schema: 733 | $ref: '#/definitions/EditDictReq' 734 | /dicts/search: 735 | get: 736 | operationId: DictsSearch 737 | produces: 738 | - application/json 739 | responses: 740 | '200': 741 | description: Ok 742 | schema: 743 | type: array 744 | items: 745 | $ref: '#/definitions/KeyValueDto' 746 | description: "" 747 | parameters: 748 | - 749 | description: "" 750 | in: query 751 | name: keyword 752 | required: false 753 | type: string 754 | - 755 | description: "" 756 | in: query 757 | name: value 758 | required: false 759 | type: string 760 | /dicts/query: 761 | get: 762 | operationId: DictsQuery 763 | produces: 764 | - application/json 765 | responses: 766 | '200': 767 | description: Ok 768 | schema: 769 | $ref: '#/definitions/ResultList$Dict' 770 | description: "" 771 | parameters: 772 | - 773 | description: "" 774 | in: query 775 | name: keyword 776 | required: false 777 | type: string 778 | - 779 | description: "" 780 | in: query 781 | name: index 782 | required: false 783 | type: number 784 | format: double 785 | default: 1 786 | - 787 | description: "" 788 | in: query 789 | name: size 790 | required: false 791 | type: number 792 | format: double 793 | default: 10 794 | '/dicts/{id}': 795 | get: 796 | operationId: DictsFindOne 797 | produces: 798 | - application/json 799 | responses: 800 | '200': 801 | description: Ok 802 | schema: 803 | $ref: '#/definitions/Dict' 804 | description: "" 805 | parameters: 806 | - 807 | description: "" 808 | in: path 809 | name: id 810 | required: true 811 | type: string 812 | /users/profile: 813 | get: 814 | operationId: UsersProfile 815 | produces: 816 | - application/json 817 | responses: 818 | '200': 819 | description: Ok 820 | schema: 821 | $ref: '#/definitions/User' 822 | description: "" 823 | parameters: [] 824 | /users/: 825 | post: 826 | operationId: UsersCreate 827 | produces: 828 | - text/html 829 | responses: 830 | '204': 831 | description: 'No content' 832 | description: "" 833 | consumes: 834 | - application/json 835 | parameters: 836 | - 837 | description: "" 838 | in: body 839 | name: user 840 | required: true 841 | schema: 842 | $ref: '#/definitions/CreateUserReq' 843 | put: 844 | operationId: UsersUpdate 845 | produces: 846 | - application/json 847 | responses: 848 | '200': 849 | description: Ok 850 | schema: 851 | $ref: '#/definitions/User' 852 | description: "" 853 | consumes: 854 | - application/json 855 | parameters: 856 | - 857 | description: "" 858 | in: body 859 | name: user 860 | required: true 861 | schema: 862 | $ref: '#/definitions/EditUserReq' 863 | /users/search: 864 | get: 865 | operationId: UsersSearch 866 | produces: 867 | - application/json 868 | responses: 869 | '200': 870 | description: Ok 871 | schema: 872 | type: array 873 | items: 874 | $ref: '#/definitions/KeyValueDto' 875 | description: "" 876 | parameters: 877 | - 878 | description: "" 879 | in: query 880 | name: keyword 881 | required: false 882 | type: string 883 | - 884 | description: "" 885 | in: query 886 | name: value 887 | required: false 888 | type: string 889 | /users/query: 890 | get: 891 | operationId: UsersQuery 892 | produces: 893 | - application/json 894 | responses: 895 | '200': 896 | description: Ok 897 | schema: 898 | $ref: '#/definitions/ResultList$User' 899 | description: "" 900 | parameters: 901 | - 902 | description: "" 903 | in: query 904 | name: keyword 905 | required: false 906 | type: string 907 | - 908 | description: "" 909 | in: query 910 | name: index 911 | required: false 912 | type: number 913 | format: double 914 | default: 1 915 | - 916 | description: "" 917 | in: query 918 | name: size 919 | required: false 920 | type: number 921 | format: double 922 | default: 10 923 | '/users/{id}': 924 | get: 925 | operationId: UsersFindOne 926 | produces: 927 | - application/json 928 | responses: 929 | '200': 930 | description: Ok 931 | schema: 932 | $ref: '#/definitions/User' 933 | description: "" 934 | parameters: 935 | - 936 | description: "" 937 | in: path 938 | name: id 939 | required: true 940 | type: string 941 | /menus/: 942 | post: 943 | operationId: MenusCreate 944 | produces: 945 | - text/html 946 | responses: 947 | '204': 948 | description: 'No content' 949 | description: "" 950 | consumes: 951 | - application/json 952 | parameters: 953 | - 954 | description: "" 955 | in: body 956 | name: entry 957 | required: true 958 | schema: 959 | $ref: '#/definitions/CreateMenuRes' 960 | put: 961 | operationId: MenusUpdate 962 | produces: 963 | - application/json 964 | responses: 965 | '200': 966 | description: Ok 967 | schema: 968 | $ref: '#/definitions/Menu' 969 | description: "" 970 | consumes: 971 | - application/json 972 | parameters: 973 | - 974 | description: "" 975 | in: body 976 | name: entry 977 | required: true 978 | schema: 979 | $ref: '#/definitions/EditMenuRes' 980 | /menus/search: 981 | get: 982 | operationId: MenusSearch 983 | produces: 984 | - application/json 985 | responses: 986 | '200': 987 | description: Ok 988 | schema: 989 | type: array 990 | items: 991 | $ref: '#/definitions/KeyValueDto' 992 | description: "" 993 | parameters: 994 | - 995 | description: "" 996 | in: query 997 | name: keyword 998 | required: false 999 | type: string 1000 | - 1001 | description: "" 1002 | in: query 1003 | name: value 1004 | required: false 1005 | type: string 1006 | /menus/query: 1007 | get: 1008 | operationId: MenusQuery 1009 | produces: 1010 | - application/json 1011 | responses: 1012 | '200': 1013 | description: Ok 1014 | schema: 1015 | $ref: '#/definitions/ResultList$Menu' 1016 | description: "" 1017 | parameters: 1018 | - 1019 | description: "" 1020 | in: query 1021 | name: keyword 1022 | required: false 1023 | type: string 1024 | - 1025 | description: "" 1026 | in: query 1027 | name: index 1028 | required: false 1029 | type: number 1030 | format: double 1031 | default: 1 1032 | - 1033 | description: "" 1034 | in: query 1035 | name: size 1036 | required: false 1037 | type: number 1038 | format: double 1039 | default: 10 1040 | '/menus/{id}': 1041 | get: 1042 | operationId: MenusFindOne 1043 | produces: 1044 | - application/json 1045 | responses: 1046 | '200': 1047 | description: Ok 1048 | schema: 1049 | $ref: '#/definitions/Menu' 1050 | description: "" 1051 | parameters: 1052 | - 1053 | description: "" 1054 | in: path 1055 | name: id 1056 | required: true 1057 | type: string 1058 | /logs/search: 1059 | get: 1060 | operationId: LogsSearch 1061 | produces: 1062 | - application/json 1063 | responses: 1064 | '200': 1065 | description: Ok 1066 | schema: 1067 | type: array 1068 | items: 1069 | $ref: '#/definitions/KeyValueDto' 1070 | description: "" 1071 | parameters: 1072 | - 1073 | description: "" 1074 | in: query 1075 | name: keyword 1076 | required: false 1077 | type: string 1078 | - 1079 | description: "" 1080 | in: query 1081 | name: value 1082 | required: false 1083 | type: string 1084 | /logs/query: 1085 | get: 1086 | operationId: LogsQuery 1087 | produces: 1088 | - application/json 1089 | responses: 1090 | '200': 1091 | description: Ok 1092 | schema: 1093 | $ref: '#/definitions/ResultList$Log' 1094 | description: "" 1095 | parameters: 1096 | - 1097 | description: "" 1098 | in: query 1099 | name: keyword 1100 | required: false 1101 | type: string 1102 | - 1103 | description: "" 1104 | in: query 1105 | name: index 1106 | required: false 1107 | type: number 1108 | format: double 1109 | default: 1 1110 | - 1111 | description: "" 1112 | in: query 1113 | name: size 1114 | required: false 1115 | type: number 1116 | format: double 1117 | default: 10 1118 | '/logs/{id}': 1119 | get: 1120 | operationId: LogsFindOne 1121 | produces: 1122 | - application/json 1123 | responses: 1124 | '200': 1125 | description: Ok 1126 | schema: 1127 | $ref: '#/definitions/Log' 1128 | description: "" 1129 | parameters: 1130 | - 1131 | description: "" 1132 | in: path 1133 | name: id 1134 | required: true 1135 | type: string 1136 | /cats/: 1137 | post: 1138 | operationId: ProductsCreate 1139 | produces: 1140 | - text/html 1141 | responses: 1142 | '204': 1143 | description: 'No content' 1144 | description: "" 1145 | consumes: 1146 | - application/json 1147 | parameters: 1148 | - 1149 | description: "" 1150 | in: body 1151 | name: createCatDto 1152 | required: true 1153 | schema: 1154 | $ref: '#/definitions/CreateProductDto' 1155 | get: 1156 | operationId: ProductsFindAll 1157 | produces: 1158 | - application/json 1159 | responses: 1160 | '200': 1161 | description: Ok 1162 | schema: 1163 | type: array 1164 | items: 1165 | $ref: '#/definitions/Product' 1166 | description: "" 1167 | parameters: [] 1168 | '/cats/{id}': 1169 | get: 1170 | operationId: ProductsFindOne 1171 | produces: 1172 | - text/html 1173 | responses: 1174 | '204': 1175 | description: 'No content' 1176 | description: "" 1177 | parameters: 1178 | - 1179 | description: "" 1180 | in: path 1181 | name: id 1182 | required: true 1183 | type: number 1184 | format: double 1185 | swagger: '2.0' 1186 | securityDefinitions: {} 1187 | produces: 1188 | - application/json 1189 | -------------------------------------------------------------------------------- /docs/client.http: -------------------------------------------------------------------------------- 1 | 2 | ### List JsonNode 3 | GET http://localhost:5600/api/users/profile HTTP/1.1 4 | Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoidmVsbGVuZ3MiLCJpYXQiOjE1NTIxMzg0MjQsImV4cCI6MTU1MjE0MjAyNH0.hhrLgydv1bZcUu2d5dwfyjEU_PFlA-latRWNK9l_lJSxNt-Lu29UpahlDA8bdV9wq45Uv6menWNR512rm8VWTQ 5 | content-type: application/json 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('ts-node/register'); 2 | require('./src/main'); -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "ts", 4 | "tsx", 5 | "js", 6 | "json" 7 | ], 8 | "transform": { 9 | "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" 10 | }, 11 | "testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$", 12 | "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], 13 | "coverageReporters": ["json", "lcov"] 14 | } -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /log4js.debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "appenders": { 3 | "access": { 4 | "type": "dateFile", 5 | "filename": "log/access.log", 6 | "pattern": "-yyyy-MM-dd", 7 | "category": "http" 8 | }, 9 | "app": { 10 | "type": "console", 11 | "filename": "log/app.log", 12 | "maxLogSize": 10485760, 13 | "numBackups": 3 14 | }, 15 | "errorFile": { 16 | "type": "file", 17 | "filename": "log/errors.log" 18 | }, 19 | "errors": { 20 | "type": "logLevelFilter", 21 | "level": "ERROR", 22 | "appender": "errorFile" 23 | } 24 | }, 25 | "categories": { 26 | "default": { 27 | "appenders": ["app", "errors"], 28 | "level": "DEBUG" 29 | }, 30 | "http": { 31 | "appenders": ["access"], 32 | "level": "DEBUG" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /log4js.json: -------------------------------------------------------------------------------- 1 | { 2 | "appenders": { 3 | "access": { 4 | "type": "dateFile", 5 | "filename": "log/access.log", 6 | "pattern": "-yyyy-MM-dd", 7 | "category": "http" 8 | }, 9 | "app": { 10 | "type": "file", 11 | "filename": "log/app.log", 12 | "maxLogSize": 10485760, 13 | "numBackups": 3 14 | }, 15 | "errorFile": { 16 | "type": "file", 17 | "filename": "log/errors.log" 18 | }, 19 | "errors": { 20 | "type": "logLevelFilter", 21 | "level": "ERROR", 22 | "appender": "errorFile" 23 | } 24 | }, 25 | "categories": { 26 | "default": { 27 | "appenders": ["app", "errors"], 28 | "level": "DEBUG" 29 | }, 30 | "http": { 31 | "appenders": ["access"], 32 | "level": "DEBUG" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /nestx.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /nodemon-debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.spec.ts"], 5 | "exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts" 6 | } 7 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["./src"], 3 | "ext": "ts", 4 | "ignore": ["./src/**/*.spec.ts"], 5 | "exec": "node ./index", 6 | "env": { 7 | "NODE_ENV": "development" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ormconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "mysql", 3 | "host": "localhost", 4 | "port": 3306, 5 | "username": "root", 6 | "password": "Viking@123", 7 | "database": "typerx", 8 | "entities": ["src/**/**.entity{.ts,.js}"], 9 | "synchronize": true 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestx", 3 | "version": "0.2.0", 4 | "description": "A full stack management system base on nest", 5 | "keywords": [ 6 | "mongodb", 7 | "nest", 8 | "json-schema", 9 | "typescript" 10 | ], 11 | "homepage": "https://github.com/vellengs/nestx-server#readme", 12 | "bugs": { 13 | "url": "https://github.com/vellengs/nestx-server/issues" 14 | }, 15 | "license": "MIT", 16 | "author": "vellengs@qq.com", 17 | "main": "index.js", 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/vellengs/nestx-server.git" 21 | }, 22 | "scripts": { 23 | "build": "tsc -p tsconfig.build.json", 24 | "format": "prettier --write \"src/**/*.ts\"", 25 | "start": "ts-node -r tsconfig-paths/register src/main.ts", 26 | "start:dev": "nodemon", 27 | "start:debug": "nodemon --config nodemon-debug.json", 28 | "prestart:prod": "rimraf dist && npm run build", 29 | "start:prod": "node dist/main.js", 30 | "lint": "tslint -p tsconfig.json -c tslint.json", 31 | "swagger": "swaggerGen -c ./swagger.config.json", 32 | "test": "jest", 33 | "test:watch": "jest --watch", 34 | "test:cov": "jest --coverage", 35 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 36 | "test:e2e": "jest --config ./test/jest-e2e.json" 37 | }, 38 | "dependencies": { 39 | "@nestjs/common": "^6.0.2", 40 | "@nestjs/core": "^6.0.2", 41 | "@nestjs/jwt": "^6.0.0", 42 | "@nestjs/mongoose": "^6.0.0", 43 | "@nestjs/passport": "^6.0.0", 44 | "@nestjs/platform-express": "^6.0.2", 45 | "@nestjs/platform-socket.io": "^6.0.2", 46 | "@nestjs/swagger": "^3.0.2", 47 | "@nestjs/testing": "^6.0.2", 48 | "@nestjs/typeorm": "^6.0.0", 49 | "@nestjs/websockets": "^6.0.2", 50 | "bcrypt": "^3.0.4", 51 | "bcrypt-nodejs": "0.0.3", 52 | "bluebird": "^3.5.3", 53 | "class-transformer": "^0.1.10", 54 | "class-validator": "^0.8.5", 55 | "compression": "^1.7.3", 56 | "copy-webpack-plugin": "^4.6.0", 57 | "core-js": "^2.6.1", 58 | "csurf": "^1.9.0", 59 | "deprecate": "^1.1.0", 60 | "dotenv": "^4.0.0", 61 | "express-rate-limit": "^3.3.2", 62 | "file-saver": "^1.3.3", 63 | "font-awesome": "^4.7.0", 64 | "helmet": "^3.15.0", 65 | "log4js": "^4.0.2", 66 | "moment": "^2.19.3", 67 | "mongoose": "^5.4.0", 68 | "mysql": "^2.15.0", 69 | "nest-swagger": "0.0.4", 70 | "optional": "^0.1.4", 71 | "passport": "^0.4.0", 72 | "passport-http-bearer": "^1.0.1", 73 | "passport-jwt": "^4.0.0", 74 | "rxjs": "^6.0.0", 75 | "rxjs-compat": "^6.0.0", 76 | "swagger-ui-express": "^4.0.2", 77 | "typeorm": "^0.2.1", 78 | "webpack-node-externals": "^1.6.0", 79 | "zone.js": "^0.8.20" 80 | }, 81 | "devDependencies": { 82 | "@types/bcrypt": "^3.0.0", 83 | "@types/bluebird": "^3.5.26", 84 | "@types/compression": "0.0.36", 85 | "@types/dotenv": "^4.0.2", 86 | "@types/express": "^4.11.1", 87 | "@types/express-rate-limit": "^3.3.0", 88 | "@types/helmet": "0.0.43", 89 | "@types/jest": "^22.2.3", 90 | "@types/jsonwebtoken": "^7.2.7", 91 | "@types/jszip": "^3.1.2", 92 | "@types/mockjs": "^1.0.0", 93 | "@types/mongoose": "^5.0.11", 94 | "@types/node": "^6.0.100", 95 | "@types/passport-jwt": "^3.0.1", 96 | "@types/supertest": "^2.0.4", 97 | "codecov": "^3.0.0", 98 | "codelyzer": "~4.0.1", 99 | "concurrently": "^3.5.1", 100 | "jest": "^22.4.3", 101 | "lint-staged": "^5.0.0", 102 | "nodemon": "^1.14.12", 103 | "npm-run-all": "^4.1.1", 104 | "prettier": "^1.12.1", 105 | "pretty-quick": "^1.4.1", 106 | "protractor": "~5.1.2", 107 | "stylelint": "^8.2.0", 108 | "stylelint-config-standard": "^17.0.0", 109 | "supertest": "^3.0.0", 110 | "ts-jest": "^22.4.4", 111 | "ts-loader": "^3.5.0", 112 | "ts-node": "~3.2.0", 113 | "tsconfig-paths": "^3.3.1", 114 | "tslint": "~5.7.0", 115 | "tslint-sonarts": "^1.6.0", 116 | "typescript": "^3.3.4000", 117 | "webpack-bundle-analyzer": "^2.9.0", 118 | "xlsx": "^0.11.16" 119 | }, 120 | "jest": { 121 | "moduleFileExtensions": [ 122 | "js", 123 | "json", 124 | "ts" 125 | ], 126 | "rootDir": "src", 127 | "testRegex": ".spec.ts$", 128 | "transform": { 129 | "^.+\\.(t|j)s$": "ts-jest" 130 | }, 131 | "coverageDirectory": "../coverage" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vellengs/nestx-server/3baba62018dabf9d498a4abf878428e09ebb2cae/src/.DS_Store -------------------------------------------------------------------------------- /src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let app: TestingModule; 7 | 8 | beforeAll(async () => { 9 | app = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | }); 14 | 15 | describe('root', () => { 16 | it('should return "Hello World!"', () => { 17 | const appController = app.get(AppController); 18 | expect(appController.root()).toBe('Hello World!'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) { } 7 | 8 | @Get() 9 | root(): string { 10 | return this.appService.root(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { MongooseModule } from '@nestjs/mongoose'; 5 | 6 | import { TypeOrmModule } from '@nestjs/typeorm'; 7 | import { AuthModule } from './auth/auth.module'; 8 | import { CoreModule } from './core/core.module'; 9 | import { CommerceModule } from './commerce/commerce.module'; 10 | import { CmsModule } from './cms/cms.module'; 11 | import { MONGODB_URI } from './utils/secrets'; 12 | @Module({ 13 | imports: [ 14 | TypeOrmModule.forRoot(), 15 | MongooseModule.forRoot(MONGODB_URI), 16 | AuthModule, 17 | CoreModule, 18 | CmsModule, 19 | CommerceModule, 20 | ], 21 | controllers: [AppController], 22 | providers: [AppService], 23 | }) 24 | export class AppModule { } -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | root(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body, Get } from '@nestjs/common'; 2 | import { AuthService } from './auth.service'; 3 | import { Token } from './interfaces/jwt-payload.interface'; 4 | import { ApiResponse } from '@nestjs/swagger'; 5 | import { LoginDto } from './dto/Login.dto'; 6 | import { RegisterDto } from './dto/Register.dto'; 7 | 8 | @Controller('auth') 9 | export class AuthController { 10 | constructor(private readonly authService: AuthService) { } 11 | 12 | @Post('login') 13 | @ApiResponse({ status: 201, description: 'Successful Login' }) 14 | @ApiResponse({ status: 400, description: 'Bad Request' }) 15 | async login(@Body() payload: LoginDto): Promise { 16 | return this.authService.login(payload); 17 | } 18 | 19 | @Post('register') 20 | @ApiResponse({ status: 201, description: 'Successful Registration' }) 21 | @ApiResponse({ status: 400, description: 'Bad Request' }) 22 | async register(@Body() payload: RegisterDto): Promise { 23 | return await this.authService.register(payload); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { JwtModule } from '@nestjs/jwt'; 3 | import { PassportModule } from '@nestjs/passport'; 4 | import { AuthController } from './auth.controller'; 5 | import { AuthService } from './auth.service'; 6 | import { JwtStrategy } from './jwt.strategy'; 7 | import { ConfigModule } from './../config/config.module'; 8 | import { ConfigService } from './../config/config.service'; 9 | import { CoreModule } from './../core/core.module'; 10 | @Module({ 11 | imports: [ 12 | CoreModule, 13 | PassportModule.register({ defaultStrategy: 'jwt' }), 14 | JwtModule.registerAsync({ 15 | imports: [ 16 | ConfigModule, 17 | ], 18 | useFactory: async (configService: ConfigService) => { 19 | return { 20 | secretOrPrivateKey: configService.get('JWT_SECRET_KEY'), 21 | signOptions: { 22 | algorithm: configService.get('JWT_ALGORITHM'), 23 | expiresIn: configService.get('JWT_EXPIRE_IN'), 24 | }, 25 | }; 26 | }, 27 | inject: [ 28 | ConfigService, 29 | ], 30 | }), 31 | ], 32 | controllers: [AuthController], 33 | providers: [AuthService, JwtStrategy], 34 | exports: [ 35 | PassportModule.register({ defaultStrategy: 'jwt' }), 36 | ], 37 | }) 38 | export class AuthModule { } 39 | -------------------------------------------------------------------------------- /src/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException, NotAcceptableException } from '@nestjs/common'; 2 | import { JwtService } from '@nestjs/jwt'; 3 | import { JwtPayload, Token } from './interfaces/jwt-payload.interface'; 4 | import { LoginDto } from './dto/login.dto'; 5 | import { RegisterDto } from './dto/Register.dto'; 6 | import { UsersService } from './../core/controllers/users.service'; 7 | 8 | @Injectable() 9 | export class AuthService { 10 | 11 | constructor( 12 | private readonly jwtService: JwtService, 13 | private readonly userService: UsersService, 14 | ) { } 15 | 16 | async login(payload: LoginDto): Promise { 17 | const user = await this.userService.login(payload.username, payload.password); 18 | if (user) { 19 | return await this.createToken(user); 20 | } else { 21 | throw new UnauthorizedException(); 22 | } 23 | } 24 | 25 | async createToken(payload: LoginDto): Promise { 26 | const accessToken = this.jwtService.sign({ account: payload.username }); 27 | return { 28 | expiresIn: 3600, 29 | accessToken, 30 | }; 31 | } 32 | 33 | async register(payload: RegisterDto): Promise { 34 | const user = await this.userService.create(payload).catch((error) => { 35 | // TODO log error. 36 | throw new NotAcceptableException('register failure'); 37 | }); 38 | return await this.createToken(user); 39 | } 40 | 41 | async validateUser(payload: JwtPayload) { 42 | console.log('payload:', payload); 43 | return this.userService.findOne({ 44 | username: payload.account 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/auth/dto/Login.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString, MinLength } from 'class-validator'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | 4 | export class LoginDto { 5 | @IsNotEmpty() 6 | @IsString() 7 | @ApiModelProperty({ 8 | required: true 9 | }) 10 | readonly username: string; 11 | 12 | @ApiModelProperty({ 13 | required: true, 14 | }) 15 | @IsNotEmpty() 16 | @MinLength(5, { 17 | message: 'Your password is too short! It must be 5 characters or more!', 18 | }) 19 | readonly password: string; 20 | } 21 | -------------------------------------------------------------------------------- /src/auth/dto/Register.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString, MinLength } from 'class-validator'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | 4 | export class RegisterDto { 5 | @IsNotEmpty() 6 | @MinLength(5) 7 | @IsString() 8 | @ApiModelProperty({ 9 | required: true 10 | }) 11 | readonly username: string; 12 | 13 | @IsNotEmpty() 14 | @MinLength(5) 15 | @IsString() 16 | @ApiModelProperty({ 17 | required: true 18 | }) 19 | readonly password: string; 20 | 21 | @IsNotEmpty() 22 | @MinLength(11) 23 | @IsString() 24 | @ApiModelProperty({ 25 | required: true 26 | }) 27 | readonly mobile: string; 28 | } -------------------------------------------------------------------------------- /src/auth/guards/jwt-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ExecutionContext, 3 | Injectable, 4 | UnauthorizedException, 5 | } from '@nestjs/common'; 6 | import { AuthGuard } from '@nestjs/passport'; 7 | 8 | @Injectable() 9 | export class JwtAuthGuard extends AuthGuard('jwt') { 10 | canActivate(context: ExecutionContext) { 11 | // Add your custom authentication logic here 12 | // for example, call super.logIn(request) to establish a session. 13 | return super.canActivate(context); 14 | } 15 | 16 | handleRequest(err: any, user: any, info: any) { 17 | if (err || !user) { 18 | throw err || new UnauthorizedException(); 19 | } 20 | return user; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/auth/interfaces/jwt-payload.interface.ts: -------------------------------------------------------------------------------- 1 | export interface JwtPayload { 2 | account: string; 3 | } 4 | 5 | export interface Token { 6 | expiresIn: number; 7 | accessToken: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/auth/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { ExtractJwt, Strategy } from 'passport-jwt'; 4 | import { AuthService } from './auth.service'; 5 | import { JwtPayload } from './interfaces/jwt-payload.interface'; 6 | import { ConfigService } from '../config'; 7 | 8 | @Injectable() 9 | export class JwtStrategy extends PassportStrategy(Strategy) { 10 | constructor( 11 | private readonly authService: AuthService, 12 | configService: ConfigService, 13 | ) { 14 | super({ 15 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 16 | secretOrKey: configService.get('JWT_SECRET_KEY'), 17 | }); 18 | } 19 | 20 | async validate(payload: JwtPayload) { 21 | const user = await this.authService.validateUser(payload); 22 | if (!user) { 23 | throw new UnauthorizedException(); 24 | } 25 | return user; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/cms/cms.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | @Module({ 4 | imports: [], 5 | controllers: [], 6 | providers: [], 7 | }) 8 | export class CmsModule { } 9 | -------------------------------------------------------------------------------- /src/cms/cms.provider.ts: -------------------------------------------------------------------------------- 1 | import { Connection } from 'mongoose'; 2 | import { schema as CategorySchema } from './schemas/category.schema'; 3 | 4 | export const CmsProviders = [ 5 | { 6 | provide: 'CmsModelToken', 7 | useFactory: (connection: Connection) => { 8 | connection.model('Category', CategorySchema); 9 | }, 10 | inject: ['DbConnectionToken'], 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /src/cms/interface/photo.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Photo extends Document { 4 | id: number; 5 | username: string; 6 | password: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/cms/photo.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { PhotoService } from './photo.service'; 3 | 4 | @Controller('cms/photo') 5 | export class PhotoController { 6 | constructor(private readonly photoService: PhotoService) { } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/cms/photo.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable, Inject } from '@nestjs/common'; 3 | import { Photo } from './interface/photo.interface'; 4 | 5 | @Injectable() 6 | export class PhotoService { 7 | constructor(@Inject('UserModelToken') private readonly userModel: Model) { } 8 | } 9 | -------------------------------------------------------------------------------- /src/cms/schemas/article.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const schema = new Schema({ 6 | name: { 7 | type: t.String 8 | }, 9 | paths: [{ type: t.ObjectId, ref: 'Category' }], 10 | parent: { 11 | type: t.ObjectId, 12 | ref: 'Category' 13 | } 14 | }, option); 15 | -------------------------------------------------------------------------------- /src/cms/schemas/category.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const schema = new Schema({ 6 | name: { 7 | type: t.String 8 | }, 9 | paths: [{ type: t.ObjectId, ref: 'Category' }], 10 | parent: { 11 | type: t.ObjectId, 12 | ref: 'Category' 13 | } 14 | }, option); 15 | -------------------------------------------------------------------------------- /src/cms/schemas/comment.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const schema = new Schema({ 6 | name: { 7 | type: t.String 8 | }, 9 | paths: [{ type: t.ObjectId, ref: 'Category' }], 10 | parent: { 11 | type: t.ObjectId, 12 | ref: 'Category' 13 | } 14 | }, option); 15 | -------------------------------------------------------------------------------- /src/cms/schemas/media.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const schema = new Schema({ 6 | name: { 7 | type: t.String 8 | }, 9 | paths: [{ type: t.ObjectId, ref: 'Category' }], 10 | parent: { 11 | type: t.ObjectId, 12 | ref: 'Category' 13 | } 14 | }, option); 15 | -------------------------------------------------------------------------------- /src/cms/schemas/page.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const schema = new Schema({ 6 | name: { 7 | type: t.String 8 | }, 9 | paths: [{ type: t.ObjectId, ref: 'Category' }], 10 | parent: { 11 | type: t.ObjectId, 12 | ref: 'Category' 13 | } 14 | }, option); 15 | -------------------------------------------------------------------------------- /src/commerce/commerce.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ProductsController } from './products.controller'; 3 | import { ProductsService } from './products.service'; 4 | 5 | @Module({ 6 | controllers: [ProductsController], 7 | providers: [ProductsService] 8 | }) 9 | export class CommerceModule { } 10 | -------------------------------------------------------------------------------- /src/commerce/dto/create-product.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsInt } from 'class-validator'; 2 | 3 | export class CreateProductDto { 4 | @IsString() 5 | readonly name: string; 6 | 7 | @IsString() 8 | readonly title: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/commerce/interfaces/product.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | readonly name: string; 3 | readonly title: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/commerce/products.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Post, 5 | Body, 6 | UseGuards, 7 | UseInterceptors, 8 | Param, 9 | } from '@nestjs/common'; 10 | import { RolesGuard } from '../common/guards/roles.guard'; 11 | import { Roles } from '../common/decorators/roles.decorator'; 12 | import { LoggingInterceptor } from '../common/interceptors/logging.interceptor'; 13 | import { TransformInterceptor } from '../common/interceptors/transform.interceptor'; 14 | import { ParseIntPipe } from '../common/pipes/parse-int.pipe'; 15 | import { RoleTypes } from './../config/enums'; 16 | import { ProductsService } from './products.service'; 17 | import { CreateProductDto } from './dto/create-product.dto'; 18 | import { Product } from './interfaces/Product.interface'; 19 | 20 | @Controller('cats') 21 | @UseGuards(RolesGuard) 22 | @UseInterceptors(LoggingInterceptor, TransformInterceptor) 23 | export class ProductsController { 24 | constructor(private readonly productsService: ProductsService) { } 25 | 26 | @Post() 27 | @Roles(RoleTypes.admin) 28 | async create(@Body() createCatDto: CreateProductDto) { 29 | this.productsService.create(createCatDto); 30 | } 31 | 32 | @Get() 33 | async findAll(): Promise { 34 | return this.productsService.findAll(); 35 | } 36 | 37 | @Get(':id') 38 | findOne( 39 | @Param('id', new ParseIntPipe()) 40 | id: number, 41 | ) { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/commerce/products.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Product } from './interfaces/Product.interface'; 3 | 4 | @Injectable() 5 | export class ProductsService { 6 | private readonly items: Product[] = []; 7 | 8 | create(item: Product) { 9 | this.items.push(item); 10 | } 11 | 12 | findAll(): Product[] { 13 | return this.items; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/common/decorators/roles.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | 3 | export const Roles = (...roles: string[]) => SetMetadata('roles', roles); 4 | -------------------------------------------------------------------------------- /src/common/filters/http-exception.filter.ts: -------------------------------------------------------------------------------- 1 | import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common'; 2 | import { HttpException } from '@nestjs/common'; 3 | 4 | @Catch(HttpException) 5 | export class HttpExceptionFilter implements ExceptionFilter { 6 | catch(exception: HttpException, host: ArgumentsHost) { 7 | const ctx = host.switchToHttp(); 8 | const response = ctx.getResponse(); 9 | const request = ctx.getRequest(); 10 | 11 | response.status(status).json({ 12 | statusCode: exception.getStatus(), 13 | timestamp: new Date().toISOString(), 14 | path: request.url, 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/common/guards/roles.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; 2 | import { Reflector } from '@nestjs/core'; 3 | 4 | @Injectable() 5 | export class RolesGuard implements CanActivate { 6 | constructor(private readonly reflector: Reflector) { } 7 | 8 | canActivate(context: ExecutionContext): boolean { 9 | const roles = this.reflector.get('roles', context.getHandler()); 10 | if (!roles) { 11 | return true; 12 | } 13 | const request = context.switchToHttp().getRequest(); 14 | const user = request.user; 15 | const hasRole = user.roles.some((role: string) => !!roles.find((item) => item === role)); 16 | return user && user.roles && hasRole(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/common/interceptors/exception.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallHandler, 3 | ExecutionContext, 4 | Injectable, 5 | NestInterceptor, 6 | HttpException, 7 | HttpStatus, 8 | } from '@nestjs/common'; 9 | import { Observable, throwError } from 'rxjs'; 10 | import { catchError } from 'rxjs/operators'; 11 | 12 | @Injectable() 13 | export class ErrorsInterceptor implements NestInterceptor { 14 | intercept(context: ExecutionContext, next: CallHandler): Observable { 15 | return next 16 | .handle() 17 | .pipe( 18 | catchError(err => 19 | throwError(new HttpException('Message', HttpStatus.BAD_GATEWAY)), 20 | ) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/common/interceptors/logging.interceptor.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { 4 | CallHandler, 5 | ExecutionContext, 6 | Injectable, 7 | NestInterceptor, 8 | } from '@nestjs/common'; 9 | import { Observable } from 'rxjs'; 10 | import { tap } from 'rxjs/operators'; 11 | 12 | @Injectable() 13 | export class LoggingInterceptor implements NestInterceptor { 14 | intercept(context: ExecutionContext, next: CallHandler): Observable { 15 | console.log('Before...'); 16 | 17 | const now = Date.now(); 18 | return next 19 | .handle() 20 | .pipe(tap(() => console.log(`After... ${Date.now() - now}ms`))); 21 | } 22 | } -------------------------------------------------------------------------------- /src/common/interceptors/timeout.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import { timeout } from 'rxjs/operators'; 4 | 5 | @Injectable() 6 | export class TimeoutInterceptor implements NestInterceptor { 7 | intercept(context: ExecutionContext, next: CallHandler): Observable { 8 | return next 9 | .handle() 10 | .pipe(timeout(5000)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/common/interceptors/transform.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | export interface Response { 6 | data: T; 7 | } 8 | 9 | @Injectable() 10 | export class TransformInterceptor 11 | implements NestInterceptor> { 12 | intercept(context: ExecutionContext, next: CallHandler): Observable { 13 | return next.handle().pipe(map(data => ({ data }))); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/common/interfaces/result.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ResultList { 2 | list: T[]; 3 | count?: number; 4 | query?: Query; 5 | } 6 | 7 | export interface Query { 8 | size: number; 9 | index: number; 10 | } -------------------------------------------------------------------------------- /src/common/middlewares/logger.middleware.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Injectable, NestMiddleware } from '@nestjs/common'; 3 | 4 | @Injectable() 5 | export class LoggerMiddleware implements NestMiddleware { 6 | use(req: any, res: any, next: () => void) { 7 | // console.log(`[${context}] Request...`); 8 | next(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/common/pipes/parse-int.pipe.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException } from '@nestjs/common'; 2 | import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common'; 3 | 4 | @Injectable() 5 | export class ParseIntPipe implements PipeTransform { 6 | async transform(value: string, metadata: ArgumentMetadata) { 7 | const val = parseInt(value, 10); 8 | if (isNaN(val)) { 9 | throw new BadRequestException('Validation failed'); 10 | } 11 | return val; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/common/pipes/validation.pipe.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException } from '@nestjs/common'; 2 | import { 3 | PipeTransform, 4 | Injectable, 5 | ArgumentMetadata, 6 | HttpStatus, 7 | } from '@nestjs/common'; 8 | import { validate } from 'class-validator'; 9 | import { plainToClass } from 'class-transformer'; 10 | 11 | @Injectable() 12 | export class ValidationPipe implements PipeTransform { 13 | async transform(value: any, metadata: ArgumentMetadata) { 14 | const { metatype } = metadata; 15 | if (!metatype || !this.toValidate(metatype)) { 16 | return value; 17 | } 18 | const object = plainToClass(metatype, value); 19 | const errors = await validate(object); 20 | if (errors.length > 0) { 21 | throw new BadRequestException('Validation failed'); 22 | } 23 | return value; 24 | } 25 | 26 | private toValidate(metatype: any): boolean { 27 | const types = [String, Boolean, Number, Array, Object]; 28 | return !types.find(type => metatype === type); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/common/services/controller.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Get, Param, Post, Body, Put, Delete, ParseIntPipe, Query } from "@nestjs/common"; 2 | import { Id, RepositoryService } from "./repository.service"; 3 | import { ResultList } from "../interfaces/result.interface"; 4 | import { ObjectID } from "typeorm"; 5 | 6 | @Injectable() 7 | export class ControllerService { 8 | 9 | constructor(private readonly service: RepositoryService) { } 10 | 11 | @Get(':size/:index') 12 | async findAll( 13 | @Param('index', new ParseIntPipe()) index: number = 1, 14 | @Param('size', new ParseIntPipe()) size: number = 10, 15 | @Query() query: any): Promise> { 16 | return await this.service.findAll(index, size, query); 17 | } 18 | 19 | @Get(':id') 20 | async findOne(@Param('id') id: string | number | Date | ObjectID): Promise { 21 | return await this.service.findOne(id); 22 | } 23 | 24 | @Post() 25 | async create(@Body() entity: any): Promise { 26 | return await this.service.create(entity); 27 | } 28 | 29 | @Put() 30 | async update(@Body() entity: any): Promise { 31 | return await this.service.update(entity); 32 | } 33 | 34 | @Delete(':id') 35 | async remove(@Param('id') id: string): Promise { 36 | return await this.service.remove(id); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/common/services/repository.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { Repository, ObjectID, getManager } from "typeorm"; 3 | import { ResultList } from "../interfaces/result.interface"; 4 | 5 | export interface Id { 6 | id: string | number | Date | ObjectID; 7 | } 8 | 9 | @Injectable() 10 | export class RepositoryService { 11 | 12 | constructor( 13 | private repository: Repository 14 | ) { } 15 | 16 | async findAll(index: number, size: number, query: any): Promise> { 17 | return new Promise>(async (x) => { 18 | let result: ResultList = { 19 | list: await this.repository.find({ skip: size * (index - 1), take: size }), 20 | count: await this.repository.count(), 21 | query: { 22 | index: index, 23 | size: size 24 | } 25 | } 26 | x(result); 27 | }) 28 | } 29 | 30 | async findOne(id: string | number | Date | ObjectID): Promise { 31 | return await this.repository.findOne(id); 32 | } 33 | 34 | async create(entity: any): Promise { 35 | return await this.repository.save(entity); 36 | } 37 | 38 | async update(entity: T): Promise { 39 | let index = await this.repository.findOne(entity.id); 40 | if (index) { 41 | Object.assign(index, entity); 42 | await getManager().transaction(async transactionalEntityManager => { 43 | await transactionalEntityManager.save(index); 44 | }) 45 | 46 | return index 47 | } 48 | } 49 | 50 | async remove(id: string | number | Date | ObjectID): Promise { 51 | let entity = await this.repository.findOne(id); 52 | return await this.repository.remove(entity); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/config/config.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigService } from './config.service'; 3 | 4 | @Module({ 5 | providers: [ 6 | { 7 | provide: ConfigService, 8 | useValue: new ConfigService('.env'), 9 | }, 10 | ], 11 | exports: [ConfigService], 12 | }) 13 | export class ConfigModule {} -------------------------------------------------------------------------------- /src/config/config.service.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | import * as fs from 'fs'; 3 | 4 | export class ConfigService { 5 | private readonly envConfig: { [key: string]: string }; 6 | 7 | constructor(filePath: string) { 8 | this.envConfig = dotenv.parse(fs.readFileSync(filePath)); 9 | } 10 | 11 | get(key: string): string { 12 | return this.envConfig[key]; 13 | } 14 | } -------------------------------------------------------------------------------- /src/config/enums.ts: -------------------------------------------------------------------------------- 1 | export enum RoleTypes { 2 | admin = 'admin', 3 | user = 'user', 4 | supplier = 'supplier', 5 | } -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config.module'; 2 | export * from './config.service'; -------------------------------------------------------------------------------- /src/core/controllers/dicts.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body, UseGuards, Param, Put, ParseIntPipe, Query } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | import { plainToClass } from 'class-transformer'; 4 | import { ResultList } from './../../common/interfaces/result.interface'; 5 | import { DictsService } from './dicts.service'; 6 | import { Dict } from './../interfaces/dict.interface'; 7 | import { CreateDictReq, EditDictReq, KeyValueDto } from './../dto'; 8 | 9 | @Controller('dicts') 10 | @UseGuards(AuthGuard('jwt')) 11 | export class DictsController { 12 | constructor(private readonly dictService: DictsService) { } 13 | 14 | @Post() 15 | async create(@Body() entry: CreateDictReq) { 16 | return this.dictService.create(plainToClass(CreateDictReq, entry)); 17 | } 18 | 19 | @Put() 20 | async update(@Body() entry: EditDictReq): Promise { 21 | return this.dictService.update(plainToClass(EditDictReq, entry)); 22 | } 23 | 24 | @Get('search') 25 | async search( 26 | @Query('keyword') keyword?: string, 27 | @Query('value') value?: string, 28 | ): Promise { 29 | return this.dictService.search(keyword, value); 30 | } 31 | 32 | @Get('query') 33 | async query( 34 | @Query('keyword') keyword?: string, 35 | @Query('index', new ParseIntPipe()) index: number = 1, 36 | @Query('size', new ParseIntPipe()) size: number = 10, 37 | ): Promise> { 38 | return this.dictService.query(index, size, { keyword }); 39 | } 40 | 41 | @Get(':id') 42 | async findOne(@Param('id') id: string): Promise { 43 | return this.dictService.findById(id); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/core/controllers/dicts.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { MongooseService } from './../mongoose.service'; 5 | import { DictModel } from './../interfaces'; 6 | 7 | @Injectable() 8 | export class DictsService extends MongooseService { 9 | 10 | defaultQueryFields = ['name', 'translate', 'expand']; 11 | constructor(@InjectModel('Dict') protected readonly model: Model) { 12 | super(model); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/core/controllers/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import { DictsController } from './dicts.controller'; 3 | import { UsersController } from './users.controller'; 4 | import { DictsService } from './dicts.service'; 5 | import { UsersService } from './users.service'; 6 | import { MenusController } from './menus.controller'; 7 | import { MenusService } from './menus.service'; 8 | import { LogsService } from './logs.service'; 9 | import { LogsController } from './logs.controller'; 10 | 11 | export const CoreControllers = [ 12 | DictsController, 13 | UsersController, 14 | MenusController, 15 | LogsController, 16 | ]; 17 | 18 | export const CoreServices = [ 19 | DictsService, 20 | UsersService, 21 | MenusService, 22 | LogsService, 23 | ]; -------------------------------------------------------------------------------- /src/core/controllers/logs.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, UseGuards, Param, ParseIntPipe, Query } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | import { ResultList } from './../../common/interfaces/result.interface'; 4 | import { LogsService } from './logs.service'; 5 | import { Log } from './../interfaces'; 6 | import { KeyValueDto } from './../dto'; 7 | 8 | @Controller('logs') 9 | @UseGuards(AuthGuard('jwt')) 10 | export class LogsController { 11 | constructor(private readonly logService: LogsService) { } 12 | 13 | @Get('search') 14 | async search( 15 | @Query('keyword') keyword?: string, 16 | @Query('value') value?: string, 17 | ): Promise { 18 | return this.logService.search(keyword, value); 19 | } 20 | 21 | @Get('query') 22 | async query( 23 | @Query('keyword') keyword?: string, 24 | @Query('index', new ParseIntPipe()) index: number = 1, 25 | @Query('size', new ParseIntPipe()) size: number = 10, 26 | ): Promise> { 27 | return this.logService.query(index, size, { keyword }); 28 | } 29 | 30 | @Get(':id') 31 | async findOne(@Param('id') id: string): Promise { 32 | return this.logService.findById(id); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/core/controllers/logs.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { MongooseService } from './../mongoose.service'; 5 | import { LogModel } from './../interfaces'; 6 | 7 | @Injectable() 8 | export class LogsService extends MongooseService { 9 | 10 | constructor(@InjectModel('Log') protected readonly model: Model) { 11 | super(model); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/core/controllers/menus.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body, UseGuards, Param, Put, ParseIntPipe, Query } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | import { plainToClass } from 'class-transformer'; 4 | import { ResultList } from './../../common/interfaces/result.interface'; 5 | import { MenusService } from './menus.service'; 6 | import { Menu } from './../interfaces/Menu.interface'; 7 | import { CreateMenuRes, EditMenuRes, KeyValueDto } from './../dto'; 8 | 9 | @Controller('menus') 10 | @UseGuards(AuthGuard('jwt')) 11 | export class MenusController { 12 | constructor(private readonly menuService: MenusService) { } 13 | 14 | @Post() 15 | async create(@Body() entry: CreateMenuRes) { 16 | return this.menuService.create(plainToClass(CreateMenuRes, entry)); 17 | } 18 | 19 | @Put() 20 | async update(@Body() entry: EditMenuRes): Promise { 21 | return this.menuService.update(plainToClass(EditMenuRes, entry)); 22 | } 23 | 24 | @Get('search') 25 | async search( 26 | @Query('keyword') keyword?: string, 27 | @Query('value') value?: string, 28 | ): Promise { 29 | return this.menuService.search(keyword, value); 30 | } 31 | 32 | @Get('query') 33 | async query( 34 | @Query('keyword') keyword?: string, 35 | @Query('index', new ParseIntPipe()) index: number = 1, 36 | @Query('size', new ParseIntPipe()) size: number = 10, 37 | ): Promise> { 38 | return this.menuService.query(index, size, { keyword }); 39 | } 40 | 41 | @Get(':id') 42 | async findOne(@Param('id') id: string): Promise { 43 | return this.menuService.findById(id); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/core/controllers/menus.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { MongooseService } from './../mongoose.service'; 5 | import { MenuModel } from './../interfaces'; 6 | 7 | @Injectable() 8 | export class MenusService extends MongooseService { 9 | 10 | constructor(@InjectModel('Menu') protected readonly model: Model) { 11 | super(model); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/core/controllers/users.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body, UseGuards, Param, Put, ParseIntPipe, Query, Req, HttpStatus } from '@nestjs/common'; 2 | import { UsersService } from './users.service'; 3 | import { User } from './../interfaces/user.interface'; 4 | import { AuthGuard } from '@nestjs/passport'; 5 | import { plainToClass } from 'class-transformer'; 6 | import { ResultList } from './../../common/interfaces/result.interface'; 7 | import { KeyValueDto, CreateUserReq, EditUserReq } from './../dto'; 8 | 9 | @Controller('users') 10 | @UseGuards(AuthGuard('jwt')) 11 | export class UsersController { 12 | constructor(private readonly usersService: UsersService) { } 13 | 14 | @Get('profile') 15 | async profile(@Req() request: any): Promise { 16 | const user = request.user; 17 | return this.usersService.findById(user._id); 18 | } 19 | 20 | @Post() 21 | async create(@Body() user: CreateUserReq) { 22 | return this.usersService.create(plainToClass(CreateUserReq, user)); 23 | } 24 | 25 | @Put() 26 | async update(@Body() user: EditUserReq): Promise { 27 | return this.usersService.update(plainToClass(EditUserReq, user)); 28 | } 29 | 30 | @Get('search') 31 | async search( 32 | @Query('keyword') keyword?: string, 33 | @Query('value') value?: string, 34 | ): Promise { 35 | return this.usersService.search(keyword, value); 36 | } 37 | 38 | @Get('query') 39 | async query( 40 | @Query('keyword') keyword?: string, 41 | @Query('index', new ParseIntPipe()) index: number = 1, 42 | @Query('size', new ParseIntPipe()) size: number = 10, 43 | ): Promise> { 44 | return this.usersService.query(index, size, { keyword }, 'name'); 45 | } 46 | 47 | @Get(':id') 48 | async findOne(@Param('id') id: string): Promise { 49 | return this.usersService.findById(id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/core/controllers/users.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { User, UserModel } from './../interfaces'; 5 | import { MongooseService } from './../mongoose.service'; 6 | 7 | @Injectable() 8 | export class UsersService extends MongooseService { 9 | 10 | defaultQueryFields = [ 11 | 'username', 12 | 'avatar', 13 | 'email', 14 | 'name', 15 | 'email', 16 | 'mobile', 17 | 'isAdmin', 18 | 'isApproved', 19 | 'expired', 20 | ]; 21 | 22 | constructor(@InjectModel('User') protected readonly model: Model) { 23 | super(model); 24 | } 25 | 26 | async login(account: string, password: string): Promise { 27 | const instance = await this.model.findOne({ username: account }); 28 | if (instance) { 29 | return new Promise((resolve, reject) => { 30 | instance.comparePassword(password, (err: Error, isMatch: boolean) => { 31 | if (err) { return reject(err); } 32 | if (isMatch) { 33 | resolve(instance); 34 | } 35 | resolve(false); 36 | }); 37 | }); 38 | } 39 | return false; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { PassportModule } from '@nestjs/passport'; 4 | import { UserSchema } from './schemas/user.schema'; 5 | import { DictSchema } from './schemas/dict.schema'; 6 | import { LogSchema } from './schemas/log.schema'; 7 | import { MenuSchema } from './schemas/menu.schema'; 8 | import { RoleSchema } from './schemas/role.schema'; 9 | import { SettingSchema } from './schemas/setting.schema'; 10 | import { CoreControllers, CoreServices } from './controllers'; 11 | 12 | const models = [ 13 | { name: 'Dict', schema: DictSchema }, 14 | { name: 'Log', schema: LogSchema }, 15 | { name: 'Menu', schema: MenuSchema }, 16 | { name: 'Role', schema: RoleSchema }, 17 | { name: 'Setting', schema: SettingSchema }, 18 | { name: 'User', schema: UserSchema }, 19 | ]; 20 | 21 | @Module({ 22 | imports: [ 23 | MongooseModule.forFeature(models), 24 | PassportModule.register({ defaultStrategy: 'jwt', session: false }) 25 | ], 26 | controllers: [...CoreControllers], 27 | providers: [...CoreServices], 28 | exports: [...CoreServices] 29 | }) 30 | export class CoreModule { } 31 | -------------------------------------------------------------------------------- /src/core/crypto/crypto.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CryptoService } from './crypto.service'; 3 | 4 | @Module({ 5 | providers: [CryptoService], 6 | exports: [CryptoService], 7 | }) 8 | export class CryptoModule { } 9 | -------------------------------------------------------------------------------- /src/core/crypto/crypto.service.ts: -------------------------------------------------------------------------------- 1 | import { createCipher, createDecipher } from 'crypto'; 2 | import { Injectable } from '@nestjs/common'; 3 | 4 | @Injectable() 5 | export class CryptoService { 6 | private readonly algorithm = 'aes-256-ctr'; 7 | private readonly key = 'nest-workshop'; 8 | 9 | hash(text: string): string { 10 | const cipher = createCipher(this.algorithm, this.key); 11 | return cipher.update(text, 'utf8', 'hex') + cipher.final('hex'); 12 | } 13 | 14 | compare(text: string, hash: string): boolean { 15 | const decipher = createDecipher(this.algorithm, this.key); 16 | const decoded = 17 | decipher.update(hash, 'hex', 'utf8') + decipher.final('utf8'); 18 | return decoded === text; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/core/dto/auth.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsJSON } from 'class-validator'; 2 | 3 | export class ChangePasswordReq { 4 | @IsString() 5 | readonly password: string; 6 | 7 | @IsString() 8 | readonly confirm: string; 9 | } 10 | 11 | export class ChangeRolesReq { 12 | 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/core/dto/common.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiModelProperty } from "@nestjs/swagger"; 2 | 3 | export class KeyValueDto { 4 | @ApiModelProperty() 5 | label: string; 6 | @ApiModelProperty() 7 | value: string; 8 | } -------------------------------------------------------------------------------- /src/core/dto/dict.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsJSON } from 'class-validator'; 2 | 3 | export class CreateDictReq { 4 | @IsString() 5 | category: string; 6 | 7 | @IsString() 8 | name: string; 9 | 10 | @IsString() 11 | translate: string; 12 | 13 | @IsJSON() 14 | expand: object; 15 | } 16 | 17 | export class EditDictReq { 18 | 19 | @IsString() 20 | id: string; 21 | 22 | @IsString() 23 | category: string; 24 | 25 | @IsString() 26 | name: string; 27 | 28 | @IsString() 29 | translate: string; 30 | 31 | @IsJSON() 32 | expand: object; 33 | } 34 | 35 | export class DictRes { 36 | @IsString() 37 | category: string; 38 | 39 | @IsString() 40 | name: string; 41 | 42 | @IsString() 43 | translate: string; 44 | 45 | @IsJSON() 46 | expand: object; 47 | } 48 | -------------------------------------------------------------------------------- /src/core/dto/group.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateGroupReq { 2 | outid?: number; 3 | name: string; 4 | icon?: string; 5 | parent?: string; 6 | paths?: any[]; 7 | director?: string 8 | order: number; 9 | isRegion?: boolean; 10 | description?: string; 11 | } 12 | 13 | export class EditGroupReq { 14 | id: string; 15 | outid?: number; 16 | name: string; 17 | icon?: string; 18 | parent?: string; 19 | paths?: any[]; 20 | director?: string 21 | order: number; 22 | isRegion?: boolean; 23 | description?: string; 24 | } 25 | 26 | export class GroupRes { 27 | id: string; 28 | outid?: number; 29 | name: string; 30 | icon?: string; 31 | parent?: string; 32 | paths?: any[]; 33 | director?: string 34 | order: number; 35 | isRegion?: boolean; 36 | description?: string; 37 | } -------------------------------------------------------------------------------- /src/core/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth.dto'; 2 | export * from './common.dto'; 3 | export * from './dict.dto'; 4 | export * from './group.dto'; 5 | export * from './menu.dto'; 6 | export * from './user.dto'; -------------------------------------------------------------------------------- /src/core/dto/menu.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsInt, IsJSON } from 'class-validator'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | 4 | export class EditMenuRes { 5 | name: string; // 菜单名称 6 | slug: string; // 标识串 7 | group: boolean; // 是否是分组 8 | link: string; // 菜单链接 9 | order: number; // 顺序 10 | externalLink: string; // 扩展链接 11 | blank: boolean; // 窗口打开方式 12 | icon: string; // 图标 13 | badge: string; // 14 | badgeDot: string; // 15 | badgeStatus: string; // 16 | enable: boolean; // 隐藏 17 | expanded: boolean; 18 | acl: string; // 访问控制 19 | paths: any[]; // 菜单路径 20 | parent: string; // 父级菜单 21 | permissions?: any[]; 22 | isMenu: boolean; // 是否是菜单 23 | } 24 | 25 | export class CreateMenuRes { 26 | name: string; // 菜单名称 27 | slug: string; // 标识串 28 | group: boolean; // 是否是分组 29 | link: string; // 菜单链接 30 | order: number; // 顺序 31 | externalLink: string; // 扩展链接 32 | blank: boolean; // 窗口打开方式 33 | icon: string; // 图标 34 | badge: string; // 35 | badgeDot: string; // 36 | badgeStatus: string; // 37 | enable: boolean; // 隐藏 38 | expanded: boolean; 39 | acl: string; // 访问控制 40 | paths: any[]; // 菜单路径 41 | parent: string; // 父级菜单 42 | permissions?: any[]; 43 | isMenu: boolean; // 是否是菜单 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/core/dto/user.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsInt, IsEmail, IsOptional } from 'class-validator'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | 4 | export class CreateUserReq { 5 | @IsString() 6 | username: string; 7 | 8 | @IsString() 9 | password: string; 10 | } 11 | 12 | export class EditUserReq { 13 | @IsString() 14 | name: string; 15 | 16 | @IsString() 17 | mobile: number; 18 | 19 | @IsEmail() 20 | @IsOptional() 21 | email?: string; 22 | company?: string; 23 | siteUrl?: string; 24 | address?: string; 25 | } 26 | -------------------------------------------------------------------------------- /src/core/interfaces/dict.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Dict { 4 | _id: string; 5 | name: string; 6 | category: string; 7 | translate: string; 8 | expand: any; 9 | } 10 | 11 | export type DictModel = Dict & Document; -------------------------------------------------------------------------------- /src/core/interfaces/group.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Group { 4 | _id: string; // 编号 5 | outid: number; // 外部编号 6 | name: string; // 名称 7 | icon: string; // 图标 8 | parent: string; // 父级分组编号 9 | paths: any[]; // 路径 10 | director: string // 分组 leader 11 | order: number; // 排序 12 | isRegion: boolean; // 是否大区 13 | description: string; // 描述 14 | } 15 | 16 | export type GroupModel = Group & Document; -------------------------------------------------------------------------------- /src/core/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dict.interface'; 2 | export * from './menu.interface'; 3 | export * from './role.interface'; 4 | export * from './setting.interface'; 5 | export * from './log.interface'; 6 | export * from './user.interface'; 7 | export * from './group.interface'; 8 | 9 | -------------------------------------------------------------------------------- /src/core/interfaces/log.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Log { 4 | id: string; 5 | name: string; // 日志名称 6 | operator: string; // 操作人 7 | operatorIp: string; // 操作人 IP 8 | operation: string; // 操作事件; 9 | comment: string; 10 | createdAt: Date; // 备注 11 | } 12 | 13 | export type LogModel = Log & Document; -------------------------------------------------------------------------------- /src/core/interfaces/menu.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Menu { 4 | _id: string; 5 | name: string; // 菜单名称 6 | slug: string; // 标识串 7 | group: boolean; // 是否是分组 8 | link: string; // 菜单链接 9 | order: number; // 顺序 10 | externalLink: string; // 扩展链接 11 | blank: boolean; // 窗口打开方式 12 | icon: string; // 图标 13 | badge: string; // 14 | badgeDot: string; // 15 | badgeStatus: string; // 16 | enable: boolean; // 隐藏 17 | expanded: boolean; 18 | acl: string; // 访问控制 19 | paths: any[]; // 菜单路径 20 | parent: string | Menu; // 父级菜单 21 | permissions?: any[], 22 | isMenu: boolean; // 是否是菜单 23 | } 24 | 25 | export type MenuModel = Menu & Document; -------------------------------------------------------------------------------- /src/core/interfaces/role.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Role { 4 | id: string; 5 | name: string; 6 | role: string; 7 | description: string; 8 | permissions: string[]; 9 | } 10 | 11 | export type RoleModel = Role & Document; -------------------------------------------------------------------------------- /src/core/interfaces/setting.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface Setting { 4 | id: string; 5 | name: string; // 设置项目组 6 | key: string; // 设置项键名 7 | value: any; // 设置值 8 | description: string; // 设置描述 9 | } 10 | 11 | export type SettingModel = Setting & Document; -------------------------------------------------------------------------------- /src/core/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface User { 4 | _id: string; 5 | username: string; 6 | password: string; 7 | name: string; // 姓名 8 | keyword: string; 9 | avatar: string; // 照片 10 | type: string; // 类型 11 | groups: string[]; 12 | roles: string[]; 13 | email: string; // 邮箱 14 | mobile: string; // 手机号码 15 | profile: any; 16 | isDisable: boolean; // 是否禁用 17 | isAdmin: boolean; // 是否管理员 18 | isApproved: boolean; // 是否审核 19 | secret: string; // 密保 20 | expired: Date; // 有效期 21 | comparePassword: (password: string, cb: any) => void; 22 | } 23 | 24 | export type UserModel = User & Document; -------------------------------------------------------------------------------- /src/core/mongoose.service.ts: -------------------------------------------------------------------------------- 1 | import { Model, Document, Types } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { ObjectID } from 'typeorm'; 4 | import { ResultList } from './../common/interfaces/result.interface'; 5 | 6 | export interface Id { 7 | _id: string | number | Date | ObjectID; 8 | } 9 | 10 | export interface Criteria { 11 | [key: string]: any; 12 | } 13 | 14 | @Injectable() 15 | export class MongooseService { 16 | 17 | defaultQueryFields: string[] = []; 18 | 19 | constructor( 20 | protected model: Model 21 | ) { } 22 | 23 | async create(entry: any): Promise { 24 | const instance = new this.model(entry); 25 | return await instance.save(); 26 | } 27 | 28 | async update(entry: any, fields: string[] = this.defaultQueryFields): Promise { 29 | const instance = await this.model.findOneAndUpdate( 30 | { _id: entry._id }, 31 | { $set: entry }, 32 | { upsert: true, fields: this.getFields(fields), 'new': true }).exec(); 33 | return instance; 34 | } 35 | 36 | async query(index: number = 1, size: number = 10, 37 | query: Criteria = {}, searchField = 'name', fields: string[] = this.defaultQueryFields, sort: Criteria | string = { _id: 1 } 38 | ): Promise> { 39 | 40 | const criteria: Criteria = {}; 41 | criteria[searchField] = new RegExp(query.keyword, 'i'); 42 | const condition = query.keyword ? criteria : {}; 43 | 44 | const selectFields: Criteria = this.getFields(fields); 45 | const listQuery = this.model.find(condition).select(selectFields).sort(sort); 46 | const collection = this.model.find(condition); 47 | 48 | return new Promise>(async (resolve) => { 49 | let result: ResultList = { 50 | list: await listQuery.limit(size).skip(size * (index - 1)).lean(), 51 | count: await collection.count(), 52 | query: { 53 | index: index, 54 | size: size 55 | } 56 | } 57 | resolve(result); 58 | }) 59 | } 60 | 61 | async search( 62 | keyword?: string, id?: string, 63 | category = '', limit: number = 10, labelField = 'name', valueField = '_id', searchField = 'name' 64 | ): Promise { 65 | 66 | const criteria: Criteria = {}; 67 | criteria[searchField] = new RegExp(keyword, 'i'); 68 | const query = keyword ? criteria : {}; 69 | 70 | if (category) { 71 | query.category = category; 72 | } 73 | 74 | const fields: Criteria = {}; 75 | fields[labelField] = 1; 76 | fields[valueField] = 1; 77 | 78 | const docs = await this.model.find(query).select(fields) 79 | .limit(limit) 80 | .exec() || []; 81 | 82 | if (id && (Types.ObjectId.isValid(id) || valueField !== '_id')) { 83 | const conditions: Criteria = {}; 84 | conditions[valueField] = id; 85 | const selected = await this.model.findOne(conditions).select(fields); 86 | if (selected) { 87 | const found = docs.findIndex((doc: Criteria) => doc[valueField] == id); 88 | if (found === -1) { 89 | docs.push(selected); 90 | } 91 | } 92 | } 93 | 94 | return docs.map((item: Criteria) => { 95 | const result = { 96 | label: item[labelField], 97 | value: item[valueField] 98 | }; 99 | return result; 100 | }); 101 | } 102 | 103 | async findOne(conditions?: any): Promise { 104 | return await this.model.findOne(conditions).exec(); 105 | } 106 | 107 | async findById(id: string | number | ObjectID): Promise { 108 | return await this.model.findById(id).exec(); 109 | } 110 | 111 | async remove(id: string | number | ObjectID): Promise { 112 | let entity = await this.model.findById(id); 113 | return await this.model.remove(entity); 114 | } 115 | 116 | private getFields(fields: string[]) { 117 | const selectFields: Criteria = {}; 118 | // selectFields._id = 0; 119 | fields.forEach(field => { 120 | selectFields[field] = 1; 121 | }); 122 | return selectFields; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/core/schemas/dict.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t } from 'mongoose'; 2 | export const DictSchema = new Schema({ 3 | category: { type: t.String }, 4 | name: { type: t.String }, 5 | translate: { type: t.String }, 6 | expand: { type: t.Mixed }, 7 | }, { 8 | timestamps: true, 9 | usePushEach: true 10 | }); -------------------------------------------------------------------------------- /src/core/schemas/group.schema.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Schema, SchemaTypes as t } from 'mongoose'; 3 | 4 | export const GroupSchema = new Schema({ 5 | outid: { type: t.Number }, 6 | name: { type: t.String }, 7 | icon: { type: t.String }, 8 | isRegion: { type: t.Boolean }, 9 | order: { type: t.Number }, 10 | parent: { type: t.ObjectId, ref: 'Group' }, 11 | paths: [{ type: t.ObjectId, ref: 'Group' }], 12 | director: { type: t.ObjectId, ref: 'User' }, 13 | description: { type: t.String } 14 | }, 15 | { timestamps: true }); -------------------------------------------------------------------------------- /src/core/schemas/index.ts: -------------------------------------------------------------------------------- 1 | export { RoleSchema } from './role.schema'; 2 | export { LogSchema } from './log.schema'; 3 | export { SettingSchema } from './setting.schema'; 4 | export { DictSchema } from './dict.schema'; 5 | export { MenuSchema } from './menu.schema'; 6 | export { GroupSchema } from './group.schema'; 7 | export { UserSchema } from './user.schema'; -------------------------------------------------------------------------------- /src/core/schemas/log.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const LogSchema = new Schema({ 6 | name: { 7 | type: t.String 8 | }, 9 | operator: { 10 | type: t.String 11 | }, 12 | operatorIp: { 13 | type: t.String 14 | }, 15 | operation: { 16 | type: t.String 17 | }, 18 | comment: { 19 | type: t.String 20 | } 21 | }, option); 22 | -------------------------------------------------------------------------------- /src/core/schemas/menu.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const MenuSchema = new Schema({ 6 | name: { type: t.String }, 7 | slug: { type: t.String }, 8 | group: { type: t.Boolean }, 9 | link: { type: t.String }, 10 | externalLink: { type: t.String }, 11 | blank: { type: t.Boolean }, 12 | icon: { type: t.String }, 13 | order: { type: t.Number, default: 100 }, 14 | enable: { type: t.Boolean }, 15 | expanded: { type: t.Boolean }, 16 | acl: { type: t.String }, 17 | paths: [{ 18 | type: t.ObjectId, 19 | ref: 'Menu' 20 | }], 21 | parent: { 22 | type: t.ObjectId, 23 | ref: 'Menu' 24 | }, 25 | permissions: [ 26 | { 27 | type: t.ObjectId, 28 | ref: 'Menu' 29 | } 30 | ], 31 | isMenu: { 32 | type: t.Boolean, 33 | default: true 34 | } 35 | }, option); 36 | -------------------------------------------------------------------------------- /src/core/schemas/role.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t, SchemaOptions } from 'mongoose'; 2 | const option: SchemaOptions = {}; 3 | option.timestamps = true; 4 | 5 | export const RoleSchema = new Schema({ 6 | name: { type: t.String }, 7 | description: { type: t.String }, 8 | permissions: [{ type: t.ObjectId, ref: 'Menu' }], 9 | }, option); 10 | -------------------------------------------------------------------------------- /src/core/schemas/setting.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, SchemaTypes as t } from 'mongoose'; 2 | export const SettingSchema = new Schema({ 3 | id: { 4 | type: t.String 5 | }, 6 | name: { 7 | type: t.String 8 | }, 9 | key: { 10 | type: t.String 11 | }, 12 | value: { 13 | type: t.Mixed 14 | }, 15 | description: { 16 | type: t.String 17 | } 18 | }, { 19 | timestamps: true, 20 | usePushEach: true, 21 | }); 22 | -------------------------------------------------------------------------------- /src/core/schemas/user.schema.ts: -------------------------------------------------------------------------------- 1 | import { Schema, Error, SchemaTypes as t } from 'mongoose'; 2 | import * as bcrypt from 'bcrypt'; 3 | 4 | export const UserSchema = new Schema({ 5 | username: { type: t.String, unique: true, required: true }, 6 | password: t.String, 7 | avatar: t.String, 8 | email: t.String, 9 | name: t.String, 10 | about: t.String, 11 | location: { 12 | country: t.String, 13 | province: t.String, 14 | district: t.String, 15 | address: t.String, 16 | }, 17 | type: t.String, 18 | mobile: t.String, 19 | roles: [{ 20 | type: t.ObjectId, ref: 'Role' 21 | }], 22 | isDisable: { 23 | type: t.Boolean 24 | }, 25 | isAdmin: { 26 | type: t.Boolean 27 | }, 28 | isApproved: { 29 | type: t.Boolean 30 | }, 31 | expired: { 32 | type: t.Boolean 33 | }, 34 | }, { 35 | timestamps: true, 36 | usePushEach: true, 37 | }); 38 | 39 | function preSave(next: Function) { 40 | const user = this; 41 | if (!user.isModified('password')) { return next(); } 42 | bcrypt.genSalt(10, (err: any, salt: any) => { 43 | if (err) { return next(err); } 44 | bcrypt.hash(user.password, salt, (err: Error, hash: string) => { 45 | if (err) { return next(err); } 46 | user.password = hash; 47 | next(); 48 | }); 49 | }); 50 | } 51 | 52 | function preUpdate(next: Function) { 53 | const updateDoc = this.getUpdate(); 54 | const rawPassword = (updateDoc.$set || updateDoc).password; 55 | if (rawPassword) { 56 | const password = bcrypt.hashSync(rawPassword, bcrypt.genSaltSync(10)); 57 | this.findOneAndUpdate({}, { password: password }); 58 | } 59 | next(); 60 | } 61 | 62 | UserSchema.pre('save', preSave); 63 | UserSchema.pre('findOneAndUpdate', preUpdate); 64 | UserSchema.methods.comparePassword = function (candidatePassword: string, cb: (err: any, isMatch: any) => {}) { 65 | bcrypt.compare(candidatePassword, this.password, (err: Error, isMatch: boolean) => { 66 | if (cb) { 67 | cb(err, isMatch); 68 | } 69 | }); 70 | }; 71 | 72 | UserSchema.methods.pure = function () { 73 | const obj = this.toJSON(); 74 | delete obj.password; 75 | return obj; 76 | } -------------------------------------------------------------------------------- /src/events.gateway.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OnGatewayConnection, 3 | OnGatewayInit, 4 | SubscribeMessage, 5 | WebSocketGateway, 6 | WsResponse 7 | } from '@nestjs/websockets'; 8 | 9 | @WebSocketGateway() 10 | export class EventsGateway implements OnGatewayInit, OnGatewayConnection { 11 | afterInit(server: any): any { 12 | return undefined; 13 | } 14 | 15 | handleConnection(client: any): any { 16 | return undefined; 17 | } 18 | 19 | @SubscribeMessage('events') 20 | onEvent(client: any, data: any): WsResponse { 21 | const event = 'events'; 22 | 23 | const response = { 24 | ping: data, 25 | pong: new Date() 26 | }; 27 | 28 | return { event, data: response }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { ValidationPipe } from '@nestjs/common'; 3 | import { setupSwagger } from './swagger'; 4 | import * as compression from 'compression'; 5 | // import * as rateLimit from 'express-rate-limit'; 6 | import * as helmet from 'helmet'; 7 | import { AppModule } from './app.module'; 8 | // import { Utils } from './utils/utils'; 9 | 10 | async function bootstrap() { 11 | // const httpsOptions = Utils.getKeyAndCert(); 12 | const app = await NestFactory.create( 13 | AppModule 14 | ); 15 | app.setGlobalPrefix('api'); // TODO 16 | setupSwagger(app); 17 | app.enableCors(); 18 | app.use(helmet()); 19 | app.useGlobalPipes(new ValidationPipe()); 20 | app.use(compression()); 21 | await app.listen(5600); 22 | } 23 | bootstrap(); 24 | -------------------------------------------------------------------------------- /src/scripts/connector.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | // import * as bluebird from 'bluebird'; 3 | 4 | export function connect(uri: string) { 5 | // (mongoose).Promise = bluebird; 6 | mongoose.connect(uri).then(() => { 7 | console.log('success connected to', uri); 8 | }); 9 | const db = mongoose.connection; 10 | db.on('error', (err: any) => { 11 | throw new Error('unable to connect to database at ' + uri + err); 12 | }); 13 | return db; 14 | } -------------------------------------------------------------------------------- /src/scripts/data.export.all.ts: -------------------------------------------------------------------------------- 1 | import { connect } from './connector'; 2 | import { connection, model, Document, Model } from 'mongoose'; 3 | 4 | connect('mongodb://localhost/nestx-server'); 5 | import { writeFileSync } from 'fs'; 6 | import { CoreDatabase as Db } from './database'; 7 | 8 | function save2File(file: string, data: object) { 9 | writeFileSync(`data/export.${file}.json`, JSON.stringify(data)); 10 | } 11 | 12 | async function exportData() { 13 | const users = await Db.User.find().exec(); 14 | save2File('users', users.map((item) => { 15 | return { 16 | _id: item._id, 17 | username: item.username, 18 | name: item.name, 19 | password: item.password, 20 | avatar: item.avatar, 21 | type: item.type, 22 | email: item.email, 23 | mobile: item.mobile, 24 | roles: item.roles, 25 | groups: item.groups, 26 | isDisable: item.isDisable, 27 | isAdmin: item.isAdmin, 28 | isApproved: item.isApproved, 29 | secret: item.secret, 30 | expired: item.expired 31 | } 32 | })); 33 | 34 | const dicts = await Db.Dict.find().exec(); 35 | save2File('dicts', dicts.map((item) => { 36 | return { 37 | _id: item._id, 38 | category: item.category, 39 | name: item.name, 40 | translate: item.translate, 41 | } 42 | })); 43 | 44 | const groups = await Db.Group.find().exec(); 45 | save2File('groups', groups.map((item) => { 46 | return { 47 | _id: item._id, 48 | outid: item.outid, 49 | name: item.name, 50 | icon: item.icon, 51 | isRegion: item.isRegion, 52 | order: item.order, 53 | parent: item.parent, 54 | paths: item.paths, 55 | director: item.director, 56 | description: item.description, 57 | } 58 | })); 59 | 60 | const roles = await Db.Role.find().exec(); 61 | save2File('roles', roles.map((item) => { 62 | return { 63 | _id: item._id, 64 | name: item.name, 65 | description: item.description, 66 | permissions: item.permissions, 67 | } 68 | })); 69 | 70 | const menus = await Db.Menu.find().exec(); 71 | save2File('menus', menus.map((item) => { 72 | return { 73 | _id: item._id, 74 | name: item.name, 75 | parent: item.parent, 76 | paths: item.paths, 77 | order: item.order, 78 | isMenu: item.isMenu, 79 | link: item.link, 80 | slug: item.slug, 81 | externalLink: item.externalLink, 82 | blank: item.blank, 83 | icon: item.icon, 84 | enable: item.enable, 85 | permissions: item.permissions 86 | } 87 | })); 88 | 89 | const settings = await Db.Setting.find().exec(); 90 | save2File('settings', settings.map((item) => { 91 | return { 92 | _id: item._id, 93 | name: item.name, 94 | key: item.key, 95 | value: item.value, 96 | description: item.description 97 | } 98 | })); 99 | 100 | console.log('export data done....'); 101 | } 102 | 103 | exportData() -------------------------------------------------------------------------------- /src/scripts/data.export.ts: -------------------------------------------------------------------------------- 1 | import { connect } from "./connector"; 2 | import { MONGODB_URI } from "./../utils/secrets"; 3 | import { connection, model, Document, Model } from 'mongoose'; 4 | 5 | connect(MONGODB_URI); 6 | import { writeFileSync } from 'fs'; 7 | import { CoreDatabase as Db } from "./database"; 8 | 9 | function save2File(file: string, data: object) { 10 | writeFileSync(`data/export.${file}.json`, JSON.stringify(data)); 11 | } 12 | 13 | async function exportData() { 14 | 15 | const accounts = await Db.User.find().exec(); 16 | save2File('accounts', accounts.map((item) => { 17 | return { 18 | _id: item._id, 19 | username: item.username, 20 | name: item.name, 21 | password: item.password, 22 | avatar: item.avatar, 23 | type: item.type, 24 | email: item.email, 25 | mobile: item.mobile, 26 | roles: item.roles, 27 | groups: item.groups, 28 | isDisable: item.isDisable, 29 | isAdmin: item.isAdmin, 30 | isApproved: item.isApproved, 31 | secret: item.secret, 32 | expired: item.expired 33 | } 34 | })); 35 | 36 | const dicts = await Db.Dict.find().exec(); 37 | save2File('dicts', dicts.map((item) => { 38 | return { 39 | _id: item._id, 40 | category: item.category, 41 | name: item.name, 42 | translate: item.translate, 43 | } 44 | })); 45 | 46 | const groups = await Db.Group.find().exec(); 47 | save2File('groups', groups.map((item) => { 48 | return { 49 | _id: item._id, 50 | outid: item.outid, 51 | name: item.name, 52 | icon: item.icon, 53 | isRegion: item.isRegion, 54 | order: item.order, 55 | parent: item.parent, 56 | paths: item.paths, 57 | director: item.director, 58 | description: item.description, 59 | } 60 | })); 61 | 62 | const roles = await Db.Role.find().exec(); 63 | save2File('roles', roles.map((item) => { 64 | return { 65 | _id: item._id, 66 | name: item.name, 67 | description: item.description, 68 | permissions: item.permissions, 69 | } 70 | })); 71 | 72 | const menus = await Db.Menu.find().exec(); 73 | save2File('menus', menus.map((item) => { 74 | return { 75 | _id: item._id, 76 | name: item.name, 77 | parent: item.parent, 78 | paths: item.paths, 79 | order: item.order, 80 | isMenu: item.isMenu, 81 | link: item.link, 82 | slug: item.slug, 83 | externalLink: item.externalLink, 84 | blank: item.blank, 85 | icon: item.icon, 86 | enable: item.enable, 87 | permissions: item.permissions 88 | } 89 | })); 90 | 91 | const settings = await Db.Setting.find().exec(); 92 | save2File('settings', settings.map((item) => { 93 | return { 94 | _id: item._id, 95 | name: item.name, 96 | key: item.key, 97 | value: item.value, 98 | description: item.description 99 | } 100 | })); 101 | 102 | console.log('export data done....'); 103 | } 104 | 105 | exportData() -------------------------------------------------------------------------------- /src/scripts/data.import.ts: -------------------------------------------------------------------------------- 1 | import { MONGODB_URI } from './../utils/secrets'; 2 | import { Installer } from "./data.install"; 3 | const installer = new Installer(MONGODB_URI); 4 | installer.initData().then(() => { 5 | console.log('imported ...'); 6 | }); 7 | -------------------------------------------------------------------------------- /src/scripts/data.install.ts: -------------------------------------------------------------------------------- 1 | 2 | import { CoreDatabase as Db } from './database'; 3 | import { existsSync } from 'fs'; 4 | import { resolve } from 'path'; 5 | import { connect } from './connector'; 6 | import { Connection } from "mongoose"; 7 | 8 | export class Installer { 9 | 10 | mongooseUri = ''; 11 | db: Connection; 12 | constructor(mongooseUri: string) { 13 | this.mongooseUri = mongooseUri; 14 | this.db = connect(mongooseUri); 15 | } 16 | 17 | private static loadJson(dataFolder: string, file: string) { 18 | const filePath = resolve(dataFolder, `data/export.${file}.json`); 19 | if (existsSync(filePath)) { 20 | return require(filePath); 21 | } 22 | return []; 23 | } 24 | 25 | async initData() { 26 | const dataFolder = process.cwd(); 27 | await Db.Role.insertMany(Installer.loadJson(dataFolder, 'roles')); 28 | await Db.Dict.insertMany(Installer.loadJson(dataFolder, 'dicts')); 29 | await Db.Menu.insertMany(Installer.loadJson(dataFolder, 'menus')); 30 | await Db.Setting.insertMany(Installer.loadJson(dataFolder, 'settings')); 31 | // await Db.Account.insertMany(Installer.loadJson(dataFolder, 'accounts')); 32 | } 33 | 34 | async drop() { 35 | this.db.dropDatabase(); 36 | this.db.close(); 37 | } 38 | 39 | 40 | } -------------------------------------------------------------------------------- /src/scripts/database.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SettingSchema, 3 | DictSchema, 4 | LogSchema, 5 | MenuSchema, 6 | RoleSchema, 7 | UserSchema, 8 | GroupSchema 9 | } from './../core/schemas'; 10 | import { model } from 'mongoose'; 11 | import { 12 | DictModel, 13 | LogModel, 14 | MenuModel, 15 | RoleModel, 16 | UserModel, 17 | SettingModel, 18 | GroupModel 19 | } from './../core/interfaces'; 20 | export const CoreDatabase = { 21 | Dict: model('Dict', DictSchema), 22 | Log: model('Log', LogSchema), 23 | Menu: model('Menu', MenuSchema), 24 | Role: model('Role', RoleSchema), 25 | User: model('User', UserSchema), 26 | Setting: model('Setting', SettingSchema), 27 | Group: model('Group', GroupSchema) 28 | }; 29 | -------------------------------------------------------------------------------- /src/scripts/generate.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './../app.module'; 3 | import { createDocument } from './../swagger'; 4 | import { writeFileSync } from 'fs'; 5 | 6 | async function generate() { 7 | const app = await NestFactory.create(AppModule); 8 | const document = createDocument(app); 9 | writeFileSync('swagger.api.json', JSON.stringify(document)); 10 | } 11 | 12 | generate(); 13 | -------------------------------------------------------------------------------- /src/swagger/constants.ts: -------------------------------------------------------------------------------- 1 | export const SWAGGER_API_ROOT = 'docs'; 2 | export const SWAGGER_API_NAME = 'nestx server'; 3 | export const SWAGGER_API_DESCRIPTION = 'nestx server is a simple REST API provider.'; 4 | export const SWAGGER_API_CURRENT_VERSION = '0.0.1'; 5 | export const SWAGGER_API_AUTH_NAME = 'Authorization'; 6 | export const SWAGGER_API_AUTH_LOCATION = 'header'; -------------------------------------------------------------------------------- /src/swagger/index.ts: -------------------------------------------------------------------------------- 1 | import { INestApplication } from '@nestjs/common'; 2 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 3 | import { 4 | SWAGGER_API_ROOT, 5 | SWAGGER_API_NAME, 6 | SWAGGER_API_DESCRIPTION, 7 | SWAGGER_API_CURRENT_VERSION, 8 | SWAGGER_API_AUTH_NAME, 9 | SWAGGER_API_AUTH_LOCATION, 10 | } from './constants'; 11 | 12 | export const setupSwagger = (app: INestApplication) => { 13 | const options = new DocumentBuilder() 14 | .setTitle(SWAGGER_API_NAME) 15 | .setDescription(SWAGGER_API_DESCRIPTION) 16 | .setBasePath('api') 17 | .setVersion(SWAGGER_API_CURRENT_VERSION) 18 | .addBearerAuth(SWAGGER_API_AUTH_NAME, SWAGGER_API_AUTH_LOCATION) 19 | .setSchemes('http') 20 | .build(); 21 | const document = SwaggerModule.createDocument(app, options); 22 | SwaggerModule.setup(SWAGGER_API_ROOT, app, document); 23 | }; 24 | 25 | export const createDocument = (app: INestApplication) => { 26 | const options = new DocumentBuilder() 27 | .setTitle(SWAGGER_API_NAME) 28 | .setDescription(SWAGGER_API_DESCRIPTION) 29 | .setBasePath('api') 30 | .setVersion(SWAGGER_API_CURRENT_VERSION) 31 | .addBearerAuth(SWAGGER_API_AUTH_NAME, SWAGGER_API_AUTH_LOCATION) 32 | .setSchemes('https') 33 | .build(); 34 | const document = SwaggerModule.createDocument(app, options); 35 | return document; 36 | }; 37 | -------------------------------------------------------------------------------- /src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import * as log4js from 'log4js'; 2 | import { join } from 'path'; 3 | import { existsSync } from 'fs'; 4 | const cwd = process.cwd(); 5 | const config = join(cwd, 'log4js.json'); 6 | 7 | if (existsSync(config)) { 8 | log4js.configure(config); 9 | } 10 | 11 | export const logger = log4js.getLogger('app'); -------------------------------------------------------------------------------- /src/utils/secrets.ts: -------------------------------------------------------------------------------- 1 | import { logger } from './logger'; 2 | import { existsSync } from 'fs'; 3 | import { config } from 'dotenv'; 4 | 5 | if (existsSync('.env')) { 6 | config({ path: '.env' }); 7 | } else if (existsSync('.env.example')) { 8 | config({ path: '.env.example' }); // you can delete this after you create your own .env file! 9 | } else { 10 | console.log('Using .env file to supply config environment variables'); 11 | logger.debug( 12 | 'Using .env file to supply config environment variables', 13 | ); 14 | } 15 | 16 | export const ENVIRONMENT = process.env.NODE_ENV; 17 | const prod = ENVIRONMENT === 'production'; // Anything else is treated as 'dev' 18 | export const SESSION_SECRET = process.env['SESSION_SECRET']; 19 | 20 | export const MONGODB_URI = prod 21 | ? process.env['MONGODB_URI'] 22 | : process.env['MONGODB_URI_LOCAL']; 23 | 24 | if (!SESSION_SECRET) { 25 | console.log('No client secret. Set SESSION_SECRET environment variable.'); 26 | logger.error('No client secret. Set SESSION_SECRET environment variable.'); 27 | process.exit(1); 28 | } 29 | 30 | if (!MONGODB_URI) { 31 | console.log('No mongo connection string. Set MONGODB_URI environment variable.'); 32 | logger.error( 33 | 'No mongo connection string. Set MONGODB_URI environment variable.', 34 | ); 35 | process.exit(1); 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | 3 | export class Utils { 4 | static getKeyAndCert(key: string = 'ssl_private_key.pem', cert: string = 'ssl_certificate.crt') { 5 | if (fs.existsSync(key) && fs.existsSync(cert)) 6 | 7 | return { 8 | key: fs.readFileSync(key), 9 | cert: fs.readFileSync(cert), 10 | } 11 | return {}; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /swagger.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": { 3 | "outputDirectory": "./dist", 4 | "entryFile": "./src/main.ts", 5 | "name": "rest api interface", 6 | "description": "项目标准接口", 7 | "license": "MIT", 8 | "produces": ["application/json"], 9 | "version": "0.0.1" 10 | } 11 | } -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "src/scripts", "**/*spec.ts"] 4 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": true, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "allowJs": true, 14 | "outDir": "./dist", 15 | "baseUrl": "./src" 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules/**/*", 22 | "dist/**/*", 23 | "**/*.spec.ts" 24 | ] 25 | } -------------------------------------------------------------------------------- /tsfmt.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseIndentSize": 0, 3 | "indentSize": 4, 4 | "tabSize": 4, 5 | "indentStyle": 2, 6 | "newLineCharacter": "\r\n", 7 | "convertTabsToSpaces": true, 8 | "insertSpaceAfterCommaDelimiter": true, 9 | "insertSpaceAfterSemicolonInForStatements": true, 10 | "insertSpaceBeforeAndAfterBinaryOperators": true, 11 | "insertSpaceAfterKeywordsInControlFlowStatements": true, 12 | "insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, 13 | "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false, 14 | "insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, 15 | "insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false, 16 | "noSpaceBeforeOpenParentInFuncDecl": false, 17 | "placeOpenBraceOnNewLineForFunctions": false, 18 | "placeOpenBraceOnNewLineForControlBlocks": false 19 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "extends": [ 6 | "tslint-sonarts" 7 | ], 8 | "rules": { 9 | "arrow-return-shorthand": true, 10 | "callable-types": true, 11 | "class-name": true, 12 | "comment-format": [ 13 | true, 14 | "check-space" 15 | ], 16 | "curly": true, 17 | "deprecation": { 18 | "severity": "warn" 19 | }, 20 | "eofline": true, 21 | "forin": true, 22 | "import-blacklist": [ 23 | true, 24 | "rxjs", 25 | "rxjs/Rx" 26 | ], 27 | "import-spacing": true, 28 | "indent": [ 29 | true, 30 | "spaces" 31 | ], 32 | "interface-over-type-literal": true, 33 | "label-position": true, 34 | "max-line-length": [ 35 | true, 36 | 140 37 | ], 38 | "member-access": false, 39 | "member-ordering": [ 40 | true, 41 | { 42 | "order": [ 43 | "static-field", 44 | "instance-field", 45 | "static-method", 46 | "instance-method" 47 | ] 48 | } 49 | ], 50 | "no-arg": true, 51 | "no-bitwise": true, 52 | "no-console": [ 53 | true, 54 | "debug", 55 | "info", 56 | "time", 57 | "timeEnd", 58 | "trace" 59 | ], 60 | "no-construct": true, 61 | "no-debugger": true, 62 | "no-duplicate-super": true, 63 | "no-empty": false, 64 | "no-empty-interface": true, 65 | "no-eval": true, 66 | "no-inferrable-types": [ 67 | true, 68 | "ignore-params" 69 | ], 70 | "no-misused-new": true, 71 | "no-non-null-assertion": true, 72 | "no-shadowed-variable": true, 73 | "no-string-literal": false, 74 | "no-string-throw": true, 75 | "no-switch-case-fall-through": true, 76 | "no-trailing-whitespace": true, 77 | "no-unnecessary-initializer": true, 78 | "no-unused-expression": true, 79 | "no-use-before-declare": true, 80 | "no-var-keyword": true, 81 | "object-literal-sort-keys": false, 82 | "one-line": [ 83 | true, 84 | "check-open-brace", 85 | "check-catch", 86 | "check-else", 87 | "check-whitespace" 88 | ], 89 | "prefer-const": true, 90 | "quotemark": [ 91 | true, 92 | "single" 93 | ], 94 | "radix": true, 95 | "semicolon": [ 96 | true, 97 | "always" 98 | ], 99 | "triple-equals": [ 100 | true, 101 | "allow-null-check" 102 | ], 103 | "typedef-whitespace": [ 104 | true, 105 | { 106 | "call-signature": "nospace", 107 | "index-signature": "nospace", 108 | "parameter": "nospace", 109 | "property-declaration": "nospace", 110 | "variable-declaration": "nospace" 111 | } 112 | ], 113 | "typeof-compare": true, 114 | "unified-signatures": true, 115 | "variable-name": false, 116 | "whitespace": [ 117 | true, 118 | "check-branch", 119 | "check-decl", 120 | "check-operator", 121 | "check-separator", 122 | "check-type" 123 | ], 124 | "directive-selector": [ 125 | true, 126 | "attribute", 127 | "app", 128 | "camelCase" 129 | ], 130 | "component-selector": [ 131 | true, 132 | "element", 133 | "app", 134 | "kebab-case" 135 | ], 136 | "no-output-on-prefix": true, 137 | "use-input-property-decorator": true, 138 | "use-output-property-decorator": true, 139 | "use-host-property-decorator": true, 140 | "no-input-rename": true, 141 | "no-output-rename": true, 142 | "use-life-cycle-interface": true, 143 | "use-pipe-transform-interface": true, 144 | "component-class-suffix": true, 145 | "directive-class-suffix": true 146 | } 147 | } --------------------------------------------------------------------------------