diff --git a/app.test.js b/app.test.js new file mode 100644 index 0000000..9965c87 --- /dev/null +++ b/app.test.js @@ -0,0 +1,55 @@ +// https://molunerfinn.com/Use-Jest-To-Test-Vue-Koa/#Koa%E5%90%8E%E7%AB%AFApi%E6%B5%8B%E8%AF%95 +//https://jestjs.io/docs/api#testeachtablename-fn-timeout +//Jest CLI选项 : https://runebook.dev/zh-CN/docs/jest/cli#--forceexit +//匹配器:https://www.jianshu.com/p/c1b5676c1edd +const bamboo = require('./lib/main') +const glob = require("glob") +const path = require('path') +import request from 'supertest'; + +let server = "" +let list = [] + +/*当前根目录*/ +function isPath(args) { + return path.resolve('./' + (args || "")) +} + +function load(directory) { + const path_root = isPath() + let files = glob.sync(directory) + files = files.map(item => { + const parse = path.parse(item); + parse.father = path.basename(path.resolve(parse.dir, '..')) + parse.file_father = path.basename(path.resolve(parse.dir + '/' + parse.name, '..')) + return { + parse, + res: require(path.resolve(path_root + "/" + item)) + } + }) + return files +} + +list = load("app/*/test/*.js") + +beforeAll(() => { + return new Promise((resolve, reject) => { + bamboo.fullList.push(async (app) => { + server = app.server + //回复测试数据镜像sql文件 + await app.db.tool.reduction() + resolve() + }) + }) + +}); +afterAll(() => { + server.close(); + console.log('服务器关闭!'); +}); + + +describe.each(list)('测试清单名称:$res.describe_name', (testDescribe) => { + test.each(testDescribe.res.testList)('测试名称:$test_name', async (o) => await o.fun(request, server)); +}); + diff --git a/app/cms/init.js b/app/cms/init.js deleted file mode 100644 index 079ab68..0000000 --- a/app/cms/init.js +++ /dev/null @@ -1,4 +0,0 @@ -/*系统启动后要做初始化*/ -module.exports = (app) => { - console.log("启动完成"); -} diff --git a/app/cms/model/User.js b/app/cms/model/User.js deleted file mode 100644 index 2d2196d..0000000 --- a/app/cms/model/User.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - doc: "用户", - api: true,//是否需要生成api接口 - model: { - uid: {type: "STRING", comment: '用户id'}, - age: {type: "INTEGER", comment: '年龄', defaultValue: 0}, - age2: {type: "INTEGER", comment: '年龄', defaultValue: 0}, - }, -} - - diff --git a/app/cms/model/UserInfo.js b/app/cms/model/UserInfo.js deleted file mode 100644 index 8637425..0000000 --- a/app/cms/model/UserInfo.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - doc : "用户信息表", - model: { - uid : {type: "STRING", comment: '用户id'}, - phone: {type: "STRING", comment: '手机号', defaultValue: null,}, - - } -} diff --git a/app/err/api/index.js b/app/err/api/index.js new file mode 100644 index 0000000..c8741b6 --- /dev/null +++ b/app/err/api/index.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = { + // path : "cms/api/index",//可以覆盖自动生成的路由地址 + params: { + "age": "int?" + }, + fun : async (ctx, app) => { + + const body = ctx.params + const User = await app.db.table("User").find() + //成功返回的2种方式 + // ctx.body = app.res.success(User,"success",200)//"success",200可以省略 + return User + + //异常返回的2种方式 + // ctx.body = app.res.error(User, "error", 400)//"error",400可以省略 + // return false + } +} diff --git a/app/message/api/joinRoom.js b/app/message/api/joinRoom.js new file mode 100644 index 0000000..3fd1cca --- /dev/null +++ b/app/message/api/joinRoom.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = { + name:"加入房间", + doc:``, + params: { + "type": ["accountPassword"],//登录类型 + "data": "object",//数据 + }, + fun : async (ctx, app) => { + const {type, data} = ctx.params + if (await $components.login[type](data)) { + return $res.success({ + token: await $components.jwt.sign({}) + }, "登录成功") + } + + + return $res.error({}, "登录失败") + + } +} diff --git a/app/message/io/disconnect.js b/app/message/io/disconnect.js new file mode 100644 index 0000000..354c9bc --- /dev/null +++ b/app/message/io/disconnect.js @@ -0,0 +1,12 @@ +module.exports = { + name : "离开房间", + doc : ``, + params: { + // "room_name": 'string',//房间 + // "uid" : 'string',//用户id + }, + fun : (socket, {msg, onname, callback}) => { + console.log('断线:', socket.id); + + } +} diff --git a/app/message/io/joinRoom.js b/app/message/io/joinRoom.js new file mode 100644 index 0000000..da11d7f --- /dev/null +++ b/app/message/io/joinRoom.js @@ -0,0 +1,20 @@ +module.exports = { + name : "加入房间", + doc : ``, + params: { + "room_name": 'string',//房间 + "uid" : 'string',//用户id + }, + fun : (socket, {msg, onname, callback}) => { + console.log('加入房间:', msg); + socket.join(msg.room_name)//加入房间 + socket.emit('success', { + event: onname, + res : app.res.success(""), + }) + socket.to(msg.room_name).emit('success', { + event: onname, + res : app.res.success(msg, "有人加入房间"), + }) + } +} diff --git a/app/message/io/leaveRoom.js b/app/message/io/leaveRoom.js new file mode 100644 index 0000000..6786c34 --- /dev/null +++ b/app/message/io/leaveRoom.js @@ -0,0 +1,12 @@ +module.exports = { + name : "离开房间", + doc : ``, + params: { + // "room_name": 'string',//房间 + // "uid" : 'string',//用户id + }, + fun : (socket, {msg, onname, callback}) => { + console.log('离开房间:', socket.id); + + } +} diff --git a/app/message/io/test.js b/app/message/io/test.js deleted file mode 100644 index a294951..0000000 --- a/app/message/io/test.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (app, msg, callback) => { - console.log('接收到的消息:', msg); -} diff --git a/app/message/model/Contact.js b/app/message/model/Contact.js new file mode 100644 index 0000000..1f53a7b --- /dev/null +++ b/app/message/model/Contact.js @@ -0,0 +1,12 @@ +module.exports = { + doc: "联系人表", + api: true,//是否需要生成api接口 + model: { + uid: {type: "STRING", comment: '用户id/群id'}, + to_uid: {type: "STRING", comment: '接收人用户id'}, + type: {type: "STRING", comment: '类型,单人/群:one/group'}, + status: {type: "STRING", comment: '状态:已结束/未结束:y/n'}, + }, +} + + diff --git a/app/message/model/Message.js b/app/message/model/Message.js new file mode 100644 index 0000000..a1ca813 --- /dev/null +++ b/app/message/model/Message.js @@ -0,0 +1,20 @@ +module.exports = { + doc: "消息表", + api: true,//是否需要生成api接口 + model: { + uid: {type: "STRING", comment: '发送人用户id'}, + name: {type: "STRING", comment: '发送人名称'}, + avatar: {type: "STRING", comment: '发送人头像'}, + to_uid: {type: "STRING", comment: '接收人用户id'}, + to_name: {type: "STRING", comment: '接收人名称'}, + to_avatar: {type: "STRING", comment: '接收人头像'}, + status: {type: "STRING", comment: '状态:已读/未读:y/n'}, + type: {type: "STRING", comment: '消息类型:file / image / text / event'}, + sendTime: {type: "STRING", comment: '消息发送时间'}, + content: {type: "STRING", comment: '消息内容,如果type=file,此属性表示文件的URL地址'}, + fileSize: {type: "STRING", comment: '文件大小'}, + fileName: {type: "STRING", comment: '文件名称'}, + }, +} + + diff --git a/app/message/model/autoRes.js b/app/message/model/autoRes.js new file mode 100644 index 0000000..99559be --- /dev/null +++ b/app/message/model/autoRes.js @@ -0,0 +1,10 @@ +module.exports = { + doc: "自动回复表", + api: true,//是否需要生成api接口 + model: { + type: {type: "STRING", comment: '类型,自动回复/问候语:auto/hello'}, + status: {type: "STRING", comment: '状态:显示/隐藏:y/n'}, + }, +} + + diff --git a/app/message/test/index.js b/app/message/test/index.js new file mode 100644 index 0000000..5a5f0f5 --- /dev/null +++ b/app/message/test/index.js @@ -0,0 +1,39 @@ +module.exports = { + describe_name: "permission/权限管理", + testList : [ + { + test_name: "账号密码注册", + fun : async (request, server) => { + const response = await request(server) + .post('/permission/api/registered') + .send({ + type: "accountPassword", + data: { + account : "test", + password: "123456", + }, + }) + // expect(response.status).toEqual(200) + expect(response.body.status).toEqual(200) + expect(response.body.data).toHaveProperty('token') + } + }, + { + test_name: "账号密码登录", + fun : async (request, server) => { + const response = await request(server) + .post('/permission/api/login') + .send({ + type: "accountPassword", + data: { + account : "test", + password: "123456", + }, + }) + // expect(response.status).toEqual(200) + expect(response.body.status).toEqual(200) + expect(response.body.data).toHaveProperty('token') + } + }, + ] +} diff --git a/app/permission/api/login.js b/app/permission/api/login.js new file mode 100644 index 0000000..3aa1e6b --- /dev/null +++ b/app/permission/api/login.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = { + params: { + "type": ["accountPassword"],//登录类型 + "data": "object",//数据 + }, + fun : async (ctx, app) => { + const {type, data} = ctx.params + if (await $components.login[type](data)) { + return $res.success({ + token: await $components.jwt.sign({}) + }, "登录成功") + } + + + return $res.error({}, "登录失败") + + } +} diff --git a/app/permission/api/registered.js b/app/permission/api/registered.js new file mode 100644 index 0000000..d9835e8 --- /dev/null +++ b/app/permission/api/registered.js @@ -0,0 +1,19 @@ +'use strict'; + +module.exports = { + params: { + "type": ["accountPassword"],//注册类型 + "data": "object",//数据 + }, + fun : async (ctx, app) => { + const {type, data} = ctx.params + if (await $components.registered[type](data)) { + return $res.success({ + token: await $components.jwt.sign({}) + }, "注册成功") + } + + return $res.error({}, "登录失败") + + } +} diff --git a/app/permission/components/authentication/index.js b/app/permission/components/authentication/index.js new file mode 100644 index 0000000..b4ed191 --- /dev/null +++ b/app/permission/components/authentication/index.js @@ -0,0 +1,26 @@ +module.exports = { + //获取用户所属组织信息 + async getUserToOrg(uid) { + + }, + //获取用户所属分组信息 + async getUserToGroup(uid) { + + }, + //获取用户权限 + async getUserToPermission(uid) { + + }, + //获取用户角色信息 + async getUserToRole(uid) { + + }, + //获取用户组织/分组/角色/权限 + async getUserAll(uid) { + + }, + //获取组织下全部分组 + async getOrgToGroupAll(uid) { + + }, +} diff --git a/app/permission/components/encrypt/index.js b/app/permission/components/encrypt/index.js new file mode 100644 index 0000000..f7aa6ba --- /dev/null +++ b/app/permission/components/encrypt/index.js @@ -0,0 +1,23 @@ +const crypto = require('crypto'); +module.exports = { + // 随机数(盐值) + getRandomSalt() { + return Math.random().toString().slice(2, 5); + }, + // 加密用户密码(原始密码,盐值) + // 密码同样是123456,由于采用了随机盐值,前后运算得出的结果是不同的。 + // 这样带来的好处是,多个用户,同样的密码,攻击者需要进行多次运算才能够完全破解。 + // 同样是纯数字3位短盐值,随机盐值破解所需的运算量,是固定盐值的1000倍。 + cryptPwd(password, salt) { + + // 密码“加盐” + const saltPassword = password + ':' + salt; + // 加盐密码的md5值 + const md5 = crypto.createHash('md5'); + return md5.update(saltPassword).digest('hex'); + }, + // 密码验证,如果验证通过 返回 true + cryptPwdVerification(password, salt, user_password_md5) { + return this.cryptPwd(password, salt) === user_password_md5; + }, +} diff --git a/app/permission/components/jwt/config.js b/app/permission/components/jwt/config.js new file mode 100644 index 0000000..7b255cb --- /dev/null +++ b/app/permission/components/jwt/config.js @@ -0,0 +1,5 @@ +module.exports = { + secret : 'sQ6CIfqS4SqF1zZqRZbCDAT5@T]X4fCD',//秘钥 + algorithm: 'HS256',//数字签名或 MAC 算法 + expiresIn: "7d",//有效期:例如:1000, "2 days", "10h", "7d". 数值被解释为秒数。如果使用字符串,请确保提供时间单位(天、小时等),否则默认使用毫秒单位("120"等于"120ms")。 +} diff --git a/app/permission/components/jwt/index.js b/app/permission/components/jwt/index.js new file mode 100644 index 0000000..c4d6f08 --- /dev/null +++ b/app/permission/components/jwt/index.js @@ -0,0 +1,17 @@ +const jwt = require("jsonwebtoken"); +const config = require("./config.js"); + +module.exports = { + /** + * 签发token + * @param {object} data 加入到签名里的数据. + * @return {string} token 令牌. + */ + sign: (data) => jwt.sign(data, config.secret, {algorithm: config.algorithm, expiresIn: config.expiresIn}), + /** + * 验证token + * @param {string} token 令牌. + * @return {boolean} 验证通过返回true. + */ + verify: (token) => jwt.verify(token, config.secret, {algorithm: config.algorithm, expiresIn: config.expiresIn}), +} diff --git a/app/permission/components/login/accountPassword.js b/app/permission/components/login/accountPassword.js new file mode 100644 index 0000000..78f2c32 --- /dev/null +++ b/app/permission/components/login/accountPassword.js @@ -0,0 +1,15 @@ +/** + * app.components.login.accountPassword + * 账号密码登陆 + * @param {string} data.account 账号. + * @param {string} data.password 密码. + * @return {object} res 验证是否通过. + */ +module.exports = async (data) => { + const {account, password} = data + const User = await app.db.table("User").where({account}).find() + if (!User) return false + const {salt} = User + const ver = app.components.encrypt.cryptPwdVerification(password, salt, User.password) + return ver +} diff --git a/app/permission/components/registered/accountPassword.js b/app/permission/components/registered/accountPassword.js new file mode 100644 index 0000000..31e52f5 --- /dev/null +++ b/app/permission/components/registered/accountPassword.js @@ -0,0 +1,14 @@ +/** + * app.components.registered.accountPassword + * 账号密码注册 + * @param {string} data.account 账号. + * @param {string} data.password 密码. + * @return {boolean} 注册 是否 成功. + */ +module.exports = async (data) => { + const {account, password} = data + const salt = app.components.encrypt.getRandomSalt() + const md5 = app.components.encrypt.cryptPwd(password, salt) + await app.db.table("User").data({account, password: md5, salt}).save() + return true +} diff --git a/app/middleware/全局中间件定义文件 b/app/permission/components/公共函数 similarity index 100% rename from app/middleware/全局中间件定义文件 rename to app/permission/components/公共函数 diff --git a/app/permission/middleware/rbac/config.js b/app/permission/middleware/rbac/config.js new file mode 100644 index 0000000..3c29701 --- /dev/null +++ b/app/permission/middleware/rbac/config.js @@ -0,0 +1,5 @@ +module.exports = { + path : "app/*/api/*.js", + prefix : "",//接口前缀 + statusTobody: true,//是否跟随body结果(如需接口报错也返回200,那么设置为false) +} diff --git a/app/permission/middleware/rbac/index.js b/app/permission/middleware/rbac/index.js new file mode 100644 index 0000000..dc338e3 --- /dev/null +++ b/app/permission/middleware/rbac/index.js @@ -0,0 +1,18 @@ +'use strict'; +/** + * RBAC用户、角色、权限、组设 + * saas的权限验证 + */ +const config = require("./config") + +// 错误处理 +module.exports = { + sort : 1, //排序 + use : true, // 是否使用 + fun : async (ctx, next, app) => { + + + await next() + + } +} diff --git a/app/permission/model/Group.js b/app/permission/model/Group.js new file mode 100644 index 0000000..52dd4b9 --- /dev/null +++ b/app/permission/model/Group.js @@ -0,0 +1,13 @@ +module.exports = { + doc: "分组表", + api: true,//是否需要生成api接口 + model: { + gid: {type: "STRING", comment: '分组id'}, + name: {type: "STRING", comment: '分组名称'}, + to_gid: {type: "STRING", comment: '上级分组id'}, + to_oid: {type: "STRING", comment: '所属组织id'}, + + }, +} + + diff --git a/app/permission/model/Organization.js b/app/permission/model/Organization.js new file mode 100644 index 0000000..11fff7c --- /dev/null +++ b/app/permission/model/Organization.js @@ -0,0 +1,11 @@ +module.exports = { + doc: "组织表", + api: true,//是否需要生成api接口 + model: { + oid: {type: "STRING", comment: '组织id'}, + name: {type: "STRING", comment: '组织名称'}, + general_management: {type: "STRING", comment: '是否为总管理平台(可以管理所有组织)'}, + }, +} + + diff --git a/app/permission/model/Permission.js b/app/permission/model/Permission.js new file mode 100644 index 0000000..a3d19ad --- /dev/null +++ b/app/permission/model/Permission.js @@ -0,0 +1,13 @@ +module.exports = { + doc: "权限列表", + api: true,//是否需要生成api接口 + model: { + pid: {type: "STRING", comment: '权限id'}, + to_pccode: {type: "STRING", comment: '所属权限分类码'}, + name: {type: "STRING", comment: '权限名称'}, + code: {type: "STRING", comment: '权限识别码'}, + value: {type: "STRING", comment: '权限值'}, + }, +} + + diff --git a/app/permission/model/PermissionClass.js b/app/permission/model/PermissionClass.js new file mode 100644 index 0000000..0d0871b --- /dev/null +++ b/app/permission/model/PermissionClass.js @@ -0,0 +1,10 @@ +module.exports = { + doc: "权限分类表", + api: true,//是否需要生成api接口 + model: { + name: {type: "STRING", comment: '权限分类名称'}, + pccode: {type: "STRING", comment: '权限分类识别码'}, + }, +} + + diff --git a/app/permission/model/Role.js b/app/permission/model/Role.js new file mode 100644 index 0000000..40508a2 --- /dev/null +++ b/app/permission/model/Role.js @@ -0,0 +1,13 @@ +module.exports = { + doc: "角色表", + api: true,//是否需要生成api接口 + model: { + rid: {type: "STRING", comment: '角色id'}, + name: {type: "STRING", comment: '角色名称'}, + to_rid: {type: "STRING", comment: '上级角色id'}, + to_oid: {type: "STRING", comment: '所属组织id'}, + to_gid: {type: "STRING", comment: '所属分组id'}, + }, +} + + diff --git a/app/permission/model/RoleToPermission.js b/app/permission/model/RoleToPermission.js new file mode 100644 index 0000000..e879513 --- /dev/null +++ b/app/permission/model/RoleToPermission.js @@ -0,0 +1,11 @@ +module.exports = { + doc: "角色拥有的权限", + api: true,//是否需要生成api接口 + model: { + rpid: {type: "STRING", comment: '角色权限id'}, + to_rid: {type: "STRING", comment: '角色id'}, + to_pid: {type: "STRING", comment: '权限id'}, + }, +} + + diff --git a/app/permission/model/User.js b/app/permission/model/User.js new file mode 100644 index 0000000..80a3a57 --- /dev/null +++ b/app/permission/model/User.js @@ -0,0 +1,16 @@ +module.exports = { + doc: "用户表", + api: true,//是否需要生成api接口 + model: { + uid: {type: "STRING", comment: '用户id'}, + account: {type: "STRING", comment: '用户账号'}, + password: {type: "STRING", comment: '用户加密密码'}, + salt: {type: "STRING", comment: '用户加密随机数'}, + to_oid: {type: "STRING", comment: '所属组织id'}, + to_gid: {type: "STRING", comment: '所属分组id'}, + to_rid: {type: "STRING", comment: '所属角色id'}, + to_uid: {type: "STRING", comment: '上级用户id'}, + }, +} + + diff --git a/app/permission/model/UserInfo.js b/app/permission/model/UserInfo.js new file mode 100644 index 0000000..1c9b7fe --- /dev/null +++ b/app/permission/model/UserInfo.js @@ -0,0 +1,20 @@ +module.exports = { + doc: "用户资料表", + api: true,//是否需要生成api接口 + model: { + uid: {type: "STRING", comment: '用户id'}, + account: {type: "STRING", comment: '用户账号'}, + name: {type: "STRING", comment: '名称'}, + avatar: {type: "STRING", comment: '头像'}, + wechat_openid: {type: "STRING", comment: '微信openid'}, + phone_number: {type: "STRING", comment: '手机号'}, + age: {type: "STRING", comment: '年龄'}, + sex: {type: "STRING", comment: '性别'}, + province: {type: "STRING", comment: '省份'}, + city: {type: "STRING", comment: '城市'}, + area: {type: "STRING", comment: '地区'}, + address: {type: "STRING", comment: '详细地址'}, + }, +} + + diff --git a/app/schedule/定时任务 b/app/permission/model/数据库模型 similarity index 100% rename from app/schedule/定时任务 rename to app/permission/model/数据库模型 diff --git a/app/permission/test/index.js b/app/permission/test/index.js new file mode 100644 index 0000000..5a5f0f5 --- /dev/null +++ b/app/permission/test/index.js @@ -0,0 +1,39 @@ +module.exports = { + describe_name: "permission/权限管理", + testList : [ + { + test_name: "账号密码注册", + fun : async (request, server) => { + const response = await request(server) + .post('/permission/api/registered') + .send({ + type: "accountPassword", + data: { + account : "test", + password: "123456", + }, + }) + // expect(response.status).toEqual(200) + expect(response.body.status).toEqual(200) + expect(response.body.data).toHaveProperty('token') + } + }, + { + test_name: "账号密码登录", + fun : async (request, server) => { + const response = await request(server) + .post('/permission/api/login') + .send({ + type: "accountPassword", + data: { + account : "test", + password: "123456", + }, + }) + // expect(response.status).toEqual(200) + expect(response.body.status).toEqual(200) + expect(response.body.data).toHaveProperty('token') + } + }, + ] +} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..fedc50e --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [['@babel/preset-env', { targets: { node: 'current' } }]], +} diff --git a/config/app.js b/config/app.js deleted file mode 100644 index b67e74f..0000000 --- a/config/app.js +++ /dev/null @@ -1,4 +0,0 @@ -/*应用配置*/ -module.exports = { - port: 3000, -} diff --git a/config/cache.js b/config/cache.js deleted file mode 100644 index dfff369..0000000 --- a/config/cache.js +++ /dev/null @@ -1 +0,0 @@ -/*缓存配置*/ diff --git a/config/console.js b/config/console.js deleted file mode 100644 index 5a5d42f..0000000 --- a/config/console.js +++ /dev/null @@ -1 +0,0 @@ -/*控制台配置*/ diff --git a/config/database.js b/config/database.js deleted file mode 100644 index c1a0bda..0000000 --- a/config/database.js +++ /dev/null @@ -1,77 +0,0 @@ -/*数据库配置*/ -module.exports = { - mysql : { - database: "bamboo", - username: "bamboo", - password: "bamboo", - options : { - dialect: 'mysql', - host : "192.168.1.26", - port : 3306, - // 禁用日志记录或提供自定义日志记录功能;默认值:console.log - // logging: false, - // model的全局配置 - define: { - // 添加create,update,delete时间戳 - timestamps: true, - // 添加软删除 - paranoid: false, - // 防止修改表名为复数 - freezeTableName: true, - // 防止驼峰式字段被默认转为下划线 - underscored: false, - }, - // 由于orm用的UTC时间,这里必须加上东八区,否则取出来的时间相差8小时 - timezone: '+08:00', - // 连接数 = ((核心数 * 2) + 有效磁盘数) - pool : {// 连接池 - max : require('os').cpus().length * 2 + 1, - min : 0, - acquire: 60000, - idle : 100000, - }, - dialectOptions: { - charset : "utf8mb4", - collate : "utf8mb4_general_ci", - supportBigNumbers: true, - bigNumberStrings : true, - dateStrings : true, - typeCast(field, next) {// 让读取date类型数据时返回字符串而不是UTC时间 - if (field.type === 'DATETIME') { - // console.log(field); - return field.string(); - } - return next(); - }, - }, - }, - }, - redis : {}, - sqlite: { - dialect: 'sqlite', - storage: 'app/sqlite/database.sqlite', - logging: false, - // model的全局配置 - define : { - // 添加create,update,delete时间戳 - timestamps: true, - // 添加软删除 - paranoid: false, - // 防止修改表名为复数 - freezeTableName: true, - // 防止驼峰式字段被默认转为下划线 - underscored: false, - }, - dialectOptions: { - typeCast(field, next) {// 让读取date类型数据时返回字符串而不是UTC时间 - if (field.type === 'DATETIME') { - // console.log(field); - return field.string(); - } - return next(); - }, - }, - }, - - -} diff --git a/config/extend.js b/config/extend.js deleted file mode 100644 index 87e9f64..0000000 --- a/config/extend.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - dir: { - "event" : "app/*/event/*.js", - "status" : "app/*/status/*.js", - "controller": "app/*/controller/*.js", - "model" : "app/*/model/*.js", - "middleware": "middleware/*.js", - "extend" : "extend/*.js", - "schedule" : "schedule/*.js", - "sqlite" : "sqlite/model/*.js", - "config" : "config/*.js", - } -} diff --git a/config/filesystem.js b/config/filesystem.js deleted file mode 100644 index 79367cd..0000000 --- a/config/filesystem.js +++ /dev/null @@ -1 +0,0 @@ -/*文件磁盘配置*/ diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..29cf1bb --- /dev/null +++ b/config/index.js @@ -0,0 +1,5 @@ +//全局配置,权重大于中间件配置与插件配置 +// 如果设置了全局配置,会覆盖中间件配置与插件配置 +module.exports = { + +} diff --git a/config/log.js b/config/log.js deleted file mode 100644 index eb1f857..0000000 --- a/config/log.js +++ /dev/null @@ -1,35 +0,0 @@ -/*日志配置*/ -'use strict'; -module.exports = { - replaceConsole: true, - pm2 : true, - appenders : { - stdout: {//控制台输出 - type: 'console' - }, - req : { //请求转发日志 - type : 'dateFile', //指定日志文件按时间打印 - filename : 'logs/req/req', //指定输出文件路径 - pattern : 'yyyy-MM-dd.log', - alwaysIncludePattern: true - }, - err : { //错误日志 - type : 'dateFile', - filename : 'logs/err/err', - pattern : 'yyyy-MM-dd.log', - alwaysIncludePattern: true - }, - oth : { //其他日志 - type : 'dateFile', - filename : 'logs/oth/oth', - pattern : 'yyyy-MM-dd.log', - alwaysIncludePattern: true - } - - }, - categories : { - //appenders:采用的appender,取appenders项,level:设置级别 - default: {appenders: ['stdout', 'req'], level: 'debug'}, - err : {appenders: ['stdout', 'err'], level: 'error'}, - } -} diff --git a/config/plugin.js b/config/plugin.js deleted file mode 100644 index fadd470..0000000 --- a/config/plugin.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -export default { - routerPlus: { - enable: true, - package: 'egg-router-plus', - }, - jwt: { - enable: true, - package: "egg-jwt" - }, - cors: { - enable: true, - package: 'egg-cors', - }, - validate: { - enable: true, - package: 'egg-validate', - }, - sequelize:{ - enable: true, - package: 'egg-sequelize', - }, - axiosPlus: { - enable: true, - package: 'egg-axios-plus', - }, -}; - diff --git a/config/route.js b/config/route.js deleted file mode 100644 index b06c2f7..0000000 --- a/config/route.js +++ /dev/null @@ -1 +0,0 @@ -/*URL和路由配置*/ diff --git a/config/socket.js b/config/socket.js deleted file mode 100644 index e69de29..0000000 diff --git a/config/token.js b/config/token.js deleted file mode 100644 index e69de29..0000000 diff --git a/config/view.js b/config/view.js deleted file mode 100644 index e69de29..0000000 diff --git a/extend/axios/config.js b/extend/axios/config.js new file mode 100644 index 0000000..165df58 --- /dev/null +++ b/extend/axios/config.js @@ -0,0 +1,47 @@ +//更多配置:http://axios-js.com/zh-cn/docs/index.html#%E8%AF%B7%E6%B1%82%E9%85%8D%E7%BD%AE + +module.exports = { + // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。 + // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL + baseURL: 'https://some-domain.com/api/', + // `headers` 是即将被发送的自定义请求头 + headers: {'X-Requested-With': 'XMLHttpRequest'}, + // `timeout` 指定请求超时的毫秒数(0 表示无超时时间) + // 如果请求话费了超过 `timeout` 的时间,请求将被中断 + timeout: 30 * 1000, + + // `withCredentials` 表示跨域请求时是否需要使用凭证 + withCredentials: false, // default + + // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' + responseType: 'json', // default + + // `responseEncoding` indicates encoding to use for decoding responses + // Note: Ignored for `responseType` of 'stream' or client-side requests + responseEncoding: 'utf8', // default + + // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称 + xsrfCookieName: 'XSRF-TOKEN', // default + + // `xsrfHeaderName` is the name of the http header that carries the xsrf token value + xsrfHeaderName: 'X-XSRF-TOKEN', // default + + // `maxContentLength` 定义允许的响应内容的最大尺寸 + // maxContentLength: 2000, + + // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目 + // 如果设置为0,将不会 follow 任何重定向 + // maxRedirects: 5, // default + + // 'proxy' 定义代理服务器的主机名称和端口 + // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据 + // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。 + // proxy: { + // host: '127.0.0.1', + // port: 9000, + // auth: { + // username: 'mikeymike', + // password: 'rapunz3l' + // } + // }, +} diff --git a/extend/axios/index.js b/extend/axios/index.js new file mode 100644 index 0000000..16ef833 --- /dev/null +++ b/extend/axios/index.js @@ -0,0 +1,16 @@ +/** + * Axios 是一个基于 promise 的 HTTP 库 + */ +const config = require("./config") +const axios = require('axios'); +const request = require('./request'); +const requestError = require('./request.error'); +const response = require('./response'); +const responseError = require('./response.error'); +module.exports = async (app) => { + Object.assign(axios.defaults, config) + axios.interceptors.request.use(request,requestError) + axios.interceptors.response.use(response,responseError) + app.axios = axios + app.alias["$axios"] = app.axios +} diff --git a/extend/axios/request.error.js b/extend/axios/request.error.js new file mode 100644 index 0000000..43940dd --- /dev/null +++ b/extend/axios/request.error.js @@ -0,0 +1,6 @@ +//请求拦截器错误时 +module.exports = (error) => { + // 对请求错误做些什么 + return Promise.reject(error); +} + diff --git a/extend/axios/request.js b/extend/axios/request.js new file mode 100644 index 0000000..f466033 --- /dev/null +++ b/extend/axios/request.js @@ -0,0 +1,5 @@ +//请求拦截器 +module.exports = (config) => { + // 在发送请求之前做些什么 + return config; +} diff --git a/extend/axios/response.error.js b/extend/axios/response.error.js new file mode 100644 index 0000000..ef48910 --- /dev/null +++ b/extend/axios/response.error.js @@ -0,0 +1,6 @@ +//响应拦截器错误时 +module.exports = (error) => { + // 对请求错误做些什么 + return Promise.reject(error); +} + diff --git a/extend/axios/response.js b/extend/axios/response.js new file mode 100644 index 0000000..5187691 --- /dev/null +++ b/extend/axios/response.js @@ -0,0 +1,5 @@ +//响应拦截器 +module.exports = (response) => { + // 对响应数据做点什么 + return response; +} diff --git a/extend/body/body.js b/extend/body/body.js index 65a1482..f41f87d 100644 --- a/extend/body/body.js +++ b/extend/body/body.js @@ -1,14 +1,14 @@ module.exports = { success: (body, msg, status) => { return { - res : body || null, + data : body || null, status: status || 200, message : msg || "success" } }, error : (body, msg, status) => { return { - res : body || null, + data : body || null, status: status || 400, message : msg || "error" } diff --git a/extend/body/index.js b/extend/body/index.js index 2139109..8f33ba5 100644 --- a/extend/body/index.js +++ b/extend/body/index.js @@ -6,4 +6,5 @@ const body = require("./body") module.exports = async (app) => { app.res = body + app.alias["$res"] = app.res } diff --git a/extend/bullmq/index.js b/extend/bullmq/index.js index aae84fe..cbf230c 100644 --- a/extend/bullmq/index.js +++ b/extend/bullmq/index.js @@ -10,7 +10,7 @@ const config = require("./config") const mq = require("./queue") module.exports = async (app) => { app.mq = mq - + app.alias["$mq"] = app.mq const queueName = "boobam_schedule" const queue = mq.Queue(queueName); diff --git a/extend/components/config.js b/extend/components/config.js new file mode 100644 index 0000000..8f436d5 --- /dev/null +++ b/extend/components/config.js @@ -0,0 +1,4 @@ +module.exports = { + path : "components/*/*.js", + app_path: "app/*/components/*/*.js", +} diff --git a/extend/components/index.js b/extend/components/index.js new file mode 100644 index 0000000..7117b59 --- /dev/null +++ b/extend/components/index.js @@ -0,0 +1,21 @@ +/** + * 通用组件 + * 加载components文件夹下的函数 + */ +const config = require("./config") +module.exports = async (app) => { + const list = await app.load(config.path) + Object.assign(list, await app.load(config.app_path)) + app.components = {} + list.forEach(item => { + if (!app.components[item.parse.file_father]) { + app.components[item.parse.file_father] = {} + } + if (item.parse.name==='index') { + Object.assign(app.components[item.parse.file_father],item.res) + }else { + app.components[item.parse.file_father][item.parse.name] = item.res + } + }) + app.alias["$components"] = app.components +} diff --git a/extend/event/index.js b/extend/event/index.js index cc138e8..ed0246b 100644 --- a/extend/event/index.js +++ b/extend/event/index.js @@ -6,6 +6,7 @@ const config = require("./config") const EventEmitter = require('events'); module.exports = async (app) => { app.event = new EventEmitter() + app.alias["$event"] = app.event const list = await app.load(config.path) list.forEach(item => { Object.keys(item.res).forEach(key => { diff --git a/extend/middleware/config.js b/extend/middleware/config.js index 932b25b..0cc1b8c 100644 --- a/extend/middleware/config.js +++ b/extend/middleware/config.js @@ -1,3 +1,4 @@ module.exports = { - path:"middleware/*/index.js" + path:"middleware/*/index.js", + app_path: "app/*/middleware/*/index.js", } diff --git a/extend/middleware/index.js b/extend/middleware/index.js index 870d70f..d12db00 100644 --- a/extend/middleware/index.js +++ b/extend/middleware/index.js @@ -2,19 +2,25 @@ * 加载中间件 * 加载app/middleware文件夹下的中间件 */ + const config = require("./config") module.exports = async (app) => { let list = await app.load(config.path) + let list2 = await app.load(config.app_path) + list=[...list,...list2] + // Object.assign(list, list2) list = app.xe.orderBy(list, "res.sort") list = list.filter(item => item.res.use) list.forEach(async item => { + console.log('加载的中间件:',item.parse.dir); if (item.res.loadFun) { //如果中间件定义了特殊加载方法 await item.res.loadFun(app, item.res.fun) } else { - app.use(async (ctx, next) => { - return await item.res.fun(ctx, next, app) - }) + app.use(async (ctx, next) => { + + return await item.res.fun(ctx, next, app) + }) } }) diff --git a/extend/mysql/api.js b/extend/mysql/api.js index 00750ce..9c5d870 100644 --- a/extend/mysql/api.js +++ b/extend/mysql/api.js @@ -2,22 +2,22 @@ const Sequelize = require("sequelize"); const xe = require("xe-utils") module.exports = class Api { - constructor(sequelize,models) { + constructor(sequelize, models) { this.sequelize = sequelize this.models = models this.init() } init() { - this.tableName = "" - this.tableWhere = "" - this.tableData = "" - this.tablePage = "" - this.tableLimit = "" - this.tableOrder = "" - this.tableGroup = "" - this.tableAttributes = "" - this.t = "" + this.tableName = null + this.tableWhere = null + this.tableData = null + this.tablePage = null + this.tableLimit = null + this.tableOrder = null + this.tableGroup = null + this.tableAttributes = null + this.t = null } /** @@ -166,7 +166,7 @@ module.exports = class Api { } const res = await this.models[this.tableName].findOne( { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -184,12 +184,12 @@ module.exports = class Api { } const res = await this.models[this.tableName].findAll( { - where: this.tableWhere, - offset: (this.tablePage && this.tableLimit) ? this.tablePage - 1 * this.tableLimit : null, - limit: this.tableLimit, - order: this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 - group: this.tableGroup, - attributes: this.tableAttributes, + where : this.tableWhere, + offset : (this.tablePage && this.tableLimit) ? this.tablePage - 1 * this.tableLimit : null, + limit : this.tableLimit, + order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 + group : this.tableGroup, + attributes : this.tableAttributes, transaction: this.t } ) @@ -210,8 +210,8 @@ module.exports = class Api { } const res = await this.models[this.tableName].findOrCreate( { - defaults: this.tableData, - where: this.tableWhere, + defaults : this.tableData, + where : this.tableWhere, transaction: this.t } ) @@ -232,12 +232,12 @@ module.exports = class Api { } const {count, rows} = await this.models[this.tableName].findAndCountAll( { - where: this.tableWhere, - offset: (this.tablePage && this.tableLimit) ? this.tablePage - 1 * this.tableLimit : null, - limit: this.tableLimit, - order: this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 - group: this.tableGroup, - attributes: this.tableAttributes, + where : this.tableWhere, + offset : (this.tablePage && this.tableLimit) ? this.tablePage - 1 * this.tableLimit : null, + limit : this.tableLimit, + order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 + group : this.tableGroup, + attributes : this.tableAttributes, transaction: this.t } ) @@ -264,7 +264,7 @@ module.exports = class Api { let res = await this.models[this.tableName].findOne( { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -279,7 +279,7 @@ module.exports = class Api { await this.models[this.tableName].update( data, { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -296,14 +296,14 @@ module.exports = class Api { await this.models[this.tableName].decrement( this.tableData, { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) res = await this.models[this.tableName].findOne( { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -329,7 +329,7 @@ module.exports = class Api { } let res = await this.models[this.tableName].findOne( { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -344,7 +344,7 @@ module.exports = class Api { await this.models[this.tableName].update( data, { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -361,14 +361,14 @@ module.exports = class Api { await this.models[this.tableName].increment( this.tableData, { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) res = await this.models[this.tableName].findOne( { - where: this.tableWhere, + where : this.tableWhere, transaction: this.t } ) @@ -387,10 +387,10 @@ module.exports = class Api { const res = await this.models[this.tableName].count( this.tableData, { - where: this.tableWhere, - order: this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 - group: this.tableGroup, - attributes: this.tableAttributes, + where : this.tableWhere, + order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 + group : this.tableGroup, + attributes : this.tableAttributes, transaction: this.t } ) @@ -408,10 +408,10 @@ module.exports = class Api { const res = await this.models[this.tableName].sum( this.tableData, { - where: this.tableWhere, - order: this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 - group: this.tableGroup, - attributes: this.tableAttributes, + where : this.tableWhere, + order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 + group : this.tableGroup, + attributes : this.tableAttributes, transaction: this.t } ) @@ -429,10 +429,10 @@ module.exports = class Api { const res = await this.models[this.tableName].max( this.tableData, { - where: this.tableWhere, - order: this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 - group: this.tableGroup, - attributes: this.tableAttributes, + where : this.tableWhere, + order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 + group : this.tableGroup, + attributes : this.tableAttributes, transaction: this.t } ) @@ -450,10 +450,10 @@ module.exports = class Api { const res = await this.models[this.tableName].min( this.tableData, { - where: this.tableWhere, - order: this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 - group: this.tableGroup, - attributes: this.tableAttributes, + where : this.tableWhere, + order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序 + group : this.tableGroup, + attributes : this.tableAttributes, transaction: this.t } ) diff --git a/extend/mysql/config.js b/extend/mysql/config.js index e1c2a6f..a7c06a2 100644 --- a/extend/mysql/config.js +++ b/extend/mysql/config.js @@ -1,11 +1,11 @@ module.exports = { database: "bamboo", - username: "bamboo", - password: "bamboo", + username: "root", + password: "123456", options: { dialect: 'mysql', - host: "192.168.1.26", - port: 3306, + host: "127.0.0.1", + port: 3357, // 禁用日志记录或提供自定义日志记录功能;默认值:console.log // logging: false, // model的全局配置 @@ -44,4 +44,16 @@ module.exports = { }, }, path:"app/*/model/*.js", + sqlite:{ + host: 'localhost', + dialect: 'sqlite', + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000 + }, + storage: './database.sqlite', + operatorsAliases: false + } } diff --git a/extend/mysql/index.js b/extend/mysql/index.js index ebfbdf6..8e92c4d 100644 --- a/extend/mysql/index.js +++ b/extend/mysql/index.js @@ -2,6 +2,7 @@ * mysql/sequelize * * 参考文档: + * https://demopark.github.io/sequelize-docs-Zh-CN/ * https://sequelize.org/ * https://sequelize.org/api/v6/identifiers * https://www.sequelize.com.cn/ @@ -10,6 +11,7 @@ const config = require("./config") const Sequelize = require("sequelize"); const operatorsAliases = require("./operatorsAliases"); const Api = require("./api"); +const Tool = require("./tool.js"); module.exports = async (app) => { const {database, username, password, options,} = config const sequelize = new Sequelize(database, username, password, { @@ -27,11 +29,11 @@ module.exports = async (app) => { model[key] = res.model[key] } model['id'] = { - type: Sequelize.INTEGER, - comment: '表自增id', - allowNull: false, - unique: 'id', - primaryKey: true, + type : Sequelize.INTEGER, + comment : '表自增id', + allowNull : false, + unique : 'id', + primaryKey : true, autoIncrement: true, } sequelize.define(parse.name, model) @@ -41,6 +43,10 @@ module.exports = async (app) => { app.models = sequelize.models const api = new Api(sequelize, sequelize.models) app.db = api + app.db.tool = new Tool(app, sequelize) + //全局变量 + app.alias["$db"] = app.db + // console.log(api); // console.log(await app.db.table("User").find()); @@ -60,6 +66,7 @@ module.exports = async (app) => { } }) + // await app.db.tool.reduction() }) diff --git a/extend/mysql/tool.js b/extend/mysql/tool.js new file mode 100644 index 0000000..ece5fa9 --- /dev/null +++ b/extend/mysql/tool.js @@ -0,0 +1,50 @@ +const config = require("./config") +import mysqldump from 'mysqldump'; + +const Importer = require('mysql-import'); + +module.exports = class { + constructor(app, sequelize) { + this.app = app + this.sequelize = sequelize + this.file = this.app.root + '/' + config.database + '.sql' + } + + //备份数据库所有表 + async backup(file) { + this.file = file || this.app.root + '/' + config.database + '.sql' + console.log('备份数据库中...'); + await mysqldump({ + connection: { + host : config.options.host, + port : config.options.port, + user : config.username, + password: config.password, + database: config.database, + + }, + dumpToFile: this.file, + }); + console.log('备份数据库完成:', this.file); + } + //还原备份sql文件 + async reduction() { + console.time("reduction") + //先备份旧数据 + await this.backup(this.app.root + '/' + 'extend/mysql/backup/' + Date.now() + '.sql') + //清空数据库 + await this.sequelize.drop() + //导入sql文件 + const importer = new Importer({ + host : config.options.host, + port : config.options.port, + user : config.username, + password: config.password, + database: config.database, + }); + this.file = this.app.root + '/' + config.database + '.sql' + await importer.import(this.file) + console.timeEnd("reduction") + } + +} diff --git a/config/middleware.js b/extend/parameter/config.js similarity index 58% rename from config/middleware.js rename to extend/parameter/config.js index c3d00a9..631f375 100644 --- a/config/middleware.js +++ b/extend/parameter/config.js @@ -1,4 +1,2 @@ -'use strict'; module.exports = { - } diff --git a/extend/parameter/index.js b/extend/parameter/index.js new file mode 100644 index 0000000..c623de8 --- /dev/null +++ b/extend/parameter/index.js @@ -0,0 +1,10 @@ +/** + * 参数验证插件 + */ +const config = require("./config") +const Parameter = require('parameter'); +const parameter = new Parameter(); +module.exports = async (app) => { + app.parameter = parameter + app.alias["$parameter"] = app.parameter +} diff --git a/extend/redis/index.js b/extend/redis/index.js index 003525c..3670717 100644 --- a/extend/redis/index.js +++ b/extend/redis/index.js @@ -11,5 +11,7 @@ module.exports = async (app) => { const redis = new Redis(config); app.redis = redis app.Redis = Redis - + //全局变量 + app.alias["$redis"] = app.redis + app.alias["$Redis"] = app.Redis } diff --git a/extend/socket.io/config.js b/extend/socket.io/config.js index ed67080..a69f045 100644 --- a/extend/socket.io/config.js +++ b/extend/socket.io/config.js @@ -1,3 +1,5 @@ module.exports = { - path:"app/*/io/*.js", + path: "app/*/io/*.js", + port: 3210, + test_port: 3211, } diff --git a/extend/socket.io/index.js b/extend/socket.io/index.js index 3614682..fe770be 100644 --- a/extend/socket.io/index.js +++ b/extend/socket.io/index.js @@ -7,11 +7,16 @@ */ const config = require("./config") const path = require('path') +const uuid = require("uuid"); module.exports = async (app) => { + const parameter = app.parameter const server = require('http').createServer(app.callback()); - const io = require('socket.io')(server); + const io = require('socket.io')(server, {cors: true}); + // let leaveRoom = () => { + // } let list = await app.load(config.path) app.io = io + app.alias["$io"] = app.io //等待所有插件载入完成后 app.willReadyList.push(async () => { //默认命名空间 @@ -24,17 +29,35 @@ module.exports = async (app) => { } }); console.log('连接=>', "id:" + socket.id); + //监听disconnect事件 - socket.on('disconnect', (eventName, callback) => { - console.log('断开=X', "id:" + socket.id) - }) + // socket.on('disconnect', (eventName, callback) => { + // console.log('断开=X', "id:" + socket.id) + // leaveRoom(socket, {msg: '', onname: 'leaveRoom', callback}) + // }) for (let el of list) { //空间名称 // const namespace = path.basename(path.resolve(el.parse.dir, '..')) //事件名称 const onname = el.parse.name - socket.on(onname, (msg, callback) => { - el.res(app, msg, callback) + // if (onname === 'leaveRoom') { + // //离开房间处理 + // leaveRoom = (socket, {msg, onname, callback}) => el.res.fun(socket, {msg, onname, callback}) + // } + socket.on(onname, (msg, callback, anotherSocketId) => { + console.log('anotherSocketId', socket.id); + const validate = parameter.validate(el.res.params, msg); + if (validate) { + socket.emit('error', { + event: onname, + res : app.res.error(validate, "参数验证不通过", 204) + }) + console.error('socket', socket.id, msg, onname, app.res.error(validate, "参数验证不通过", 204)); + // ctx.body = app.res.error(validate, "参数验证不通过", 204) + } else { + el.res.fun(socket, {msg, onname, callback}) + } + }) } }); @@ -45,9 +68,15 @@ module.exports = async (app) => { //覆盖启动方法 app.startServer = () => { // 监听端口 - app.startServer = server.listen(3000, () => { - console.log('listening on *:3000'); + const port = (process.env.NODE_ENV === 'test' && config.test_port) || config.port || 3000 + const baseURL = "http://127.0.0.1" + app.config.port = port + app.config.baseURL = baseURL + app.server = server.listen(port, () => { + console.log(`http 服务: ${baseURL}:${port}`); + console.log(`socket 服务: ${baseURL}:${port}`); }); + } } diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..6ddc3b6 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,195 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ + +module.exports = { + // 测试中所有导入的模块都应该自动模拟 + // automock: false, + + // `n` 次失败后停止运行测试 + // bail: 0, + + // TJest 应该存储其缓存依赖信息的目录 + // 缓存目录:“privatevarfolderscdm9j91n110831mbzcxnqtlz700000gnTjest_dx”, + + // 每次测试前自动清除模拟调用、实例、上下文和结果 + clearMocks: true, + + // 指示是否应在执行测试时收集覆盖率信息 + collectCoverage: false, + + // 一组 glob 模式,指示应为其收集覆盖信息的一组文件 + // collectCoverageFrom: undefined, + + // Jest 应该输出其覆盖文件的目录 + coverageDirectory: "coverage", + + // 用于跳过覆盖收集的正则表达式模式字符串数组 + coveragePathIgnorePatterns: [ + "/node_modules/" + ], + + // 指示应使用哪个提供程序来检测代码以进行覆盖 + coverageProvider: "v8", + + // Jest 在编写报道报告时使用的记者姓名列表 + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // 为覆盖结果配置最小阈值强制执行的对象 + // coverageThreshold: undefined, + + // 自定义依赖项提取器的路径 + // dependencyExtractor: undefined, + + // 调用已弃用的 API 会引发有用的错误消息 + // errorOnDeprecated: false, + + // 假定时器的默认配置 + // fakeTimers: { + // "enableGlobally": false + // }, + + // 使用 glob 模式数组强制从被忽略的文件中收集覆盖率 + // forceCoverageMatch: [], + + // 导出异步函数的模块的路径,该函数在所有测试套件之前触发一次 + // globalSetup: undefined, + + // 导出所有测试套件后触发一次的异步函数的模块的路径 + // globalTeardown: undefined, + + // 一组需要在所有测试环境中可用的全局变量 + // globals: {}, + + // 用于运行测试的最大工作人员数量。可以指定为 % 或数字。例如。 maxWorkers: 10% 将使用 CPU 数量的 10% + 1 作为最大工作线程数。 maxWorkers: 2 将使用最多 2 个工人。 + // maxWorkers: "50%", + + // 从需要模块的位置递归搜索的目录名称数组 + // moduleDirectories: [ + // "node_modules" + // ], + + // 您的模块使用的一系列文件扩展名 + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + // 从正则表达式到模块名称或模块名称数组的映射,允许使用单个模块存根资源 + // moduleNameMapper: {}, + + // 一个正则表达式模式字符串数组,在被认为对模块加载器“可见”之前与所有模块路径匹配 + // modulePathIgnorePatterns: [], + + // 激活测试结果通知 + // notify: false, + + // 一个指定通知模式的枚举。需要 { 通知:真 } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + // testEnvironment: "jest-environment-node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + //Jest 用于检测测试文件的 glob 模式 + testMatch: ["**/+(*.)+(spec|test).+(ts|js)?(x)"], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // 从正则表达式到转换器路径的映射 + // transform: undefined, + + // 与所有源文件路径匹配的正则表达式模式字符串数组,匹配的文件将跳过转换 + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, + + + "testTimeout": 1000000 +}; diff --git a/lib/bamboo/index.js b/lib/bamboo/index.js index e38bae6..f1252f0 100644 --- a/lib/bamboo/index.js +++ b/lib/bamboo/index.js @@ -11,12 +11,25 @@ module.exports = class Bamboo extends Koa { /*初始化业务目录*/ constructor(extend_directory, root) { super(); + //全局配置 + this.config = {} + //xe-utils工具 this.xe = xe + //根目录 this.root = this.isPath() || root; + //插件路径 this.extend_directory = extend_directory || `extend/*/index.js` + //等待扩展已经加载完成后立即执行的任务列表 this.willReadyList = [] - console.log(`当前根目录${this.root}`); - console.log(`当前插件目录${this.extend_directory}`); + //全局别名,推荐命名规则,$+全局变量名,如$db + this.alias = { + "app": this, + "$": this, + } + //等待启动完成后立即执行的任务列表 + this.fullList = [] + // console.log(`当前根目录${this.root}`); + // console.log(`当前插件目录${this.extend_directory}`); this.loadEvent() } @@ -26,17 +39,33 @@ module.exports = class Bamboo extends Koa { for (let listElement of list) { await listElement.res(this) } - this.willReadyList.push(() => { - this.startServer() - }) + await this.loadAlias() await this.willReady() } + /*加载别名到全局变量,注意只有完成加载扩展后才能使用*/ + async loadAlias() { + for (let key of Object.keys(this.alias)) { + global[key] = this.alias[key] + } + } + /*扩展已经加载完成触发*/ async willReady() { + this.willReadyList.push(async () => { + await this.startServer() + await this.full() + }) for (let WRL of this.willReadyList) { - await WRL() + await WRL(this) + } + } + + /*启动完成*/ + async full() { + for (let ful of this.fullList) { + await ful(this) } } @@ -60,6 +89,7 @@ module.exports = class Bamboo extends Koa { files = files.map(item => { const parse = path.parse(item); parse.father = path.basename(path.resolve(parse.dir, '..')) + parse.file_father = path.basename(path.resolve(parse.dir + '/' + parse.name, '..')) return { parse, res: require(path.resolve(path_root + "/" + item)) @@ -73,7 +103,7 @@ module.exports = class Bamboo extends Koa { /*启动服务*/ startServer(prod) { - this.listen(prod || 3000) - console.log('启动服务:3000'); + app.server = this.listen(prod || 3000) + console.log(`http服务:http://127.0.0.1:${prod || 3000}`); } } diff --git a/lib/main.js b/lib/main.js index ca4290e..b0b8bec 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,4 +1,4 @@ -//入口文件 +// 入口文件 require('@babel/register')({ presets: ['@babel/env'], plugins: [ @@ -12,7 +12,8 @@ require('@babel/register')({ ] }) -require('@babel/polyfill') +// require('@babel/polyfill') const bamboo = require('./bamboo/index') const app = new bamboo(); // app.listen(3000); +module.exports = app diff --git a/middleware/bodyparser/index.js b/middleware/bodyparser/index.js index 16e20ec..b37a306 100644 --- a/middleware/bodyparser/index.js +++ b/middleware/bodyparser/index.js @@ -1,8 +1,8 @@ 'use strict'; const bodyParser = require('koa-bodyparser') -// 错误处理 +// 解析json的入参 module.exports = { - sort: 4, //排序 + sort: 2, //排序 use : true, // 是否使用 fun : bodyParser() } diff --git a/middleware/cors/index.js b/middleware/cors/index.js new file mode 100644 index 0000000..24dba9f --- /dev/null +++ b/middleware/cors/index.js @@ -0,0 +1,8 @@ +'use strict'; +const cors = require('@koa/cors') +// 跨域 +module.exports = { + sort: 0, //排序 + use : true, // 是否使用 + fun : cors() +} diff --git a/middleware/error/index.js b/middleware/error/index.js index 9709b52..e3181cc 100644 --- a/middleware/error/index.js +++ b/middleware/error/index.js @@ -3,7 +3,7 @@ const error = require('koa-json-error') // 错误处理 module.exports = { sort: 3, //排序 - use : true, // 是否使用 + use : false, // 是否使用 fun : error((err) => { return { status : err.status, diff --git a/middleware/modelToApi/config.js b/middleware/modelToApi/config.js new file mode 100644 index 0000000..6f5f96f --- /dev/null +++ b/middleware/modelToApi/config.js @@ -0,0 +1,4 @@ +module.exports = { + path : "app/*/model/*.js", + prefix : "auto",//接口前缀 +} diff --git a/middleware/modelToApi/index.js b/middleware/modelToApi/index.js new file mode 100644 index 0000000..6a48d56 --- /dev/null +++ b/middleware/modelToApi/index.js @@ -0,0 +1,27 @@ +'use strict'; +/** + * 加载模型到路由 + * 加载app/api目录下的文件到路由 + */ +const Router = require('koa-router') +const config = require("./config") + +// 错误处理 +module.exports = { + sort : 999, //排序 + use : false, // 是否使用 + loadFun: async (app, fun) => { // 自行定义中间件的加载方式,将覆盖默认加载方法 + const router = await fun(app) + app.use(router.routes()).use(router.allowedMethods()) + }, + fun : async (app) => { + const router = new Router({ //设置前缀 + prefix: config.prefix + }); + let list = await app.load(config.path) + list.forEach(item => { + + }) + return router + } +} diff --git a/middleware/params/index.js b/middleware/params/index.js index 6004ce9..96552ad 100644 --- a/middleware/params/index.js +++ b/middleware/params/index.js @@ -1,10 +1,10 @@ 'use strict'; + /*封装入参*/ module.exports = { - sort: 3, //排序 + sort: 5, //排序 use : true, // 是否使用 fun : async (ctx, next, app) => { - ctx.params = ctx.request.body || ctx.query await next() diff --git a/middleware/router/config.js b/middleware/router/config.js index 3c29701..0dddb8c 100644 --- a/middleware/router/config.js +++ b/middleware/router/config.js @@ -1,5 +1,5 @@ module.exports = { path : "app/*/api/*.js", prefix : "",//接口前缀 - statusTobody: true,//是否跟随body结果(如需接口报错也返回200,那么设置为false) + statusTobody: false,//是否跟随body结果(如需接口报错也返回200,那么设置为false) } diff --git a/middleware/router/index.js b/middleware/router/index.js index c27c625..67e9b47 100644 --- a/middleware/router/index.js +++ b/middleware/router/index.js @@ -5,7 +5,7 @@ */ const Router = require('koa-router') const config = require("./config") - +// const parameter = require('./parameter'); // 错误处理 module.exports = { sort : 999, //排序 @@ -15,36 +15,56 @@ module.exports = { app.use(router.routes()).use(router.allowedMethods()) }, fun : async (app) => { + const parameter = app.parameter const router = new Router({ //设置前缀 prefix: config.prefix }); let list = await app.load(config.path) list.forEach(item => { const url = item.res.path ? item.res.path : '/' + item.parse.father + '/api/' + item.parse.name + console.log(url); router.all(url, async (ctx, next) => { - /*处理body结果*/ - try { - const res = await item.res.fun(ctx, app) - if (!ctx.body) { - if (res) { - ctx.body = app.res.success(res) - } else if (res === false) { - ctx.body = app.res.error() - } else { - ctx.body = app.res.success() - } + await next(); + const validate = parameter.validate(item.res.params, ctx.params); + let res = null + if (validate) { + ctx.body = app.res.error(validate, "参数验证不通过", 204) + } else { + /*处理body结果*/ + try { + res = await item.res.fun(ctx, app) + } catch (err) { + console.log(err); + ctx.body = app.res.error(null, "系统错误", 500) + } + } + + if (!ctx.body) { + if (res) { + if (res.status&&res.message) { + ctx.body = res + }else{ + ctx.body = app.res.success(res) + } + } else if (res === false) { + ctx.body = app.res.error() + } else { + ctx.body = app.res.success() } - } catch (err) { - console.log(err); - ctx.body = app.res.error(null, "系统错误", 500) - ctx.status = 500 } //是否跟随body结果 ctx.status = config.statusTobody ? ctx.body.status || 400 : 200 - await next(); + }) }) + + router.all('/', async (ctx, next) => { + + ctx.body = app.res.success("ok") + ctx.status = 200 + await next(); + }) return router } } diff --git a/middleware/router/parameter.js b/middleware/router/parameter.js new file mode 100644 index 0000000..d11518a --- /dev/null +++ b/middleware/router/parameter.js @@ -0,0 +1,11 @@ +const Parameter = require('parameter'); +const parameter = new Parameter(); + +//添加规则 +// parameter.addRule('123', (rule, value) => { +// if (value !== '123') { +// return 'must be 123'; +// } +// }); + +module.exports = parameter diff --git a/nodemon.json b/nodemon.json index b9054c1..f3f10bf 100644 --- a/nodemon.json +++ b/nodemon.json @@ -5,5 +5,5 @@ ".idea", "node_modules/**/node_modules" ], - "exec": "node lib/main.js" + "exec": "npm run start" } diff --git a/package.json b/package.json index ad4a792..d841b1e 100644 --- a/package.json +++ b/package.json @@ -6,24 +6,30 @@ "scripts": { "dev": "nodemon", "start": "node lib/main.js", - "server": "cross-env NODE_ENV=development nodemon bin/main.js" + "server": "cross-env NODE_ENV=development nodemon bin/main.js", + "test": "jest --forceExit" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { - "@babel/core": "^7.5.5", + "@babel/cli": "^7.18.6", + "@babel/core": "^7.18.6", + "@babel/node": "^7.18.6", "@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-decorators": "^7.17.9", "@babel/plugin-transform-runtime": "^7.5.5", - "@babel/preset-env": "^7.5.5", - "@babel/register": "^7.5.5", + "@babel/preset-env": "^7.18.6", + "@babel/register": "^7.18.6", + "@babel/runtime": "^7.18.6", "art-template": "^4.13.2", "babel-plugin-webpack-alias": "^2.1.2", "babel-polyfill": "^6.26.0", "babel-register": "^6.26.0", - "cross-env": "^5.2.0", + "chai": "^4.3.6", + "cross-env": "^7.0.3", "ejs": "^2.6.2", + "jest": "^28.1.1", "koa": "^2.7.0", "koa-art-template": "^1.1.1", "koa-bodyparser": "^4.2.1", @@ -33,24 +39,28 @@ "koa-views": "^6.2.0", "koa-webpack": "^6.0.0", "koa-webpack-middleware": "^1.0.7", + "mocha": "^10.0.0", + "mysql-import": "^5.0.21", "nodeenv": "^1.0.0", - "nodemon": "^1.19.1" + "nodemon": "^1.19.4", + "sequelize-mock": "^0.10.2", + "supertest": "^6.2.3" }, "dependencies": { - "@babel/core": "^7.16.7", - "@babel/node": "^7.16.8", "@babel/plugin-proposal-decorators": "^7.18.2", "@babel/polyfill": "^7.4.4", - "@babel/preset-env": "^7.16.8", - "@babel/runtime": "^7.5.5", + "@koa/cors": "^3.3.0", "ajv": "^8.11.0", + "axios": "^0.27.2", "babel-loader": "^8.2.3", "bamboo_smf": "^1.0.5", "bullmq": "^1.85.1", "clean-webpack-plugin": "^4.0.0", - "cross-env": "^7.0.3", + "crypto": "^1.0.1", "glob": "^8.0.1", "ioredis": "^5.0.5", + "jest": "^28.1.1", + "jsonwebtoken": "^8.5.1", "koa-bodyparser": "^4.3.0", "koa-json-error": "^3.1.2", "koa-logger": "^2.0.1", @@ -58,6 +68,7 @@ "koa-router": "^7.4.0", "log4js": "^6.4.4", "mysql2": "^2.3.3", + "mysqldump": "^3.2.0", "node-schedule": "^2.1.0", "object-path": "^0.11.8", "parameter": "^3.6.0", @@ -68,6 +79,8 @@ "shelljs": "^0.8.5", "socket.io": "^4.5.1", "sqlite3": "^5.0.2", + "supertest": "^6.2.3", + "uuid": "^8.3.2", "webpack": "^4.41.5", "webpack-cli": "3.3.10", "webpack-node-externals": "^3.0.0",