# ld-egg-frame

> #### 端信息校验（auth/webAuth） 主要功能：登录用户身份信息校验 说明：依据client头字段进行不同端的校验，框架提供默认的`web=>app/middleware/webAuth.js`中间件，用户可以自定义其他端的中间件校验。 如果配置了接口权限校验，会根据用户信息进行接口权限校验。

Latest version **2.1.11** (published 2021-09-18) · MIT license · 0 weekly downloads

## Install

```sh
npm install ld-egg-frame
pnpm add ld-egg-frame
yarn add ld-egg-frame
bun add ld-egg-frame
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 2.1.11 |
| Published | 2021-09-18 |
| First published | 2021-02-02 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Node | >=12.0.0 |
| Dependencies | 14 |
| Unpacked size | 79.4 KB |
| Known vulnerabilities | 0 (+24 in 2 direct dependencies) |
| Install scripts | no |
| Maintainers | phpnobody |
| Keywords | egg, egg-framework |

## Links

- npm: https://www.npmjs.com/package/ld-egg-frame
- npm.io page: https://npm.io/package/ld-egg-frame

## Dependencies (14)

- [egg](https://npm.io/package/egg.md) ^2
- [uuid](https://npm.io/package/uuid.md) ^8.3.2
- [axios](https://npm.io/package/axios.md) ^0.21.1
- [qiniu](https://npm.io/package/qiniu.md) ^7.3.2
- [lodash](https://npm.io/package/lodash.md) ^4.17.20
- [egg-jwt](https://npm.io/package/egg-jwt.md) ^3.1.7
- [extend2](https://npm.io/package/extend2.md) ^1.0.0
- [egg-cors](https://npm.io/package/egg-cors.md) ^2.2.3
- [egg-redis](https://npm.io/package/egg-redis.md) ^2.4.0
- [is-type-of](https://npm.io/package/is-type-of.md) ^1.2.1
- [egg-mongoose](https://npm.io/package/egg-mongoose.md) ^3.2.0
- [ld-validator](https://npm.io/package/ld-validator.md) ^2.0.7
- [egg-router-plus](https://npm.io/package/egg-router-plus.md) ^1.3.1
- [egg-view-nunjucks](https://npm.io/package/egg-view-nunjucks.md) ^2.3.0

## Recent versions

- 2.1.11 (latest) — 2021-09-18
- 2.1.10 — 2021-08-31
- 2.1.9 — 2021-08-29
- 2.1.8 — 2021-08-09
- 2.1.7 — 2021-06-21
- 2.1.6 — 2021-06-18
- 2.1.5 — 2021-06-16
- 2.1.3 — 2021-06-11
- 2.1.2 — 2021-06-06
- 2.1.1 — 2021-06-03
- 2.1.0 — 2021-06-03
- 2.0.3 — 2021-05-28
- 2.0.2 — 2021-05-27
- 2.0.1 — 2021-05-27
- 2.0.0 — 2021-05-24
- … 32 more at https://npm.io/package/ld-egg-frame/versions

## README

## 框架说明

#### 端信息校验（auth/webAuth）
    主要功能：登录用户身份信息校验
    说明：依据client头字段进行不同端的校验，框架提供默认的`web=>app/middleware/webAuth.js`中间件，用户可以自定义其他端的中间件校验。
    如果配置了接口权限校验，会根据用户信息进行接口权限校验。

```
// 自定义端信息校验案例(例如weapp中间校验)
// 新建中间件 app/middleware/weappAuth.js
module.exports = (opts, app) => {
  return async function weapp(ctx, next) {
    // 常用的写法
    if (ctx.request.header.client !== 'weapp') return await next();
    const jwtDecoded = ctx.getJwtDecoded();
    
    // TODO 业务处理
    ctx.weappUser = await app.getModel('weappUser').findById(jwtDecoded.userId);

    await next();
  };
};

