refactor: 重构优化

This commit is contained in:
xiaoxian521 2022-11-09 15:51:06 +08:00
parent 07b1d2c00a
commit 724e662a52
15 changed files with 2022 additions and 175 deletions

86
.gitignore vendored
View File

@ -1,4 +1,84 @@
node_modules # Created by .ignore support plugin (hsz.mobi)
.DS_Store ### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist dist
yarn.lock
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE
.idea
docs
.DS_Store

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021 啝裳 Copyright (c) Lastest RealityBoy
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,27 +1,34 @@
# 接口 <p align="center">
pure-admin官方后端
<br />
采用nodejs编写
</p>
## 快速启动
### 打开 `MySql`
来到项目的 `src/config/index.ts` 文件,查看第 `50` 行,将 `mysql` 的账号密码跟您的 `mysql` 保持一致
### 安装依赖
## 安装依赖
``` ```
yarn install pnpm install
``` ```
## 项目启动 ### 项目启动
采用 [nodemon](https://github.com/remy/nodemon) 运行项目,修改代码自动更新,无需重启
``` ```
yarn dev pnpm start
``` ```
## Swagger文档访问地址 ### `Swagger` 文档访问地址
http://localhost:3000 http://localhost:3000
## 在swagger中添加token验证 ## 如何在 `Swagger` 中添加 `token` 验证
① 先请求验证码接口拿到验证码info字段就是验证码
② 然后请求登录接口你可以在网页的Network中拿到登录成功后返回的token复制
③ 最后回到swagger点击右上角的绿色边框Authorize你会看到一个Value的输入框将复制的token前面加上Bearer 粘贴上去点确定即可Authorize
注意Bearer后面有一个空格哦
## 注意点 ① 在注册接口注册个账号,然后去请求登录接口,请求成功之后看下面的返回值 `accessToken`,复制这个 `token`
请先全局安装typescript、ts-node如安装请忽略 ② 回到 `Swagger`,点击右上角的绿色边框 `Authorize`,您会看到一个 `Value` 的输入框,将复制的 `token` 前面加上 `Bearer ` 粘贴上去,点确定即可,注意需要在 `Bearer` 后面加个一个空格
```
npm install -g typescript
npm install -g ts-node
```

12
nodemon.json Normal file
View File

@ -0,0 +1,12 @@
{
"restartable": "rs",
"ignore": [".git", "node_modules/", "dist/", "coverage/"],
"watch": ["src/"],
"execMap": {
"ts": "node -r ts-node/register"
},
"env": {
"NODE_ENV": "development"
},
"ext": "js,json,ts"
}

View File

@ -1,33 +1,32 @@
{ {
"name": "backend-ts", "name": "pure-admin-backend",
"version": "1.0.0", "version": "lastest",
"description": "API接口", "description": "pure-admin官方后端",
"main": "index.js",
"scripts": { "scripts": {
"build": "tsc", "start": "nodemon --config nodemon.json src/server.ts"
"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", "author": "xiaoxian521",
"license": "ISC", "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": { "devDependencies": {
"body-parser": "^1.19.0", "@types/express": "^4.17.14",
"nodemon": "^1.19.4", "@types/formidable": "^2.0.5",
"open": "^7.3.0", "@types/jsonwebtoken": "^8.5.9",
"tslint": "^5.20.1", "@types/node": "^18.11.9",
"typescript": "^3.9.7" "body-parser": "^1.20.1",
"dayjs": "^1.11.6",
"dotenv": "^16.0.3",
"esno": "^0.16.3",
"express": "^4.18.2",
"express-swagger-generator": "^1.1.17",
"formidable": "^2.0.1",
"jsonwebtoken": "^8.5.1",
"mysql2": "^2.3.3",
"nodemon": "^2.0.20",
"open": "^8.4.0",
"svg-captcha": "^1.4.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.4",
"winston": "^3.8.2"
}, },
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git" "repository": "https://github.com/xiaoxian521/pure-admin-backend"
} }

1736
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ import * as express from "express";
import * as bodyParser from "body-parser"; import * as bodyParser from "body-parser";
class App { class App {
public app: express.Application; public app;
constructor() { constructor() {
this.app = express(); this.app = express();
this.config(); this.config();

View File

@ -15,7 +15,7 @@ export default {
options: { options: {
swaggerDefinition: { swaggerDefinition: {
info: { info: {
description: "Pure-Admin官方接口", description: "pure-admin官方后端",
title: "Swagger", title: "Swagger",
version: require("../../package.json").version, version: require("../../package.json").version,
}, },
@ -34,10 +34,13 @@ export default {
}, },
route: { route: {
url: "./swagger-ui.html", url: "./swagger-ui.html",
docs: "/swagger.json", //swagger文件 api // swagger文件 api
docs: "/swagger.json",
}, },
basedir: __dirname, //app absolute path // app absolute path
files: ["../router/api/*.ts"], //Path to the API handle folder basedir: __dirname,
// path to the API handle folder
files: ["../router/api/*.ts"],
}, },
logs: { logs: {
level: process.env.LOG_LEVEL || "silly", level: process.env.LOG_LEVEL || "silly",

View File

@ -1,4 +1,4 @@
// 创建用户表 /** 创建用户表 */
const user = const user =
"CREATE TABLE if not EXISTS users(id int PRIMARY key auto_increment,username varchar(32),password varchar(32),time DATETIME)"; "CREATE TABLE if not EXISTS users(id int PRIMARY key auto_increment,username varchar(32),password varchar(32),time DATETIME)";

View File

@ -1,20 +1,22 @@
import * as dayjs from "dayjs";
import * as mysql from "mysql2"; import * as mysql from "mysql2";
import secret from "../../config"; import secret from "../../config";
import * as jwt from "jsonwebtoken"; import * as jwt from "jsonwebtoken";
import { createHash } from "crypto"; import { createHash } from "crypto";
import Logger from "../../loaders/logger"; import Logger from "../../loaders/logger";
import { Message } from "../../utils/enums";
import { Request, Response } from "express"; import { Request, Response } from "express";
import { createMathExpr } from "svg-captcha"; // import { createMathExpr } from "svg-captcha";
import getFormatDate from "../../utils/date"; import getFormatDate from "../../utils/date";
import { Code, Info } from "../../utils/infoEnum";
import { connection } from "../../utils/initMysql"; import { connection } from "../../utils/initMysql";
// import { formidable } from "formidable";
// let path = require("path");
export interface dataModel { /** 保存验证码 */
length: number; // let generateVerify: number;
}
// 保存验证码 /** 过期时间 单位:毫秒 默认 1分钟过期方便演示 */
let generateVerify: number; let expiresIn = 60000;
/** /**
* @typedef Error * @typedef Error
@ -26,11 +28,17 @@ let generateVerify: number;
* @property {[integer]} code * @property {[integer]} code
*/ */
// /**
// * @typedef Login
// * @property {string} username.required - 用户名 - eg: admin
// * @property {string} password.required - 密码 - eg: 123456
// * @property {integer} verify.required - 验证码
// */
/** /**
* @typedef Login * @typedef Login
* @property {string} username.required - - eg: admin * @property {string} username.required - - eg: admin
* @property {string} password.required - - eg: 123456 * @property {string} password.required - - eg: 123456
* @property {integer} verify.required -
*/ */
/** /**
@ -48,18 +56,21 @@ let generateVerify: number;
*/ */
const login = async (req: Request, res: Response) => { const login = async (req: Request, res: Response) => {
const { username, password, verify } = req.body; // const { username, password, verify } = req.body;
// if (generateVerify !== verify) return res.json({ // if (generateVerify !== verify) return res.json({
// code: Code.failCode, // success: false,
// info: Info[0] // data: {
// message: Message[0];
// }
// }) // })
const { username, password } = req.body;
let sql: string = let sql: string =
"select * from users where username=" + "'" + username + "'"; "select * from users where username=" + "'" + username + "'";
connection.query(sql, async function (err, data: dataModel) { connection.query(sql, async function (err, data: any) {
if (data.length == 0) { if (data.length == 0) {
await res.json({ await res.json({
code: Code.failCode, success: false,
info: Info[1], data: { message: Message[1] },
}); });
} else { } else {
if ( if (
@ -70,30 +81,57 @@ const login = async (req: Request, res: Response) => {
accountId: data[0].id, accountId: data[0].id,
}, },
secret.jwtSecret, secret.jwtSecret,
{ expiresIn: 20000 } { expiresIn }
); );
await res.json({ if (username === "admin") {
code: Code.successCode, await res.json({
info: Info[2], success: true,
expires: 20000, data: {
name: username, message: Message[2],
accessToken, username,
}); // 这里模拟角色,根据自己需求修改
roles: ["admin"],
accessToken,
// 这里模拟刷新token根据自己需求修改
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
expires: new Date(new Date()).getTime() + expiresIn,
},
});
} else {
await res.json({
success: true,
data: {
message: Message[2],
username,
// 这里模拟角色,根据自己需求修改
roles: ["common"],
accessToken,
// 这里模拟刷新token根据自己需求修改
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
expires: new Date(new Date()).getTime() + expiresIn,
},
});
}
} else { } else {
await res.json({ await res.json({
code: Code.failCode, success: false,
info: Info[3], data: { message: Message[3] },
}); });
} }
} }
}); });
}; };
// /**
// * @typedef Register
// * @property {string} username.required - 用户名
// * @property {string} password.required - 密码
// * @property {integer} verify.required - 验证码
// */
/** /**
* @typedef Register * @typedef Register
* @property {string} username.required - - eg: admin * @property {string} username.required -
* @property {string} password.required - - eg: 123456 * @property {string} password.required -
* @property {integer} verify.required -
*/ */
/** /**
@ -111,24 +149,25 @@ const login = async (req: Request, res: Response) => {
*/ */
const register = async (req: Request, res: Response) => { const register = async (req: Request, res: Response) => {
const { username, password, verify } = req.body; // const { username, password, verify } = req.body;
if (generateVerify !== verify) const { username, password } = req.body;
return res.json({ // if (generateVerify !== verify)
code: Code.failCode, // return res.json({
info: Info[0], // success: false,
}); // data: { message: Message[0] },
// });
if (password.length < 6) if (password.length < 6)
return res.json({ return res.json({
code: Code.failCode, success: false,
info: Info[4], data: { message: Message[4] },
}); });
let sql: string = let sql: string =
"select * from users where username=" + "'" + username + "'"; "select * from users where username=" + "'" + username + "'";
connection.query(sql, async (err, data: dataModel) => { connection.query(sql, async (err, data: any) => {
if (data.length > 0) { if (data.length > 0) {
await res.json({ await res.json({
code: Code.failCode, success: false,
info: Info[5], data: { message: Message[5] },
}); });
} else { } else {
let time = await getFormatDate(); let time = await getFormatDate();
@ -151,8 +190,8 @@ const register = async (req: Request, res: Response) => {
Logger.error(err); Logger.error(err);
} else { } else {
await res.json({ await res.json({
code: Code.successCode, success: true,
info: Info[6], data: { message: Message[6] },
}); });
} }
}); });
@ -181,7 +220,7 @@ const updateList = async (req: Request, res: Response) => {
const { username } = req.body; const { username } = req.body;
let payload = null; let payload = null;
try { try {
const authorizationHeader = req.get("Authorization"); const authorizationHeader = req.get("Authorization") as string;
const accessToken = authorizationHeader.substr("Bearer ".length); const accessToken = authorizationHeader.substr("Bearer ".length);
payload = jwt.verify(accessToken, secret.jwtSecret); payload = jwt.verify(accessToken, secret.jwtSecret);
} catch (error) { } catch (error) {
@ -201,8 +240,8 @@ const updateList = async (req: Request, res: Response) => {
Logger.error(err); Logger.error(err);
} else { } else {
await res.json({ await res.json({
code: Code.successCode, success: true,
info: Info[7], data: { message: Message[7] },
}); });
} }
}); });
@ -230,7 +269,7 @@ const deleteList = async (req: Request, res: Response) => {
const { id } = req.params; const { id } = req.params;
let payload = null; let payload = null;
try { try {
const authorizationHeader = req.get("Authorization"); const authorizationHeader = req.get("Authorization") as string;
const accessToken = authorizationHeader.substr("Bearer ".length); const accessToken = authorizationHeader.substr("Bearer ".length);
payload = jwt.verify(accessToken, secret.jwtSecret); payload = jwt.verify(accessToken, secret.jwtSecret);
} catch (error) { } catch (error) {
@ -242,8 +281,8 @@ const deleteList = async (req: Request, res: Response) => {
console.log(err); console.log(err);
} else { } else {
await res.json({ await res.json({
code: Code.successCode, success: true,
info: Info[8], data: { message: Message[8] },
}); });
} }
}); });
@ -273,7 +312,7 @@ const searchPage = async (req: Request, res: Response) => {
const { page, size } = req.body; const { page, size } = req.body;
let payload = null; let payload = null;
try { try {
const authorizationHeader = req.get("Authorization"); const authorizationHeader = req.get("Authorization") as string;
const accessToken = authorizationHeader.substr("Bearer ".length); const accessToken = authorizationHeader.substr("Bearer ".length);
payload = jwt.verify(accessToken, secret.jwtSecret); payload = jwt.verify(accessToken, secret.jwtSecret);
} catch (error) { } catch (error) {
@ -286,8 +325,8 @@ const searchPage = async (req: Request, res: Response) => {
Logger.error(err); Logger.error(err);
} else { } else {
await res.json({ await res.json({
code: Code.successCode, success: true,
info: data, data,
}); });
} }
}); });
@ -316,7 +355,7 @@ const searchVague = async (req: Request, res: Response) => {
const { username } = req.body; const { username } = req.body;
let payload = null; let payload = null;
try { try {
const authorizationHeader = req.get("Authorization"); const authorizationHeader = req.get("Authorization") as string;
const accessToken = authorizationHeader.substr("Bearer ".length); const accessToken = authorizationHeader.substr("Bearer ".length);
payload = jwt.verify(accessToken, secret.jwtSecret); payload = jwt.verify(accessToken, secret.jwtSecret);
} catch (error) { } catch (error) {
@ -324,8 +363,8 @@ const searchVague = async (req: Request, res: Response) => {
} }
if (username === "" || username === null) if (username === "" || username === null)
return res.json({ return res.json({
code: Code.failCode, success: false,
info: Info[9], data: { message: Message[9] },
}); });
let sql: string = "select * from users"; let sql: string = "select * from users";
sql += " WHERE username LIKE " + mysql.escape("%" + username + "%"); sql += " WHERE username LIKE " + mysql.escape("%" + username + "%");
@ -335,32 +374,34 @@ const searchVague = async (req: Request, res: Response) => {
Logger.error(err); Logger.error(err);
} else { } else {
await res.json({ await res.json({
code: Code.successCode, success: true,
info: data, data,
}); });
} }
}); });
}); });
}; };
/** // const upload = async (req: Request, res: Response) => {};
* @route GET /captcha
* @summary
* @group captcha -
* @returns {object} 200
* @security JWT
*/
const captcha = async (req: Request, res: Response) => { // /**
const create = createMathExpr({ // * @route GET /captcha
mathMin: 1, // * @summary 图形验证码
mathMax: 4, // * @group captcha - 图形验证码
mathOperator: "+", // * @returns {object} 200
}); // * @security JWT
generateVerify = Number(create.text); // */
res.type("svg"); // 响应的类型
res.json({ code: Code.successCode, info: create.text, svg: create.data }); // const captcha = async (req: Request, res: Response) => {
}; // const create = createMathExpr({
// mathMin: 1,
// mathMax: 4,
// mathOperator: "+",
// });
// generateVerify = Number(create.text);
// res.type("svg"); // 响应的类型
// res.json({ success: true, data: { text: create.text, svg: create.data } });
// };
export { export {
login, login,
@ -369,5 +410,6 @@ export {
deleteList, deleteList,
searchPage, searchPage,
searchVague, searchVague,
captcha, // upload,
// captcha,
}; };

View File

@ -1,5 +1,5 @@
import app from "./app"; import app from "./app";
import * as open from "open"; // import * as open from "open";
import config from "./config"; import config from "./config";
import { user } from "./models/mysql"; import { user } from "./models/mysql";
import Logger from "./loaders/logger"; import Logger from "./loaders/logger";
@ -16,7 +16,8 @@ import {
deleteList, deleteList,
searchPage, searchPage,
searchVague, searchVague,
captcha, // upload,
// captcha,
} from "./router/api/mysql"; } from "./router/api/mysql";
app.post("/login", (req, res) => { app.post("/login", (req, res) => {
@ -43,9 +44,13 @@ app.post("/searchVague", (req, res) => {
searchVague(req, res); searchVague(req, res);
}); });
app.get("/captcha", (req, res) => { // app.post("/upload", (req, res) => {
captcha(req, res); // upload(req, res);
}); // });
// app.get("/captcha", (req, res) => {
// captcha(req, res);
// });
app app
.listen(config.port, () => { .listen(config.port, () => {

View File

@ -1,11 +1,5 @@
// 状态码 /** 返回信息 */
export const enum Code { export enum Message {
failCode = -1,
successCode = 0,
}
// 返回信息
export enum Info {
"请输入正确的验证码", "请输入正确的验证码",
"账号尚未被注册", "账号尚未被注册",
"登录成功", "登录成功",

View File

@ -2,7 +2,7 @@ import * as mysql from "mysql2";
import mysqlConfig from "../config"; import mysqlConfig from "../config";
import Logger from "../loaders/logger"; import Logger from "../loaders/logger";
//user数据库 /** user数据库 */
export const connection = mysql.createConnection( export const connection = mysql.createConnection(
Object.assign({ database: "user" }, mysqlConfig.mysql) Object.assign({ database: "user" }, mysqlConfig.mysql)
); );

View File

@ -1,18 +1,16 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNEXT",
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node",
"pretty": true, "pretty": true,
"sourceMap": true, "sourceMap": true,
"target": "es6", "outDir": "dist",
"outDir": "./dist", "allowSyntheticDefaultImports": true
"baseUrl": "./lib"
}, },
"include": [ "include": ["src/**/*.ts", ],
"src/**/*.ts", "src/router/api/user.js"
],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"src/router/api/*.ts",
"dist" "dist"
] ]
} }

View File

@ -1,29 +0,0 @@
{
"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": []
}