GithubHelp home page GithubHelp logo

eggjs / egg Goto Github PK

View Code? Open in Web Editor NEW
18.9K 18.9K 1.8K 9.95 MB

🥚 Born to build better enterprise frameworks and apps with Node.js & Koa

Home Page: https://eggjs.org

License: MIT License

JavaScript 98.75% Shell 0.11% TypeScript 0.96% Less 0.18%
egg eggjs enterprise framework koa koa-middleware koa2 node-framework nodejs

egg's People

Contributors

atian25 avatar azard avatar cemremengu avatar coogleyao avatar darrenwong avatar davidnotes avatar dead-horse avatar fengmk2 avatar gxcsoccer avatar gztchan avatar hyj1991 avatar jtyjty99999 avatar killagu avatar kylezhang avatar leoner avatar lslxdx avatar mansonchor avatar monkindey avatar okoala avatar popomore avatar semantic-release-bot avatar seweitung avatar sf-zhou avatar shaoshuai0102 avatar sunshinewyf avatar thonatos avatar waitingsong avatar whxaxes avatar xadillax avatar zhennann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

egg's Issues

Label

增加不同类别的 label

  1. semver: major, semver: minor, semver: patch
  2. core
  3. plugin: security,
  4. tools: egg-mock, egg-bin
  5. feature, fix, refactor, improve, test, doc, release, discussion

初始化项目出错 read ECONNRESET

[test] egg-init --type simple showcase
[egg-init] /Users/xx/test/showcase is not exists, now create it.
[egg-init] dest dir is /Users/xx/test/showcase
Error: read ECONNRESET
    at exports._errnoException (util.js:896:11)
    at TLSWrap.onread (net.js:556:26)

egg-loader

关于 loader 有几个问题,需要先确定下

  • 运行环境及其名称,是否还是这几个 prod, test, default, local, unittest
  • 运行环境如何确定,除了 process.env.NODE_ENV
  • 是否还需要支持 framework 封装这个功能?

先想到这些,后面有再补充

1.0 Milestone

Core

Tool

Plugin

framework

  • aliyun-egg

Doc

  • egg README
  • 其他 README 规范
  • CONTRIBUTING
  • Tutorial
  • benchmark @fengmk2

release ready

开发方式

  1. 先提交源码保证可以用,依赖层级比较深。
  2. 补测试,测试的目录接口和源码保持一致。
  3. 新仓库先提交空的 README,提交 PR 完成功能,补测试需要测试通过

proxymock 改造

  • egg-proxymock -> egg-mockdata ?
  • 配合 loader #11 的改造, 支持 mock 不同的对象
  • 除了原支持的 exports.fn 方式, 增加 class 的支持, 如继承 ServiceClass 然后替换

统一多实例的插件创建方式,文档化提供最佳实践

类似 oss / mysql 等都可能存在需要创建多个实例的需求,在插件里面现在有两种实现方式:

  1. 新增一个 createInstance 方法来自定义创建。
  2. 配置支持数组形式,然后通过 this.mysql.db1 来获取

对外的插件最好有一个统一的形式。特别是 aliyun-egg 估计会遇到挺多类似的问题

Framework Slogan

