feat: add

This commit is contained in:
xiaoxian521
2021-11-22 12:30:58 +08:00
parent f7c6bf5d46
commit 15fd86ba73
31 changed files with 878 additions and 0 deletions

31
src/app.ts Normal file
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,19 @@
// 状态码
export const enum Code {
failCode = -1,
successCode = 0
}
// 返回信息
export enum Info {
"请输入正确的验证码",
"账号尚未被注册",
"登录成功",
"密码错误",
"密码长度不能小于6位",
"账号已被注册",
"账号注册成功",
"修改成功",
"删除成功",
"搜索信息不能为空",
}

13
src/utils/initMysql.ts Normal file
View 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}表创建成功`)
})
}