├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── README_CN.md ├── assets └── uploads │ └── .gitkeep ├── ecosystem.config.js ├── gulpfile.js ├── logs └── .gitkeep ├── package-lock.json ├── package.json ├── sql └── x-restful-api-generator-koa.sql ├── src ├── api.js ├── app.js ├── apps │ ├── Articles │ │ ├── Ctrl.js │ │ ├── Model.js │ │ └── Routers.js │ ├── Files │ │ ├── Ctrl.js │ │ ├── Model.js │ │ ├── Routers.js │ │ └── upload.html │ └── demoModule_001 │ │ ├── Ctrl.js │ │ ├── Model.js │ │ └── Routers.js ├── auth.js ├── config.js ├── db.js ├── middleware │ └── index.js ├── routers.js ├── schema │ └── articles.js ├── server.js └── utils │ ├── index.js │ └── log.js ├── static └── .gitkeep └── test └── appListen.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | assets/*.js 3 | src/schema/*.js 4 | src/apps/Files/*.html 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | // 指定ECMAScript支持的版本,6为ES6,这里为了兼容async和await,设置为8 5 | ecmaVersion: 8, 6 | sourceType: 'module' 7 | }, 8 | extends: 'standard', 9 | plugins: [ 10 | 'html', 11 | 'promise' 12 | ], 13 | env: { 14 | 'node': true 15 | }, 16 | rules: { 17 | // allow console 18 | 'no-console': 0, 19 | // allow paren-less arrow functions 20 | 'arrow-parens': 0, 21 | // allow async-await 22 | 'generator-star-spacing': 0, 23 | // allow debugger during development 24 | 'no-debugger': 0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | test/unit/coverage 5 | test/e2e/reports 6 | selenium-debug.log 7 | .idea/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - "7.6.0" 5 | install: 6 | - travis_wait npm install 7 | script: 8 | - npm run test 9 | - npm run cov 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-present, OXOYO 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # X-RESTful-API-Generator-Koa 2 | 3 | A RESTful API service generator by Koa. 4 | 5 | [ ![Travis CI Status](https://travis-ci.org/OXOYO/X-RESTful-API-Generator-Koa.svg?branch=master)](https://travis-ci.org/OXOYO/X-RESTful-API-Generator-Koa) 6 | [![codecov.io](https://codecov.io/github/OXOYO/X-RESTful-API-Generator-Koa/coverage.svg?branch=master)](https://codecov.io/github/OXOYO/X-RESTful-API-Generator-Koa?branch=master) 7 | [![node](https://img.shields.io/badge/node-v7.6.0+-blue.svg)](https://nodejs.org/) 8 | [![GitHub package version](https://img.shields.io/github/package-json/v/OXOYO/X-RESTful-API-Generator-Koa.svg)]() 9 | [![dependencies Status](https://david-dm.org/OXOYO/X-RESTful-API-Generator-Koa/status.svg)](https://david-dm.org/OXOYO/X-RESTful-API-Generator-Koa) 10 | [![codebeat badge](https://codebeat.co/badges/824b49d9-dd7f-4502-9965-76aef840f8d2)](https://codebeat.co/projects/github-com-oxoyo-x-restful-api-generator-koa-master) 11 | [![license](https://img.shields.io/github/license/OXOYO/X-RESTful-API-Generator-Koa.svg)]() 12 | [![Gitter](https://img.shields.io/gitter/room/X-RESTful-API-Generator-Koa/chat.svg)](https://gitter.im/X-RESTful-API-Generator-Koa/chat) 13 | 14 | English | [简体中文](./README_CN.md) 15 | 16 | ## Start 17 | ```bash 18 | # clone 19 | git clone 20 | 21 | # install dependencies 22 | npm i 23 | 24 | # import the src/x-restful-api-generator-koa.sql into the database. 25 | 26 | # start serve with hot reload 27 | npm run dev 28 | ``` 29 | Node.js >= 7.6.0 required. 30 | 31 | ## Develop Step 32 | 1.Edit config.js 33 | 34 | 2.Export the database to schema by [sequelize-auto](https://github.com/sequelize/sequelize-auto) 35 | 36 | ```bash 37 | npm -g install mysql 38 | sequelize-auto -o "./src/schema" -d x-restful-api-generator-koa -h localhost -u root -p 3306 -e mysql 39 | ``` 40 | 41 | 3.Create module directories and files 42 | ```bash 43 | apps 44 | \_ newModules 45 | Ctrl.js 46 | Model.js 47 | Routers.js 48 | ``` 49 | 50 | ## Build 51 | ```bash 52 | npm run build 53 | ``` 54 | 55 | ## production 56 | ```bash 57 | pm2 start ecosystem.config.js --name x-restful-api-generator-koa --env production 58 | ``` 59 | 60 | ## Demo 61 | 62 | To run the demo, you need to import the `./src/x-restful-api-generator-koa.sql` into Mysql 63 | 64 | ### demoModule_001 65 | URL: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/login 66 | 67 | Resonse: 68 | ```bash 69 | { 70 | "code": 200, 71 | "msg": "登录成功!", 72 | "data": { 73 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6InRlc3QiLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlhdCI6MTUwODkyMTY2OSwiZXhwIjoxNTA5MDA4MDY5fQ.2occtME3kLUDxntJXOz5e1dkspybGIVqbDPRgaE6lZA" 74 | } 75 | } 76 | ``` 77 | 78 | URL: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/list 79 | 80 | Response: 81 | ```bash 82 | { 83 | "code": 200, 84 | "msg": "查询列表成功!", 85 | "data": { 86 | "count": 300, 87 | "list": [] 88 | } 89 | } 90 | ``` 91 | 92 | URL: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/verify/list 93 | 94 | Response: 95 | ```bash 96 | { 97 | "code": 9999, 98 | "msg": "token无效!请重新登录!", 99 | "data": {} 100 | } 101 | ``` 102 | 103 | ### Articles 104 | 105 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/add 106 | 107 | Request Body: 108 | ```bash 109 | { 110 | title: 'myArticle_001' 111 | content: 'yyyyyyyyyyyyyyyyyyyyyyy' 112 | } 113 | ``` 114 | 115 | Response: 116 | ```bash 117 | { 118 | "code": 200, 119 | "msg": "添加文章成功!", 120 | "data": { 121 | "id": 3, 122 | "title": "myArticle_001", 123 | "content": "yyyyyyyyyyyyyyyyyyyyyyy", 124 | "updatedAt": "2017-10-27T07:52:21.745Z", 125 | "createdAt": "2017-10-27T07:52:21.745Z" 126 | } 127 | } 128 | ``` 129 | 130 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/update 131 | 132 | Request Body: 133 | ```bash 134 | { 135 | id: '3' 136 | content: 'xxxxxx' 137 | } 138 | ``` 139 | 140 | Response: 141 | ```bash 142 | { 143 | "code": 200, 144 | "msg": "编辑文章成功!", 145 | "data": { 146 | "id": 3, 147 | "title": "myArticle_003", 148 | "content": "xxxxxx", 149 | "createdAt": "2017-10-26T23:52:21.000Z", 150 | "updatedAt": "2017-10-27T00:08:55.000Z" 151 | } 152 | } 153 | ``` 154 | 155 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/remove 156 | 157 | Request Body: 158 | ```bash 159 | { 160 | id: '2' 161 | } 162 | ``` 163 | 164 | Response: 165 | ```bash 166 | { 167 | "code": 200, 168 | "msg": "删除文章成功!", 169 | "data": 1 170 | } 171 | ``` 172 | 173 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/list?pageSize=1¤tPage=1 174 | 175 | Response: 176 | ```bash 177 | { 178 | "code": 200, 179 | "msg": "查询账号列表成功!", 180 | "data": { 181 | "count": 2, 182 | "list": [ 183 | { 184 | "id": 1, 185 | "title": "myArticle_001", 186 | "content": "yyyyyyyyyyyyyyyyyyyyyyy", 187 | "createdAt": "2017-10-26T23:46:10.000Z", 188 | "updatedAt": "2017-10-26T23:46:10.000Z" 189 | } 190 | ] 191 | } 192 | } 193 | ``` 194 | 195 | ### File Upload 196 | URL: http://localhost:63342/X-RESTful-API-Generator-Koa/src/apps/Files/upload.html 197 | 198 | Response: 199 | ```bash 200 | { 201 | "status": 200, 202 | "msg": "上传成功!", 203 | "data": { 204 | "file": { 205 | "fieldname": "file", 206 | "originalname": "app.png", 207 | "encoding": "7bit", 208 | "mimetype": "image/png", 209 | "destination": "E:\\Webstorm_WorkSpace\\X-RESTful-API-Generator-Koa\\assets\\uploads", 210 | "filename": "1510817484098.png", 211 | "path": "E:\\Webstorm_WorkSpace\\X-RESTful-API-Generator-Koa\\assets\\uploads\\1510817484098.png", 212 | "size": 958 213 | }, 214 | "filename": "1510817484098.png", 215 | "url": "//localhost:3000/assets/uploads/1510817484098.png" 216 | } 217 | } 218 | ``` 219 | 220 | ## TODO 221 | 222 | ```bash 223 | 1.error handler middleware 224 | ``` 225 | 226 | ## License 227 | [MIT](http://opensource.org/licenses/MIT) 228 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # X-RESTful-API-Generator-Koa 2 | 3 | 一个基于 Koa 的 RESTful API 服务脚手架。 4 | 5 | [ ![Travis CI Status](https://travis-ci.org/OXOYO/X-RESTful-API-Generator-Koa.svg?branch=master)](https://travis-ci.org/OXOYO/X-RESTful-API-Generator-Koa) 6 | [![codecov.io](https://codecov.io/github/OXOYO/X-RESTful-API-Generator-Koa/coverage.svg?branch=master)](https://codecov.io/github/OXOYO/X-RESTful-API-Generator-Koa?branch=master) 7 | [![node](https://img.shields.io/badge/node-v7.6.0+-blue.svg)](https://nodejs.org/) 8 | [![GitHub package version](https://img.shields.io/github/package-json/v/OXOYO/X-RESTful-API-Generator-Koa.svg)]() 9 | [![dependencies Status](https://david-dm.org/OXOYO/X-RESTful-API-Generator-Koa/status.svg)](https://david-dm.org/OXOYO/X-RESTful-API-Generator-Koa) 10 | [![codebeat badge](https://codebeat.co/badges/824b49d9-dd7f-4502-9965-76aef840f8d2)](https://codebeat.co/projects/github-com-oxoyo-x-restful-api-generator-koa-master) 11 | [![license](https://img.shields.io/github/license/OXOYO/X-RESTful-API-Generator-Koa.svg)]() 12 | [![Gitter](https://img.shields.io/gitter/room/X-RESTful-API-Generator-Koa/chat.svg)](https://gitter.im/X-RESTful-API-Generator-Koa/chat) 13 | 14 | [English](./README.md) | 简体中文 15 | 16 | ## 开始 17 | ```bash 18 | # clone 19 | git clone 20 | 21 | # install dependencies 22 | npm i 23 | 24 | # import the src/x-restful-api-generator-koa.sql into the database. 25 | 26 | # start serve with hot reload 27 | npm run dev 28 | ``` 29 | Node.js 版本需 >= 7.6.0. 30 | 31 | ## 开发步骤 32 | 1.编辑 config.js 33 | 34 | 2.使用 [sequelize-auto](https://github.com/sequelize/sequelize-auto) 将数据库导出为 schema 模型 35 | 36 | ```bash 37 | npm -g install mysql 38 | sequelize-auto -o "./src/schema" -d x-restful-api-generator-koa -h localhost -u root -p 3306 -e mysql 39 | ``` 40 | 41 | 3.创建模块目录、文件 42 | ```bash 43 | apps 44 | \_ newModules 45 | Ctrl.js 46 | Model.js 47 | Routers.js 48 | ``` 49 | 50 | ## 打包 51 | ```bash 52 | npm run build 53 | ``` 54 | 55 | ## 生产环境 56 | ```bash 57 | pm2 start ecosystem.config.js --name x-restful-api-generator-koa --env production 58 | ``` 59 | 60 | ## 栗子🌰 61 | 62 | 运行 demo 需要先将 `./src/x-restful-api-generator-koa.sql` 导入Mysql 63 | 64 | ### demoModule_001 65 | URL: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/login 66 | 67 | Resonse: 68 | ```bash 69 | { 70 | "code": 200, 71 | "msg": "登录成功!", 72 | "data": { 73 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6InRlc3QiLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlhdCI6MTUwODkyMTY2OSwiZXhwIjoxNTA5MDA4MDY5fQ.2occtME3kLUDxntJXOz5e1dkspybGIVqbDPRgaE6lZA" 74 | } 75 | } 76 | ``` 77 | 78 | URL: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/list 79 | 80 | Response: 81 | ```bash 82 | { 83 | "code": 200, 84 | "msg": "查询列表成功!", 85 | "data": { 86 | "count": 300, 87 | "list": [] 88 | } 89 | } 90 | ``` 91 | 92 | URL: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/verify/list 93 | 94 | Response: 95 | ```bash 96 | { 97 | "code": 9999, 98 | "msg": "token无效!请重新登录!", 99 | "data": {} 100 | } 101 | ``` 102 | 103 | ### Articles 104 | 105 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/add 106 | 107 | Request Body: 108 | ```bash 109 | { 110 | title: 'myArticle_001' 111 | content: 'yyyyyyyyyyyyyyyyyyyyyyy' 112 | } 113 | ``` 114 | 115 | Response: 116 | ```bash 117 | { 118 | "code": 200, 119 | "msg": "添加文章成功!", 120 | "data": { 121 | "id": 3, 122 | "title": "myArticle_001", 123 | "content": "yyyyyyyyyyyyyyyyyyyyyyy", 124 | "updatedAt": "2017-10-27T07:52:21.745Z", 125 | "createdAt": "2017-10-27T07:52:21.745Z" 126 | } 127 | } 128 | ``` 129 | 130 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/update 131 | 132 | Request Body: 133 | ```bash 134 | { 135 | id: '3' 136 | content: 'xxxxxx' 137 | } 138 | ``` 139 | 140 | Response: 141 | ```bash 142 | { 143 | "code": 200, 144 | "msg": "编辑文章成功!", 145 | "data": { 146 | "id": 3, 147 | "title": "myArticle_003", 148 | "content": "xxxxxx", 149 | "createdAt": "2017-10-26T23:52:21.000Z", 150 | "updatedAt": "2017-10-27T00:08:55.000Z" 151 | } 152 | } 153 | ``` 154 | 155 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/remove 156 | 157 | Request Body: 158 | ```bash 159 | { 160 | id: '2' 161 | } 162 | ``` 163 | 164 | Response: 165 | ```bash 166 | { 167 | "code": 200, 168 | "msg": "删除文章成功!", 169 | "data": 1 170 | } 171 | ``` 172 | 173 | URL: http://localhost:3000/x-restful-api-generator-koa/articles/list?pageSize=1¤tPage=1 174 | 175 | Response: 176 | ```bash 177 | { 178 | "code": 200, 179 | "msg": "查询账号列表成功!", 180 | "data": { 181 | "count": 2, 182 | "list": [ 183 | { 184 | "id": 1, 185 | "title": "myArticle_001", 186 | "content": "yyyyyyyyyyyyyyyyyyyyyyy", 187 | "createdAt": "2017-10-26T23:46:10.000Z", 188 | "updatedAt": "2017-10-26T23:46:10.000Z" 189 | } 190 | ] 191 | } 192 | } 193 | ``` 194 | 195 | ### File Upload 196 | URL: http://localhost:63342/X-RESTful-API-Generator-Koa/src/apps/Files/upload.html 197 | 198 | Response: 199 | ```bash 200 | { 201 | "status": 200, 202 | "msg": "上传成功!", 203 | "data": { 204 | "file": { 205 | "fieldname": "file", 206 | "originalname": "app.png", 207 | "encoding": "7bit", 208 | "mimetype": "image/png", 209 | "destination": "E:\\Webstorm_WorkSpace\\X-RESTful-API-Generator-Koa\\assets\\uploads", 210 | "filename": "1510817484098.png", 211 | "path": "E:\\Webstorm_WorkSpace\\X-RESTful-API-Generator-Koa\\assets\\uploads\\1510817484098.png", 212 | "size": 958 213 | }, 214 | "filename": "1510817484098.png", 215 | "url": "//localhost:3000/assets/uploads/1510817484098.png" 216 | } 217 | } 218 | ``` 219 | 220 | ## TODO 221 | 222 | ```bash 223 | 1.error handler middleware 224 | ``` 225 | 226 | ## License 227 | [MIT](http://opensource.org/licenses/MIT) 228 | -------------------------------------------------------------------------------- /assets/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OXOYO/X-RESTful-API-Generator-Koa/ac4715102d21702d3fc7fac254fc4a8008306313/assets/uploads/.gitkeep -------------------------------------------------------------------------------- /ecosystem.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/31. 3 | */ 4 | 5 | module.exports = { 6 | apps : [ 7 | // 应用配置 8 | { 9 | name: 'x-restful-api-generator-koa', 10 | script: './dist/server.js', 11 | watch: true, 12 | env: { 13 | NODE_ENV: 'development' 14 | }, 15 | env_production : { 16 | NODE_ENV: 'production' 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/25. 3 | */ 4 | 5 | const gulp = require('gulp') 6 | const gulpEslint = require('gulp-eslint') 7 | const gulpNodemon = require('gulp-nodemon') 8 | const gulpSequence = require('gulp-sequence') 9 | const eslintFriendlyFormatter = require('eslint-friendly-formatter') 10 | 11 | const config = { 12 | server: { 13 | script: './src/server.js' 14 | } 15 | } 16 | 17 | const lintFiles = (files) => { 18 | return gulp.src( 19 | files 20 | ).pipe( 21 | gulpEslint({ 22 | configFile: './.eslintrc.js' 23 | }) 24 | ).pipe( 25 | gulpEslint.format(eslintFriendlyFormatter) 26 | ).pipe( 27 | gulpEslint.result(results => { 28 | // Called for each ESLint result. 29 | console.log(`ESLint result: ${results.filePath}`) 30 | console.log(`# Messages: ${results.messages.length}`) 31 | console.log(`# Warnings: ${results.warningCount}`) 32 | console.log(`# Errors: ${results.errorCount}`) 33 | }) 34 | ).pipe( 35 | gulpEslint.results(results => { 36 | // Called once for all ESLint results. 37 | console.log(`Total Results: ${results.length}`) 38 | console.log(`Total Warnings: ${results.warningCount}`) 39 | console.log(`Total Errors: ${results.errorCount}`) 40 | }) 41 | ) 42 | } 43 | 44 | // eslint 45 | gulp.task('ESlint', () => { 46 | lintFiles([ 47 | 'src/**', 48 | '!node_modules/**' 49 | ]) 50 | }) 51 | 52 | // nodemon 53 | gulp.task('nodemon', () => { 54 | let stream = gulpNodemon({ 55 | script: config.server.script, 56 | ext: 'js', 57 | env: { 58 | 'NODE_ENV': 'development' 59 | }, 60 | tasks: (changedFiles) => { 61 | lintFiles(changedFiles) 62 | return [] 63 | } 64 | }) 65 | stream.on('restart', () => { 66 | console.log('Service restarted!') 67 | }).on('crash', () => { 68 | console.error('Service has crashed!\n') 69 | // restart the server in 10 seconds 70 | // stream.emit('restart', 10) 71 | }) 72 | }) 73 | 74 | // default 75 | gulp.task('default', () => { 76 | gulpSequence('ESlint', 'nodemon')(() => { 77 | console.log('Service started!') 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OXOYO/X-RESTful-API-Generator-Koa/ac4715102d21702d3fc7fac254fc4a8008306313/logs/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-restful-api-generator-koa", 3 | "version": "1.0.1", 4 | "description": "A RESTful API service generator by Koa.", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "npm run build && mocha -u bdd", 8 | "cov": "istanbul cover node_modules/mocha/bin/_mocha && codecov", 9 | "dev": "gulp default", 10 | "start": "gulp default", 11 | "build": "babel src -d dist" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/OXOYO/X-RESTful-API-Generator-Koa.git" 16 | }, 17 | "author": "OXOYO", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/OXOYO/X-RESTful-API-Generator-Koa/issues" 21 | }, 22 | "homepage": "https://github.com/OXOYO/X-RESTful-API-Generator-Koa#readme", 23 | "dependencies": { 24 | "codecov": "^3.0.0", 25 | "jsonwebtoken": "^8.1.0", 26 | "koa": "^2.3.0", 27 | "koa-bodyparser": "^4.2.0", 28 | "koa-compose": "^4.0.0", 29 | "koa-convert": "^1.2.0", 30 | "koa-cors": "0.0.16", 31 | "koa-generic-session": "^2.0.0", 32 | "koa-helmet": "^3.2.0", 33 | "koa-logger": "^3.1.0", 34 | "koa-multer": "^1.0.2", 35 | "koa-qs": "^2.0.0", 36 | "koa-router": "^7.2.1", 37 | "koa-static": "^4.0.1", 38 | "log4js": "^2.3.11", 39 | "mysql2": "^1.4.2", 40 | "require-directory": "^2.1.1", 41 | "sequelize": "^4.18.0", 42 | "sequelize-auto": "^0.4.29" 43 | }, 44 | "devDependencies": { 45 | "babel-cli": "^6.26.0", 46 | "babel-plugin-transform-runtime": "^6.23.0", 47 | "babel-polyfill": "^6.26.0", 48 | "babel-preset-es2015": "^6.24.1", 49 | "babel-preset-stage-2": "^6.24.1", 50 | "babel-register": "^6.26.0", 51 | "babel-runtime": "^6.26.0", 52 | "eslint-config-standard": "^10.2.1", 53 | "eslint-friendly-formatter": "^3.0.0", 54 | "eslint-plugin-html": "^3.2.2", 55 | "eslint-plugin-import": "^2.8.0", 56 | "eslint-plugin-node": "^5.2.1", 57 | "eslint-plugin-promise": "^3.6.0", 58 | "eslint-plugin-standard": "^3.0.1", 59 | "gulp": "^3.9.1", 60 | "gulp-eslint": "^4.0.0", 61 | "gulp-nodemon": "^2.2.1", 62 | "gulp-sequence": "^0.4.6", 63 | "istanbul": "^0.4.5", 64 | "mocha": "^4.0.1", 65 | "nodemon": "^1.12.1", 66 | "supertest": "^3.0.0" 67 | }, 68 | "engines": { 69 | "node": ">= 7.6.0", 70 | "npm": ">= 4.1.2" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /sql/x-restful-api-generator-koa.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.6.4 3 | -- https://www.phpmyadmin.net/ 4 | -- 5 | -- Host: 127.0.0.1 6 | -- Generation Time: 2017-10-27 16:24:41 7 | -- 服务器版本: 5.7.14 8 | -- PHP Version: 5.6.25 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8mb4 */; 18 | 19 | -- 20 | -- Database: `x-restful-api-generator-koa` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- 表的结构 `articles` 27 | -- 28 | 29 | CREATE TABLE `articles` ( 30 | `id` int(11) NOT NULL, 31 | `title` varchar(100) NOT NULL, 32 | `content` varchar(600) NOT NULL, 33 | `createdAt` timestamp NOT NULL, 34 | `updatedAt` timestamp NOT NULL 35 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 36 | 37 | -- 38 | -- 转存表中的数据 `articles` 39 | -- 40 | 41 | INSERT INTO `articles` (`id`, `title`, `content`, `createdAt`, `updatedAt`) VALUES 42 | (1, 'myArticle_001', 'yyyyyyyyyyyyyyyyyyyyyyy', '2017-10-27 07:46:10', '2017-10-27 07:46:10'), 43 | (3, 'myArticle_003', 'xxxxxx', '2017-10-27 07:52:21', '2017-10-27 08:08:55'); 44 | 45 | -- 46 | -- Indexes for dumped tables 47 | -- 48 | 49 | -- 50 | -- Indexes for table `articles` 51 | -- 52 | ALTER TABLE `articles` 53 | ADD PRIMARY KEY (`id`); 54 | 55 | -- 56 | -- 在导出的表使用AUTO_INCREMENT 57 | -- 58 | 59 | -- 60 | -- 使用表AUTO_INCREMENT `articles` 61 | -- 62 | ALTER TABLE `articles` 63 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; 64 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 65 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 66 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 67 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import compose from 'koa-compose' 6 | import Router from 'koa-router' 7 | 8 | // 导入配置信息 9 | import { Api as ApiConfig } from './config' 10 | // 导入路由表 11 | import routers from './routers' 12 | 13 | export default function api () { 14 | let router = new Router({ prefix: ApiConfig.prefix }) 15 | Object.keys(routers).forEach(name => routers[name](router)) 16 | // 添加根路由 17 | router.get('/', async (ctx, next) => { 18 | await next() 19 | ctx.status = 200 20 | ctx.body = {} 21 | }) 22 | 23 | return compose([ 24 | router.routes(), 25 | router.allowedMethods({ 26 | throw: true 27 | }) 28 | ]) 29 | } 30 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import Koa from 'koa' 6 | 7 | import middleware from './middleware' 8 | import utils from './utils' 9 | import api from './api' 10 | 11 | const app = new Koa() 12 | const env = process.env.NODE_ENV || 'development' 13 | 14 | // 注册log 15 | app.use(async (ctx, next) => { 16 | let startTime = new Date() 17 | let ms 18 | try { 19 | await next() 20 | ms = new Date() - startTime 21 | utils.log.response(ctx, ms) 22 | } catch (err) { 23 | ms = new Date() - startTime 24 | utils.log.error(ctx, err, ms) 25 | } 26 | }) 27 | 28 | // 注册qs 29 | require('koa-qs')(app) 30 | // 注册路由 31 | app.use(api()) 32 | // 注册中间件 33 | app.use(middleware(app)) 34 | 35 | if (env === 'development') { 36 | // logger 37 | app.use((ctx, next) => { 38 | const start = new Date() 39 | return next().then(() => { 40 | const ms = new Date() - start 41 | console.log(`${ctx.method} ${ctx.url} - ${ms}ms`) 42 | }) 43 | }) 44 | } 45 | 46 | app.on('error', function (err, ctx) { 47 | console.log('Service error', err) 48 | }) 49 | 50 | exports = module.exports = app 51 | -------------------------------------------------------------------------------- /src/apps/Articles/Ctrl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/27. 3 | */ 4 | 5 | import Model from './Model' 6 | 7 | export default { 8 | getList: async (ctx, next) => { 9 | await next() 10 | let reqQuery = ctx.query 11 | // 查询结果 12 | let res = await Model.getList(reqQuery) 13 | // 处理结果 14 | if (res) { 15 | res = { 16 | code: 200, 17 | msg: '查询账号列表成功!', 18 | data: { 19 | count: res.count, 20 | list: res.rows 21 | } 22 | } 23 | } else { 24 | res = { 25 | code: 5000, 26 | msg: '查询账号列表失败!', 27 | data: {} 28 | } 29 | } 30 | 31 | ctx.body = res || {} 32 | }, 33 | // 添加账号 34 | doAdd: async (ctx, next) => { 35 | await next() 36 | // 查询结果 37 | let reqBody = ctx.request.body 38 | let timeNow = new Date() 39 | let data = { 40 | ...reqBody, 41 | createdAt: timeNow, 42 | updatedAt: timeNow 43 | } 44 | let res 45 | if (data && data.title && data.content) { 46 | res = await Model.doAdd(data) 47 | // 最后一项为插入成功与否标识 48 | let [resAccount] = res 49 | let isSuccess = res.pop() 50 | // 处理结果 51 | if (isSuccess) { 52 | res = { 53 | code: 200, 54 | msg: '添加文章成功!', 55 | data: resAccount 56 | } 57 | } else if (resAccount) { 58 | res = { 59 | code: 5000, 60 | msg: '添加文章失败,该文章已存在!', 61 | data: resAccount 62 | } 63 | } else { 64 | res = { 65 | code: 5000, 66 | msg: '添加文章失败!', 67 | data: {} 68 | } 69 | } 70 | } else { 71 | res = { 72 | code: 5001, 73 | msg: '添加文章失败,上送参数有误!', 74 | data: {} 75 | } 76 | } 77 | 78 | ctx.body = res || {} 79 | }, 80 | doUpdate: async (ctx, next) => { 81 | await next() 82 | let reqBody = ctx.request.body 83 | let timeNow = new Date() 84 | let data = { 85 | ...reqBody, 86 | updatedAt: timeNow 87 | } 88 | let res 89 | if (data) { 90 | res = await Model.doUpdate(data) 91 | // 处理结果 92 | if (res) { 93 | let detail = await Model.getDetail(data) 94 | res = { 95 | code: 200, 96 | msg: '编辑文章成功!', 97 | data: detail 98 | } 99 | } else { 100 | res = { 101 | code: 5000, 102 | msg: '编辑文章失败!', 103 | data: {} 104 | } 105 | } 106 | } else { 107 | res = { 108 | code: 5001, 109 | msg: '编辑文章失败,上送参数有误!', 110 | data: {} 111 | } 112 | } 113 | 114 | ctx.body = res || {} 115 | }, 116 | doRemove: async (ctx, next) => { 117 | await next() 118 | let reqBody = ctx.request.body 119 | let data = reqBody 120 | let res 121 | if ((Object.keys(data)).length) { 122 | res = await Model.doRemove(data) 123 | // 处理结果 124 | if (res) { 125 | res = { 126 | code: 200, 127 | msg: '删除文章成功!', 128 | data: res 129 | } 130 | } else { 131 | res = { 132 | code: 5000, 133 | msg: '删除文章失败!', 134 | data: {} 135 | } 136 | } 137 | } else { 138 | res = { 139 | code: 5001, 140 | msg: '删除文章失败,上送参数有误!', 141 | data: {} 142 | } 143 | } 144 | 145 | ctx.body = res || {} 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/apps/Articles/Model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/27. 3 | */ 4 | 5 | import db from '../../db' 6 | 7 | const articlesModel = '../../schema/articles' 8 | const articlesSchema = db.import(articlesModel) 9 | 10 | export default { 11 | getList: async (data) => { 12 | let options = {} 13 | // 处理分页 14 | let pageSize = data.pageSize || 10 15 | let currentPage = data.currentPage || 1 16 | options['limit'] = parseInt(pageSize) 17 | options['offset'] = parseInt((currentPage - 1) * pageSize) 18 | // 拼装where条件 19 | let whereObj = {} 20 | // 处理关键词过滤 21 | let filterType = data.filterType || null 22 | if (filterType && data.keywords) { 23 | // 模糊匹配 24 | whereObj[filterType] = { 25 | $like: '%' + data.keywords + '%' 26 | } 27 | } 28 | if ((Object.keys(whereObj)).length) { 29 | options['where'] = whereObj 30 | } 31 | let res = await articlesSchema.findAndCountAll(options) 32 | return res 33 | }, 34 | // 添加账号 35 | doAdd: async (data) => { 36 | let res = await articlesSchema.findOrCreate({ 37 | where: { 38 | title: data.title 39 | }, 40 | defaults: data 41 | }) 42 | return res 43 | }, 44 | doUpdate: async (data) => { 45 | let res = await articlesSchema.update(data, { 46 | where: { 47 | id: data.id 48 | } 49 | }) 50 | return res 51 | }, 52 | doRemove: async (data) => { 53 | // 删除账号 54 | let res = await articlesSchema.destroy({ 55 | 'where': { 56 | 'id': Object.values(data) 57 | } 58 | }) 59 | return res 60 | }, 61 | getDetail: async (data) => { 62 | let res = await articlesSchema.findById(data.id) 63 | return res 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/apps/Articles/Routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/27. 3 | */ 4 | 5 | import Ctrl from './Ctrl' 6 | import auth from '../../auth' 7 | 8 | const namespace = '/articles/' 9 | 10 | export default (router) => { 11 | router 12 | // full path: 13 | // http://localhost:3000/x-restful-api-generator-koa/articles/verify/list 14 | .get(namespace + 'verify/list', auth.verify, Ctrl.getList) 15 | // full path: 16 | // http://localhost:3000/x-restful-api-generator-koa/articles/list 17 | .get(namespace + 'list', Ctrl.getList) 18 | // full path: 19 | // http://localhost:3000/x-restful-api-generator-koa/articles/add 20 | .post(namespace + 'add', Ctrl.doAdd) 21 | // full path: 22 | // http://localhost:3000/x-restful-api-generator-koa/articles/update 23 | .post(namespace + 'update', Ctrl.doUpdate) 24 | // full path: 25 | // http://localhost:3000/x-restful-api-generator-koa/articles/remove 26 | .post(namespace + 'remove', Ctrl.doRemove) 27 | } 28 | -------------------------------------------------------------------------------- /src/apps/Files/Ctrl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/1. 3 | */ 4 | const path = require('path') 5 | const multer = require('koa-multer') 6 | 7 | export default { 8 | doUpload: async (ctx, next) => { 9 | await next() 10 | let uploadDir = 'assets/uploads/' 11 | // 配置 12 | let storage = multer.diskStorage({ 13 | // 文件保存路径 14 | destination: (req, file, cb) => { 15 | cb(null, path.resolve(uploadDir)) 16 | }, 17 | // 文件重命名 18 | filename: (req, file, cb) => { 19 | let originalnameArr = file.originalname.split('.') 20 | let postfix = originalnameArr[originalnameArr.length - 1] 21 | console.log('originalnameArr', originalnameArr) 22 | let timeNow = Date.now() 23 | cb(null, timeNow + '.' + postfix) 24 | } 25 | }) 26 | // 上传实例 27 | let upload = multer({ 28 | storage: storage 29 | }) 30 | // 执行单文件上传 31 | let handle = await upload.single('file') 32 | let response = await handle(ctx) 33 | console.log('upload res', response) 34 | let res 35 | if (response) { 36 | res = { 37 | status: 200, 38 | msg: '上传成功!', 39 | data: { 40 | file: response.req.file, 41 | filename: response.req.file.filename, 42 | url: '//' + response.request.header.host + '/' + uploadDir + response.req.file.filename 43 | } 44 | } 45 | } else { 46 | res = { 47 | status: 5000, 48 | msg: '上传失败!', 49 | data: response 50 | } 51 | } 52 | ctx.body = res || {} 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/apps/Files/Model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/1. 3 | */ 4 | -------------------------------------------------------------------------------- /src/apps/Files/Routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/1. 3 | */ 4 | 5 | import Ctrl from './Ctrl' 6 | 7 | const namespace = '/files/' 8 | 9 | export default (router) => { 10 | router 11 | // full path: 12 | // http://localhost:3000/x-restful-api-generator-koa/files/upload 13 | .post(namespace + 'upload', Ctrl.doUpload) 14 | } 15 | -------------------------------------------------------------------------------- /src/apps/Files/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 文件上传Demo 7 | 80 | 81 | 82 |
83 |
84 | 文件上传Demo 85 |
86 |
87 | 上传图片 88 |
89 | 90 |
91 |
92 | 96 |
97 | 98 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /src/apps/demoModule_001/Ctrl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import Model from './Model' 6 | import auth from '../../auth' 7 | 8 | export default { 9 | doLogin: async (ctx, next) => { 10 | await next() 11 | let token = auth.sign({ 12 | userName: 'test', 13 | password: '123456' 14 | }) 15 | let res = { 16 | code: 200, 17 | msg: '登录成功!', 18 | data: { 19 | token: token 20 | } 21 | } 22 | ctx.body = res || {} 23 | }, 24 | getList: async (ctx, next) => { 25 | await next() 26 | console.log('Enter getList Ctrl.') 27 | let reqQuery = ctx.query 28 | // 查询结果 29 | let res = await Model.getList(reqQuery) 30 | // let res = { 31 | // count: 100, 32 | // list: [] 33 | // } 34 | // 处理结果 35 | if (res) { 36 | res = { 37 | code: 200, 38 | msg: '查询列表成功!', 39 | data: { 40 | count: res.count, 41 | list: res.list 42 | } 43 | } 44 | } else { 45 | res = { 46 | code: 5000, 47 | msg: '查询列表失败!', 48 | data: {} 49 | } 50 | } 51 | 52 | ctx.body = res || {} 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/apps/demoModule_001/Model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | export default { 6 | getList: async (data) => { 7 | let res = { 8 | count: 300, 9 | list: [] 10 | } 11 | return res 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/apps/demoModule_001/Routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import Ctrl from './Ctrl' 6 | import auth from '../../auth' 7 | 8 | const namespace = '/demoModule_001/' 9 | 10 | export default (router) => { 11 | router 12 | // full path: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/verify/list 13 | .get(namespace + 'verify/list', auth.verify, Ctrl.getList) 14 | // full path: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/list 15 | .get(namespace + 'list', Ctrl.getList) 16 | // full path: http://localhost:3000/x-restful-api-generator-koa/demoModule_001/login 17 | .get(namespace + 'login', Ctrl.doLogin) 18 | } 19 | -------------------------------------------------------------------------------- /src/auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import jsonwebtoken from 'jsonwebtoken' 6 | import { Cookie as CookieConfig } from './config' 7 | 8 | export default { 9 | // 生成token 10 | sign: function (data) { 11 | let token = jsonwebtoken.sign(data, CookieConfig.keys.secret, { expiresIn: '1d' }) 12 | return token 13 | }, 14 | // token 验证 15 | verify: async function (ctx, next) { 16 | // 支持多种方式传递token 17 | let key = CookieConfig.keys.token 18 | console.log('ctx.query', typeof ctx.query, ctx.query.hasOwnProperty) 19 | let token 20 | let decoded 21 | // 校验结果 22 | let verifyRes = { 23 | // 标识 24 | flag: false, 25 | // 数据 26 | data: {} 27 | } 28 | if (ctx.body && Object.prototype.hasOwnProperty.call(ctx.body, key)) { 29 | token = ctx.body[key] 30 | } else if (ctx.query && Object.prototype.hasOwnProperty.call(ctx.query, key)) { 31 | token = ctx.query[key] 32 | } else if (ctx.headers && Object.prototype.hasOwnProperty.call(ctx.headers, key)) { 33 | token = ctx.headers[key] 34 | } else { 35 | token = null 36 | } 37 | console.log('verify token', token) 38 | // 1.判断是否存在token 39 | if (token) { 40 | try { 41 | // 2.1.verify验证token 42 | decoded = jsonwebtoken.verify(token, CookieConfig.keys.secret) 43 | console.log('decoded', decoded, new Date() / 1000) 44 | // 2.1.验证token是否过期 45 | if (decoded.exp * 1000 <= new Date()) { 46 | verifyRes = { 47 | flag: false, 48 | data: { 49 | status: 9999, 50 | msg: 'token过期!请重新登录!', 51 | data: {} 52 | } 53 | } 54 | } else { 55 | verifyRes = { 56 | flag: true, 57 | data: {} 58 | } 59 | } 60 | } catch (err) { 61 | verifyRes = { 62 | flag: false, 63 | data: { 64 | status: 9999, 65 | msg: 'token校验失败!请重新登录!', 66 | data: err 67 | } 68 | } 69 | } 70 | } else { 71 | verifyRes = { 72 | flag: false, 73 | data: { 74 | status: 9999, 75 | msg: 'token无效!请重新登录!', 76 | data: {} 77 | } 78 | } 79 | } 80 | // 判断校验结果,分别处理 81 | if (verifyRes.flag) { 82 | // token有效,传递给上下文 83 | ctx['userInfo'] = decoded 84 | await next() 85 | } else { 86 | // token无效,直接返回 87 | await next() 88 | ctx.body = verifyRes.data 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | export const System = { 6 | protocol: 'http://', 7 | host: 'localhost', 8 | port: 3000, 9 | accessHost: [ 10 | 'localhost', 11 | '127.0.0.1' 12 | ] 13 | } 14 | 15 | export const DB = { 16 | host: 'localhost', 17 | port: 3306, 18 | username: 'root', 19 | password: '', 20 | database: 'x-restful-api-generator-koa', 21 | prefix: '' 22 | } 23 | 24 | export const Api = { 25 | // 自定义URL前缀 26 | prefix: '/x-restful-api-generator-koa' 27 | } 28 | 29 | export const Cookie = { 30 | keys: { 31 | token: 'x-restful-api-key', 32 | secret: 'x-restful-api-generator-koa' 33 | } 34 | } 35 | 36 | export const Log = { 37 | appenders: { 38 | out: { 39 | type: 'console' 40 | }, 41 | task: { 42 | type: 'dateFile', 43 | filename: 'logs/task', 44 | pattern: '-yyyy-MM-dd.log', 45 | alwaysIncludePattern: true 46 | }, 47 | result: { 48 | type: 'dateFile', 49 | filename: 'logs/result', 50 | pattern: '-yyyy-MM-dd.log', 51 | alwaysIncludePattern: true 52 | }, 53 | error: { 54 | type: 'dateFile', 55 | filename: 'logs/error', 56 | pattern: '-yyyy-MM-dd.log', 57 | alwaysIncludePattern: true 58 | }, 59 | default: { 60 | type: 'dateFile', 61 | filename: 'logs/default', 62 | pattern: '-yyyy-MM-dd.log', 63 | alwaysIncludePattern: true 64 | }, 65 | rate: { 66 | type: 'dateFile', 67 | filename: 'logs/rate', 68 | pattern: '-yyyy-MM-dd.log', 69 | alwaysIncludePattern: true 70 | } 71 | }, 72 | categories: { 73 | default: { 74 | appenders: ['out', 'default'], 75 | level: 'info' 76 | }, 77 | task: { 78 | appenders: ['task'], 79 | level: 'info' 80 | }, 81 | result: { 82 | appenders: ['result'], 83 | level: 'info' 84 | }, 85 | error: { 86 | appenders: ['error'], 87 | level: 'error' 88 | }, 89 | rate: { 90 | appenders: ['rate'], 91 | level: 'info' 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import Sequelize from 'sequelize' 6 | import { DB as DBConfig } from './config' 7 | 8 | const db = new Sequelize( 9 | DBConfig.database, 10 | DBConfig.username, 11 | DBConfig.password, 12 | { 13 | host: DBConfig.host, 14 | dialect: 'mysql', 15 | dialectOptions: { 16 | charset: 'utf8' 17 | }, 18 | pool: { 19 | max: 5, 20 | min: 0, 21 | idle: 30000 22 | }, 23 | define: { 24 | timestamps: false 25 | } 26 | } 27 | ) 28 | 29 | export default db 30 | -------------------------------------------------------------------------------- /src/middleware/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | import compose from 'koa-compose' 6 | import convert from 'koa-convert' 7 | import helmet from 'koa-helmet' 8 | import logger from 'koa-logger' 9 | import cors from 'koa-cors' 10 | import bodyParser from 'koa-bodyparser' 11 | import KoaStatic from 'koa-static' 12 | 13 | import { System as SystemConfig } from '../config' 14 | 15 | export default function middleware (app) { 16 | return compose([ 17 | logger(), 18 | helmet(), 19 | KoaStatic('.'), 20 | // 跨域处理 21 | convert(cors({ 22 | origin: function (request) { 23 | let host = request.header.origin 24 | let isIncludes = false 25 | // console.log('host', request.header) 26 | // FIXME 安全起见,上线时需注掉如下判断 27 | if (!host) { 28 | return '*' 29 | } 30 | for (let i in SystemConfig.accessHost) { 31 | if (host.includes(SystemConfig.accessHost[i])) { 32 | isIncludes = true 33 | break 34 | } 35 | } 36 | if (isIncludes) { 37 | return host 38 | } 39 | return SystemConfig.host 40 | }, 41 | exposeHeaders: [], 42 | maxAge: 5, 43 | credentials: true, 44 | allowMethods: ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS'], 45 | allowHeaders: ['Content-Type', 'Content-Length', 'Authorization', 'Accept', 'X-Requested-With', 'Origin'] 46 | })), 47 | bodyParser({ 48 | strict: false, 49 | jsonLimit: '20mb', 50 | formLimit: '10mb', 51 | textLimit: '20mb' 52 | }) 53 | ]) 54 | } 55 | -------------------------------------------------------------------------------- /src/routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/10/23. 3 | */ 4 | 5 | // 实现动态加载路由 6 | const requireDirectory = require('require-directory') 7 | const routersObj = {} 8 | 9 | // 解析apps路由 10 | let apps = requireDirectory(module, './apps') 11 | for (let key in apps) { 12 | let routers = apps[key]['Routers'] 13 | routersObj[key] = routers.default 14 | } 15 | console.log(routersObj) 16 | export default routersObj 17 | -------------------------------------------------------------------------------- /src/schema/articles.js: -------------------------------------------------------------------------------- 1 | /* jshint indent: 2 */ 2 | 3 | module.exports = function(sequelize, DataTypes) { 4 | return sequelize.define('articles', { 5 | id: { 6 | type: DataTypes.INTEGER(11), 7 | allowNull: false, 8 | primaryKey: true, 9 | autoIncrement: true 10 | }, 11 | title: { 12 | type: DataTypes.STRING(100), 13 | allowNull: false 14 | }, 15 | content: { 16 | type: DataTypes.STRING(600), 17 | allowNull: false 18 | }, 19 | createdAt: { 20 | type: DataTypes.TIME, 21 | allowNull: false 22 | }, 23 | updatedAt: { 24 | type: DataTypes.TIME, 25 | allowNull: false 26 | } 27 | }, { 28 | tableName: 'articles' 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/2. 3 | */ 4 | require('babel-register') 5 | require('babel-polyfill') 6 | 7 | var app = require('./app') 8 | var SystemConfig = require('./config').System 9 | 10 | var server = app.listen(SystemConfig.port, function () { 11 | let serverPath = SystemConfig.protocol + SystemConfig.host + (SystemConfig.port ? ':' + SystemConfig.port : SystemConfig.port) 12 | console.log('RESTful API Server is listening to ' + serverPath) 13 | }) 14 | 15 | exports = module.exports = server 16 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/2. 3 | */ 4 | 5 | import { log } from './log' 6 | 7 | export default { 8 | log 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/2. 3 | */ 4 | 5 | import { configure, getLogger } from 'log4js' 6 | import { Log as LogConfig } from '../config' 7 | 8 | // 格式化响应日志 9 | const formatRes = (ctx, ms) => { 10 | let tmpArr = [] 11 | 12 | tmpArr.push('\n' + '********** RESPONSE START **********' + '\n\n') 13 | tmpArr.push(formatReq(ctx.request, ms) + '\n') 14 | tmpArr.push(' response status: ' + ctx.status + '\n') 15 | tmpArr.push(' response body: ' + '\n ' + JSON.stringify(ctx.body) + '\n\n') 16 | tmpArr.push('********** RESPONSE END **********' + '\n') 17 | 18 | return tmpArr.join('') 19 | } 20 | 21 | // 格式化错误日志 22 | const formatError = (ctx, err, ms) => { 23 | let tmpArr = [] 24 | 25 | tmpArr.push('\n' + '********** ERROR START **********' + '\n\n') 26 | tmpArr.push(formatReq(ctx.request, ms)) 27 | tmpArr.push(' err name: ' + err.name + '\n') 28 | tmpArr.push(' err message: ' + err.message + '\n') 29 | tmpArr.push(' err stack: ' + err.stack + '\n\n') 30 | tmpArr.push('********** ERROR END **********' + '\n') 31 | 32 | return tmpArr.join('') 33 | } 34 | 35 | // 格式化请求日志 36 | const formatReq = (req, ms) => { 37 | let tmpArr = [] 38 | 39 | tmpArr.push(' request method: ' + req.method + '\n') 40 | tmpArr.push(' request originalUrl: ' + req.originalUrl + '\n') 41 | tmpArr.push(' request client ip: ' + req.ip + '\n') 42 | if (req.method === 'GET') { 43 | tmpArr.push(' request query: ' + JSON.stringify(req.query) + '\n') 44 | } else { 45 | tmpArr.push(' request body: ' + '\n ' + JSON.stringify(req.body) + '\n') 46 | } 47 | tmpArr.push(' response time: ' + ms + '\n') 48 | 49 | return tmpArr.join('') 50 | } 51 | 52 | // 加载配置文件 53 | configure(LogConfig) 54 | 55 | // log 中间件 56 | export const log = { 57 | error: (ctx, error, ms) => { 58 | if (ctx && error) { 59 | getLogger('error').error(formatError(ctx, error, ms)) 60 | } 61 | }, 62 | response: (ctx, ms) => { 63 | if (ctx) { 64 | getLogger('result').info(formatRes(ctx, ms)) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OXOYO/X-RESTful-API-Generator-Koa/ac4715102d21702d3fc7fac254fc4a8008306313/static/.gitkeep -------------------------------------------------------------------------------- /test/appListen.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by OXOYO on 2017/11/1. 3 | */ 4 | 5 | var request = require('supertest') 6 | 7 | var app = require('../dist/app') 8 | var server = app.listen() 9 | 10 | describe('appListen', function () { 11 | it('listen success', function (done) { 12 | request(server) 13 | .get('/x-restful-api-generator-koa/') 14 | .expect(200) 15 | .end(function () { 16 | server.close() 17 | done() 18 | }) 19 | }) 20 | }) 21 | --------------------------------------------------------------------------------