// 配置 app/config/config.default.js， 新增配置
expotrs.middleware = [ 'weappAuth' ]
exports.weappAuth = {
    ... // 自定义需要的配置
}
```

    
#### 配置
###### jwt配置
```
// 其他配置详见egg-jwt
{
    enable: true,
    access: ['GET /abc/cba'] // 配置jwt白名单，这里配置接口同时会跳过webAuth的校验
}
```
###### auth配置
```
// 如果关闭后，仅校验头部带client的接口，其他接口不做校验，默认开启
{ enable: true }
```
###### webAuth配置
```
{
    enable: true,
    interfaceEnable: true, // 是否开启接口校验
    // 通用接口，不进行接口校验，但是有进行token校验
    interfaceAccess: [
        'GET /user/menu',
        'GET /common/uptoken',
        'GET /common/upsetting',
        'GET /user/info',
    ],
}
```

###### 安全配置（跨域）
```
exports.security = {
  origin: '',
  allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH',
  csrf: { enable: false },
  domainWhiteList: [ 'http://localhost:9527' ],
};
```

## 基于mongoose封装的快捷方法
```
  // 查看某个值最大值的对象
  async max(field, filter = {}) {
    const res = await this.findOne(filter).sort({ [field]: -1 });
    return res;
  },
  // 查看某个值最小值的对象
  async min(field, filter = {}) {
    const res = await this.findOne(filter).sort({ [field]: 1 });
    return res;
  },
  // 查看某个值的最小值
  async minField(field, filter = {}) {
    const res = await this.findOne(filter, field).sort({ [field]: 1 });
    return res ? res._doc[field] : void 0;
  },
  // 查看某个值的最大值
  async maxField(field, filter = {}) {
    const res = await this.findOne(filter, field).sort({ [field]: -1 });
    return res ? res._doc[field] : void 0;
  },
  // 查找符合条件的id
  async findIds(filter = {}) {
    const docs = await this.find(filter, '_id');
    return docs.map(e => e.id);
  },
  // 查找符合条件的字段
  async findFields(filter = {}, key) {
    const docs = await this.find(filter, key);
    return docs.map(e => e[key]);
  },
  // 符合查询条件的内容是否存在
  async exist(filter) {
    const q = await this.countDocuments(filter);
    return q > 0;
  },
  // 查看id数组的数据是否全部
  async idsExist(ids) {
    const q = await this.countDocuments({ _id: { $in: ids } });
    return q === ids.length;
  },
  // 递增
  async inc(filter, doc) {
    if (isObjectId) filter = { _id: filter };
    return await this.update(filter, { $inc: doc });
  },
  // 列表查询
  async list(filter, page = 1, perPage = 10, select = null, opts = {}) {
    page = Number(page || 1);
    perPage = Number(perPage || 10);
    const skip = perPage * (page - 1);
    const sort = opts.sort || { createdAt: -1 };
    const list = await this.find(filter, select, _.omit(opts, [ 'sort' ])).skip(skip).limit(perPage)
      .sort(sort);
    const total = await this.countDocuments(filter);
    return {
      list,
      pagination: {
        perPage,
        page,
        total,
      },
    };
  },
  async getOne(filter, select = null, opts = {}) {
    if (isObjectId) filter = { _id: filter };
    const doc = await this.findOne(filter, select, opts);
    if (!doc) throw Error('getOne：未找到文档');
    return doc;
  },
  async update(id, data) {
    const doc = await this.findById(id);
    if (!doc) throw Error('update：未找到文档');
    Object.keys(data).forEach(k => { doc.set(k, data[k]); });
    doc.save();
    return doc;
  },
  async upsert(condition, data, opts = {}) {
    opts = Object.assign({ upsert: true, new: true }, opts);
    const old = await this.findOne(condition);
    if (!old) Object.assign(data, opts.initData || {});
    return await this.findOneAndUpdate(condition, data, opts);
  },
  async delete(id) {
    const doc = await this.findById(id);
    if (!doc) throw Error('delete：未找到文档');
    doc.remove();
    return doc;
  },
```

## 框架默认
#### 默认表
##### user表

```
// 校验规则
exports.fields = {
  account: 'required|string',
  password: 'required|password',
  name: 'string',
  phoneNumber: 'mobile',
  roleIds: 'required|array',
  creatorId: 'objectId',
};

exports.update = {
  enable: [ 'name', 'roleIds', 'phoneNumber' ],
  privacy: [ 'password' ],
};
exports.fetch = {
  privacy: [ 'password' ],
};
exports.fetchAll = exports.fetchList = {
  input: {
    roleId: 'objectId',
  },
  search: [ 'account' ],
  privacy: [ 'password' ],
  async beforeHook() {
    const { ctx } = this;
    const roleId = ctx.input.roleId;
    if (roleId) {
      ctx.addFilter({
        roleIds: roleId,
      });
    }
  },
};

exports.destory = {
  privacy: [ 'password' ],
  async beforeHook() {
    const { ctx, app } = this;
    const operator = ctx.user;
    const delUser = await app.getModel('user').findById(ctx.params._id);
    if (delUser.account === 'super') ctx.throwBizError('超管用户不允许删除');
    if (operator.account !== 'super') ctx.throwBizError('仅允许超管删除用户');
  },
};


exports.autoRestful = true;
exports.zhName = '用户';
exports.support = [ 'update', 'fetchList', 'fetch', 'fetchAll', 'destory' ];

```

##### role表
```
exports.fields = {
  name: 'required|string',
  mark: 'string',
  menuIds: 'array',
  interfaceIds: 'array',
  permissions: 'required|array',
};

