add(mariadb,redis): 增加示例的代码

This commit is contained in:
yoga 2022-07-30 19:16:28 +08:00
parent b1fc491019
commit 0a54e36c20
17 changed files with 363 additions and 1 deletions

2
initProject.ps1 Normal file
View File

@ -0,0 +1,2 @@
nvm
yarn

View File

@ -1,6 +1,6 @@
{
"name": "koa2-template",
"version": "1.0.0",
"version": "1.0.1",
"description": "init a project with me",
"main": "index.js",
"scripts": {

0
public/js/.example.js Normal file
View File

1
scripts/clean.ps1 Normal file
View File

@ -0,0 +1 @@
# 删除项目中的 占位/示例 文件(.example.*),不影响项目内容

12
src/controller/user.js Normal file
View File

@ -0,0 +1,12 @@
const { d } = require("../models/user");
class UserController {
async register(ctx, next) {
ctx.json({ code: 0, message: "success" });
}
async getUserList(ctx, next) {
ctx.json({ code: 0, message: "success" });
}
}
module.exports = new UserController();

0
src/data/upload/.example Normal file
View File

27
src/db/mariadb.js Normal file
View File

@ -0,0 +1,27 @@
// connect to third-service of database or other.
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;

49
src/db/redis.js Normal file
View File

@ -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'
});
}); */

View File

@ -0,0 +1,49 @@
const jwt = require('jsonwebtoken')
const { JWT_SECRET } = require('../config/config.default')
const {
tokenExpiredError,
invalidToken,
hasNotAdminPermission,
} = require('../constant/error.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,
}

View File

@ -0,0 +1,92 @@
const bcrypt = require('bcryptjs')
const { getUerInfo } = require('../service/user.service')
const {
userFormateError,
userAlreadyExited,
userRegisterError,
userDoesNotExist,
userLoginError,
invalidPassword,
} = require('../constant/error.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,
}

30
src/models/user.js Normal file
View File

@ -0,0 +1,30 @@
const { DataTypes } = require('sequelize')
const seq = require('../db/mariadb')
// 创建模型(Model user -> 数据库表 users)
const User = seq.define('user', {
// id 会被sequelize自动创建, 管理
username: {
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

6
src/routers/user.js Normal file
View File

@ -0,0 +1,6 @@
const Router = require("koa-router");
const router = new Router({ prefix: "/user" });
router.post("/list", getUserList);
module.exports = router;

29
src/services/redis.js Normal file
View File

@ -0,0 +1,29 @@
const redisClient = require("../../db/redis");
/**
Object.prototype.toString = function () {
return JSON.stringify(this);
};
*/
class RedisService {
client = redisClient;
save2Redis(key, value) {
redisClient.set(key, value);
}
getByKey(key, callback = null) {
if (callback) {
redisClient.get(key, callback);
} else {
redisClient.get(key, (err, value) => {
if (err) return null;
console.log(value);
// return value;
});
}
}
}
module.exports = new RedisService();

42
src/services/user.js Normal file
View File

@ -0,0 +1,42 @@
const User = require('../models/user')
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()

5
src/util/bcrypt.js Normal file
View File

@ -0,0 +1,5 @@
const bcryptjs = require("bcryptjs");
class Bcrypt {}
module.exports = new Bcrypt();

0
src/views/.example Normal file
View File

18
test/.example.redis.js Normal file
View File

@ -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();
});