搭建框架
mkdir egg-example && cd egg-example
npm init egg --type=simple
npm i
npm i egg-bag-framework // 安装框架覆盖多种模块和工具
完整代码下载
链接:pan.baidu.com/s/1QGKSyjlI…
提取码:ai46
下载完成后,安装框架
npm i egg-bag-framework
// package.json
"egg": {
"framework": "egg-bag-framework" // 作为启动入口
},
可选配置 config.default.js
/* eslint valid-jsdoc: "off" */
'use strict';
/**
* @param {Egg.EggAppInfo} appInfo app info
*/
module.exports = appInfo => {
const config = exports = {};
config.mysql = {
client: {
host: '127.0.0.1',
port: '3306',
user: 'itnavs',
password: '123456',
database: 'itnavs',
},
app: true,
agent: false,
};
config.redis = {
client: {
port: 6379,
host: '127.0.0.1',
password: 'auth',
db: 0,
},
};
config.sequelize = {
dialect: 'mysql',
database: 'itnavs',
host: '127.0.0.1',
port: '3306',
username: 'itnavs',
password: '123456',
underscored: false,
timezone: '+08:00',
define: {
timestamps: true,
freezeTableName: true,
},
};
return {
...config,
};
};
本地安装mysql
安装-PHPStudy(小皮)V8.1
本地redis
安装-本地redis
egg-bag-framework 框架说明
内置多种模块,中间件以及工具
源码地址,
演示地址
中间件
校验sing签名是否合法,防止随意发起请求
'use strict';
module.exports = (option, app) => {
return async function sing(ctx, next) {
const sing = ctx.request.header.sing;
const { domain, expireTime, cache } = ctx.app.config.website;
const default_cache = 'redis';
if (sing) {
let getSing = null;
if (cache === default_cache) {
getSing = await app.redis.get(sing);
} else {
getSing = await app.lru.get(sing);
}
if (getSing) {
ctx.body = ctx.resultData({ msg: 'sing签名已过期' }); // 在存在说明既过期
} else {
try {
const decSing = ctx.helper.aesDecrypt(sing);
const singData = JSON.parse(decSing);
if (singData.domain === domain) {
if (cache === default_cache) {
await app.redis.set(sing, 1);
} else {
await app.lru.set(sing, 1);
}
await app.redis.set(sing, 1);
await app.redis.expire(sing, expireTime);
await next();
} else {
ctx.body = ctx.resultData({ msg: 'sing签名不合法,缺少字符串' });
}
} catch (e) {
ctx.body = ctx.resultData({ msg: 'sing签名不合法' });
}
}
} else {
ctx.body = ctx.resultData({ msg: '缺少sing签名' });
}
};
};
限流中间件
防止接口被恶意盗刷,如果被攻击了,请求使用nginx或者服务器配置白黑名单
'use strict';
const { RateLimiterMemory } = require('rate-limiter-flexible'); // 限流中间件
module.exports = () => {
// 创建一个基于内存的令牌桶速率限制器,每秒限制 12 次请求
const opts = {
points: 12,
duration: 1,
};
const rateLimiter = new RateLimiterMemory(opts);
return async function limiter(ctx, next) {
rateLimiter.consume(ctx.request.ip)
.then(rateLimiterRes => {
next();
})
.catch(rateLimiterRes => {
ctx.body = ctx.resultData({ msg: '触发限流了', code: 2001 });
});
};
};
验证jwt
'use strict';
module.exports = () => {
return async function authority(ctx, next) {
const authorization = ctx.request.header.authorization;
if (authorization) {
try {
ctx.helper.verifyToken(authorization); // 验证jwt
await next();
} catch (err) {
ctx.body = ctx.resultData({ msg: 'access_token过期', code: 1003 });
}
} else {
ctx.body = ctx.resultData({ msg: '缺少access_token', code: 1003 });
}
};
};
内置模块
jwt
(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的
// plugin.js
exports.jwt = {
enable: true,
package: 'egg-jwt',
};
// config.default.js
config.jwt = {
secret: 'ABCD20231017QWERYSUNXSJL', // 可以自定义
sign: {
expiresIn: 8 * 60 * 60, // 过期时间8小时
},
};
Validate
参数校验模块
// plugin.js
exports.validate = {
enable: true,
package: 'egg-validate',
};
// config.default.js
config.validate = {
convert: true,
translate() {
const args = Array.prototype.slice.call(arguments);
return I18n.__.apply(I18n, args);
},
};
redis
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理
// plugin.js
exports.redis = {
enable: true,
package: 'egg-redis',
};
// config.default.js
config.redis = {
client: {
port: 6379,
host: '127.0.0.1',
password: 'auth',
db: 0,
},
}
lru
本地缓存
exports.lru = {
enable: true,
package: 'egg-lru', // 本地缓存
};
config.lru = {
client: {
max: 3000, // 所有lru缓存配置可用
maxAge: 1000 * 60 * 30, // 60 min cache
},
app: true, // 加载到app中,默认是打开的
agent: false, // 加载到代理中,默认为关闭
};
上传模式
// config.default.js
config.multipart = {
mode: 'file',
fileSize: '50mb', // 接收文件大小
whitelist: [ // 允许接收的文件类型
'.png',
'.jpg',
'.webp',
'.gif',
'.zip',
'.doc',
'.docx',
'.txt',
'.xlsx',
'.pdf',
'.mp4',
'.webm',
'.mov',
'.flv',
'.avi',
'.f4v',
'.mov',
'.m4v',
'.rmvb',
'.rm',
'.mpg',
'.mpeg',
],
};
Sequelize
Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能
// plugin.js
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
config.sequelize = {
dialect: 'mysql',
database: 'pm_webleading',
host: '127.0.0.1',
port: '3306',
username: 'pm_webleading',
password: '123456',
underscored: false,
timezone: '+08:00',
define: {
timestamps: true,
freezeTableName: true,
},
};
Mysql
MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一
// plugin.js
exports.mysql = {
enable: true,
package: 'egg-mysql',
};
// config.default.js
config.mysql = {
client: {
// host
host: '127.0.0.1',
// 端口号
port: '3306',
// 用户名
user: 'pm_webleading',
// 密码
password: '123456',
// 数据库名
database: 'pm_webleading',
},
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
};
内置工具
已挂在egg.js,ctx对象上调用方法如下
'use strict';
const {Controller} = require('egg');
class HomeController extends Controller {
async index() {
const {ctx} = this;
try {
for (const file of ctx.request.files) {
const filePath = await ctx.helper.uploadLocaFile({file}) // cxt.helper.xxxx
}
} finally {
await ctx.cleanupRequestFiles();
}
}
}
module.exports = HomeController;
上传文件本地
可以上传以上配置类型的文件
uploadLocaFile({ file, filePath }) {
const { ctx } = this;
return new Promise(async (resolve, reject) => {
try {
const filename = file.filename;
const extname = path.extname(filename);
const _filePath = filePath || `public/upload/${ctx.helper.nanoid()}${extname}`;
const localPath = path.join(ctx.app.baseDir, 'app', _filePath);
// 读取文件
const source = fs.createReadStream(file.filepath);
// 创建写入流
const target = fs.createWriteStream(localPath);
await pump(source, target);
resolve(_filePath);
} catch (err) {
reject(err);
}
});
}
上传图片-带压缩
只能上传图片
uploadLocalImage({ file, filePath, width = 500, quality = 75 }) {
const { ctx } = this;
const extname = path.extname(file.filename);
const _filePath = filePath || `public/image/${ctx.helper.nanoid()}${extname}`;
const localPath = path.join(ctx.app.baseDir, 'app', _filePath);
return new Promise((resolve, reject) => {
Jimp.read(file.filepath)
.then(image => {
image.resize(width, Jimp.AUTO)
.quality(quality)
.write(localPath);
resolve(_filePath);
})
.catch(err => {
reject(err);
});
});
}
对称加密
aesEncrypt(data, options) {
options = Object.assign({ key: this.app.config.website.key, iv: this.app.config.website.iv }, options);
let str = data;
if (typeof data === 'object') {
str = JSON.stringify(data);
}
str = CryptoJS.enc.Utf8.parse(str);
const crypto = CryptoJS.AES.encrypt(str, CryptoJS.enc.Utf8.parse(options.key), {
iv: CryptoJS.enc.Utf8.parse(options.iv),
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return crypto.toString(); // 对称加密内容
},
对称解密
aesDecrypt(data, options) {
options = Object.assign({ key: this.app.config.website.key, iv: this.app.config.website.iv }, options);
const decrypt = CryptoJS.AES.decrypt(data, CryptoJS.enc.Utf8.parse(options.key), {
iv: CryptoJS.enc.Utf8.parse(options.iv),
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return CryptoJS.enc.Utf8.stringify(decrypt); // 对称解密内容
},
非对称加密
encrypt(str, options) {
options = Object.assign({ publicKey: this.app.config.website.publicKey }, options);
const encrypted = new JSEncrypt();
encrypted.setPublicKey(options.publicKey.toString());
return encrypted.encrypt(str); // 非对称加密字符串
},
非对称解密
decrypt(str, options) {
options = Object.assign({ privateKey: this.app.config.website.privateKey }, options);
const decrypted = new JSEncrypt(); // 创建解密对象实例
decrypted.setPrivateKey(options.privateKey.toString()); // 设置私钥
return decrypted.decrypt(str); // 非对称解密内容
},
md5加密
md5(data) {
let str = data;
if (typeof data === 'object') {
str = JSON.stringify(data);
}
return CryptoJS.MD5(str)
.toString();
},
随机ID
nanoid(size = 12) {
const nanoid = customAlphabet(alphabet.join(''), size);
if (size >= 12) {
return dayjs()
.format('YYYYMMDD') + nanoid(); // 获取不重复随机ID
}
return nanoid(); // 获取重复随机ID
},
jwt-生成token-校验token
generateToken(data) {
return this.app.jwt.sign(data, this.app.config.jwt.secret); // 生成token
},
verifyToken(token) {
return this.app.jwt.verify(token, this.app.config.jwt.secret); // 验证token
},
源码地址
github.com/hangjob/egg…
github.com/hangjob/vue…