exports.create = {
  unique: [ 'name' ],
  default: { permissions: [] },
};

exports.update = {
  unique: [ 'name' ],
};
exports.fetchList = {
  search: [ 'name', 'mark' ],
};

exports.destory = {
  async afterHook(entity) {
    const { app } = this;
    await app.getModel('user').updateMany({ roleIds: entity.id }, { $pull: { roleIds: entity.id } });
  },
};
exports.zhName = '角色';
exports.autoRestful = true;

```
##### menu表
```
exports.fields = {
  path: 'required|string',
  component: 'required|string',
  name: 'required|string',
  meta: 'required|object',
  hide: 'boolean',
  alwaysShow: 'boolean',
  parentId: 'objectId',
  type: 'required|string|in:root,sub',
  interfaceIds: 'required|array',
};

exports.update = exports.create = {
  unique: [ 'name' ],
  async beforeHook() {
    const { ctx, app } = this;
    const input = ctx.input;
    if (input.type === 'sub' && !input.parentId) ctx.throwBizError('子菜单必须传父菜单Id');

    if (input.parentId) {
      const parentExist = await app.getModel('menu').qk.exist({ _id: input.parentId });
      if (!parentExist) ctx.throwBizError('父级菜单不存在');
    }

    const interfaceExist = await app.getModel('interface').qk.idsExist(input.interfaceIds);
    if (!interfaceExist) ctx.throwBizError('存在未知接口');

  },
};


exports.autoRestful = true;
exports.zhName = '菜单';
exports.support = [ 'create', 'update', 'fetch', 'destory' ];
```
##### interface表

```
exports.fields = {
  name: 'string',
  alias: 'string',
  path: 'required|string',
  mark: 'required|string',
  method: 'required|string',
  state: 'required|string',
  access: 'required|boolean',
};

exports.fetchAll = exports.fetchList = {
  search: [ 'name', 'mark' ],
  input: {
    access: 'boolean',
    state: 'string',
  },
  match: [ 'state', 'access' ],
};
exports.autoRestful = true;
exports.zhName = '接口';
exports.support = [ 'fetchAll', 'fetchList' ];

```


#### 默认控制器
- controller/common.js
- controller/permission.js
- controller/user.js
- controller/web.js

#### 默认服务
- service/user.js
- service/role.js
- service/menu.js
- service/interface.js


#### 默认中间件
- middleware/errorHandler.js
- middleware/jwt.js

#### 默认路由
```
  // 权限配置
  router.put('刷新接口信息', '/sys/permission/interface', controller.permission.refreshInterface);
  router.put('更新白名单接口', '/sys/permission/interface/access', controller.permission.updateAccessInterface);
  router.put('绑定角色', '/user/role', controller.permission.bindRole);
  router.put('配置角色权限', '/role/permission', controller.permission.setRolePermission);
  router.get('获取菜单树', '/menu/tree', controller.permission.getMenuTree);
  router.get('获取用户菜单树', '/user/menu', controller.permission.getUserMenu);
  router.get('获取用户信息', '/user/info', controller.user.getInfo);
  router.post('添加用户', '/user', controller.user.addUser);

  // 后台 登录注册
  router.post('后台登录', '/web/login', controller.web.login);
  router.post('后台注册', '/web/register', controller.web.register);

  // 资源上传
  router.get('获取OSS上传token', '/common/uptoken', controller.common.uptoken);
  router.get('获取OSS配置', '/common/upsetting', controller.common.uploadSetting);
```



```
  // 权限配置
  router.put('刷新接口信息', '/sys/permission/interface', controller.permission.refreshInterface);
  router.put('更新白名单接口', '/sys/permission/interface/access', controller.permission.updateAccessInterface);
  router.put('绑定角色', '/user/role', controller.permission.bindRole);
  router.put('配置角色权限', '/role/permission', controller.permission.setRolePermission);
  router.get('获取菜单树', '/menu/tree', controller.permission.getMenuTree);
  router.get('获取用户菜单树', '/user/menu', controller.permission.getUserMenu);
  router.get('获取用户信息', '/user/info', controller.user.getInfo);
  router.post('添加用户', '/user', controller.user.addUser);

  // 后台 登录注册
  router.post('后台登录', '/web/login', controller.web.login);
  router.post('后台注册', '/web/register', controller.web.register);

  // 资源上传
  router.get('获取OSS上传token', '/common/uptoken', controller.common.uptoken);
  router.get('获取OSS配置', '/common/upsetting', controller.common.uploadSetting);
```

## 备忘
- 中间件的顺序：coreMiddleware > 插件中间件 > 应用中间件

## 参考
[egg.js官网](https://eggjs.org/zh-cn/intro/index.html)

---
_Source: https://npm.io/package/ld-egg-frame · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
