1.0.1 • Published 3 years ago

egg-message1 v1.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

egg-message

egg插件开发package.json详解

  • 命名规范

    • npm 包名以 egg- 开头,且为全小写,例如:egg-xx。比较长的词组用中划线:egg-foo-bar
    • 对应的插件名使用小驼峰,小驼峰转换规则以 npm 包名的中划线为准 egg-foo-bar => fooBar
    • 对于可以中划线也可以不用的情况,不做强制约定,例如:userservice(egg-userservice) 还是 user-service(egg-user-service) 都可以
  • package.json 书写规范

    • 只书写我自己不知道的关键字,其他的请自行在package.json中自我查询
    • eggPlugin:
      • {String} name - 插件名(必须配置),具有唯一性,配置依赖关系时会指定依赖插件的 name。
      • {Array} dependencies - 当前插件强依赖的插件列表(如果依赖的插件没找到,应用启动失败)
        • 此处注意:不要和package.json本身的dependencies关键字弄混,这个在eggPlugin内的,是依赖的egg插件,egg-xxxx格式的,而外面的是正常普通npm包】
        • 比如我们的插件依赖一个叫egg-pwd的插件,那么"dependencies": "pwd" ,不需要再去增加 egg-
      • {Array} optionalDependencies - 当前插件的可选依赖插件列表(如果依赖的插件未开启,只会 warning,不会影响应用启动)。
      • {Array} env - 只有在指定运行环境才能开启,具体有哪些环境可以参考运行环境。此配置是可选的,一般情况下都不需要配置。
        {
          "name": "egg-rpc",
          "eggPlugin": {
            "name": "rpc", // 插件名
            "dependencies": [ "registry" ], // 强依赖的插件列表
            "optionalDependencies": [ "vip" ], // 可选依赖插件列表
            "env": [ "local", "test", "unittest", "prod" ] // 指定运行环境
          }
        }
    • description:意义与keywords相似,就是对于插件做一个简介,让使用者更明白他是做什么的
    • 在 keywords 里加上 egg、egg-plugin、eggPlugin 等关键字,便于索引【其实就是类似与淘宝对于商品的多个关键词,便于在插件发布后,提高插件的曝光率】【与description差不多意思】
      {
        "name": "egg-view-nunjucks",
        "version": "1.0.0", // 你的插件的版本号
        "description": "view plugin for egg",
        "eggPlugin": {
          "name": "nunjucks",
          "dep": [
            "security"
          ]
        },
        "keywords": [
          "egg",
          "egg-plugin",
          "eggPlugin",
          "egg-plugin-view",
          "egg-view",
          "nunjucks"
        ],
      }
      举例:针对于egg-view-nunjucks, 我们写的几个关键字,egg,egg-plugin,eggPlugin:属于egg框架能用的插件,egg-plugin-view,egg-view,nunjucks告诉我们这个插件与什么有关
    • 发布文件配置(files)

      files 字段用于描述我们使用 npm publish 命令后推送到 npm 服务器的文件列表,如果指定文件夹,则文件夹内的所有内容都会包含进来。我们可以查看下载的 antd 的 package.json 的files 字段,内容如下:

        "files": [
          "dist",
          "lib",
          "es"
        ],

      可以看到下载后的 antd 包是下面的目录结构: image1-url

      • repository: 项目代码存放地方
        "repository": {
          "type": "git",
          "url": "git+https://github.com/eggjs/egg-passport.git" // git地址
        },
      • bugs: 项目问题反馈的Url或报告问题的email地址
        "bugs": {
          "email": "project@hostname.com", // 反馈问题邮箱
          "url": "https://github.com/owner/project/issues" // 反馈问题地址
        },

压缩响应消息,

Install

暂时内部使用,直接copy。发布后, $ npm i xxx

Usage

enable message plugin

// 发布前
// config/plugin.js
exports.message = {
  enable: true,
  package: '../../lib/plugin/egg-message',
};
// 发布后
exports.message = {
  enable: true,
  package: 'egg-message',
};

Using

// config/config.default.js
exports.msgFilter = {
  threshold: 1024 // 小于 1k 的响应体不压缩【默认1024】
};
exports.message = {
    enable: true, // 
    client: {
        /**
         * parameter config  https://github.com/node-modules/parameter
         */
        validate: {
            // enable: true,
            convert: true,
            validateRoot: true
        },
    },
    /**
     * logAli 上传慢查询日志插件配置
     */
    aliLog: {
        enable: true, // 是否将应用日志发送到远程 阿里云sls
        limitTime: 20, // 毫秒, 上传大于此值的日志,
        SlowQueryLog: 'slow_query_log', // 慢查询日志上传表名
        ErrorLog: 'error_log' // 错误日志上传表名
    }
};

Authenticate Requests

