mirror of
https://github.com/pure-admin/pure-admin-backend.git
synced 2025-04-24 15:27:17 +08:00
feat: add
This commit is contained in:
parent
f7c6bf5d46
commit
15fd86ba73
8
.env
Normal file
8
.env
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Port
|
||||||
|
PORT=3000
|
||||||
|
|
||||||
|
# JWT_SECRET
|
||||||
|
JWT_SECRET = '708DD1DC5BC5A169'
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
LOG_LEVEL='debug'
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
yarn.lock
|
27
README.md
Normal file
27
README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# 接口
|
||||||
|
|
||||||
|
## 安装依赖
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目启动
|
||||||
|
```
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Swagger文档访问地址
|
||||||
|
http://localhost:3000
|
||||||
|
|
||||||
|
## 在swagger中添加token验证
|
||||||
|
① 先请求验证码接口,拿到验证码(info字段就是验证码)
|
||||||
|
② 然后请求登录接口,你可以在网页的Network中拿到登录成功后返回的token,复制
|
||||||
|
③ 最后回到swagger,点击右上角的绿色边框Authorize,你会看到一个Value的输入框,将复制的token前面加上Bearer 粘贴上去,点确定即可(Authorize)
|
||||||
|
(注意Bearer后面有一个空格哦)
|
||||||
|
|
||||||
|
## 注意点
|
||||||
|
请先全局安装typescript、ts-node,如安装请忽略
|
||||||
|
```
|
||||||
|
npm install -g typescript
|
||||||
|
npm install -g ts-node
|
||||||
|
```
|
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "backend-ts",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "API接口",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "ts-node ./src/server.ts",
|
||||||
|
"start": "nodemon ./dist/server.js",
|
||||||
|
"prod": "npm run build && npm run start",
|
||||||
|
"yarn:clean": "yarn cache clean"
|
||||||
|
},
|
||||||
|
"author": "xiaoxian521",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/express": "^4.17.9",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-swagger-generator": "^1.1.17",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"mysql2": "^2.2.5",
|
||||||
|
"svg-captcha": "^1.4.0",
|
||||||
|
"winston": "^3.3.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"nodemon": "^1.19.4",
|
||||||
|
"open": "^7.3.0",
|
||||||
|
"tslint": "^5.20.1",
|
||||||
|
"typescript": "^3.9.7"
|
||||||
|
},
|
||||||
|
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git"
|
||||||
|
}
|
BIN
public/favicon-16x16.png
Normal file
BIN
public/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 665 B |
BIN
public/favicon-32x32.png
Normal file
BIN
public/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 628 B |
59
public/index.html
Normal file
59
public/index.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<!-- HTML for static distribution bundle build -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Swagger UI</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="./swagger-ui.css">
|
||||||
|
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||||
|
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
|
||||||
|
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||||
|
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||||
|
<script>
|
||||||
|
window.onload = function () {
|
||||||
|
// Begin Swagger UI call region
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: "http://localhost:3000/swagger.json",
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
})
|
||||||
|
// End Swagger UI call region
|
||||||
|
|
||||||
|
window.ui = ui
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
74
public/oauth2-redirect.html
Normal file
74
public/oauth2-redirect.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en-US">
|
||||||
|
<head>
|
||||||
|
<title>Swagger UI: OAuth2 Redirect</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
function run () {
|
||||||
|
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||||
|
var sentState = oauth2.state;
|
||||||
|
var redirectUrl = oauth2.redirectUrl;
|
||||||
|
var isValid, qp, arr;
|
||||||
|
|
||||||
|
if (/code|token|error/.test(window.location.hash)) {
|
||||||
|
qp = window.location.hash.substring(1);
|
||||||
|
} else {
|
||||||
|
qp = location.search.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = qp.split("&")
|
||||||
|
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
|
||||||
|
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||||
|
function (key, value) {
|
||||||
|
return key === "" ? value : decodeURIComponent(value)
|
||||||
|
}
|
||||||
|
) : {}
|
||||||
|
|
||||||
|
isValid = qp.state === sentState
|
||||||
|
|
||||||
|
if ((
|
||||||
|
oauth2.auth.schema.get("flow") === "accessCode"||
|
||||||
|
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||||
|
) && !oauth2.auth.code) {
|
||||||
|
if (!isValid) {
|
||||||
|
oauth2.errCb({
|
||||||
|
authId: oauth2.auth.name,
|
||||||
|
source: "auth",
|
||||||
|
level: "warning",
|
||||||
|
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qp.code) {
|
||||||
|
delete oauth2.state;
|
||||||
|
oauth2.auth.code = qp.code;
|
||||||
|
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||||
|
} else {
|
||||||
|
let oauthErrorMsg
|
||||||
|
if (qp.error) {
|
||||||
|
oauthErrorMsg = "["+qp.error+"]: " +
|
||||||
|
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||||
|
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
oauth2.errCb({
|
||||||
|
authId: oauth2.auth.name,
|
||||||
|
source: "auth",
|
||||||
|
level: "error",
|
||||||
|
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||||
|
}
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('DOMContentLoaded', function () {
|
||||||
|
run();
|
||||||
|
});
|
||||||
|
</script>
|
3
public/swagger-ui-bundle.js
Normal file
3
public/swagger-ui-bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
public/swagger-ui-bundle.js.map
Normal file
1
public/swagger-ui-bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
3
public/swagger-ui-es-bundle-core.js
Normal file
3
public/swagger-ui-es-bundle-core.js
Normal file
File diff suppressed because one or more lines are too long
1
public/swagger-ui-es-bundle-core.js.map
Normal file
1
public/swagger-ui-es-bundle-core.js.map
Normal file
File diff suppressed because one or more lines are too long
3
public/swagger-ui-es-bundle.js
Normal file
3
public/swagger-ui-es-bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
public/swagger-ui-es-bundle.js.map
Normal file
1
public/swagger-ui-es-bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
3
public/swagger-ui-standalone-preset.js
Normal file
3
public/swagger-ui-standalone-preset.js
Normal file
File diff suppressed because one or more lines are too long
1
public/swagger-ui-standalone-preset.js.map
Normal file
1
public/swagger-ui-standalone-preset.js.map
Normal file
File diff suppressed because one or more lines are too long
4
public/swagger-ui.css
Normal file
4
public/swagger-ui.css
Normal file
File diff suppressed because one or more lines are too long
1
public/swagger-ui.css.map
Normal file
1
public/swagger-ui.css.map
Normal file
File diff suppressed because one or more lines are too long
3
public/swagger-ui.js
Normal file
3
public/swagger-ui.js
Normal file
File diff suppressed because one or more lines are too long
1
public/swagger-ui.js.map
Normal file
1
public/swagger-ui.js.map
Normal file
File diff suppressed because one or more lines are too long
31
src/app.ts
Normal file
31
src/app.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import * as express from "express"
|
||||||
|
import * as bodyParser from "body-parser"
|
||||||
|
|
||||||
|
class App {
|
||||||
|
public app: express.Application
|
||||||
|
constructor() {
|
||||||
|
this.app = express()
|
||||||
|
this.config()
|
||||||
|
}
|
||||||
|
private config(): void {
|
||||||
|
// 支持json编码的主体
|
||||||
|
this.app.use(bodyParser.json())
|
||||||
|
// 支持编码的主体
|
||||||
|
this.app.use(bodyParser.urlencoded({
|
||||||
|
extended: true,
|
||||||
|
}))
|
||||||
|
// 设置静态访问目录(Swagger)
|
||||||
|
this.app.use(express.static('public'))
|
||||||
|
// 设置跨域访问
|
||||||
|
this.app.all('*', (req, res, next) => {
|
||||||
|
res.header('Access-Control-Allow-Origin', '*')
|
||||||
|
res.header('Access-Control-Allow-Headers', 'content-type')
|
||||||
|
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
|
||||||
|
res.header('X-Powered-By', ' 3.2.1')
|
||||||
|
res.header('Content-Type', 'application/json;charset=utf-8')
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new App().app
|
66
src/config/index.ts
Normal file
66
src/config/index.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import * as dotenv from "dotenv"
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || "development"
|
||||||
|
|
||||||
|
const envFound = dotenv.config()
|
||||||
|
if (envFound.error) {
|
||||||
|
|
||||||
|
throw new Error("⚠️ Couldn't find .env file ⚠️")
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
port: parseInt(process.env.PORT, 10),
|
||||||
|
databaseURL: process.env.MONGODB_URI,
|
||||||
|
jwtSecret: process.env.JWT_SECRET,
|
||||||
|
jwtAlgorithm: process.env.JWT_ALGO,
|
||||||
|
options: {
|
||||||
|
swaggerDefinition: {
|
||||||
|
info: {
|
||||||
|
description: 'CURD-TS专用接口',
|
||||||
|
title: 'Swagger',
|
||||||
|
version: require('../../package.json').version
|
||||||
|
},
|
||||||
|
host: `localhost:${parseInt(process.env.PORT, 10)}`,
|
||||||
|
basePath: '/',
|
||||||
|
produces: ['application/json', 'application/xml'],
|
||||||
|
schemes: ['http', 'https'],
|
||||||
|
securityDefinitions: {
|
||||||
|
JWT: {
|
||||||
|
type: 'apiKey',
|
||||||
|
in: 'header',
|
||||||
|
name: 'Authorization',
|
||||||
|
description: 'Bearer Authorization'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
route: {
|
||||||
|
url: './swagger-ui.html',
|
||||||
|
docs: '/swagger.json' //swagger文件 api
|
||||||
|
},
|
||||||
|
basedir: __dirname, //app absolute path
|
||||||
|
files: ['../router/api/*.ts'] //Path to the API handle folder
|
||||||
|
},
|
||||||
|
logs: {
|
||||||
|
level: process.env.LOG_LEVEL || 'silly',
|
||||||
|
},
|
||||||
|
agenda: {
|
||||||
|
dbCollection: process.env.AGENDA_DB_COLLECTION,
|
||||||
|
pooltime: process.env.AGENDA_POOL_TIME,
|
||||||
|
concurrency: parseInt(process.env.AGENDA_CONCURRENCY, 10),
|
||||||
|
},
|
||||||
|
mysql: {
|
||||||
|
host: 'localhost',
|
||||||
|
charset: 'utf8_general_ci',
|
||||||
|
user: 'root',
|
||||||
|
password: '123456789'
|
||||||
|
},
|
||||||
|
mongodb: {},
|
||||||
|
sqlite: {},
|
||||||
|
api: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
emails: {
|
||||||
|
apiKey: process.env.MAILGUN_API_KEY,
|
||||||
|
domain: process.env.MAILGUN_DOMAIN
|
||||||
|
}
|
||||||
|
}
|
35
src/loaders/logger.ts
Normal file
35
src/loaders/logger.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
import config from "../config"
|
||||||
|
import * as winston from "winston"
|
||||||
|
|
||||||
|
const transports = []
|
||||||
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
|
transports.push(
|
||||||
|
new winston.transports.Console()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
transports.push(
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.cli(),
|
||||||
|
winston.format.splat(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoggerInstance = winston.createLogger({
|
||||||
|
level: config.logs.level,
|
||||||
|
levels: winston.config.npm.levels,
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp({
|
||||||
|
format: 'YYYY-MM-DD HH:mm:ss'
|
||||||
|
}),
|
||||||
|
winston.format.errors({ stack: true }),
|
||||||
|
winston.format.splat(),
|
||||||
|
winston.format.json()
|
||||||
|
),
|
||||||
|
transports
|
||||||
|
})
|
||||||
|
|
||||||
|
export default LoggerInstance
|
6
src/models/mysql/index.ts
Normal file
6
src/models/mysql/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// 创建用户表
|
||||||
|
const user = 'CREATE TABLE if not EXISTS users(id int PRIMARY key auto_increment,username varchar(32),password varchar(32),time DATETIME)'
|
||||||
|
|
||||||
|
export {
|
||||||
|
user
|
||||||
|
}
|
347
src/router/api/mysql.ts
Normal file
347
src/router/api/mysql.ts
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
import * as mysql from "mysql2"
|
||||||
|
import secret from "../../config"
|
||||||
|
import * as jwt from "jsonwebtoken"
|
||||||
|
import { createHash } from "crypto"
|
||||||
|
import Logger from "../../loaders/logger"
|
||||||
|
import { Request, Response } from "express"
|
||||||
|
import { createMathExpr } from "svg-captcha"
|
||||||
|
import getFormatDate from "../../utils/date"
|
||||||
|
import { Code, Info } from "../../utils/infoEnum"
|
||||||
|
import { connection } from "../../utils/initMysql"
|
||||||
|
|
||||||
|
export interface dataModel {
|
||||||
|
length: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存验证码
|
||||||
|
let generateVerify: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Error
|
||||||
|
* @property {string} code.required
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Response
|
||||||
|
* @property {[integer]} code
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Login
|
||||||
|
* @property {string} username.required - 用户名 - eg: admin
|
||||||
|
* @property {string} password.required - 密码 - eg: 123456
|
||||||
|
* @property {integer} verify.required - 验证码
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route POST /login
|
||||||
|
* @param {Login.model} point.body.required - the new point
|
||||||
|
* @produces application/json application/xml
|
||||||
|
* @consumes application/json application/xml
|
||||||
|
* @summary 登录
|
||||||
|
* @group 用户登录、注册相关
|
||||||
|
* @returns {Response.model} 200
|
||||||
|
* @returns {Array.<Login>} Login
|
||||||
|
* @headers {integer} 200.X-Rate-Limit
|
||||||
|
* @headers {string} 200.X-Expires-After
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const login = async (req: Request, res: Response) => {
|
||||||
|
const { username, password, verify } = req.body
|
||||||
|
if (generateVerify !== verify) return res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[0]
|
||||||
|
})
|
||||||
|
let sql: string = 'select * from users where username=' + "'" + username + "'"
|
||||||
|
connection.query(sql, async function (err, data: dataModel) {
|
||||||
|
if (data.length == 0) {
|
||||||
|
await res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[1]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (createHash('md5').update(password).digest('hex') == data[0].password) {
|
||||||
|
const accessToken = jwt.sign({
|
||||||
|
accountId: data[0].id
|
||||||
|
}, secret.jwtSecret, { expiresIn: 3600 })
|
||||||
|
await res.json({
|
||||||
|
code: Code.successCode,
|
||||||
|
info: Info[2],
|
||||||
|
accessToken
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[3]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Register
|
||||||
|
* @property {string} username.required - 用户名 - eg: admin
|
||||||
|
* @property {string} password.required - 密码 - eg: 123456
|
||||||
|
* @property {integer} verify.required - 验证码
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route POST /register
|
||||||
|
* @param {Register.model} point.body.required - the new point
|
||||||
|
* @produces application/json application/xml
|
||||||
|
* @consumes application/json application/xml
|
||||||
|
* @summary 注册
|
||||||
|
* @group 用户登录、注册相关
|
||||||
|
* @returns {Response.model} 200
|
||||||
|
* @returns {Array.<Register>} Register
|
||||||
|
* @headers {integer} 200.X-Rate-Limit
|
||||||
|
* @headers {string} 200.X-Expires-After
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const register = async (req: Request, res: Response) => {
|
||||||
|
const { username, password, verify } = req.body
|
||||||
|
if (generateVerify !== verify) return res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[0]
|
||||||
|
})
|
||||||
|
if (password.length < 6) return res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[4]
|
||||||
|
})
|
||||||
|
let sql: string = 'select * from users where username=' + "'" + username + "'"
|
||||||
|
connection.query(sql, async (err, data: dataModel) => {
|
||||||
|
if (data.length > 0) {
|
||||||
|
await res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[5]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let time = await getFormatDate()
|
||||||
|
let sql: string = 'insert into users (username,password,time) value(' + "'" + username + "'" + ',' + "'" + createHash('md5').update(password).digest('hex') +
|
||||||
|
"'" + ',' + "'" + time + "'" + ')'
|
||||||
|
connection.query(sql, async function (err) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
} else {
|
||||||
|
await res.json({
|
||||||
|
code: Code.successCode,
|
||||||
|
info: Info[6]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef UpdateList
|
||||||
|
* @property {string} username.required - 用户名 - eg: admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route PUT /updateList/{id}
|
||||||
|
* @summary 列表更新
|
||||||
|
* @param {UpdateList.model} point.body.required - 用户名
|
||||||
|
* @param {UpdateList.model} id.path.required - 用户id
|
||||||
|
* @group 用户管理相关
|
||||||
|
* @returns {object} 200
|
||||||
|
* @returns {Array.<UpdateList>} UpdateList
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const updateList = async (req: Request, res: Response) => {
|
||||||
|
const { id } = req.params
|
||||||
|
const { username } = req.body
|
||||||
|
let payload = null
|
||||||
|
try {
|
||||||
|
const authorizationHeader = req.get("Authorization")
|
||||||
|
const accessToken = authorizationHeader.substr("Bearer ".length)
|
||||||
|
payload = jwt.verify(accessToken, secret.jwtSecret)
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(401).end()
|
||||||
|
}
|
||||||
|
let modifySql: string = 'UPDATE users SET username = ? WHERE id = ?'
|
||||||
|
let sql: string = 'select * from users where id=' + id
|
||||||
|
connection.query(sql, function (err, data) {
|
||||||
|
connection.query(sql, function (err) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
} else {
|
||||||
|
let modifyParams: string[] = [username, id]
|
||||||
|
// 改
|
||||||
|
connection.query(modifySql, modifyParams, async function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
} else {
|
||||||
|
await res.json({
|
||||||
|
code: Code.successCode,
|
||||||
|
info: Info[7]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef DeleteList
|
||||||
|
* @property {integer} id.required - 当前id
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route DELETE /deleteList/{id}
|
||||||
|
* @summary 列表删除
|
||||||
|
* @param {DeleteList.model} id.path.required - 用户id
|
||||||
|
* @group 用户管理相关
|
||||||
|
* @returns {object} 200
|
||||||
|
* @returns {Array.<DeleteList>} DeleteList
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const deleteList = async (req: Request, res: Response) => {
|
||||||
|
const { id } = req.params
|
||||||
|
let payload = null
|
||||||
|
try {
|
||||||
|
const authorizationHeader = req.get("Authorization")
|
||||||
|
const accessToken = authorizationHeader.substr("Bearer ".length)
|
||||||
|
payload = jwt.verify(accessToken, secret.jwtSecret)
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(401).end()
|
||||||
|
}
|
||||||
|
let sql: string = 'DELETE FROM users where id=' + "'" + id + "'"
|
||||||
|
connection.query(sql, async function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err)
|
||||||
|
} else {
|
||||||
|
await res.json({
|
||||||
|
code: Code.successCode,
|
||||||
|
info: Info[8]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef SearchPage
|
||||||
|
* @property {integer} page.required - 第几页 - eg: 1
|
||||||
|
* @property {integer} size.required - 数据量(条)- eg: 5
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route POST /searchPage
|
||||||
|
* @param {SearchPage.model} point.body.required - the new point
|
||||||
|
* @produces application/json application/xml
|
||||||
|
* @consumes application/json application/xml
|
||||||
|
* @summary 分页查询
|
||||||
|
* @group 用户管理相关
|
||||||
|
* @returns {Response.model} 200
|
||||||
|
* @returns {Array.<SearchPage>} SearchPage
|
||||||
|
* @headers {integer} 200.X-Rate-Limit
|
||||||
|
* @headers {string} 200.X-Expires-After
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const searchPage = async (req: Request, res: Response) => {
|
||||||
|
const { page, size } = req.body
|
||||||
|
let payload = null
|
||||||
|
try {
|
||||||
|
const authorizationHeader = req.get("Authorization")
|
||||||
|
const accessToken = authorizationHeader.substr("Bearer ".length)
|
||||||
|
payload = jwt.verify(accessToken, secret.jwtSecret)
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(401).end()
|
||||||
|
}
|
||||||
|
let sql: string = 'select * from users limit ' + size + ' offset ' + size * (page - 1)
|
||||||
|
connection.query(sql, async function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
} else {
|
||||||
|
await res.json({
|
||||||
|
code: Code.successCode,
|
||||||
|
info: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef SearchVague
|
||||||
|
* @property {string} username.required - 用户名 - eg: admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route POST /searchVague
|
||||||
|
* @param {SearchVague.model} point.body.required - the new point
|
||||||
|
* @produces application/json application/xml
|
||||||
|
* @consumes application/json application/xml
|
||||||
|
* @summary 模糊查询
|
||||||
|
* @group 用户管理相关
|
||||||
|
* @returns {Response.model} 200
|
||||||
|
* @returns {Array.<SearchVague>} SearchVague
|
||||||
|
* @headers {integer} 200.X-Rate-Limit
|
||||||
|
* @headers {string} 200.X-Expires-After
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const searchVague = async (req: Request, res: Response) => {
|
||||||
|
const { username } = req.body
|
||||||
|
let payload = null
|
||||||
|
try {
|
||||||
|
const authorizationHeader = req.get("Authorization")
|
||||||
|
const accessToken = authorizationHeader.substr("Bearer ".length)
|
||||||
|
payload = jwt.verify(accessToken, secret.jwtSecret)
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(401).end()
|
||||||
|
}
|
||||||
|
if (username === "" || username === null) return res.json({
|
||||||
|
code: Code.failCode,
|
||||||
|
info: Info[9]
|
||||||
|
})
|
||||||
|
let sql: string = 'select * from users'
|
||||||
|
sql += " WHERE username LIKE " + mysql.escape("%" + username + "%")
|
||||||
|
connection.query(sql, function (err, data) {
|
||||||
|
connection.query(sql, async function (err) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err)
|
||||||
|
} else {
|
||||||
|
await res.json({
|
||||||
|
code: Code.successCode,
|
||||||
|
info: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route GET /captcha
|
||||||
|
* @summary 图形验证码
|
||||||
|
* @group captcha - 图形验证码
|
||||||
|
* @returns {object} 200
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const captcha = async (req: Request, res: Response) => {
|
||||||
|
const create = createMathExpr({
|
||||||
|
mathMin: 1,
|
||||||
|
mathMax: 4,
|
||||||
|
mathOperator: "+"
|
||||||
|
})
|
||||||
|
generateVerify = Number(create.text)
|
||||||
|
res.type('svg') // 响应的类型
|
||||||
|
res.json({ code: Code.successCode, info: create.text, svg: create.data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
login,
|
||||||
|
register,
|
||||||
|
updateList,
|
||||||
|
deleteList,
|
||||||
|
searchPage,
|
||||||
|
searchVague,
|
||||||
|
captcha,
|
||||||
|
}
|
61
src/server.ts
Normal file
61
src/server.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import app from "./app"
|
||||||
|
import * as open from "open"
|
||||||
|
import config from "./config"
|
||||||
|
import { user } from "./models/mysql"
|
||||||
|
import Logger from "./loaders/logger"
|
||||||
|
import { queryTable } from "./utils/initMysql"
|
||||||
|
const expressSwagger = require("express-swagger-generator")(app)
|
||||||
|
expressSwagger(config.options)
|
||||||
|
|
||||||
|
queryTable(user)
|
||||||
|
|
||||||
|
import {
|
||||||
|
login,
|
||||||
|
register,
|
||||||
|
updateList,
|
||||||
|
deleteList,
|
||||||
|
searchPage,
|
||||||
|
searchVague,
|
||||||
|
captcha,
|
||||||
|
} from "./router/api/mysql"
|
||||||
|
|
||||||
|
app.post('/login', (req, res) => {
|
||||||
|
login(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/register', (req, res) => {
|
||||||
|
register(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.put('/updateList/:id', (req, res) => {
|
||||||
|
updateList(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.delete('/deleteList/:id', (req, res) => {
|
||||||
|
deleteList(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/searchPage', (req, res) => {
|
||||||
|
searchPage(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/searchVague', (req, res) => {
|
||||||
|
searchVague(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/captcha', (req, res) => {
|
||||||
|
captcha(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(config.port, () => {
|
||||||
|
Logger.info(`
|
||||||
|
################################################
|
||||||
|
🛡️ Swagger文档地址: http://localhost:${config.port} 🛡️
|
||||||
|
################################################
|
||||||
|
`)
|
||||||
|
}).on('error', err => {
|
||||||
|
Logger.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
open(`http://localhost:${config.port}`) // 自动打开默认浏览器
|
23
src/utils/date.ts
Normal file
23
src/utils/date.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
interface dateModel {
|
||||||
|
getMonth: () => any
|
||||||
|
getDate: () => string | number
|
||||||
|
getFullYear: () => string | number
|
||||||
|
getHours: () => string | number
|
||||||
|
getMinutes: () => string | number
|
||||||
|
getSeconds: () => string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function getFormatDate(): Promise<Date | string> {
|
||||||
|
let date: dateModel = new Date()
|
||||||
|
let month: string | number = date.getMonth() + 1
|
||||||
|
let strDate = date.getDate()
|
||||||
|
if (month >= 1 && month <= 9) {
|
||||||
|
month = "0" + month
|
||||||
|
}
|
||||||
|
if (strDate >= 0 && strDate <= 9) {
|
||||||
|
strDate = "0" + strDate
|
||||||
|
}
|
||||||
|
let currentDate = date.getFullYear() + "-" + month + "-" + strDate +
|
||||||
|
" " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds()
|
||||||
|
return currentDate
|
||||||
|
}
|
19
src/utils/infoEnum.ts
Normal file
19
src/utils/infoEnum.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 状态码
|
||||||
|
export const enum Code {
|
||||||
|
failCode = -1,
|
||||||
|
successCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回信息
|
||||||
|
export enum Info {
|
||||||
|
"请输入正确的验证码",
|
||||||
|
"账号尚未被注册",
|
||||||
|
"登录成功",
|
||||||
|
"密码错误",
|
||||||
|
"密码长度不能小于6位",
|
||||||
|
"账号已被注册",
|
||||||
|
"账号注册成功",
|
||||||
|
"修改成功",
|
||||||
|
"删除成功",
|
||||||
|
"搜索信息不能为空",
|
||||||
|
}
|
13
src/utils/initMysql.ts
Normal file
13
src/utils/initMysql.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as mysql from "mysql2"
|
||||||
|
import mysqlConfig from "../config"
|
||||||
|
import Logger from "../loaders/logger"
|
||||||
|
|
||||||
|
//user数据库
|
||||||
|
export const connection = mysql.createConnection(Object.assign({ database: 'user' }, mysqlConfig.mysql))
|
||||||
|
|
||||||
|
export function queryTable(s: string): void {
|
||||||
|
connection.query(s, (err) => {
|
||||||
|
err ? Logger.error(err) : Logger.info(`${s}表创建成功`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"pretty": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es6",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./lib"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts", "src/router/api/user.js"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
29
tslint.json
Normal file
29
tslint.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"defaultSeverity": "error",
|
||||||
|
"extends": [
|
||||||
|
"tslint:recommended"
|
||||||
|
],
|
||||||
|
"jsRules": {},
|
||||||
|
"rules": {
|
||||||
|
"interface-name": [
|
||||||
|
true,
|
||||||
|
"never-prefix"
|
||||||
|
],
|
||||||
|
"no-console": [
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"semicolon": [
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"quotemark": [
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"object-literal-sor t-keys": [
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"max-classes-per-file": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rulesDirectory": []
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user