Here are a few ideas for the Egg.js framework slogan

  1. A supreme node.js framework builder to maximize productivity
  2. A modular framework builder for node.js developer
  3. The last node.js framework you ever need
  4. The node.js framework changes everything
  5. A promise to your success
  6. A node.js framework Builder
  7. next generation web framework builder for node.js (Note: compared with http://koajs.com/ slogan)
  8. Modular, opinionated, feature rich web framework for Node.js (Note: compared with http://expressjs.com/ slogan)

Singleton

刚看了 Singleton 的文档, 有几点疑问, 讨论下:

  • 我们之前的实现里面, 有很多用 Symbol + this[HELPER] 之类的方式去实现只实例化一次的对象, 这个是不是可以类似提供下简化方法?
  • 是否要考虑 loader singleton 的约定?
  • Singleton 其实是支持多实例的,那这个命名是不是不太准确?
  • 示例里面的 DataService 这个命名感觉有点问题, 会认为是放在 app/service 下, 但这个就是每个请求一个实例了.

Master 去除 loader 依赖

master 现在的 loader 会加载 config

  1. 读取 env,可以单独实现
  2. 获取 logger 的配置,master 的 logger 可以直接使用 consoleLogger,error 打到 stderr

中文文档

[RFC] egg-opentracing

分布式链路追踪是当今微服务架构中必不可少的组成部分,一个请求穿插于各个应用系统、中间件、数据库之间,通过 tracing 就能知道整个请求的链路以及各部分的耗时。Egg 会基于开放的 opentracing 来实现这个功能。

概念

Trace:一个 Trace 代表在微服务架构中的一次链路请求,可能经过一个或多个微服务应用。

Span:一个 span 代表系统中具有开始时间和执行时长的逻辑运行单元,一个 Trace 会包含多个 Span。

SpanContext:代表一个 Span 的信息,比如 TraceId,spanId,以及该 Span 的相关信息。注意:这个和 egg 的 ctx 没有关系。

Baggage:还有一些信息也在 SpanContext 中,我们称之为 Baggage,这些信息会在整个 Trace 中传递。

Tag:Span 中用来识别 span 的一些信息,比如

  • error
  • component: httplib, jdbc, mongoose
  • span.kind: client, server
  • http.url
  • http.method
  • http.status_code
  • framework
  • peer.hostname

Carrier:在微服务架构中,两个应用之前会通过不同的协议进行通讯,但是 opentracing 是无法感知的,所以定义了 Carrier 这个概念,他通过 Inject 和 Extract 和 SpanContext 之前进行互相转换,然后 Carrier 可以在协议中传递。比如 HTTP 请求,SpanContext 通过 Inject 设置 HTTPCarrier(转换成请求头),在请求接收方通过 Extract 将 HTTPCarrier 提取成 SpanContext。

Collector:我们通过 Collector 收集每个 span 的具体信息,然后记录,可以通过日志的方式或请求上报的方式。

实现方案

Egg 提供 ctx.tracer 实例作为一次 Trace,下面按照一个微服务应用整个请求周期描述各个 API

接收请求

比如收到了一个 HTTP 请求,在 server 入口应该

// app/middleware/meta.js
module.exports = () => {
  return async function meta(ctx, next) {
    // 从 http header 获取 span 上下文
    const spanContext = ctx.tracer.extract('HTTP', ctx.header);
    // 创建 span 时指定父级 span
    const span = ctx.tracer.startSpan('http_server', { childOf: spanContext });
    await next();
    span.finish();
  };
}

发送请求

比如发起一个 RPC 请求,需要在 RPC 客户端

const span = ctx.tracer.startSpan('rpcclient');
const rpcCarrier = {};
ctx.tracer.inject(span.context, 'RPC', rpcCarrier);
ctx.rpcclient.invoke(data, rpcCarrier);
span.finish();

定义 Carrier

上面使用到了 inject 和 extract 其实是需要这里提前定义的

// lib/http_carrier.js
module.exports = class HttpCarrier {
    
  inject(spanContext) {
    // 转换
    return header;
  }
  
  extract(header) {
    // 转换
    return spanContext;
  }
  
}

// config/config.default.js
exports.opentracing = {
  carrier: {
    'HTTP': require('../lib/http_carrier'),
  },
};

定义 Collector

还需要定义一个收集器

// lib/log_collector.js
module.exports = class LogColletor {
    
  collect(spanContext) {
    this.ctx.logger.write(this.format(spanContext));
  }

}

// config/config.default.js
exports.opentracing = {
  collector: {
    log: require('../lib/log_collector'),
  },
};

覆盖默认实现

Egg 的 Tracer 和 Span 对象都是 opentracing 的一个实现,但你也可以通过覆盖的方式来实现。

// config/config.default.js
exports.opentracing = {
  globalTracer: YourTracer,
};

分布式跟踪系统

Zipkin, Dapper, HTrace, X-Trace

重构 messenger

提供方法:

  1. broadcast(action, data):发送给所有的 agent / app 进程(包括自己)
  2. sendToApp(action, data): 发送给所有的 app 进程
    • 在 app 调用该方法上会发送给自己和其他的 app 进程
    • 在 agent 上调用该方法会发送给所有的 app 进程
  3. sendToAgent(action, data): 发送给 agent 进程
    • 在 app 调用该方法上会发送 agent 进程
    • 在 agent 上调用该方法会发送给 agent 自己
  4. sendRandom(action, data):
    • app 上没有该方法(现在 egg 的实现是等同于 sentToAgent)
    • agent 上回随机发送消息给一个 app 进程(由 master 来控制发送给谁)
  5. sendTo(pid, action, data): 发送给指定进程

egg-mock

egg-mock is a mock server and a list of mocked API with egg-mock plugin.

  • mm.app
  • mm.cluster
  • API
  • test with egg

Example cookie does not work

Running cookie example, I clicked on "remember" checkbox, then submit. It routes to /remember route, none of the logic in remember route is being run due to error secret is missing. See the full error log

2016-08-11 23:52:47,376 INFO 10862 [-/::1/-/8ms GET /] status 200 (rt: 8ms)
2016-08-11 23:52:49,389 WARN 10862 [-/::1/-/15ms POST /remember] nodejs.ForbiddenError: secret is missing
    at Object.module.exports.throw (/Users/mattma/Desktop/egg/egg/node_modules/koa/lib/context.js:91:23)
    at Object.context.assertCSRF.context.assertCsrf (/Users/mattma/Desktop/egg/egg/node_modules/koa-csrf/index.js:69:30)
    at Object.csrf (/Users/mattma/Desktop/egg/egg/node_modules/egg-security/lib/middlewares/csrf.js:39:12)
    at next (native)
    at Object.<anonymous> (/Users/mattma/Desktop/egg/egg/node_modules/koa-compose/index.js:29:12)
    at next (native)
    at onFulfilled (/Users/mattma/Desktop/egg/egg/node_modules/co/index.js:65:19)
    at /Users/mattma/Desktop/egg/egg/node_modules/co/index.js:54:5
    at Object.co (/Users/mattma/Desktop/egg/egg/node_modules/co/index.js:50:10)
    at Object.toPromise (/Users/mattma/Desktop/egg/egg/node_modules/co/index.js:118:63)

egg-core

Loader

做到灵活性,扩展性强。

API

loader 只提供原子粒度的 API,由框架来自行组织

Env

  • getServerEnv
  • getEggPaths
  • getLoadUnits
  • getAppname

Low Level API

  • loadFile
  • loadToApp
  • loadToContext
  • loadExtend

High Level API

  • loadPlugin
  • loadConfig
  • loadController
  • loadMiddleware
  • loadApplicationExtend
  • loadContextExtend
  • loadRequestExtend
  • loadResponseExtend
  • loadHelperExtend
  • loadCustomApp
  • loadCustomAgent
  • loadService

和插件的关系

插件需要用到 loader 功能需要增加一个 loader 插件,比如 db 的功能

|- app
  `- db
    `- a.js

新增 egg-loader-db 插件

// app.js
module.exports = function(loader) {
  app.loader.loadToApp('app/db', 'db');
};

db 插件直接依赖 egg-loader-db 插件

load unit

每个加载单元的目录结构都是类似的,框架、插件、应用的路径都是称为 loadDir。

|- app
  |- extend
  |- service
  |- controller
  |- middleware
  `- router.js
|- config
  |- config.js
  `- plugin.js
|- agent.js
`- app.js

LoaderOptions

  • directory: null,
  • target: null,
  • ignore: undefined,
  • lowercaseFirst: false,
  • initializer: null,
  • call: true,
  • override: false,
  • inject: undefined,

Action

  • 去除 loading
  • app.js/agent.js 支持返回一个 promise #51
  • 调整 console
  • 去除 lib/core 潜规则?
  • 去除 eggPath 和 customEgg 参数
  • 增加 strict 模式 #52

Core

egg-core 是包含了 loader 和应用初始化的功能,简单理解就是一个有目录约定的 koa,初始化包括了 loader。

  • loader
  • ready
  • logger
  • router

egg 就像其他框架一样只需要继承 egg-core 就可以直接使用


@atian25

嗯, 只需提供一个机制给框架开发者扩展, 如集团那边是有 proxy, 其他开发者可以定义:

this.db.xxx -> app/db/xxx.js
this.filters.xxx -> app/filters/xxx.js

术语整理

将现有用到的术语整理出来,保持统一,写文档和代码都好理解

确定 Release 规则

背景

Egg 参与人员过多,仓库过多,经常会遇到某个人没有某仓库的发布权限。

Release 时会修改版本和更新 changelog,这个也需要 review。

目标

  1. Changelog 可 review
  2. npm 自动发布
  3. 锁定 master 目录不允许提交

方案

锁定 master 目录不允许任何人提交,并添加合并检测(review+2,travis 和 codecov 通过)。

Release 时本地修改版本和 changelog 后提交 PR,review 后合并。

Travis 根据 commit 信息判断是否 release(非 PR,master 分支,切 commit 匹配),通过 eggjs 账号 发布到 npm。

Action

  • 支持 travis 发布 npm 包,还需要自动 push tag
  • “本地生成 changelog 和版本”有工具支持,保证生成格式一致
  • 文档更新
  • 确定 commit 信息 chore(release): release 1.0.0
  • 自动更新 github release

Updated at 2017-02-09


screenshot 2016-08-16 8 25 51

以后所有的仓库都需要这样配置,不允许直接 push 到 master。但是 release 的时候还有一步需要 push,这个规则需要明确下。

aliyun-egg需要的能力

通用安全插件、阿里云各服务插件(oss、cdn、等等常用的) @fishbar

  • egg-oss
  • egg-rds -> egg-mysql
  • egg-security
  • mts
  • ots
  • mc
  • ons

待补充

[RFC] 抽取 passport 模型

  • egg-passport-github
  • egg-passport-weibo @shaoshuai0102
  • egg-passport-gitlab
  • egg-passport-google
  • egg-passport-taobao

RFC: passport

由于业界的 passport 已经足够简单,egg 做起来就是配置。对应应用开发者,更加是配置,基本无需编写代码就能实现。

一个应用的 authenticate 通用过程

  1. 用户会选择应用提供的其中一种 strategy 进行 authenticate 登录,如应用同时提供了 twitter 和 facebook 两种方式。
  2. 用户去 twitter 登录成功后,会返回应用网站,然后 egg-passport-twitter 插件,会使用 verify callback 来完成对统一约定 user 的封装,继续返回给 egg-passport 做后续处理。
  3. egg-passport 会触发 app.passport.verify hook,这样应用层就能做最终的核实,例如做新用户注册,老用户绑定校验等等,这里也是用户数据持久化的地方。
  4. 用户登录之后,每次访问应用都会根据 app.passport.deserializeUserapp.passport.serializeUser 来还原 session。

说明

  • 框架内置了高度抽象的 passport 插件。
  • passport-xxx 插件都需要声明依赖内置的 passport 插件。
  • passport-xxx 插件开发者只需要简单结合一下 http://passportjs.org 现有的模块,就可以快速完成一个网站的 authenticate 接入。
    如要对接 Twitter 的 oauth,那么只需要引入 passport-twitter,并实现一个 passport-twitter 插件即可。

统一 user 字段约定

ctx.user 是一个 getter,真实数据来自于 ctx.state.user

每个 passport-xxx 插件都需要按照约定封装一个 user。

  • 必须 user.provider: 用户信息来自那种登录策略,如 twitter, weibo, facebook,这样就能通过此字段读取 user.profile 信息了。一般都是自动填充
  • 必须 user.id: 用户 id,字符串
  • 必须 user.name: 用户名
  • 可选 user.displayName: 昵称
  • 可选 user.token: oauth1 的话必须提供
  • 可选 user.tokenSecret: oauth1 的话必须提供
  • 可选 user.accessToken: oauth2 的话必须提供
  • 可选 user.refreshToken: oauth2 的话必须提供
  • 可选 user.profile: profile 的其他字段,每个平台提供的都不太一样,需要各种区别

passport 配置

// config/config.${env}.js
exports.passport = {
  twitter: {
    consumerKey: 'your-consumer-key',
    consumerSecret: 'your-consumer-secret',
    // 更多配置请查看 passport-twitter 插件
  },
};

应用需要关注的3个用户数据处理 hooks

// app.js
module.exports = app => {
  // 1. 核实登录用户信息,自行判断是否需要做数据调整,
  // 也可以在这里由应用自身统一 user 的数据结构,并且保存到数据库。
  // - 必须返回核实后的 user,即 verifiedUser,数据结构可以由应用自行统一约定
  // - 核实失败,throw 一个带有 status = 401 的 error 异常
  // - 其他异常,将会当作服务端内部异常处理
  //
  // passport-xxx 插件已经按约定生成了 user 数据,应用可以在此做最终的用户校验,并且做数据持久化
  // 在 strategy.authenticate 成功之后调用
  app.passport.verify(function* (ctx, user) {
    
  });
  
  // 自行处理经过 verify 后的 verifiedUser 数据如何序列化到 session
  // 在 http 请求结束阶段,保存数据到 session 之前调用
  app.passport.serializeUser(function* (ctx, verifiedUser) {
    // you can store profile and handle token here
  });
  
  // 根据 sesssion 中的 sessionUser 信息,还原出 verifiedUser
  // 在 http 请求开始阶段,还原 session 之后调用
  app.passport.deserializeUser(function* (ctx, sessionUser) {

  });
};

Deadline -- 8月底前必须完成的事.

做下备忘, 然后定下时间点和分猪肉吧.

我们还剩下不到 15 天了, 包括周六日.

  • 类库
    • 完成 egg 的所有核心库
    • egg 插件检查 (文档, 注释英文, 更新依赖版本, 发布 1.x 版本, public ) #5
    • 框架
      • aliyun-egg 还搞么?
    • example
  • 数据收集 @fengmk2 @gxcsoccer
    • benchmark
    • 业务使用情况统计, 应用数/进程数 (先放到内网 issue, 并走数据披露流程)
  • 文档编写
    • 编写使用文档, 参考 chair 文档
      • 列出大纲 (#55) @atian25
      • 术语整理
      • 文档列表(待分猪肉)
    • 文档翻译为 English @mattma
    • Web 规范文档的翻译 @mattma
      • 是否需要补充 logger 的规范进去?
      • #12
    • Api 文档构建 #34
    • jsconf ppt 编写 ( @atian25 本周出大纲 review )
  • 官网建设 (已出原型图, 使用 hexo 发布, @popomore)
  • 清理 milestone, #5

cc @eggjs/core @eggjs/contributor

Frontend send ajax request to Egg has an error of CORS or OPTIONS 405 Method Not Allowed

Goal: Egg works as simple RESTful api server, Frontend communicates with Egg via Ajax requests. Browser (Chrome latest) has Cross Origin enabled by default. Egg enabled CORS, frontend sends a request and need to get the payload back from Egg.

I have created an Angular frrontend and trying to talk to Egg ONLY via RESTful api. But I run into an issue of CORS. This is the error message from browser console:

XMLHttpRequest cannot load http://localhost:7001/api/user. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access.

I am not sure that it is an error from front end, or egg, or its plugins: egg-rest, egg-cors, or just an User error.

Here is how you can duplicate the issue.

1st Use [examples/restful_api] , then enabled cors plugins at [here](https://github.com/eggjs/egg/tree/master/examples/restful_api)(https://github.com/eggjs/egg/blob/master/examples/restful_api/config/plugin.js#L3)

// restful_api/config/plugin.js
exports.cors = true;
exports.rest = true;

Note: it works on curl with/without cors=true. and all CRUD operation works as expected.

2nd Frontend ajax request: Here I included a simple HTML with jquery ajax caller.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
  <script>
    jQuery.ajax({
      url: "http://localhost:7001/api/user",
      type: "GET",
      contentType: "application/json",
      async: true,
      crossDomain: true,
      beforeSend: function(xhr) {
        xhr.setRequestHeader('X-Test-Header', 'test-value');
      },
      success: function( users ) {
        console.log('users: ', users)
      }
    });
  </script>
</body>
</html>

3rd - Load up in the browser, I see the error below:

screen shot 2016-08-14 at 9 57 59 am

Error on terminal console

screen shot 2016-08-14 at 10 02 38 am

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.