Use app.passport.mount(strategy[, options]), specifying the 'github' and 'twitter' strategy, to authenticate requests.

// app/router.js
module.exports = app => {
  app.get('/', 'home.index');

  // authenticates routers
  app.passport.mount('github');
  // this is a passport router helper, it's equal to the below codes
  //
  // const github = app.passport.authenticate('github');
  // app.get('/passport/github', github);
  // app.get('/passport/github/callback', github);

  // custom options.login url and options.successRedirect
  app.passport.mount('twitter', {
    loginURL: '/account/twitter',
    // auth success redirect to /
    successRedirect: '/',
  });
};

Verify and store user

Use app.passport.verify(async (ctx, user) => {}) hook:

// app.js
module.exports = app => {
  app.passport.verify(async (ctx, user) => {
    // check user
    assert(user.provider, 'user.provider should exists');
    assert(user.id, 'user.id should exists');

    // find user from database
    //
    // Authorization Table
    // column   | desc
    // ---      | --
    // provider | provider name, like github, twitter, facebook, weibo and so on
    // uid      | provider unique id
    // user_id  | current application user id
    const auth = await ctx.model.Authorization.findOne({
      uid: user.id,
      provider: user.provider,
    });
    const existsUser = await ctx.model.User.findOne({ id: auth.user_id });
    if (existsUser) {
      return existsUser;
    }
    // call user service to register a new user
    const newUser = await ctx.service.user.register(user);
    return newUser;
  });
};

How to develop an egg-passport-${provider} plugin

See example: egg-passport-twitter.

  • Plugin dependencies on egg-passport to use app.passport APIs.
// package.json
{
  "eggPlugin": {
    "name": "passportTwitter",
    "dependencies": [
      "passport"
    ]
  },
}
  • Define config and set default values

Must use key and secret instead of consumerKey|clientID and consumerSecret|clientSecret.

// config/config.default.js
exports.passportTwitter: {
  key: '',
  secret: '',
  callbackURL: '/passport/twitter/callback',
};
  • Init Strategy in app.js and format user in verify callback
// app.js
const debug = require('debug')('egg-passport-twitter');
const assert = require('assert');
const Strategy = require('passport-twitter').Strategy;

module.exports = app => {
  const config = app.config.passportTwitter;
  // must set passReqToCallback to true
  config.passReqToCallback = true;
  assert(config.key, '[egg-passport-twitter] config.passportTwitter.key required');
  assert(config.secret, '[egg-passport-twitter] config.passportTwitter.secret required');
  // convert to consumerKey and consumerSecret
  config.consumerKey = config.key;
  config.consumerSecret = config.secret;

  // register twitter strategy into `app.passport`
  // must require `req` params
  app.passport.use('twitter', new Strategy(config, (req, token, tokenSecret, params, profile, done) => {
    // format user
    const user = {
      provider: 'twitter',
      id: profile.id,
      name: profile.username,
      displayName: profile.displayName,
      photo: profile.photos && profile.photos[0] && profile.photos[0].value,
      token,
      tokenSecret,
      params,
      profile,
    };
    debug('%s %s get user: %j', req.method, req.url, user);
    // let passport do verify and call verify hook
    app.passport.doVerify(req, user, done);
  }));
};
  • That's all!

APIs

extent application

  • app.passport.mount(strategy, options): Mount the login and the login callback routers to use the given strategy.
  • app.passport.authenticate(strategy, options): Create a middleware that will authorize a third-party account using the given strategy name, with optional options.
  • app.passport.verify(handler): Verify authenticated user
  • app.passport.serializeUser(handler): Serialize user before store into session
  • app.passport.deserializeUser(handler): Deserialize user after restore from session

extend context

  • ctx.user: get the current authenticated user
  • ctx.isAuthenticated(): Test if request is authenticated
  • * ctx.login(user[, options]): Initiate a login session for user.
  • ctx.logout(): Terminate an existing login session

Unit Tests

This plugin has includes some mock methods to helper you writing unit tests more conveniently.

app.mockUser([user]): Mock an authenticated user

const mm = require('egg-mock');

describe('mock user demo', () => {
  let app;
  before(() => {
    app = mm.app();
    return app.ready();
  });
  after(() => app.close());

  afterEach(mm.restore);

  it('should show authenticated user info', () => {
    app.mockUser();
    return request(app.callback())
      .get('/')
      .expect(/user name: mock_name/)
      .expect(200);
  });
});

app.mockUserContext([user]): Mock a context instance with authenticated user

it('should get authenticated user and call service', async () => {
  const ctx = app.mockUserContext();
  const result = await ctx.service.findUser({ id: ctx.user.id });
  assert(result.user.id === ctx.user.id);
});

Questions & Suggestions

Please open an issue here.

License

MIT