first
This commit is contained in:
commit
4126d94026
|
@ -0,0 +1,31 @@
|
||||||
|
APP_PORT = 3000
|
||||||
|
|
||||||
|
MYSQL_DIALECT = mysql
|
||||||
|
MYSQL_HOST =
|
||||||
|
MYSQL_PORT = 3306
|
||||||
|
MYSQL_DBNAME =
|
||||||
|
MYSQL_USER =
|
||||||
|
MYSQL_PASSWD =
|
||||||
|
|
||||||
|
MARIA_DIALECT = mariadb
|
||||||
|
MARIA_HOST =
|
||||||
|
MARIA_PORT = 3306
|
||||||
|
MARIA_DBNAME =
|
||||||
|
MARIA_USER =
|
||||||
|
MARIA_PASSWD =
|
||||||
|
|
||||||
|
MONGO_DIALECT = mongodb
|
||||||
|
MONGO_HOST =
|
||||||
|
MONGO_PORT = 21017
|
||||||
|
MONGO_DBNAME =
|
||||||
|
MONGO_USER =
|
||||||
|
MONGO_PASSWD =
|
||||||
|
|
||||||
|
REDIS_DIALECT = redis
|
||||||
|
REDIS_HOST =
|
||||||
|
REDIS_PORT = 6379
|
||||||
|
REDIS_DBNAME =
|
||||||
|
REDIS_USER =
|
||||||
|
REDIS_PASSWD =
|
||||||
|
|
||||||
|
JWT_SECRET =
|
|
@ -0,0 +1,7 @@
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
.env*
|
||||||
|
!.env.template
|
||||||
|
|
||||||
|
upload/*
|
||||||
|
log/*
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "koa2-template",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "init a project with me",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon ./src/index.js",
|
||||||
|
"test":"node ./test/redis.test.js"
|
||||||
|
},
|
||||||
|
"author": "Hawkin",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"dotenv": "^16.0.0",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"koa": "^2.13.4",
|
||||||
|
"koa-body": "^5.0.0",
|
||||||
|
"koa-parameter": "^3.0.1",
|
||||||
|
"koa-router": "^10.1.1",
|
||||||
|
"koa-static": "^5.0.0",
|
||||||
|
"mariadb": "^3.0.0",
|
||||||
|
"mongoose": "^6.3.2",
|
||||||
|
"mysql2": "^2.3.3",
|
||||||
|
"redis": "^4.1.0",
|
||||||
|
"redis-connection-pool": "^2.0.2",
|
||||||
|
"sequelize": "^6.19.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.16"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = (err, ctx) => {
|
||||||
|
let status;
|
||||||
|
switch (err.code) {
|
||||||
|
default:
|
||||||
|
status = 500;
|
||||||
|
}
|
||||||
|
ctx.status = status;
|
||||||
|
ctx.body = err;
|
||||||
|
// console.log(err)
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const Koa = require('koa')
|
||||||
|
const KoaBody = require('koa-body')
|
||||||
|
const KoaStatic = require('koa-static')
|
||||||
|
const parameter = require('koa-parameter')
|
||||||
|
|
||||||
|
require('../config/env.config')
|
||||||
|
const router = require('../router')
|
||||||
|
const errHandler = require('./err.handler')
|
||||||
|
|
||||||
|
const app = new Koa()
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
KoaBody({
|
||||||
|
multipart: true,
|
||||||
|
formidable: {
|
||||||
|
uploadDir: path.join(__dirname, '../upload'),
|
||||||
|
keepExtensions: true,
|
||||||
|
},
|
||||||
|
parsedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
app.use(KoaStatic(path.join(__dirname, '../upload')))
|
||||||
|
app.use(parameter(app))
|
||||||
|
|
||||||
|
app.use(router.routes()).use(router.allowedMethods())
|
||||||
|
|
||||||
|
// 统一的错误处理
|
||||||
|
app.on('error', errHandler)
|
||||||
|
|
||||||
|
module.exports = app
|
|
@ -0,0 +1,2 @@
|
||||||
|
const { config } = require("dotenv");
|
||||||
|
config();
|
|
@ -0,0 +1,9 @@
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
|
||||||
|
const { MONGO_HOST, MONGO_PORT, MONGO_DBNAME, MONGO_USER, MONGO_PASSWD } =
|
||||||
|
process.env;
|
||||||
|
const URI = `mongodb://${MONGO_USER}:${MONGO_PASSWD}@${MONGO_HOST}:${MONGO_PORT}/${MONGO_DBNAME}`;
|
||||||
|
|
||||||
|
mongoose.connect(URI);
|
||||||
|
|
||||||
|
module.exports = { mongoose };
|
|
@ -0,0 +1,49 @@
|
||||||
|
const redis = require("redis");
|
||||||
|
// require("../config/env.config");
|
||||||
|
|
||||||
|
const { REDIS_HOST, REDIS_PORT, REDIS_DBNAME, REDIS_USER, REDIS_PASSWD } =
|
||||||
|
process.env; // REDIS_USER 和 REDIS_PASSWD,需要服务器配置ACL用户名密码
|
||||||
|
const URL = `redis://${REDIS_USER}:${REDIS_PASSWD}@${REDIS_HOST}:${REDIS_PORT}/${REDIS_DBNAME}`;
|
||||||
|
|
||||||
|
// https://github.com/redis/node-redis/tree/master/docs
|
||||||
|
// module.exports = redis.createClient({URL});
|
||||||
|
const client = redis.createClient({
|
||||||
|
// url: URL,
|
||||||
|
socket: { host: REDIS_HOST, port: REDIS_PORT },
|
||||||
|
password: REDIS_PASSWD,
|
||||||
|
// database: REDIS_DBNAME,
|
||||||
|
// legacyMode: true, // 语法向后(v3)部分兼容
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("error", (err) => console.log(`redis client ~ Error ${err}`));
|
||||||
|
client.on("connect", () => console.log("redis client ~ connect"));
|
||||||
|
client.on("reconnecting", () => console.log("redis client ~ reconnecting"));
|
||||||
|
client.on("ready", () => console.log("redis client ~ ready"));
|
||||||
|
client.on("end", () => console.log("redis client ~ end"));
|
||||||
|
|
||||||
|
client.connect();
|
||||||
|
client.ping();
|
||||||
|
// try {
|
||||||
|
// } catch (error) {
|
||||||
|
// console.log(error);
|
||||||
|
// }
|
||||||
|
|
||||||
|
module.exports = client;
|
||||||
|
|
||||||
|
//* 方式二:redis 连接池
|
||||||
|
//! unusable
|
||||||
|
/* var redisPool = require("redis-connection-pool")("myRedisPool", {
|
||||||
|
host: REDIS_HOST, // default
|
||||||
|
port: REDIS_PORT, //default
|
||||||
|
max_clients: 30, // defalut
|
||||||
|
perform_checks: false, // checks for needed push/pop functionality
|
||||||
|
database: 0, // database number to use
|
||||||
|
options: {
|
||||||
|
auth_pass: REDIS_PASSWD,
|
||||||
|
}, //options for createClient of node-redis, optional
|
||||||
|
});
|
||||||
|
redisPool.set("test-key", "foobar", function (err) {
|
||||||
|
redisPool.get("test-key", function (err, reply) {
|
||||||
|
console.log(reply); // 'foobar'
|
||||||
|
});
|
||||||
|
}); */
|
|
@ -0,0 +1,26 @@
|
||||||
|
const { Sequelize } = require("sequelize");
|
||||||
|
|
||||||
|
const {
|
||||||
|
MARIA_DIALECT,
|
||||||
|
MARIA_HOST,
|
||||||
|
MARIA_PORT,
|
||||||
|
MARIA_DBNAME,
|
||||||
|
MARIA_USRNAME,
|
||||||
|
MARIA_PASSWD,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
const seq = new Sequelize(MARIA_DBNAME, MARIA_USRNAME, MARIA_PASSWD, {
|
||||||
|
host: MARIA_HOST,
|
||||||
|
port: MARIA_PORT,
|
||||||
|
dialect: 'mariadb',
|
||||||
|
});
|
||||||
|
|
||||||
|
seq.authenticate()
|
||||||
|
.then(() => {
|
||||||
|
console.log('数据库连接成功')
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log('数据库连接失败', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = seq;
|
|
@ -0,0 +1,27 @@
|
||||||
|
const { Sequelize } = require("sequelize");
|
||||||
|
|
||||||
|
const {
|
||||||
|
MYSQL_DIALECT,
|
||||||
|
MYSQL_HOST,
|
||||||
|
MYSQL_PORT,
|
||||||
|
MYSQL_DBNAME,
|
||||||
|
MYSQL_USRNAME,
|
||||||
|
MYSQL_PASSWD,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
const seq = new Sequelize(MYSQL_DBNAME, MYSQL_USRNAME, MYSQL_PASSWD, {
|
||||||
|
host: MYSQL_HOST,
|
||||||
|
port: MYSQL_PORT,
|
||||||
|
dialect: MYSQL_DIALECT,
|
||||||
|
});
|
||||||
|
|
||||||
|
// seq
|
||||||
|
// .authenticate()
|
||||||
|
// .then(() => {
|
||||||
|
// console.log('数据库连接成功')
|
||||||
|
// })
|
||||||
|
// .catch(err => {
|
||||||
|
// console.log('数据库连接失败', err)
|
||||||
|
// })
|
||||||
|
|
||||||
|
module.exports = seq;
|
|
@ -0,0 +1,7 @@
|
||||||
|
import app from "./app/index.js";
|
||||||
|
|
||||||
|
const { APP_PORT } = process.env;
|
||||||
|
|
||||||
|
app.listen(APP_PORT, () => {
|
||||||
|
console.log(`server is running on http://localhost:${APP_PORT}`);
|
||||||
|
});
|
|
@ -0,0 +1,48 @@
|
||||||
|
const jwt = require('jsonwebtoken')
|
||||||
|
|
||||||
|
const { JWT_SECRET } = require('../config/config.default')
|
||||||
|
|
||||||
|
const {
|
||||||
|
tokenExpiredError,
|
||||||
|
invalidToken,
|
||||||
|
hasNotAdminPermission,
|
||||||
|
} = require('../constant/err.type')
|
||||||
|
|
||||||
|
const auth = async (ctx, next) => {
|
||||||
|
const { authorization = '' } = ctx.request.header
|
||||||
|
const token = authorization.replace('Bearer ', '')
|
||||||
|
// console.log(token)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// user中包含了payload的信息(id, user_name, is_admin)
|
||||||
|
const user = jwt.verify(token, JWT_SECRET)
|
||||||
|
ctx.state.user = user
|
||||||
|
} catch (err) {
|
||||||
|
switch (err.name) {
|
||||||
|
case 'TokenExpiredError':
|
||||||
|
console.error('token已过期', err)
|
||||||
|
return ctx.app.emit('error', tokenExpiredError, ctx)
|
||||||
|
case 'JsonWebTokenError':
|
||||||
|
console.error('无效的token', err)
|
||||||
|
return ctx.app.emit('error', invalidToken, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
const hadAdminPermission = async (ctx, next) => {
|
||||||
|
const { is_admin } = ctx.state.user
|
||||||
|
|
||||||
|
if (!is_admin) {
|
||||||
|
console.error('该用户没有管理员的权限', ctx.state.user)
|
||||||
|
return ctx.app.emit('error', hasNotAdminPermission, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
auth,
|
||||||
|
hadAdminPermission,
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
const bcrypt = require('bcryptjs')
|
||||||
|
|
||||||
|
const { getUerInfo } = require('../service/user.service')
|
||||||
|
const {
|
||||||
|
userFormateError,
|
||||||
|
userAlreadyExited,
|
||||||
|
userRegisterError,
|
||||||
|
userDoesNotExist,
|
||||||
|
userLoginError,
|
||||||
|
invalidPassword,
|
||||||
|
} = require('../constant/err.type')
|
||||||
|
|
||||||
|
const userValidator = async (ctx, next) => {
|
||||||
|
const { user_name, password } = ctx.request.body
|
||||||
|
// 合法性
|
||||||
|
if (!user_name || !password) {
|
||||||
|
console.error('用户名或密码为空', ctx.request.body)
|
||||||
|
ctx.app.emit('error', userFormateError, ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyUser = async (ctx, next) => {
|
||||||
|
const { user_name } = ctx.request.body
|
||||||
|
|
||||||
|
// if (await getUerInfo({ user_name })) {
|
||||||
|
// ctx.app.emit('error', userAlreadyExited, ctx)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
try {
|
||||||
|
const res = await getUerInfo({ user_name })
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
console.error('用户名已经存在', { user_name })
|
||||||
|
ctx.app.emit('error', userAlreadyExited, ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取用户信息错误', err)
|
||||||
|
ctx.app.emit('error', userRegisterError, ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
const crpytPassword = async (ctx, next) => {
|
||||||
|
const { password } = ctx.request.body
|
||||||
|
|
||||||
|
const salt = bcrypt.genSaltSync(10)
|
||||||
|
// hash保存的是 密文
|
||||||
|
const hash = bcrypt.hashSync(password, salt)
|
||||||
|
|
||||||
|
ctx.request.body.password = hash
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyLogin = async (ctx, next) => {
|
||||||
|
// 1. 判断用户是否存在(不存在:报错)
|
||||||
|
const { user_name, password } = ctx.request.body
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getUerInfo({ user_name })
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
console.error('用户名不存在', { user_name })
|
||||||
|
ctx.app.emit('error', userDoesNotExist, ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 密码是否匹配(不匹配: 报错)
|
||||||
|
if (!bcrypt.compareSync(password, res.password)) {
|
||||||
|
ctx.app.emit('error', invalidPassword, ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return ctx.app.emit('error', userLoginError, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
userValidator,
|
||||||
|
verifyUser,
|
||||||
|
crpytPassword,
|
||||||
|
verifyLogin,
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
const { DataTypes } = require('sequelize')
|
||||||
|
|
||||||
|
const seq = require('../../db/seq.maria')
|
||||||
|
|
||||||
|
// 创建模型(Model zd_user -> 表 zd_users)
|
||||||
|
const User = seq.define('zd_user', {
|
||||||
|
// id 会被sequelize自动创建, 管理
|
||||||
|
user_name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true,
|
||||||
|
comment: '用户名, 唯一',
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: DataTypes.CHAR(64),
|
||||||
|
allowNull: false,
|
||||||
|
comment: '密码',
|
||||||
|
},
|
||||||
|
is_admin: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
comment: '是否为管理员, 0: 不是管理员(默认); 1: 是管理员',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 强制同步数据库(创建数据表)
|
||||||
|
// User.sync({ force: true })
|
||||||
|
|
||||||
|
module.exports = User
|
|
@ -0,0 +1,24 @@
|
||||||
|
const { mongoose } = require("../../db/mgs.mongo");
|
||||||
|
|
||||||
|
const BookSchema = mongoose.Schema({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
require: true,
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
wordCount: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
publishTime: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Book = mongoose.model("Book", BookSchema);
|
||||||
|
|
||||||
|
// let book = new Book({});
|
||||||
|
|
||||||
|
module.exports = { Book };
|
|
@ -0,0 +1,14 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const Router = require('koa-router')
|
||||||
|
const router = new Router()
|
||||||
|
|
||||||
|
fs.readdirSync(__dirname).forEach(file => {
|
||||||
|
// console.log(file)
|
||||||
|
if (file !== 'index.js') {
|
||||||
|
let r = require('./' + file)
|
||||||
|
router.use(r.routes())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -0,0 +1,42 @@
|
||||||
|
const User = require('../../model/maria/use.model')
|
||||||
|
|
||||||
|
class UserService {
|
||||||
|
async createUser(user_name, password) {
|
||||||
|
// 插入数据
|
||||||
|
// await表达式: promise对象的值
|
||||||
|
const res = await User.create({ user_name, password })
|
||||||
|
// console.log(res)
|
||||||
|
return res.dataValues
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUerInfo({ id, user_name, password, is_admin }) {
|
||||||
|
const whereOpt = {}
|
||||||
|
|
||||||
|
id && Object.assign(whereOpt, { id })
|
||||||
|
user_name && Object.assign(whereOpt, { user_name })
|
||||||
|
password && Object.assign(whereOpt, { password })
|
||||||
|
is_admin && Object.assign(whereOpt, { is_admin })
|
||||||
|
|
||||||
|
const res = await User.findOne({
|
||||||
|
attributes: ['id', 'user_name', 'password', 'is_admin'],
|
||||||
|
where: whereOpt,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res ? res.dataValues : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateById({ id, user_name, password, is_admin }) {
|
||||||
|
const whereOpt = { id }
|
||||||
|
const newUser = {}
|
||||||
|
|
||||||
|
user_name && Object.assign(newUser, { user_name })
|
||||||
|
password && Object.assign(newUser, { password })
|
||||||
|
is_admin && Object.assign(newUser, { is_admin })
|
||||||
|
|
||||||
|
const res = await User.update(newUser, { where: whereOpt })
|
||||||
|
// console.log(res)
|
||||||
|
return res[0] > 0 ? true : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new UserService()
|
|
@ -0,0 +1,5 @@
|
||||||
|
const { Book } = require("../../model/mongo/book.model");
|
||||||
|
|
||||||
|
let book = new Book({});
|
||||||
|
|
||||||
|
module.exports = { book };
|
|
@ -0,0 +1,25 @@
|
||||||
|
const client = require("../../db/redis");
|
||||||
|
|
||||||
|
function save2Redis(key, value) {
|
||||||
|
client.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByKey(key, callback = null) {
|
||||||
|
if (callback) {
|
||||||
|
client.get(key, callback);
|
||||||
|
} else {
|
||||||
|
client.get(key, (err, value) => {
|
||||||
|
if (err) return null;
|
||||||
|
console.log(value)
|
||||||
|
// return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Object.prototype.toString = function () {
|
||||||
|
return JSON.stringify(this);
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = { client, save2Redis, getByKey };
|
|
@ -0,0 +1,2 @@
|
||||||
|
const { book } = require("../src/service/mongo/book.service");
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
const {
|
||||||
|
client,
|
||||||
|
save2Redis,
|
||||||
|
getByKey,
|
||||||
|
} = require("../src/service/redis/base.service");
|
||||||
|
|
||||||
|
save2Redis("key1", "i am string value");
|
||||||
|
// redis 储存时默认调用 Object.toString 而非 JSON.stringify
|
||||||
|
// save2Redis("key2", { key: "i am string value from Object.toString()" }); // value是object,报参数类型错误
|
||||||
|
|
||||||
|
getByKey("key2"); // 无console输出
|
||||||
|
|
||||||
|
client.rPush("alist", "abc");
|
||||||
|
// client.rPush("alist", 123); // value是object,报参数类型错误
|
||||||
|
client.lRange("alist", 0, -1, (err, v) => {
|
||||||
|
console.log(v); // 无console输出
|
||||||
|
client.disconnect();
|
||||||
|
});
|
Loading…
Reference in New Issue