├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets-with-roadhog ├── .webpackrc ├── README.md ├── app │ ├── assets │ │ ├── .eslintrc │ │ ├── assets │ │ │ └── yay.jpg │ │ ├── components │ │ │ ├── MainLayout │ │ │ │ ├── Header.jsx │ │ │ │ ├── MainLayout.css │ │ │ │ └── MainLayout.jsx │ │ │ └── Users │ │ │ │ ├── UserModal.jsx │ │ │ │ ├── Users.css │ │ │ │ └── Users.jsx │ │ ├── constants.js │ │ ├── index.css │ │ ├── index.js │ │ ├── models │ │ │ └── users.js │ │ ├── router.jsx │ │ ├── routes │ │ │ ├── IndexPage.css │ │ │ ├── IndexPage.jsx │ │ │ ├── Users.css │ │ │ └── Users.jsx │ │ ├── services │ │ │ └── users.js │ │ └── utils │ │ │ └── request.js │ ├── controller │ │ └── home.js │ └── router.js ├── config │ ├── config.default.js │ ├── config.prod.js │ └── plugin.js ├── package.json └── test │ └── app │ └── controller │ └── home.test.js ├── assets-with-umi ├── .autod.conf.js ├── .env ├── README.md ├── app │ ├── controller │ │ └── home.js │ ├── router.js │ ├── view │ │ └── index.html │ └── web │ │ ├── .eslintrc │ │ ├── config │ │ ├── config.js │ │ ├── config.local.js │ │ └── config.prod.js │ │ ├── layouts │ │ ├── index.jsx │ │ └── index.module.less │ │ └── pages │ │ ├── discover │ │ ├── index.jsx │ │ └── index.module.less │ │ ├── home │ │ ├── index.jsx │ │ └── index.module.less │ │ ├── index.js │ │ ├── order │ │ ├── index.jsx │ │ └── index.module.less │ │ ├── profile │ │ ├── index.jsx │ │ └── index.module.less │ │ └── shop │ │ ├── index.jsx │ │ └── index.module.less ├── config │ ├── config.default.js │ └── plugin.js ├── package.json └── test │ └── app │ └── controller │ └── home.test.js ├── bin ├── base.js ├── list.js └── test.js ├── bodyParser ├── REAME.md ├── app │ ├── controller │ │ └── home.js │ └── router.js ├── config │ └── config.default.js ├── package.json └── test │ └── home.test.js ├── cnode-api-async └── README.md ├── cnode-api ├── README.md ├── README.zh-CN.md ├── app │ ├── controller │ │ └── topics.js │ ├── middleware │ │ └── error_handler.js │ ├── router.js │ └── service │ │ └── topics.js ├── config │ ├── config.default.js │ └── plugin.js ├── package.json └── test │ └── app │ ├── controller │ └── topics.test.js │ └── service │ └── topics.test.js ├── cookie-session ├── app │ ├── controller │ │ └── home.js │ └── router.js ├── config │ └── config.default.js ├── package.json └── test │ └── index.test.js ├── cookie ├── app │ ├── controller │ │ └── cookie.js │ └── router.js ├── config │ └── config.default.js ├── package.json └── test │ └── index.test.js ├── custom-env ├── README.md ├── app │ ├── controller │ │ └── home.js │ └── router.js ├── config │ ├── config.default.js │ ├── config.prod.js │ └── config.sit.js ├── package.json └── test │ └── app │ └── controller │ └── home.test.js ├── download ├── app │ ├── controller │ │ └── index.js │ ├── public │ │ └── hello.txt │ └── router.js ├── config │ └── config.default.js ├── package.json └── test │ └── index.test.js ├── example.js ├── framework ├── README.md ├── app │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ ├── router.js │ │ └── view │ │ │ └── home.tpl │ ├── package.json │ └── test │ │ └── app │ │ └── controller │ │ └── home.test.js └── yadan │ ├── app │ ├── extend │ │ ├── application.js │ │ └── context.js │ └── service │ │ └── test.js │ ├── config │ ├── config.default.js │ └── plugin.js │ ├── index.js │ ├── lib │ ├── agent.js │ └── application.js │ ├── package.json │ └── test │ └── lib │ └── framework.test.js ├── hackernews-async-ts-di ├── .autod.conf.js ├── .gitignore ├── .vscode │ └── settings.json ├── README.md ├── app │ ├── controller │ │ ├── index.d.ts │ │ └── news.ts │ ├── extend │ │ └── filter.ts │ ├── public │ │ ├── css │ │ │ └── news.css │ │ └── favicon.png │ ├── router.ts │ ├── service │ │ └── HackerNews.ts │ └── view │ │ ├── layout │ │ └── layout.tpl │ │ └── news │ │ ├── detail.tpl │ │ ├── item.tpl │ │ ├── list.tpl │ │ └── user.tpl ├── config │ ├── config.ts │ ├── defaultConfig.ts │ └── plugin.ts ├── package.json ├── test │ └── app │ │ ├── controller │ │ └── news.test.ts │ │ └── service │ │ └── HackerNews.test.ts ├── tsconfig.json └── tslint.json ├── hackernews-async-ts ├── .eslintrc ├── .gitignore ├── README.md ├── app │ ├── controller │ │ └── news.ts │ ├── extend │ │ └── filter.ts │ ├── public │ │ ├── css │ │ │ └── news.css │ │ └── favicon.png │ ├── router.ts │ ├── service │ │ └── News.ts │ └── view │ │ ├── layout │ │ └── layout.tpl │ │ └── news │ │ ├── detail.tpl │ │ ├── item.tpl │ │ ├── list.tpl │ │ └── user.tpl ├── config │ ├── config.default.ts │ ├── config.local.ts │ ├── config.prod.ts │ └── plugin.ts ├── package.json ├── test │ └── app │ │ ├── controller │ │ └── news.test.ts │ │ └── service │ │ └── News.test.ts └── tsconfig.json ├── hackernews-async └── README.md ├── hackernews-datahub ├── .autod.conf.js ├── README.md ├── app │ ├── controller │ │ ├── api.js │ │ └── news.js │ ├── extend │ │ └── filter.js │ ├── public │ │ ├── css │ │ │ └── news.css │ │ └── favicon.png │ ├── router.js │ ├── service │ │ └── HackerNews.js │ └── view │ │ ├── layout │ │ └── layout.tpl │ │ └── news │ │ ├── item.tpl │ │ └── list.tpl ├── config │ ├── config.default.js │ └── plugin.js ├── data │ ├── hackernews.json │ └── hackernews │ │ ├── hackernews_ALL_topstories.json.json │ │ └── hackernews_ALL_topstories.json │ │ ├── scene │ │ ├── default.json │ │ ├── empty.json │ │ ├── list20.json │ │ └── list5.json │ │ └── schema │ │ ├── request.json │ │ └── response.json ├── macaca-datahub.config.js ├── package.json └── test │ ├── e2e │ ├── datahub.test.js │ ├── helper.js │ └── mocha.opts │ └── unittest │ └── app │ ├── controller │ └── news.test.js │ └── service │ └── HackerNews.test.js ├── hackernews ├── .eslintrc ├── README.md ├── app │ ├── controller │ │ └── news.js │ ├── extend │ │ └── filter.js │ ├── public │ │ ├── css │ │ │ └── news.css │ │ └── favicon.png │ ├── router.js │ ├── service │ │ └── HackerNews.js │ └── view │ │ ├── layout │ │ └── layout.tpl │ │ └── news │ │ ├── detail.tpl │ │ ├── item.tpl │ │ ├── list.tpl │ │ └── user.tpl ├── config │ ├── config.default.js │ └── plugin.js ├── package.json └── test │ └── app │ ├── controller │ └── news.test.js │ └── service │ └── HackerNews.test.js ├── hello-tegg ├── .eslintignore ├── .eslintrc ├── .gitignore ├── app │ ├── biz │ │ ├── HelloService.ts │ │ └── package.json │ ├── controller │ │ └── HelloController.ts │ └── middleware │ │ └── trace_method.ts ├── config │ ├── config.default.ts │ └── plugin.ts ├── package.json ├── test │ ├── biz │ │ └── HelloService.test.ts │ └── controller │ │ └── HelloController.test.ts └── tsconfig.json ├── helloworld ├── app │ ├── controller │ │ ├── foo.js │ │ └── home.js │ └── router.js ├── config │ └── config.default.js ├── package.json └── test │ └── index.test.js ├── httpclient ├── README.md ├── app.js ├── app │ ├── controller │ │ └── httpclient.js │ └── router.js ├── config │ └── config.default.js └── package.json ├── ipc ├── README.md ├── agent.js ├── app.js ├── app │ ├── controller │ │ └── api.js │ ├── router.js │ ├── schedule │ │ ├── force_refresh.js │ │ └── pull_refresh.js │ └── service │ │ └── source.js ├── lib │ └── subscriber.js └── package.json ├── middleware ├── app │ └── middleware │ │ └── hello.js ├── config │ └── config.default.js ├── package.json └── test │ └── index.test.js ├── multipart-file-mode ├── .gitignore ├── app │ ├── controller │ │ ├── ajax.js │ │ ├── form.js │ │ ├── home.js │ │ └── multiple.js │ ├── router.js │ └── view │ │ ├── index.html │ │ ├── layout.html │ │ └── page │ │ ├── ajax.html │ │ ├── form.html │ │ ├── multiple.html │ │ └── multiple_result.html ├── config │ ├── config.default.js │ └── plugin.js ├── package.json └── test │ ├── index.test.js │ ├── kfc.jpeg │ └── mc.jpeg ├── multipart ├── .gitignore ├── app │ ├── controller │ │ ├── ajax.js │ │ ├── buffer.js │ │ ├── form.js │ │ ├── home.js │ │ └── multiple.js │ ├── router.js │ └── view │ │ ├── index.html │ │ ├── layout.html │ │ └── page │ │ ├── ajax.html │ │ ├── buffer.html │ │ ├── form.html │ │ ├── multiple.html │ │ └── multiple_result.html ├── config │ ├── config.default.js │ └── plugin.js ├── package.json └── test │ ├── index.test.js │ ├── kfc.jpeg │ └── mc.jpeg ├── package.json ├── passport ├── .gitignore ├── README.md ├── app │ ├── controller │ │ ├── home.js │ │ └── user.js │ └── router.js ├── config │ ├── config.default.js │ └── plugin.js └── package.json ├── progressive ├── step1 │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ ├── extend │ │ │ └── context.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ ├── package.json │ └── test │ │ └── index.test.js ├── step2 │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ ├── config.default.js │ │ └── plugin.js │ ├── lib │ │ └── plugin │ │ │ └── egg-ua │ │ │ ├── app │ │ │ └── extend │ │ │ │ └── context.js │ │ │ └── package.json │ ├── package.json │ └── test │ │ └── index.test.js ├── step3 │ ├── egg-ua │ │ ├── app │ │ │ └── extend │ │ │ │ └── context.js │ │ ├── package.json │ │ └── test │ │ │ ├── fixtures │ │ │ └── test-app │ │ │ │ ├── app │ │ │ │ └── router.js │ │ │ │ ├── config │ │ │ │ └── config.default.js │ │ │ │ └── package.json │ │ │ └── ua.test.js │ └── example-app │ │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ │ ├── config │ │ ├── config.default.js │ │ └── plugin.js │ │ ├── package.json │ │ └── test │ │ └── index.test.js └── step4 │ ├── egg-ua │ ├── app │ │ └── extend │ │ │ └── context.js │ ├── package.json │ └── test │ │ ├── fixtures │ │ └── test-app │ │ │ ├── app │ │ │ └── router.js │ │ │ ├── config │ │ │ └── config.default.js │ │ │ └── package.json │ │ └── ua.test.js │ ├── example-app │ ├── app │ │ ├── controller │ │ │ └── home.js │ │ └── router.js │ ├── config │ │ └── config.default.js │ ├── package.json │ └── test │ │ └── index.test.js │ └── example-framework │ ├── config │ ├── config.default.js │ └── plugin.js │ ├── index.js │ ├── lib │ ├── agent.js │ └── application.js │ ├── package.json │ └── test │ ├── fixtures │ └── test-app │ │ ├── app │ │ └── router.js │ │ ├── config │ │ └── config.default.js │ │ └── package.json │ └── framework.test.js ├── redefine-controller ├── app │ ├── controller │ │ └── api.js │ ├── core │ │ └── controller.js │ └── router.js ├── config │ └── config.default.js ├── package.json └── test │ └── controller │ └── api.test.js ├── schedule ├── app │ └── schedule │ │ ├── all_cron.js │ │ ├── all_interval.js │ │ ├── worker_cron.js │ │ └── worker_interval.js ├── package.json └── test │ └── index.test.js ├── sequelize-ts ├── .gitignore ├── .sequelizerc ├── README.md ├── README.zh-CN.md ├── app │ ├── controller │ │ ├── post.ts │ │ └── user.ts │ ├── extend │ │ └── helper.ts │ ├── model │ │ ├── post.ts │ │ └── user.ts │ ├── router.ts │ └── service │ │ ├── post.ts │ │ └── user.ts ├── config │ ├── config.default.ts │ ├── config.unittest.ts │ └── plugin.ts ├── database │ ├── config.json │ └── migrations │ │ ├── 20180813112934-user.js │ │ └── 20180813112942-post.js ├── package.json ├── test │ ├── app │ │ └── controller │ │ │ ├── post.test.ts │ │ │ └── user.test.ts │ ├── factories.ts │ ├── mocha.opts │ └── setup.ts └── tsconfig.json ├── sequelize ├── .autod.conf.js ├── .sequelizerc ├── README.md ├── README.zh-CN.md ├── app │ ├── controller │ │ ├── post.js │ │ └── user.js │ ├── extend │ │ └── helper.js │ ├── model │ │ ├── post.js │ │ └── user.js │ ├── router.js │ └── service │ │ ├── post.js │ │ └── user.js ├── config │ ├── config.default.js │ ├── config.unittest.js │ └── plugin.js ├── database │ ├── config.json │ └── migrations │ │ ├── 20180813112934-user.js │ │ └── 20180813112942-post.js ├── package.json └── test │ ├── .setup.js │ ├── app │ ├── controller │ │ ├── post.test.js │ │ └── user.test.js │ └── service │ │ └── post.test.js │ └── factories.js ├── sofa-rpc ├── .autod.conf.js ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── README.md ├── README.zh-CN.md ├── app │ ├── controller │ │ └── home.js │ ├── router.js │ └── rpc │ │ └── ProtoService.js ├── appveyor.yml ├── config │ ├── config.default.js │ └── proxy.js ├── package.json ├── proto │ └── ProtoService.proto └── test │ ├── app │ └── controller │ │ └── home.test.js │ └── server.test.js ├── static ├── app │ ├── controller │ │ └── home.js │ ├── public │ │ ├── foo.js │ │ ├── hi.txt │ │ └── 蛋蛋Web框架.txt │ └── router.js ├── config │ ├── config.default.js │ └── plugin.js ├── package.json └── test │ └── index.test.js ├── todomvc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── app.js ├── app │ ├── controller │ │ ├── home.js │ │ └── todo.js │ ├── middleware │ │ └── response_time.js │ ├── model │ │ └── todo.js │ ├── public │ │ └── main.js │ ├── router.js │ ├── service │ │ └── todo.js │ └── view │ │ └── home.tpl ├── config │ ├── config.default.js │ ├── config.unittest.js │ ├── database │ │ └── init.js │ └── plugin.js ├── jsconfig.json ├── package.json ├── test │ └── app │ │ └── controller │ │ └── home.test.js └── todomvc.png ├── unittest-jest ├── README.md ├── __tests__ │ ├── controller │ │ └── home.test.js │ └── index.test.js ├── app │ ├── controller │ │ └── home.js │ ├── extend │ │ └── application.js │ └── router.js ├── config │ └── config.default.js └── package.json ├── unittest ├── README.md ├── app.js ├── app │ ├── controller │ │ └── home.js │ ├── extend │ │ ├── application.js │ │ ├── context.js │ │ ├── helper.js │ │ ├── request.js │ │ └── response.js │ ├── router.js │ └── service │ │ └── user.js ├── config │ └── config.default.js ├── package.json └── test │ ├── controller │ └── home.test.js │ ├── extend │ ├── application.test.js │ ├── context.test.js │ ├── helper.test.js │ ├── request.test.js │ └── response.test.js │ ├── hello.test.js │ └── service │ └── user.test.js └── view-nunjucks ├── app ├── controller │ └── home.js ├── public │ ├── css │ │ ├── home.css │ │ └── layout.css │ └── js │ │ └── home.js ├── router.js └── view │ ├── component │ └── nav.html │ ├── home.html │ └── layout.html ├── config ├── config.default.js └── plugin.js ├── package.json ├── test └── index.test.js └── uitest ├── helper.js ├── homepage.test.js └── mocha.opts /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ##### Checklist 12 | 13 | 14 | - [ ] `npm test` passes 15 | - [ ] tests and/or benchmarks are included 16 | - [ ] documentation is changed or added 17 | - [ ] commit message follows commit guidelines 18 | 19 | ##### Affected core subsystem(s) 20 | 21 | 22 | 23 | ##### Description of change 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | pull_request: 8 | branches: [ master ] 9 | 10 | workflow_dispatch: {} 11 | 12 | jobs: 13 | Job: 14 | name: Node.js 15 | uses: artusjs/github-actions/.github/workflows/node-test.yml@master 16 | with: 17 | os: 'ubuntu-latest' 18 | version: '14, 16, 18' 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | *.log 4 | npm-debug.log 5 | .logs 6 | logs 7 | *.sw* 8 | run 9 | *-run 10 | .idea 11 | .DS_Store 12 | .tmp 13 | *.log.* 14 | cnode-api-async/*.yml 15 | !static/app/public 16 | assets-with-roadhog/config/manifest.json 17 | assets-with-umi/config/manifest.json 18 | .umi 19 | .umi-production 20 | dist 21 | screenshots 22 | reports 23 | .history 24 | yarn.lock 25 | package-lock.json 26 | .nyc_output/ 27 | **/proxy_class 28 | **/apiMeta.json 29 | **/pom.xml 30 | sofa-rpc/src 31 | sofa-rpc/target 32 | -------------------------------------------------------------------------------- /assets-with-roadhog/.webpackrc: -------------------------------------------------------------------------------- 1 | { 2 | "entry": "app/assets/*.js", 3 | "theme": { 4 | "@primary-color": "#dc6aac", 5 | "@link-color": "#dc6aac", 6 | "@border-radius-base": "2px", 7 | "@font-size-base": "16px", 8 | "@line-height-base": "1.2" 9 | }, 10 | "extraBabelPlugins": [ 11 | [ "import", { "libraryName": "antd", "style": true } ] 12 | ], 13 | "env": { 14 | "development": { 15 | "extraBabelPlugins": [ 16 | "dva-hmr" 17 | ] 18 | } 19 | }, 20 | "outputPath": "app/public", 21 | "hash": true, 22 | "manifest": { 23 | "fileName": "../../config/manifest.json" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /assets-with-roadhog/README.md: -------------------------------------------------------------------------------- 1 | # Example for building frontend app with [roadhog](https://github.com/sorrycc/roadhog) 2 | 3 | ## Development 4 | 5 | ```bash 6 | $ npm i 7 | $ npm run dev 8 | $ open http://localhost:7001/ 9 | ``` 10 | 11 | ### Deployment 12 | 13 | Build assets with roadhog that will be generated to `app/public` which served by egg. 14 | 15 | And `manifest.json` will be generated to `config` 16 | 17 | ```bash 18 | $ npm run build 19 | ``` 20 | 21 | Start server 22 | 23 | ```bash 24 | $ npm start 25 | ``` 26 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "egg/react" 3 | } 4 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/assets/yay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/examples/5ea83ef36dede99ae057adfb6972c5086d8d2e4c/assets-with-roadhog/app/assets/assets/yay.jpg -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/components/MainLayout/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Menu, Icon } from 'antd'; 3 | import { Link } from 'dva/router'; 4 | 5 | function Header({ location }) { 6 | return ( 7 | 12 | 13 | Users 14 | 15 | 16 | Home 17 | 18 | 19 | 404 20 | 21 | 22 | dva 23 | 24 | 25 | ); 26 | } 27 | 28 | export default Header; 29 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/components/MainLayout/MainLayout.css: -------------------------------------------------------------------------------- 1 | 2 | .normal { 3 | display: flex; 4 | flex-direction: column; 5 | height: 100%; 6 | } 7 | 8 | .content { 9 | flex: 1; 10 | display: flex; 11 | } 12 | 13 | .main { 14 | padding: 0 8px; 15 | flex: 1 0 auto; 16 | } 17 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/components/MainLayout/MainLayout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './MainLayout.css'; 3 | import Header from './Header'; 4 | 5 | function MainLayout({ children, location }) { 6 | return ( 7 |
8 |
9 |
10 |
11 | {children} 12 |
13 |
14 |
15 | ); 16 | } 17 | 18 | export default MainLayout; 19 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/components/Users/Users.css: -------------------------------------------------------------------------------- 1 | 2 | .normal { 3 | } 4 | 5 | .create { 6 | margin-bottom: 1.5em; 7 | } 8 | 9 | .operation a { 10 | margin: 0 .5em; 11 | } 12 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/constants.js: -------------------------------------------------------------------------------- 1 | 2 | export const PAGE_SIZE = 3; 3 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/index.css: -------------------------------------------------------------------------------- 1 | 2 | html, body, :global(#root) { 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/index.js: -------------------------------------------------------------------------------- 1 | import dva from 'dva'; 2 | import createHistory from 'history/createBrowserHistory'; 3 | import createLoading from 'dva-loading'; 4 | import { message } from 'antd'; 5 | import router from './router'; 6 | 7 | import './index.css'; 8 | 9 | const ERROR_MSG_DURATION = 3; // 3 秒 10 | 11 | // 1. Initialize 12 | const app = dva({ 13 | history: createHistory(), 14 | onError(e) { 15 | message.error(e.message, ERROR_MSG_DURATION); 16 | }, 17 | }); 18 | 19 | // 2. Plugins 20 | app.use(createLoading()); 21 | 22 | // 3. Model 23 | // Moved to router.js 24 | 25 | // 4. Router 26 | app.router(router); 27 | 28 | // 5. Start 29 | app.start('#root'); 30 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/router.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Router, Switch, Route } from 'dva/router'; 3 | import dynamic from 'dva/dynamic'; 4 | 5 | function RouterConfig({ history, app }) { 6 | const IndexPage = dynamic({ 7 | app, 8 | component: () => import('./routes/IndexPage'), 9 | }); 10 | 11 | const Users = dynamic({ 12 | app, 13 | models: () => [ 14 | import('./models/users'), 15 | ], 16 | component: () => import('./routes/Users'), 17 | }); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | 29 | export default RouterConfig; 30 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/routes/IndexPage.css: -------------------------------------------------------------------------------- 1 | 2 | .normal { 3 | font-family: Georgia, sans-serif; 4 | margin-top: 3em; 5 | text-align: center; 6 | } 7 | 8 | .title { 9 | font-size: 2.5rem; 10 | font-weight: normal; 11 | letter-spacing: -1px; 12 | } 13 | 14 | .welcome { 15 | height: 328px; 16 | background: url(../assets/yay.jpg) no-repeat center 0; 17 | background-size: 388px 328px; 18 | } 19 | 20 | .list { 21 | font-size: 1.2em; 22 | margin-top: 1.8em; 23 | list-style: none; 24 | line-height: 1.5em; 25 | } 26 | 27 | .list code { 28 | background: #f7f7f7; 29 | } 30 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/routes/IndexPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'dva'; 3 | import styles from './IndexPage.css'; 4 | import MainLayout from '../components/MainLayout/MainLayout'; 5 | 6 | function IndexPage({ location }) { 7 | return ( 8 | 9 |
10 |

Yay! Welcome to dva!

11 |
12 |
    13 |
  • To get started, edit src/index.js and save to reload.
  • 14 |
  • Getting Started
  • 15 |
16 |
17 | 18 | ); 19 | } 20 | 21 | IndexPage.propTypes = { 22 | }; 23 | 24 | export default connect()(IndexPage); 25 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/routes/Users.css: -------------------------------------------------------------------------------- 1 | 2 | .normal { 3 | width: 900px; 4 | margin: 3em auto 0; 5 | } 6 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/routes/Users.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'dva'; 3 | import styles from './Users.css'; 4 | import UsersComponent from '../components/Users/Users'; 5 | import MainLayout from '../components/MainLayout/MainLayout'; 6 | 7 | function Users({ location }) { 8 | return ( 9 | 10 |
11 | 12 |
13 |
14 | ); 15 | } 16 | 17 | export default connect()(Users); 18 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/services/users.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import { PAGE_SIZE } from '../constants'; 3 | 4 | export function fetch({ page }) { 5 | return request(`/api/users?_page=${page}&_limit=${PAGE_SIZE}`); 6 | } 7 | 8 | export function remove(id) { 9 | return request(`/api/users/${id}`, { 10 | method: 'DELETE', 11 | }); 12 | } 13 | 14 | export function patch(id, values) { 15 | return request(`/api/users/${id}`, { 16 | method: 'PATCH', 17 | body: JSON.stringify(values), 18 | }); 19 | } 20 | 21 | export function create(values) { 22 | return request('/api/users', { 23 | method: 'POST', 24 | body: JSON.stringify(values), 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/assets/utils/request.js: -------------------------------------------------------------------------------- 1 | import fetch from 'dva/fetch'; 2 | 3 | function checkStatus(response) { 4 | if (response.status >= 200 && response.status < 300) { 5 | return response; 6 | } 7 | 8 | const error = new Error(response.statusText); 9 | error.response = response; 10 | throw error; 11 | } 12 | 13 | /** 14 | * Requests a URL, returning a promise. 15 | * 16 | * @param {string} url The URL we want to request 17 | * @param {object} [options] The options we want to pass to "fetch" 18 | * @return {object} An object containing either "data" or "err" 19 | */ 20 | async function request(url, options) { 21 | const response = await fetch(url, options); 22 | 23 | checkStatus(response); 24 | 25 | const data = await response.json(); 26 | 27 | const ret = { 28 | data, 29 | headers: {}, 30 | }; 31 | 32 | if (response.headers.get('x-total-count')) { 33 | ret.headers['x-total-count'] = response.headers.get('x-total-count'); 34 | } 35 | 36 | return ret; 37 | } 38 | 39 | export default request; 40 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | async index() { 7 | await this.ctx.render('index.js'); 8 | } 9 | 10 | async api() { 11 | const ctx = this.ctx; 12 | 13 | const url = 'http://jsonplaceholder.typicode.com' + ctx.path.replace(/^\/api/, '') + '?' + ctx.querystring; 14 | 15 | const res = await this.ctx.curl(url, { 16 | method: this.ctx.method, 17 | }); 18 | ctx.body = res.data; 19 | ctx.status = res.status; 20 | } 21 | } 22 | 23 | module.exports = HomeController; 24 | -------------------------------------------------------------------------------- /assets-with-roadhog/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {Egg.Application} app - egg application 5 | */ 6 | module.exports = app => { 7 | const { router, controller } = app; 8 | router.all('/api/*', controller.home.api); 9 | router.get('*', controller.home.index); 10 | }; 11 | -------------------------------------------------------------------------------- /assets-with-roadhog/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = appInfo => { 6 | const config = exports = {}; 7 | 8 | // use for cookie sign key, should change to your own and keep security 9 | config.keys = appInfo.name + '_1513765449219_5858'; 10 | 11 | // add your config here 12 | config.middleware = []; 13 | 14 | config.view = { 15 | root: path.join(appInfo.baseDir, 'app/assets'), 16 | mapping: { 17 | '.js': 'assets', 18 | }, 19 | }; 20 | 21 | config.assets = { 22 | publicPath: '/public/', 23 | devServer: { 24 | debug: false, 25 | command: 'roadhog dev', 26 | port: 8000, 27 | env: { 28 | BROWSER: 'none', 29 | ESLINT: 'none', 30 | SOCKET_SERVER: 'http://127.0.0.1:8000', 31 | PUBLIC_PATH: 'http://127.0.0.1:8000', 32 | }, 33 | }, 34 | }; 35 | 36 | config.security = { 37 | csrf: false, 38 | }; 39 | 40 | return config; 41 | }; 42 | -------------------------------------------------------------------------------- /assets-with-roadhog/config/config.prod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.assets = { 4 | url: 'http://127.0.0.1:7001', 5 | }; 6 | -------------------------------------------------------------------------------- /assets-with-roadhog/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // had enabled by egg 4 | // exports.static = true; 5 | 6 | exports.assets = { 7 | enable: true, 8 | package: 'egg-view-assets', 9 | }; 10 | 11 | exports.nunjucks = { 12 | enable: true, 13 | package: 'egg-view-nunjucks', 14 | }; 15 | -------------------------------------------------------------------------------- /assets-with-roadhog/test/app/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mock = require('egg-mock'); 4 | 5 | describe('test/app/controller/home.test.js', () => { 6 | let app; 7 | before(() => { 8 | mock.env('local'); 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | after(() => app.close()); 13 | 14 | afterEach(mock.restore); 15 | 16 | it('should GET /', async function() { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(/http:\/\/127.0.0.1:8000\/index.js/) 20 | .expect(200); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /assets-with-umi/.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'ues strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | plugin: 'autod-egg', 6 | prefix: '^', 7 | devprefix: '^', 8 | exclude: [ 9 | 'test/fixtures', 10 | 'examples', 11 | 'docs', 12 | 'run', 13 | 'app/web/pages/.umi', 14 | 'app/web/pages/.umi-production', 15 | ], 16 | devdep: [ 17 | 'egg-bin', 18 | 'eslint', 19 | 'eslint-config-egg', 20 | 'antd-mobile', 21 | 'umi-plugin-react', 22 | 'umi-plugin-ecma5-validator', 23 | ], 24 | dep: [ 25 | 'egg', 26 | 'egg-scripts', 27 | ], 28 | semver: [ 29 | ], 30 | test: 'scripts', 31 | }; 32 | -------------------------------------------------------------------------------- /assets-with-umi/.env: -------------------------------------------------------------------------------- 1 | APP_ROOT=app/web 2 | HTML=none -------------------------------------------------------------------------------- /assets-with-umi/README.md: -------------------------------------------------------------------------------- 1 | # Example for building frontend app with [umi](https://github.com/umijs/umi) 2 | 3 | ## Development 4 | 5 | ```bash 6 | $ npm i 7 | $ npm run dev 8 | $ open http://localhost:7001/ 9 | ``` 10 | 11 | ### Deployment 12 | 13 | Build assets with umi that will be generated to `app/public` which served by egg. 14 | 15 | And `manifest.json` will be generated to `config` 16 | 17 | ```bash 18 | $ npm run build 19 | ``` 20 | 21 | Start server 22 | 23 | ```bash 24 | $ npm start 25 | ``` 26 | -------------------------------------------------------------------------------- /assets-with-umi/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | async index() { 7 | await this.ctx.render('index.html'); 8 | } 9 | 10 | async api() { 11 | const ctx = this.ctx; 12 | 13 | const url = 'https://h5.ele.me' + ctx.path.replace(/^\/api/, '') + '?' + ctx.querystring; 14 | 15 | console.log(url); 16 | const res = await this.ctx.curl(url, { 17 | method: this.ctx.method, 18 | }); 19 | ctx.body = res.data; 20 | ctx.status = res.status; 21 | } 22 | } 23 | 24 | module.exports = HomeController; 25 | -------------------------------------------------------------------------------- /assets-with-umi/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {Egg.Application} app - egg application 5 | */ 6 | module.exports = app => { 7 | const { router, controller } = app; 8 | router.all('/restapi/*', controller.home.api); 9 | router.get('*', controller.home.index); 10 | }; 11 | -------------------------------------------------------------------------------- /assets-with-umi/app/view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% if ctx.app.config.env !== 'local' -%} 12 | {{ helper.assets.getStyle('vendors.css') | safe }} 13 | {%- endif %} 14 | 15 | 16 | 17 |
18 | 19 | 23 | {{ helper.assets.getScript('umi.js') | safe }} 24 | 25 | 26 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "egg/react" 3 | } 4 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/config/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: [ 3 | [ 4 | 'umi-plugin-react', 5 | { 6 | hd: true, 7 | antd: true, 8 | dynamicImport: { 9 | webpackChunkName: true, 10 | }, 11 | }, 12 | ], 13 | ], 14 | runtimePublicPath: true, 15 | disableCSSModules: true, 16 | cssModulesWithAffix: true, 17 | }; 18 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/config/config.local.js: -------------------------------------------------------------------------------- 1 | export default { 2 | proxy: { 3 | '/restapi': { 4 | target: 'http://127.0.0.1:7001/', 5 | changeOrigin: true, 6 | }, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/config/config.prod.js: -------------------------------------------------------------------------------- 1 | export default { 2 | hash: true, 3 | publicPath: '', 4 | outputPath: '../public', 5 | manifest: { 6 | fileName: '../../config/manifest.json', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/layouts/index.module.less: -------------------------------------------------------------------------------- 1 | :global { 2 | // FIXME: antd-mobile bug, carousel 3 | .slider-decorator-0 { 4 | bottom: -40px !important; 5 | } 6 | 7 | .am-list-body { 8 | border: none; 9 | } 10 | } 11 | 12 | .contentTab { 13 | margin-bottom: 1.1rem; 14 | } 15 | 16 | .content { 17 | margin: 0; 18 | } 19 | 20 | .tabbar { 21 | position: fixed; 22 | bottom: 0; 23 | width: 100%; 24 | box-shadow: 0 -0.03rem 0.15rem rgba(0, 0, 0, .1); 25 | padding-top: 15px; 26 | background: #fff; 27 | } 28 | 29 | .tab { 30 | display: flex; 31 | flex-direction: column; 32 | align-items: center; 33 | 34 | .icon { 35 | width: 35px; 36 | height: 35px; 37 | } 38 | 39 | .title { 40 | color: #666; 41 | font-size: 24px; 42 | font-weight: 300; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/pages/discover/index.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import router from 'umi/router'; 3 | import { NavBar, Icon, Grid } from 'antd-mobile'; 4 | import styles from './index.module.less'; 5 | 6 | export default class extends React.Component { 7 | renderItem = data => { 8 | return ( 9 |
{data.title}
10 | ); 11 | } 12 | 13 | render() { 14 | const items = [ 15 | { title: '金币商城' }, 16 | { title: '有红包快抢' }, 17 | { title: '必吃爆料' }, 18 | { title: '推荐有奖' }, 19 | { title: '周边优惠' }, 20 | { title: '百元红包' }, 21 | ]; 22 | 23 | return ( 24 |
25 | router.goBack()} 27 | mode="dark" 28 | icon={} 29 | >发现 30 | 31 |
为你推荐
32 |
33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/pages/discover/index.module.less: -------------------------------------------------------------------------------- 1 | .recommend { 2 | margin: .2rem 0; 3 | padding: .2rem; 4 | background: #fff; 5 | } 6 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/pages/index.js: -------------------------------------------------------------------------------- 1 | import Home from './home/index'; 2 | export default Home; 3 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/pages/order/index.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import router from 'umi/router'; 3 | import { NavBar, Icon, Button } from 'antd-mobile'; 4 | import styles from './index.module.less'; 5 | 6 | export default class extends React.Component { 7 | renderItem = data => { 8 | return ( 9 |
{data.title}
10 | ); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | router.goBack()} 18 | mode="dark" 19 | icon={} 20 | >订单 21 |
22 | 23 |

登录后查看外卖订单

24 | 25 |
26 |
27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/pages/order/index.module.less: -------------------------------------------------------------------------------- 1 | .noLogin { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | justify-content: center; 6 | align-items: center; 7 | margin-top: 2.4rem; 8 | font-size: .32rem; 9 | color: #666; 10 | 11 | h3 { 12 | font-weight: 400; 13 | } 14 | 15 | img { 16 | width: 4rem; 17 | } 18 | 19 | :global { 20 | .am-button { 21 | padding: 0 .5rem; 22 | background: #56d176; 23 | color: #fff; 24 | border-radius: 0; 25 | font-weight: 300; 26 | font-size: .26rem; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /assets-with-umi/app/web/pages/profile/index.module.less: -------------------------------------------------------------------------------- 1 | .banner { 2 | :global { 3 | .am-list-item, 4 | .am-navbar { 5 | background-image: linear-gradient(90deg, #0af, #0085ff); 6 | } 7 | } 8 | 9 | .info, 10 | .subInfo { 11 | color: #fff; 12 | font-weight: 300; 13 | } 14 | 15 | .subInfo { 16 | font-size: .24rem; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /assets-with-umi/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = exports = {}; 5 | 6 | // use for cookie sign key, should change to your own and keep security 7 | config.keys = appInfo.name + '_1513765449219_5858'; 8 | 9 | // add your config here 10 | config.middleware = []; 11 | 12 | config.view = { 13 | mapping: { 14 | '.html': 'nunjucks', 15 | }, 16 | }; 17 | 18 | config.assets = { 19 | publicPath: '/public/', 20 | devServer: { 21 | debug: true, 22 | command: 'umi dev', 23 | port: 8000, 24 | env: { 25 | APP_ROOT: process.cwd() + '/app/web', 26 | BROWSER: 'none', 27 | ESLINT: 'none', 28 | SOCKET_SERVER: 'http://127.0.0.1:8000', 29 | PUBLIC_PATH: 'http://127.0.0.1:8000', 30 | }, 31 | }, 32 | }; 33 | 34 | config.security = { 35 | csrf: false, 36 | }; 37 | 38 | return config; 39 | }; 40 | -------------------------------------------------------------------------------- /assets-with-umi/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // had enabled by egg 4 | // exports.static = true; 5 | 6 | exports.assets = { 7 | enable: true, 8 | package: 'egg-view-assets', 9 | }; 10 | 11 | exports.nunjucks = { 12 | enable: true, 13 | package: 'egg-view-nunjucks', 14 | }; 15 | -------------------------------------------------------------------------------- /assets-with-umi/test/app/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mock = require('egg-mock'); 4 | 5 | describe('test/app/controller/home.test.js', () => { 6 | let app; 7 | before(() => { 8 | mock.env('local'); 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | after(() => app.close()); 13 | 14 | afterEach(mock.restore); 15 | 16 | it('should GET /', async function() { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(/http:\/\/127.0.0.1:8000\/umi.js/) 20 | .expect(200); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /bin/list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('./base'); 4 | 5 | class List extends Command { 6 | async run() { 7 | const cache = new Set(); 8 | const dirs = await this.getExamples(); 9 | for (let dir of dirs) { 10 | dir = dir.split('/')[0]; 11 | if (cache.has(dir)) continue; 12 | cache.add(dir); 13 | console.info('- [%s](https://github.com/eggjs/examples/tree/master/%s)', dir, dir); 14 | } 15 | } 16 | } 17 | 18 | module.exports = List; 19 | -------------------------------------------------------------------------------- /bodyParser/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | const { xml2js } = require('xml-js'); 5 | 6 | class HomeController extends Controller { 7 | async body() { 8 | const { ctx } = this; 9 | 10 | ctx.body = { 11 | type: ctx.get('content-type'), 12 | body: ctx.request.body, 13 | }; 14 | } 15 | 16 | async xml() { 17 | const { ctx } = this; 18 | 19 | const xmlContent = xml2js(ctx.request.body); 20 | const body = xmlContent.elements[0].attributes; 21 | 22 | ctx.body = { 23 | type: ctx.get('content-type'), 24 | body, 25 | }; 26 | } 27 | } 28 | 29 | module.exports = HomeController; 30 | -------------------------------------------------------------------------------- /bodyParser/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | 6 | router.post('/api/body', controller.home.body); 7 | router.post('/api/xml', controller.home.xml); 8 | }; 9 | -------------------------------------------------------------------------------- /bodyParser/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123456'; 4 | 5 | exports.bodyParser = { 6 | enableTypes: [ 'json', 'form', 'text' ], 7 | extendTypes: { 8 | json: 'application/custom-json', 9 | text: [ 'application/xml' ], 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /bodyParser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bodyParser", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "egg": "^2", 6 | "xml-js": "^1.6.9" 7 | }, 8 | "devDependencies": { 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "scripts": { 13 | "dev": "egg-bin dev", 14 | "test": "egg-bin test", 15 | "cov": "egg-bin cov" 16 | }, 17 | "private": true 18 | } 19 | -------------------------------------------------------------------------------- /cnode-api-async/README.md: -------------------------------------------------------------------------------- 1 | See [cnode-api](https://github.com/eggjs/examples/tree/master/cnode-api) 2 | -------------------------------------------------------------------------------- /cnode-api/README.md: -------------------------------------------------------------------------------- 1 | # cnode-api 2 | 3 | ## QuickStart 4 | 5 | 6 | 7 | see [egg docs][egg] for more detail. 8 | 9 | ### Development 10 | ```shell 11 | $ npm install 12 | $ npm run dev 13 | $ open http://localhost:7001/news 14 | ``` 15 | 16 | ### Deploy 17 | 18 | Use `EGG_SERVER_ENV=prod` to enable prod mode 19 | 20 | ```shell 21 | $ EGG_SERVER_ENV=prod npm start 22 | ``` 23 | 24 | ### npm scripts 25 | 26 | - Use `npm run lint` to check code style. 27 | - Use `npm test` to run unit test. 28 | - Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail. 29 | 30 | 31 | [egg]: https://eggjs.org 32 | -------------------------------------------------------------------------------- /cnode-api/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # cnode-api 2 | 3 | ## 快速入门 4 | 5 | 6 | 7 | 如需进一步了解,参见 [egg 文档][egg]。 8 | 9 | ### 本地开发 10 | ```bash 11 | $ npm install 12 | $ npm run dev 13 | $ open http://localhost:7001/news 14 | ``` 15 | 16 | ### 部署 17 | 18 | 线上正式环境用 `EGG_SERVER_ENV=prod` 来启动。 19 | 20 | ```bash 21 | $ EGG_SERVER_ENV=prod npm start 22 | ``` 23 | 24 | ### 单元测试 25 | - [egg-bin] 内置了 [mocha], [thunk-mocha], [power-assert], [istanbul] 等框架,让你可以专注于写单元测试,无需理会配套工具。 26 | - 断言库非常推荐使用 [power-assert]。 27 | - 具体参见 [egg 文档 -单元测试](https://eggjs.org/zh-cn/core/unittest)。 28 | 29 | ### 内置指令 30 | 31 | - 使用 `npm run lint` 来做代码风格检查。 32 | - 使用 `npm test` 来执行单元测试。 33 | - 使用 `npm run autod` 来自动检测依赖更新,详细参见 [autod](https://www.npmjs.com/package/autod) 。 34 | 35 | 36 | [egg]: https://eggjs.org 37 | -------------------------------------------------------------------------------- /cnode-api/app/middleware/error_handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (option, app) => { 4 | return async function(ctx, next) { 5 | try { 6 | await next(); 7 | } catch (err) { 8 | // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志 9 | app.emit('error', err, this); 10 | const status = err.status || 500; 11 | // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息 12 | const error = status === 500 && app.config.env === 'prod' 13 | ? 'Internal Server Error' 14 | : err.message; 15 | // 从 error 对象上读出各个属性,设置到响应中 16 | ctx.body = { error }; 17 | if (status === 422) { 18 | ctx.body.detail = err.errors; 19 | } 20 | ctx.status = status; 21 | } 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /cnode-api/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.resources('topics', '/api/v2/topics', 'topics'); 5 | }; 6 | -------------------------------------------------------------------------------- /cnode-api/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = {}; 5 | 6 | // should change to your own 7 | config.keys = appInfo.name + '_1490750627161_5967'; 8 | 9 | config.middleware = [ 'errorHandler' ]; 10 | 11 | return config; 12 | }; 13 | -------------------------------------------------------------------------------- /cnode-api/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.validate = { 4 | enable: true, 5 | package: 'egg-validate', 6 | }; 7 | -------------------------------------------------------------------------------- /cnode-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cnode-api-async", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^1.10.1", 8 | "egg-validate": "^1.0.0" 9 | }, 10 | "devDependencies": { 11 | "egg-bin": "^4.3.5", 12 | "egg-mock": "^3.13.1" 13 | }, 14 | "engines": { 15 | "node": ">=8.9.0" 16 | }, 17 | "scripts": { 18 | "start": "eggctl start", 19 | "dev": "egg-bin dev", 20 | "test": "npm run test-local", 21 | "test-local": "egg-bin test", 22 | "cov": "egg-bin cov", 23 | "lint": "eslint .", 24 | "ci": "npm run lint && npm run cov", 25 | "autod": "autod" 26 | }, 27 | "ci": { 28 | "version": "8" 29 | }, 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /cookie-session/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | 7 | async setSession() { 8 | const ctx = this.ctx; 9 | 10 | ctx.session.count = (ctx.session.count || 0) + 1; 11 | ctx.body = `${ctx.session.count} times, now: ${Date()}`; 12 | } 13 | 14 | } 15 | 16 | module.exports = HomeController; 17 | -------------------------------------------------------------------------------- /cookie-session/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', app.controller.home.setSession); 5 | }; 6 | -------------------------------------------------------------------------------- /cookie-session/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my cooo00ooooool keys'; 4 | exports.security = { 5 | csrf: false, 6 | ctoken: false, 7 | }; 8 | -------------------------------------------------------------------------------- /cookie-session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-session-example", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "dev": "egg-bin dev", 12 | "test": "egg-bin test", 13 | "cov": "egg-bin cov" 14 | }, 15 | "private": true 16 | } 17 | -------------------------------------------------------------------------------- /cookie-session/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('example cookie_session test', () => { 7 | let app; 8 | let cookie; 9 | 10 | before(() => { 11 | app = mm.app(); 12 | return app.ready(); 13 | }); 14 | 15 | after(() => app.close()); 16 | 17 | it('should GET / first time', () => { 18 | return app.httpRequest() 19 | .get('/') 20 | .expect(200) 21 | .expect(/^1 times/) 22 | .expect('Set-Cookie', /^EGG_SESS=[^;]+; path=\/; expires=[^;]+; httponly$/) 23 | .expect(res => { 24 | cookie = res.headers['set-cookie'][0].split(';')[0]; 25 | }); 26 | }); 27 | 28 | it('should GET / second time', () => { 29 | return app.httpRequest() 30 | .get('/') 31 | .set('Cookie', cookie) 32 | .expect(200) 33 | .expect(/^2 times/) 34 | // session.count change 35 | .expect('Set-Cookie', /^EGG_SESS=[^;]+; path=\/; expires=[^;]+; httponly$/); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /cookie/app/controller/cookie.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class CookieController extends Controller { 6 | 7 | async home() { 8 | const ctx = this.ctx; 9 | 10 | if (ctx.cookies.get('remember')) { 11 | ctx.body = '

Remembered :). Click to forget!.

'; 12 | return; 13 | } 14 | 15 | ctx.body = `

Check to 17 | .

`; 18 | } 19 | 20 | async forget() { 21 | const ctx = this.ctx; 22 | 23 | ctx.cookies.set('remember', null); 24 | ctx.redirect('/'); 25 | } 26 | 27 | async remember() { 28 | const ctx = this.ctx; 29 | 30 | const minute = 60000; 31 | if (ctx.request.body.remember) { 32 | ctx.cookies.set('remember', 1, { maxAge: minute }); 33 | } 34 | ctx.redirect('/'); 35 | } 36 | 37 | } 38 | 39 | module.exports = CookieController; 40 | -------------------------------------------------------------------------------- /cookie/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', app.controller.cookie.home); 5 | app.router.get('/forget', app.controller.cookie.forget); 6 | app.router.post('/remember', app.controller.cookie.remember); 7 | }; 8 | -------------------------------------------------------------------------------- /cookie/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my cooo00ooooool keys'; 4 | exports.security = { 5 | csrf: false, 6 | ctoken: false, 7 | }; 8 | -------------------------------------------------------------------------------- /cookie/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-example", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "dev": "egg-bin dev", 12 | "test": "egg-bin test", 13 | "cov": "egg-bin cov" 14 | }, 15 | "private": true 16 | } 17 | -------------------------------------------------------------------------------- /custom-env/README.md: -------------------------------------------------------------------------------- 1 | # custom environment 2 | 3 | Egg will load different config file in different env, in this example we create `config.default.js`, `config.sit.js` and `config.prod.js`. 4 | 5 | Egg will load `config.sit.js` when lanched by `EGG_SERVER_ENV=sit npm run dev`. You can get see the response 6 | 7 | ```bash 8 | $ curl http://127.0.0.1:7001 9 | {"env":"sit","config":"sit keys"}% 10 | ``` 11 | -------------------------------------------------------------------------------- /custom-env/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class EnvController extends Controller { 6 | async getEnv() { 7 | const ctx = this.ctx; 8 | 9 | ctx.body = { 10 | env: ctx.app.config.env, 11 | config: ctx.app.config.keys, 12 | }; 13 | } 14 | } 15 | 16 | module.exports = EnvController; 17 | -------------------------------------------------------------------------------- /custom-env/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', app.controller.home.getEnv); 5 | }; 6 | -------------------------------------------------------------------------------- /custom-env/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: 'default keys', 5 | }; 6 | -------------------------------------------------------------------------------- /custom-env/config/config.prod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: 'prod keys', 5 | }; 6 | -------------------------------------------------------------------------------- /custom-env/config/config.sit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: 'sit keys', 5 | }; 6 | -------------------------------------------------------------------------------- /custom-env/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-env", 3 | "version": "1.0.0", 4 | "description": "custom environment", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^1.10.1" 8 | }, 9 | "devDependencies": { 10 | "egg-bin": "^4.3.5", 11 | "egg-mock": "^3.13.1" 12 | }, 13 | "engines": { 14 | "node": ">=8.9.0" 15 | }, 16 | "scripts": { 17 | "start": "node index.js", 18 | "dev": "egg-bin dev", 19 | "test": "egg-bin test", 20 | "autod": "autod" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /download/app/controller/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const Controller = require('egg').Controller; 6 | 7 | class IndexController extends Controller { 8 | async index() { 9 | this.ctx.body = [ 10 | 'download', 11 | '
', 12 | 'download image', 13 | ].join(''); 14 | } 15 | 16 | async download() { 17 | const filePath = path.resolve(this.app.config.static.dir, 'hello.txt'); 18 | this.ctx.attachment('hello.txt'); 19 | this.ctx.set('Content-Type', 'application/octet-stream'); 20 | this.ctx.body = fs.createReadStream(filePath); 21 | } 22 | 23 | async downloadImage() { 24 | const url = 'http://cdn2.ettoday.net/images/1200/1200526.jpg'; 25 | const res = await this.ctx.curl(url, { 26 | streaming: true, 27 | }); 28 | 29 | this.ctx.type = 'jpg'; 30 | this.ctx.body = res.res; 31 | } 32 | } 33 | 34 | module.exports = IndexController; 35 | -------------------------------------------------------------------------------- /download/app/public/hello.txt: -------------------------------------------------------------------------------- 1 | hello, egg -------------------------------------------------------------------------------- /download/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', app.controller.index.index); 5 | app.router.get('/download', app.controller.index.download); 6 | app.router.get('/download-image', app.controller.index.downloadImage); 7 | }; 8 | -------------------------------------------------------------------------------- /download/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my secret keys'; 4 | -------------------------------------------------------------------------------- /download/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "download", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1", 9 | "supertest": "^3.0.0" 10 | }, 11 | "scripts": { 12 | "dev": "egg-bin dev", 13 | "test": "egg-bin test", 14 | "lint": "eslint app config test *.js", 15 | "cov": "egg-bin cov" 16 | }, 17 | "private": true 18 | } 19 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const Command = require('common-bin'); 7 | 8 | class Example extends Command { 9 | constructor(rawArgs) { 10 | super(rawArgs); 11 | 12 | this.load(path.join(__dirname, 'bin')); 13 | } 14 | } 15 | 16 | new Example().start(); 17 | -------------------------------------------------------------------------------- /framework/README.md: -------------------------------------------------------------------------------- 1 | # Framework Example 2 | 3 | This example contains [app] and [framework] 4 | 5 | ## Quick Start 6 | 7 | Start an application using custom framework called yadan 8 | 9 | ```bash 10 | $ cd app 11 | $ npm install 12 | $ npm link ../yadan 13 | $ npm run dev 14 | ``` 15 | 16 | Yadan is a framework, it should be published to npm normally. With this example, you just `npm link` it. 17 | 18 | ## Run Test 19 | 20 | Application 21 | 22 | ```bash 23 | $ cd app 24 | $ npm test 25 | ``` 26 | 27 | Framework 28 | 29 | ```bash 30 | $ cd yadan 31 | $ npm test 32 | ``` 33 | 34 | ## Questions & Suggestions 35 | 36 | Please open an issue [here](https://github.com/eggjs/egg/issues). 37 | 38 | [app]: https://github.com/eggjs/examples/tree/master/framework/app 39 | [framework]: https://github.com/eggjs/examples/tree/master/framework/yadan 40 | -------------------------------------------------------------------------------- /framework/app/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('yadan').Controller; 4 | 5 | class HomeController extends Controller { 6 | async render() { 7 | const ctx = this.ctx; 8 | 9 | // use service defined in framework 10 | const data = await ctx.service.test.get(123); 11 | await ctx.render('home.tpl', data); 12 | } 13 | } 14 | 15 | module.exports = HomeController; 16 | -------------------------------------------------------------------------------- /framework/app/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', app.controller.home.render); 5 | }; 6 | -------------------------------------------------------------------------------- /framework/app/app/view/home.tpl: -------------------------------------------------------------------------------- 1 | hi, {{ name }} -------------------------------------------------------------------------------- /framework/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "framework-example", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "yadan": "../yadan" 6 | }, 7 | "devDependencies": { 8 | "egg-bin": "^4.3.5", 9 | "egg-mock": "^3.13.1" 10 | }, 11 | "scripts": { 12 | "dev": "egg-bin dev", 13 | "test": "egg-bin test" 14 | }, 15 | "egg": { 16 | "framework": "yadan" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /framework/app/test/app/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mock = require('egg-mock'); 4 | 5 | describe('test/app/controller/home.test.js', () => { 6 | 7 | let app; 8 | before(() => { 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | 13 | it('should GET /', () => { 14 | return app.httpRequest() 15 | .get('/') 16 | .expect(200) 17 | .expect('hi, framework-example_123456'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /framework/yadan/app/extend/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /framework/yadan/app/extend/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /framework/yadan/app/service/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Service = require('egg').Service; 4 | 5 | /** 6 | * Test Service 7 | */ 8 | class Test extends Service { 9 | constructor(ctx) { 10 | super(ctx); 11 | this.config = this.app.config.test; 12 | } 13 | 14 | async get(id) { 15 | return { id, name: this.config.key }; 16 | } 17 | } 18 | 19 | module.exports = Test; 20 | -------------------------------------------------------------------------------- /framework/yadan/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = {}; 5 | 6 | config.keys = 'keys'; 7 | 8 | /** 9 | * some description 10 | * @member Config#test 11 | * @property {String} key - some description 12 | */ 13 | config.test = { 14 | key: appInfo.name + '_123456', 15 | }; 16 | 17 | config.view = { 18 | defaultViewEngine: 'nunjucks', 19 | }; 20 | 21 | return config; 22 | }; 23 | -------------------------------------------------------------------------------- /framework/yadan/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // add you build-in plugin here, example: 4 | exports.nunjucks = { 5 | enable: true, 6 | package: 'egg-view-nunjucks', 7 | }; 8 | -------------------------------------------------------------------------------- /framework/yadan/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Application = require('./lib/application'); 4 | const Agent = require('./lib/agent'); 5 | const egg = require('egg'); 6 | 7 | // clone egg API 8 | Object.assign(exports, egg); 9 | 10 | // override Application and Agent 11 | exports.Application = Application; 12 | exports.Agent = Agent; 13 | -------------------------------------------------------------------------------- /framework/yadan/lib/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const egg = require('egg'); 5 | const EGG_PATH = Symbol.for('egg#eggPath'); 6 | 7 | class YadanAgent extends egg.Agent { 8 | get [EGG_PATH]() { 9 | return path.dirname(__dirname); 10 | } 11 | } 12 | 13 | module.exports = YadanAgent; 14 | -------------------------------------------------------------------------------- /framework/yadan/lib/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const egg = require('egg'); 5 | const EGG_PATH = Symbol.for('egg#eggPath'); 6 | 7 | class YadanApplication extends egg.Application { 8 | get [EGG_PATH]() { 9 | return path.dirname(__dirname); 10 | } 11 | } 12 | 13 | module.exports = YadanApplication; 14 | -------------------------------------------------------------------------------- /framework/yadan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yadan", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "egg": "^1.10.1", 6 | "egg-view-nunjucks": "^2.1.4" 7 | }, 8 | "devDependencies": { 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "engines": { 13 | "node": ">=8.9.0" 14 | }, 15 | "scripts": { 16 | "test": "egg-bin test" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /framework/yadan/test/lib/framework.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const assert = require('assert'); 5 | 6 | const mm = require('egg-mock'); 7 | 8 | describe('test/lib/framework.test.js', () => { 9 | let app; 10 | before(() => { 11 | app = mm.app({ 12 | baseDir: path.join(__dirname, '../../../app'), 13 | customEgg: true, 14 | }); 15 | return app.ready(); 16 | }); 17 | after(() => app.close()); 18 | afterEach(mm.restore); 19 | 20 | it('should GET /', () => { 21 | return app.httpRequest() 22 | .get('/') 23 | .expect('hi, framework-example_123456') 24 | .expect(200); 25 | }); 26 | 27 | it('should load config', () => { 28 | assert(app.config.test.key === 'framework-example_123456'); 29 | }); 30 | 31 | it('should load service', function* () { 32 | const ctx = app.mockContext(); 33 | const data = yield ctx.service.test.get(123); 34 | assert.deepEqual(data, { 35 | id: 123, 36 | name: 'framework-example_123456', 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'ues strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | plugin: 'autod-egg', 6 | prefix: '^', 7 | devprefix: '^', 8 | exclude: [ 9 | 'test/fixtures', 10 | ], 11 | dep: [ 12 | 'egg', 13 | ], 14 | devdep: [ 15 | 'autod', 16 | 'autod-egg', 17 | 'egg-bin', 18 | ], 19 | keep: [ 20 | 'tslib', 21 | 'typescript', 22 | ], 23 | semver: [ 24 | ], 25 | test: 'scripts', 26 | }; 27 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "USE_GITIGNORE": true, 4 | "**/*.js": { 5 | "when": "$(basename).ts" 6 | }, 7 | "**/*.map": true, 8 | "run": true, 9 | "logs": true, 10 | "out": true, 11 | "node_modules": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/README.md: -------------------------------------------------------------------------------- 1 | # egg-example-hackernews-async 2 | 3 | [Hacker News](https://news.ycombinator.com/) showcase using async/await for egg 4 | 5 | ## QuickStart 6 | 7 | ### Development 8 | ```shell 9 | $ npm install 10 | $ npm run tsc:w 11 | $ npm run dev 12 | $ open http://localhost:7001/ 13 | ``` 14 | 15 | ### Deploy 16 | 17 | Use `EGG_SERVER_ENV=prod` to enable prod mode 18 | 19 | ```shell 20 | $ EGG_SERVER_ENV=prod npm start 21 | ``` 22 | 23 | ### Npm Scripts 24 | 25 | - Use `npm run autod` to auto detect dependencies upgrade 26 | - Use `npm run lint` to check code style 27 | - Use `npm test` to run unit test 28 | 29 | ### Requirement 30 | 31 | Please ensure your node version is `>=7.6.0` for async await support without flag. If your node version is `>=7.0.0 < 7.6.0`, you can run npm scripts with harmony flag 32 | 33 | ```shell 34 | # start server 35 | npm run dev -- --harmony-async-await 36 | # run test cases 37 | npm run test-local -- --harmony-async-await 38 | ``` 39 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/controller/index.d.ts: -------------------------------------------------------------------------------- 1 | import NewsController from './news'; 2 | declare module 'egg' { 3 | export interface IController { 4 | news: NewsController; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/extend/filter.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as moment from 'moment'; 4 | 5 | exports.relativeTime = (time) => moment(new Date(time * 1000)).fromNow(); 6 | 7 | exports.domain = (url) => url && url.split('/')[2]; 8 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/router.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg'; 2 | 3 | export default (app: Application) => { 4 | const controller = app.controller; 5 | app.redirect('/', '/news'); 6 | app.router.get('/news', controller.news.list); 7 | app.router.get('/news/item/:id', controller.news.detail); 8 | app.router.get('/news/user/:id', controller.news.user); 9 | }; 10 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/view/layout/layout.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}egg - HackerNews{% endblock %} 9 | 10 | 11 |
12 | 19 | {% block content %}{% endblock %} 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/view/news/detail.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | 6 | {% include "./item.tpl" %} 7 | 8 | {% if comments.length > 0%} 9 |
    12 |
    13 | [-] 14 | {{ comment.by }} 15 | {{ comment.time | relativeTime }} 16 |
    17 |
    18 | {{ helper.shtml(comment.text) }} 19 |
    20 | 21 | {% endfor %} 22 |
23 | {% else %} 24 |

No comments yet.

25 | {% endif %} 26 |
27 | {% endblock %} -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/view/news/item.tpl: -------------------------------------------------------------------------------- 1 |
2 | {{ index }}. 3 |

4 | {{ helper.shtml(item.title) }} 5 | ({{ item.url | domain }}) 6 |

7 |

8 | 9 | {{ item.score }} points by {{ item.by }} 10 | 11 | {{ item.time | relativeTime }} 12 | 13 | | {{ item.descendants }} comments 14 | 15 |

16 |
-------------------------------------------------------------------------------- /hackernews-async-ts-di/app/view/news/list.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | {% for item in list %} 6 | {% set index = ((page-1) * pageSize + loop.index) %} 7 | {% include "./item.tpl" %} 8 | {% endfor %} 9 | 10 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/app/view/news/user.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | {% block title %} 3 | Profile: {{ user.id }} | egg - HackerNews 4 | {% endblock %} 5 | {% block content %} 6 |
7 |
    8 |
  • user: {{ user.id }}
  • 9 |
  • created: {{ user.created | relativeTime }}
  • 10 |
  • karma: {{ user.karma }}
  • 11 |
  • 12 | about: 13 |
    14 | {{ helper.shtml(user.about) }} 15 |
    16 |
  • 17 |
18 | 22 |
23 | {% endblock %} -------------------------------------------------------------------------------- /hackernews-async-ts-di/config/config.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { EggAppConfig } from 'egg'; 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | import 'source-map-support/register'; 6 | import defaultConfig from './defaultConfig'; 7 | 8 | export default (appInfo: EggAppConfig) => { 9 | const config: any = {}; 10 | 11 | // should change to your own 12 | config.keys = appInfo.name + '123456'; 13 | 14 | config.siteFile = { 15 | '/favicon.ico': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/favicon.png')), 16 | }; 17 | 18 | config.view = { 19 | defaultViewEngine: 'nunjucks', 20 | mapping: { 21 | '.tpl': 'nunjucks', 22 | }, 23 | }; 24 | 25 | return { ...config, ...defaultConfig }; 26 | }; 27 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/config/defaultConfig.ts: -------------------------------------------------------------------------------- 1 | export class DefaultConfig { 2 | news = { 3 | pageSize: 30, 4 | serverUrl: 'https://hacker-news.firebaseio.com/v0', 5 | }; 6 | }; 7 | 8 | export default new DefaultConfig(); 9 | 10 | declare module 'egg' { 11 | export interface Application { 12 | config: EggAppConfig & DefaultConfig; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/config/plugin.ts: -------------------------------------------------------------------------------- 1 | exports.static = true; 2 | 3 | exports.nunjucks = { 4 | enable: true, 5 | package: 'egg-view-nunjucks', 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/test/app/service/HackerNews.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as assert from 'assert'; 4 | import { Context } from 'egg'; 5 | import { getComponent } from 'egg-di'; 6 | import mm from 'egg-mock'; 7 | import { HackerNews } from '../../../app/service/HackerNews'; 8 | 9 | describe('test/app/service/HackerNews.test.js', () => { 10 | const app = mm.app(); 11 | let ctx: Context; 12 | let hackerNews: HackerNews; 13 | 14 | before(async () => { 15 | await app.ready(); 16 | ctx = app.mockContext(); 17 | hackerNews = getComponent(HackerNews, ctx); 18 | }); 19 | 20 | after(() => app.close()); 21 | afterEach(mm.restore); 22 | 23 | it('getTopStories', async () => { 24 | const list = await hackerNews.getTopStories(); 25 | assert(list.length === 30); 26 | }); 27 | 28 | it('getItem', async () => { 29 | const item = await hackerNews.getItem(1); 30 | assert(item.id && item.title && item.url); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "noImplicitAny": false, 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "charset": "utf8", 10 | "allowJs": false, 11 | "pretty": true, 12 | "noEmitOnError": false, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "allowUnreachableCode": false, 16 | "allowUnusedLabels": false, 17 | "noFallthroughCasesInSwitch": true, 18 | "skipLibCheck": true, 19 | "skipDefaultLibCheck": true, 20 | "inlineSourceMap": true, 21 | "importHelpers": true 22 | }, 23 | "include": [ 24 | "app/**/*", 25 | "config/**/*", 26 | "test/**/*.ts" 27 | ], 28 | "exclude": [ 29 | "app/public", 30 | "app/views" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /hackernews-async-ts-di/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:latest", 3 | "rules": { 4 | "quotemark": [ 5 | true, 6 | "single", 7 | "jsx-double" 8 | ], 9 | "no-console": [ 10 | true, 11 | "dir", 12 | "log", 13 | "error", 14 | "warn" 15 | ], 16 | "space-before-function-paren": false, 17 | "interface-name": [ 18 | true, 19 | "never-prefix" 20 | ], 21 | "adjacent-overload-signatures": true, 22 | "member-access": [ 23 | false 24 | ], 25 | "member-ordering": [ 26 | true, 27 | { 28 | "order": "fields-first" 29 | } 30 | ], 31 | "object-literal-sort-keys": false, 32 | "max-classes-per-file": [ 33 | true, 34 | 10 35 | ], 36 | "variable-name": [ 37 | true, 38 | "allow-leading-underscore" 39 | ], 40 | "align": [true, "statements"] 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /hackernews-async-ts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint-config-egg/typescript" 4 | } 5 | 6 | -------------------------------------------------------------------------------- /hackernews-async-ts/.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | node_modules/ 4 | coverage/ 5 | .idea/ 6 | run/ 7 | logs/ 8 | .DS_Store 9 | .vscode 10 | *.swp 11 | *.lock 12 | *.js 13 | 14 | app/**/*.js 15 | test/**/*.js 16 | config/**/*.js 17 | app/**/*.map 18 | test/**/*.map 19 | config/**/*.map -------------------------------------------------------------------------------- /hackernews-async-ts/README.md: -------------------------------------------------------------------------------- 1 | # hackernews-async-ts 2 | 3 | [Hacker News](https://news.ycombinator.com/) showcase using typescript && egg 4 | 5 | ## QuickStart 6 | 7 | ### Development 8 | 9 | ```bash 10 | $ npm i 11 | $ npm run dev 12 | $ open http://localhost:7001/ 13 | ``` 14 | 15 | Don't tsc compile at development mode, if you had run `tsc` then you need to `npm run clean` before `npm run dev`. 16 | 17 | ### Deploy 18 | 19 | ```bash 20 | $ npm run tsc 21 | $ npm start 22 | ``` 23 | 24 | ### Npm Scripts 25 | 26 | - Use `npm run lint` to check code style 27 | - Use `npm test` to run unit test 28 | - se `npm run clean` to clean compiled js at development mode once 29 | 30 | ### Requirement 31 | 32 | - Node.js 16.x 33 | - Typescript 4.x 34 | -------------------------------------------------------------------------------- /hackernews-async-ts/app/extend/filter.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | export function relativeTime(time) { 4 | return moment(new Date(time * 1000)).fromNow(); 5 | } 6 | 7 | export function domain(url) { 8 | return url && url.split('/')[2]; 9 | } 10 | -------------------------------------------------------------------------------- /hackernews-async-ts/app/router.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg'; 2 | 3 | export default (app: Application) => { 4 | const { controller, router } = app; 5 | 6 | router.redirect('/', '/news'); 7 | router.get('/news', controller.news.list); 8 | router.get('/news/item/:id', controller.news.detail); 9 | router.get('/news/user/:id', controller.news.user); 10 | }; 11 | -------------------------------------------------------------------------------- /hackernews-async-ts/app/view/layout/layout.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}egg - HackerNews{% endblock %} 9 | 10 | 11 |
12 | 19 | {% block content %}{% endblock %} 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /hackernews-async-ts/app/view/news/detail.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | 6 | {% include "./item.tpl" %} 7 | 8 | {% if comments.length > 0%} 9 |
    12 |
    13 | [-] 14 | {{ comment.by }} 15 | {{ comment.time | relativeTime }} 16 |
    17 |
    18 | {{ helper.shtml(comment.text) }} 19 |
    20 | 21 | {% endfor %} 22 |
23 | {% else %} 24 |

No comments yet.

25 | {% endif %} 26 |
27 | {% endblock %} -------------------------------------------------------------------------------- /hackernews-async-ts/app/view/news/item.tpl: -------------------------------------------------------------------------------- 1 |
2 | {{ index }}. 3 |

4 | {{ helper.shtml(item.title) }} 5 | ({{ item.url | domain }}) 6 |

7 |

8 | 9 | {{ item.score }} points by {{ item.by }} 10 | 11 | {{ item.time | relativeTime }} 12 | 13 | | {{ item.descendants }} comments 14 | 15 |

16 |
-------------------------------------------------------------------------------- /hackernews-async-ts/app/view/news/list.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | {% for item in list %} 6 | {% set index = ((page-1) * pageSize + loop.index) %} 7 | {% include "./item.tpl" %} 8 | {% endfor %} 9 | 10 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /hackernews-async-ts/app/view/news/user.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | {% block title %} 3 | Profile: {{ user.id }} | egg - HackerNews 4 | {% endblock %} 5 | {% block content %} 6 |
7 |
    8 |
  • user: {{ user.id }}
  • 9 |
  • created: {{ user.created | relativeTime }}
  • 10 |
  • karma: {{ user.karma }}
  • 11 |
  • 12 | about: 13 |
    14 | {{ helper.shtml(user.about) }} 15 |
    16 |
  • 17 |
18 | 22 |
23 | {% endblock %} -------------------------------------------------------------------------------- /hackernews-async-ts/config/config.local.ts: -------------------------------------------------------------------------------- 1 | import { DefaultConfig } from './config.default'; 2 | 3 | export default () => { 4 | const config: DefaultConfig = {}; 5 | config.news = { 6 | pageSize: 20, 7 | }; 8 | return config; 9 | }; 10 | -------------------------------------------------------------------------------- /hackernews-async-ts/config/config.prod.ts: -------------------------------------------------------------------------------- 1 | import { DefaultConfig } from './config.default'; 2 | 3 | export default () => { 4 | const config: DefaultConfig = {}; 5 | config.news = { 6 | pageSize: 30, 7 | }; 8 | return config; 9 | }; 10 | -------------------------------------------------------------------------------- /hackernews-async-ts/config/plugin.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | nunjucks: { 3 | enable: true, 4 | package: 'egg-view-nunjucks', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /hackernews-async-ts/test/app/service/News.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'assert'; 2 | import { Context } from 'egg'; 3 | import { app } from 'egg-mock/bootstrap'; 4 | 5 | describe('test/app/service/News.test.js', () => { 6 | let ctx: Context; 7 | 8 | before(() => { 9 | ctx = app.mockContext(); 10 | }); 11 | 12 | it('getTopStories', async () => { 13 | const list = await ctx.service.news.getTopStories(); 14 | assert(list.length === 30); 15 | }); 16 | 17 | it('getItem', async () => { 18 | const item = await ctx.service.news.getItem(1); 19 | assert(item.id && item.title && item.url); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /hackernews-async-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@eggjs/tsconfig", 3 | "compilerOptions": { 4 | "declaration": false 5 | }, 6 | "exclude": [ 7 | "app/public", 8 | "app/views" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /hackernews-async/README.md: -------------------------------------------------------------------------------- 1 | See [cnode-api](https://github.com/eggjs/examples/tree/master/hackernews) 2 | -------------------------------------------------------------------------------- /hackernews-datahub/.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'ues strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | plugin: 'autod-egg', 6 | prefix: '^', 7 | devprefix: '^', 8 | exclude: [ 9 | 'test/fixtures', 10 | ], 11 | dep: [ 12 | 'egg', 13 | ], 14 | devdep: [ 15 | 'autod', 16 | 'autod-egg', 17 | 'eslint', 18 | 'eslint-config-egg', 19 | 'egg-bin', 20 | ], 21 | keep: [ 22 | ], 23 | semver: [ 24 | ], 25 | test: 'scripts', 26 | }; 27 | -------------------------------------------------------------------------------- /hackernews-datahub/app/controller/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class NewsController extends Controller { 6 | async list() { 7 | const { ctx, app } = this; 8 | const pageSize = app.config.news.pageSize; 9 | const page = parseInt(ctx.query.page) || 1; 10 | 11 | const idList = await ctx.service.hackerNews.getTopStories(page); 12 | 13 | ctx.body = { 14 | list: idList, 15 | page, pageSize, 16 | }; 17 | } 18 | } 19 | 20 | module.exports = NewsController; 21 | -------------------------------------------------------------------------------- /hackernews-datahub/app/controller/news.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class NewsController extends Controller { 6 | async list() { 7 | const { ctx, app } = this; 8 | const pageSize = app.config.news.pageSize; 9 | const page = parseInt(ctx.query.page) || 1; 10 | 11 | const idList = await ctx.service.hackerNews.getTopStories(page); 12 | await ctx.render('news/list.tpl', { 13 | list: idList[0], 14 | page, pageSize, 15 | }); 16 | } 17 | } 18 | 19 | module.exports = NewsController; 20 | 21 | -------------------------------------------------------------------------------- /hackernews-datahub/app/extend/filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const moment = require('moment'); 4 | 5 | exports.relativeTime = time => moment(new Date(time * 1000)).fromNow(); 6 | 7 | exports.domain = url => url && url.split('/')[2]; 8 | 9 | -------------------------------------------------------------------------------- /hackernews-datahub/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | app.redirect('/', '/news'); 6 | router.get('/news', controller.news.list); 7 | router.get('/api/news', controller.api.list); 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /hackernews-datahub/app/view/layout/layout.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}egg - HackerNews{% endblock %} 9 | 10 | 11 |
12 | 19 | {% block content %}{% endblock %} 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /hackernews-datahub/app/view/news/item.tpl: -------------------------------------------------------------------------------- 1 |
2 | {{ index }}. 3 |

4 | {{ helper.shtml(item.title) }} 5 | ({{ item.url | domain }}) 6 |

7 |

8 | 9 | {{ item.score }} points by {{ item.by }} 10 | 11 | {{ item.time | relativeTime }} 12 | 13 | | {{ item.descendants }} comments 14 | 15 |

16 |
17 | 18 | -------------------------------------------------------------------------------- /hackernews-datahub/app/view/news/list.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | {% if list.length === 0 %} 6 |

empty

7 | {% endif %} 8 | {% for item in list %} 9 | {% set index = ((page-1) * pageSize + loop.index) %} 10 | {% include "./item.tpl" %} 11 | {% endfor %} 12 | 13 | 19 |
20 | {% endblock %} 21 | 22 | -------------------------------------------------------------------------------- /hackernews-datahub/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const datahubConfig = require('../macaca-datahub.config'); 6 | 7 | module.exports = appInfo => { 8 | const config = {}; 9 | 10 | // should change to your own 11 | config.keys = appInfo.name + '123456'; 12 | 13 | config.siteFile = { 14 | '/favicon.ico': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/favicon.png')), 15 | }; 16 | 17 | const mockPort = datahubConfig.port; 18 | const hubName = 'hackernews'; 19 | 20 | config.news = { 21 | pageSize: 30, 22 | serverUrl: process.env.MOCK 23 | ? `http://localhost:${mockPort}/data/${hubName}` 24 | : 'https://hacker-news.firebaseio.com/v0', 25 | }; 26 | 27 | config.view = { 28 | defaultViewEngine: 'nunjucks', 29 | mapping: { 30 | '.tpl': 'nunjucks', 31 | '.nj': 'nunjucks', 32 | }, 33 | }; 34 | 35 | return config; 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /hackernews-datahub/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.nunjucks = { 4 | enable: true, 5 | package: 'egg-view-nunjucks', 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /hackernews-datahub/data/hackernews.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "hackernews", 3 | "description": "hackernews", 4 | "uniqId": "42ed34a4-723c-4056-a762-08b6a6cff2ae" 5 | } -------------------------------------------------------------------------------- /hackernews-datahub/data/hackernews/hackernews_ALL_topstories.json.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "http", 3 | "pathname": "topstories.json", 4 | "method": "ALL", 5 | "projectUniqId": "42ed34a4-723c-4056-a762-08b6a6cff2ae", 6 | "description": "get top stories", 7 | "currentScene": "default", 8 | "proxyConfig": { 9 | "proxyList": [] 10 | }, 11 | "contextConfig": {}, 12 | "uniqId": "cb8f0113-4bbc-4f29-97a1-7fc174a2f196" 13 | } -------------------------------------------------------------------------------- /hackernews-datahub/data/hackernews/hackernews_ALL_topstories.json/scene/empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "sceneName": "empty", 3 | "data": { 4 | "list": [], 5 | "page": 1, 6 | "pageSize": 5 7 | }, 8 | "interfaceUniqId": "cb8f0113-4bbc-4f29-97a1-7fc174a2f196", 9 | "uniqId": "f4c98bbd-01a1-42e8-8c49-470f551aaa59" 10 | } -------------------------------------------------------------------------------- /hackernews-datahub/data/hackernews/hackernews_ALL_topstories.json/schema/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "request", 3 | "data": { 4 | "schemaData": { 5 | "type": "object", 6 | "required": [ 7 | "page", 8 | "pageSize" 9 | ], 10 | "properties": { 11 | "page": { 12 | "type": "integer", 13 | "description": "current page" 14 | }, 15 | "pageSize": { 16 | "type": "integer", 17 | "description": "page size" 18 | } 19 | } 20 | } 21 | }, 22 | "interfaceUniqId": "cb8f0113-4bbc-4f29-97a1-7fc174a2f196", 23 | "uniqId": "5dfc02e0-2b04-4588-ad18-28e3ec4cdd39" 24 | } -------------------------------------------------------------------------------- /hackernews-datahub/macaca-datahub.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | mode: 'local', 7 | port: 5678, // must be 5678, same with datahub-nodejs-sdk's default port 8 | store: path.resolve(__dirname, 'data'), 9 | }; 10 | -------------------------------------------------------------------------------- /hackernews-datahub/test/e2e/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const wd = require('macaca-wd'); 4 | 5 | const { 6 | extendsMixIn, 7 | } = require('macaca-wd/lib/helper'); 8 | 9 | extendsMixIn(wd); 10 | 11 | exports.driver = wd.promiseChainRemote({ 12 | host: 'localhost', 13 | port: process.env.MACACA_SERVER_PORT || 3456, 14 | }); 15 | 16 | exports.BASE_URL = 'http://127.0.0.1:7001/'; 17 | -------------------------------------------------------------------------------- /hackernews-datahub/test/e2e/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter macaca-reporter 2 | --require babel-register 3 | --recursive 4 | --timeout 60000 5 | -------------------------------------------------------------------------------- /hackernews-datahub/test/unittest/app/controller/news.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { mock } = require('egg-mock/bootstrap'); 4 | 5 | describe('test/app/controller/news.test.js', () => { 6 | let app; 7 | before(async () => { 8 | app = mock.app(); 9 | await app.ready(); 10 | }); 11 | 12 | after(() => app.close()); 13 | 14 | afterEach(mock.restore); 15 | 16 | it('should GET /news', async () => { 17 | await app.httpRequest().get('/news').expect(200); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /hackernews-datahub/test/unittest/app/service/HackerNews.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { mock, assert } = require('egg-mock/bootstrap'); 4 | 5 | describe('test/unittest/app/service/HackerNews.test.js', () => { 6 | let app; 7 | let ctx; 8 | 9 | before(async () => { 10 | app = mock.app(); 11 | await app.ready(); 12 | ctx = app.mockContext(); 13 | }); 14 | 15 | after(() => app.close()); 16 | afterEach(mock.restore); 17 | 18 | it('getTopStories', async () => { 19 | const list = await ctx.service.hackerNews.getTopStories(); 20 | assert(list.length === 30); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /hackernews/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint-config-egg" 4 | } 5 | -------------------------------------------------------------------------------- /hackernews/app/extend/filter.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | exports.relativeTime = time => moment(new Date(time * 1000)).fromNow(); 4 | 5 | exports.domain = url => url && url.split('/')[2]; 6 | -------------------------------------------------------------------------------- /hackernews/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | const { router, controller } = app; 3 | app.redirect('/', '/news'); 4 | router.get('/news', controller.news.list); 5 | router.get('/news/item/:id', controller.news.detail); 6 | router.get('/news/user/:id', controller.news.user); 7 | }; 8 | -------------------------------------------------------------------------------- /hackernews/app/view/layout/layout.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}egg - HackerNews{% endblock %} 9 | 10 | 11 |
12 | 19 | {% block content %}{% endblock %} 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /hackernews/app/view/news/detail.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | 6 | {% include "./item.tpl" %} 7 | 8 | {% if comments.length > 0%} 9 |
    10 | {% for comment in comments %} 11 |
  • 12 |
    13 | [-] 14 | {{ comment.by }} 15 | {{ comment.time | relativeTime }} 16 |
    17 |
    18 | {{ helper.shtml(comment.text) }} 19 |
    20 |
  • 21 | {% endfor %} 22 |
23 | {% else %} 24 |

No comments yet.

25 | {% endif %} 26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /hackernews/app/view/news/item.tpl: -------------------------------------------------------------------------------- 1 |
2 | {{ index }}. 3 |

4 | {{ helper.shtml(item.title) }} 5 | ({{ item.url | domain }}) 6 |

7 |

8 | 9 | {{ item.score }} points by {{ item.by }} 10 | 11 | {{ item.time | relativeTime }} 12 | 13 | | {{ item.descendants }} comments 14 | 15 |

16 |
-------------------------------------------------------------------------------- /hackernews/app/view/news/list.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | 3 | {% block content %} 4 |
5 | {% for item in list %} 6 | {% set index = ((page-1) * pageSize + loop.index) %} 7 | {% include "./item.tpl" %} 8 | {% endfor %} 9 | 10 | 16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /hackernews/app/view/news/user.tpl: -------------------------------------------------------------------------------- 1 | {% extends "../layout/layout.tpl" %} 2 | {% block title %} 3 | Profile: {{ user.id }} | egg - HackerNews 4 | {% endblock %} 5 | {% block content %} 6 |
7 |
    8 |
  • user: {{ user.id }}
  • 9 |
  • created: {{ user.created | relativeTime }}
  • 10 |
  • karma: {{ user.karma }}
  • 11 |
  • 12 | about: 13 |
    14 | {{ helper.shtml(user.about) }} 15 |
    16 |
  • 17 |
18 | 22 |
23 | {% endblock %} -------------------------------------------------------------------------------- /hackernews/config/config.default.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = appInfo => { 5 | const config = {}; 6 | 7 | // should change to your own 8 | config.keys = appInfo.name + '123456'; 9 | 10 | config.siteFile = { 11 | '/favicon.ico': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/favicon.png')), 12 | }; 13 | 14 | config.news = { 15 | pageSize: 30, 16 | serverUrl: 'https://hacker-news.firebaseio.com/v0', 17 | }; 18 | 19 | config.view = { 20 | defaultViewEngine: 'nunjucks', 21 | mapping: { 22 | '.tpl': 'nunjucks', 23 | '.nj': 'nunjucks', 24 | }, 25 | }; 26 | 27 | return config; 28 | }; 29 | -------------------------------------------------------------------------------- /hackernews/config/plugin.js: -------------------------------------------------------------------------------- 1 | exports.nunjucks = { 2 | enable: true, 3 | package: 'egg-view-nunjucks', 4 | }; 5 | 6 | -------------------------------------------------------------------------------- /hackernews/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hackernews", 3 | "version": "1.0.0", 4 | "description": "hackernews showcase using async/await for egg", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^3.11.0", 8 | "egg-view-nunjucks": "^2.3.0", 9 | "moment": "^2.19.2" 10 | }, 11 | "devDependencies": { 12 | "cheerio": "^1.0.0-rc.2", 13 | "egg-bin": "^5.9.0", 14 | "egg-mock": "^5.5.0", 15 | "eslint": "^8.31.0", 16 | "eslint-config-egg": "^12.1.0" 17 | }, 18 | "engines": { 19 | "node": ">=16.0.0" 20 | }, 21 | "scripts": { 22 | "dev": "egg-bin dev", 23 | "test": "npm run test-local", 24 | "test-local": "egg-bin test", 25 | "cov": "egg-bin cov", 26 | "lint": "eslint .", 27 | "ci": "npm run lint && npm run cov" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hackernews/test/app/controller/news.test.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | const { app, assert } = require('egg-mock/bootstrap'); 3 | 4 | describe('test/app/controller/news.test.js', () => { 5 | it('should GET /news', async () => { 6 | const result = await app.httpRequest().get('/news').expect(200); 7 | const $ = cheerio.load(result.text); 8 | const listItem = $('.news-view .item'); 9 | assert.equal(listItem.length, app.config.news.pageSize); 10 | }); 11 | 12 | it('should GET /news/item/:id', async () => { 13 | await app.httpRequest() 14 | .get('/news/item/1') 15 | .expect(/\/news\/item\/1/) // just a example, use regex to test part of dom string, but should be strong characteristic 16 | .expect(200); 17 | }); 18 | 19 | it('should GET /news/user/:id', async () => { 20 | await app.httpRequest() 21 | .get('/news/user/activatedgeek') 22 | .expect(/user:<\/span> activatedgeek/) // just a example, use regex to test part of dom string, but should be strong characteristic 23 | .expect(200); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /hackernews/test/app/service/HackerNews.test.js: -------------------------------------------------------------------------------- 1 | const { app, assert, mock } = require('egg-mock/bootstrap'); 2 | 3 | describe('test/app/service/HackerNews.test.js', () => { 4 | afterEach(mock.restore); 5 | 6 | it('getTopStories', async () => { 7 | const ctx = app.mockContext(); 8 | const list = await ctx.service.hackerNews.getTopStories(); 9 | assert.equal(list.length, 30); 10 | }); 11 | 12 | it('getItem', async () => { 13 | const ctx = app.mockContext(); 14 | const item = await ctx.service.hackerNews.getItem(1); 15 | assert(item.hasOwnProperty('id') && item.hasOwnProperty('title') && item.hasOwnProperty('url')); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /hello-tegg/.eslintignore: -------------------------------------------------------------------------------- 1 | typings/ 2 | -------------------------------------------------------------------------------- /hello-tegg/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint-config-egg/typescript" 4 | } 5 | -------------------------------------------------------------------------------- /hello-tegg/.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | node_modules/ 4 | coverage/ 5 | .idea/ 6 | run/ 7 | logs/ 8 | .DS_Store 9 | .vscode 10 | *.swp 11 | *.lock 12 | *.js 13 | 14 | app/**/*.js 15 | test/**/*.js 16 | config/**/*.js 17 | app/**/*.map 18 | test/**/*.map 19 | config/**/*.map 20 | *.d.ts 21 | *.tsbuildinfo 22 | -------------------------------------------------------------------------------- /hello-tegg/app/biz/HelloService.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AccessLevel, 3 | SingletonProto, 4 | } from '@eggjs/tegg'; 5 | 6 | @SingletonProto({ 7 | accessLevel: AccessLevel.PUBLIC, 8 | }) 9 | export class HelloService { 10 | async hello(name: string): Promise { 11 | return `hello, ${name}`; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hello-tegg/app/biz/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "biz-module", 3 | "eggModule": { 4 | "name": "biz" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /hello-tegg/app/controller/HelloController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HTTPController, 3 | HTTPMethod, 4 | HTTPMethodEnum, 5 | Context, 6 | EggContext, 7 | HTTPQuery, 8 | Middleware, 9 | Inject, 10 | } from '@eggjs/tegg'; 11 | import { EggLogger } from 'egg'; 12 | import { traceMethod } from 'app/middleware/trace_method'; 13 | import { HelloService } from 'app/biz/HelloService'; 14 | 15 | @HTTPController() 16 | @Middleware(traceMethod) 17 | export class HelloController { 18 | @Inject() 19 | private readonly helloService: HelloService; 20 | 21 | @Inject() 22 | private readonly logger: EggLogger; 23 | 24 | @HTTPMethod({ 25 | method: HTTPMethodEnum.GET, 26 | path: '/hello', 27 | }) 28 | async hello(@Context() ctx: EggContext, @HTTPQuery() name: string) { 29 | this.logger.info('access url: %s', ctx.url); 30 | 31 | const message = await this.helloService.hello(name); 32 | 33 | return { 34 | success: true, 35 | data: { 36 | message, 37 | }, 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /hello-tegg/app/middleware/trace_method.ts: -------------------------------------------------------------------------------- 1 | import { EggContext, Next } from '@eggjs/tegg'; 2 | 3 | export async function traceMethod(ctx: EggContext, next: Next) { 4 | await next(); 5 | ctx.body.data.message += ` (${ctx.method})`; 6 | } 7 | -------------------------------------------------------------------------------- /hello-tegg/config/config.default.ts: -------------------------------------------------------------------------------- 1 | import { EggAppConfig, PowerPartial } from 'egg'; 2 | 3 | export default (appInfo: EggAppConfig) => { 4 | const config = {} as PowerPartial; 5 | 6 | // override config from framework / plugin 7 | config.keys = appInfo.name + '123456'; 8 | 9 | return config; 10 | }; 11 | -------------------------------------------------------------------------------- /hello-tegg/config/plugin.ts: -------------------------------------------------------------------------------- 1 | import { EggPlugin } from 'egg'; 2 | 3 | const plugin: EggPlugin = { 4 | tegg: { 5 | enable: true, 6 | package: '@eggjs/tegg-plugin', 7 | }, 8 | teggConfig: { 9 | enable: true, 10 | package: '@eggjs/tegg-config', 11 | }, 12 | teggController: { 13 | enable: true, 14 | package: '@eggjs/tegg-controller-plugin', 15 | }, 16 | }; 17 | 18 | export default plugin; 19 | -------------------------------------------------------------------------------- /hello-tegg/test/biz/HelloService.test.ts: -------------------------------------------------------------------------------- 1 | import { Context } from 'egg'; 2 | import assert from 'assert'; 3 | import { app } from 'egg-mock/bootstrap'; 4 | import { HelloService } from '../../app/biz/HelloService'; 5 | 6 | describe('test/biz/HelloService.test.ts', () => { 7 | let ctx: Context; 8 | let helloService: HelloService; 9 | 10 | beforeEach(async () => { 11 | ctx = await app.mockModuleContext(); 12 | helloService = await ctx.getEggObject(HelloService); 13 | }); 14 | 15 | afterEach(async () => { 16 | await app.destroyModuleContext(ctx); 17 | }); 18 | 19 | it('should work', async () => { 20 | const msg = await helloService.hello('killa'); 21 | assert(msg === 'hello, killa'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /hello-tegg/test/controller/HelloController.test.ts: -------------------------------------------------------------------------------- 1 | import { app } from 'egg-mock/bootstrap'; 2 | import assert from 'assert'; 3 | 4 | describe('test/controller/HelloController.test.ts', () => { 5 | it('should work', async () => { 6 | await app.httpRequest() 7 | .get('/hello?name=killa') 8 | .expect(200) 9 | .expect(res => { 10 | assert.deepStrictEqual(res.body, { 11 | success: true, 12 | data: { 13 | message: 'hello, killa (GET)', 14 | }, 15 | }); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /hello-tegg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@eggjs/tsconfig", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /helloworld/app/controller/foo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class FooController extends Controller { 6 | async render() { 7 | const ctx = this.ctx; 8 | 9 | ctx.body = 'Hello foo'; 10 | } 11 | } 12 | 13 | module.exports = FooController; 14 | -------------------------------------------------------------------------------- /helloworld/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | async render() { 7 | const ctx = this.ctx; 8 | 9 | ctx.body = 'Hello World'; 10 | } 11 | } 12 | 13 | module.exports = HomeController; 14 | -------------------------------------------------------------------------------- /helloworld/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', app.controller.home.render); 5 | app.router.get('/foo', app.controller.foo.render); 6 | }; 7 | -------------------------------------------------------------------------------- /helloworld/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my secret keys'; 4 | -------------------------------------------------------------------------------- /helloworld/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "dev": "egg-bin dev", 12 | "test": "egg-bin test", 13 | "cov": "egg-bin cov" 14 | }, 15 | "private": true 16 | } 17 | -------------------------------------------------------------------------------- /helloworld/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('example helloworld test', () => { 7 | let app; 8 | 9 | before(() => { 10 | app = mm.app(); 11 | return app.ready(); 12 | }); 13 | 14 | after(() => app.close()); 15 | 16 | it('should GET / 200', () => { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(200) 20 | .expect('Hello World'); 21 | }); 22 | 23 | it('should GET /foo', () => { 24 | return app.httpRequest() 25 | .get('/foo') 26 | .expect(200) 27 | .expect('Hello foo'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /httpclient/README.md: -------------------------------------------------------------------------------- 1 | # httpclient 2 | 3 | Example use on https://github.com/eggjs/egg/blob/master/docs/source/zh-cn/core/httpclient.md 4 | -------------------------------------------------------------------------------- /httpclient/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.beforeStart(async () => { 5 | // 示例:启动的时候去读取 https://registry.npmjs.com/egg/latest 的版本信息 6 | const result = await app.curl('https://registry.npmjs.com/egg/latest', { 7 | dataType: 'json', 8 | }); 9 | app.logger.info('egg latest version: %s', result.data.version); 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /httpclient/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', 'httpclient.home'); 5 | app.router.get('/get', 'httpclient.get'); 6 | app.router.get('/post', 'httpclient.post'); 7 | app.router.get('/put', 'httpclient.put'); 8 | app.router.get('/delete', 'httpclient.del'); 9 | app.router.get('/form', 'httpclient.form'); 10 | app.router.get('/multipart', 'httpclient.multipart'); 11 | app.router.get('/files', 'httpclient.files'); 12 | app.router.get('/stream', 'httpclient.stream'); 13 | app.router.post('/stream', 'httpclient.postStream'); 14 | app.router.get('/error', 'httpclient.error'); 15 | }; 16 | -------------------------------------------------------------------------------- /httpclient/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.security = { 4 | csrf: false, 5 | }; 6 | -------------------------------------------------------------------------------- /httpclient/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "httpclient-example", 3 | "dependencies": { 4 | "egg": "^1.10.1", 5 | "formstream": "^1.1.0" 6 | }, 7 | "devDependencies": { 8 | "egg-bin": "^4.3.5" 9 | }, 10 | "scripts": { 11 | "start": "egg-bin dev", 12 | "dev": "egg-bin dev" 13 | }, 14 | "private": true 15 | } 16 | -------------------------------------------------------------------------------- /ipc/README.md: -------------------------------------------------------------------------------- 1 | # IPC 2 | 3 | Suppose we want to cache a remote data source in memory, we implement three different ways to update cache. 4 | 5 | 1. Force all workers update memory cache from remote with a long interval. 6 | 2. Check remote source if it is changed, and then update cache. Check action can be more frequently if the source don't change everytime. 7 | 3. Subscribe the remote source, do update when received data source changed. Need a subscriber. 8 | 9 | We'll combine schedule and IPC to implement these features. 10 | 11 | ## start 12 | 13 | ```bash 14 | $ npm start 15 | ``` 16 | 17 | It will start app with two workers when you run this command. 18 | -------------------------------------------------------------------------------- /ipc/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Subscriber = require('./lib/subscriber'); 4 | 5 | module.exports = agent => { 6 | agent.logger.info('init subscriber'); 7 | const subscriber = new Subscriber(); 8 | subscriber.on('changed', () => agent.messenger.sendToApp('refresh', 'push')); 9 | subscriber.on('error', err => agent.logger.error(err)); 10 | }; 11 | -------------------------------------------------------------------------------- /ipc/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.beforeStart(async () => { 5 | // ensure memory cache exists before app ready 6 | await app.runSchedule('force_refresh'); 7 | }); 8 | 9 | const { messenger } = app; 10 | 11 | messenger.on('refresh', by => { 12 | app.logger.info('start update by %s', by); 13 | // create an anonymous context to access service 14 | const ctx = app.createAnonymousContext(); 15 | // a convenient way to excute with generator function 16 | // can replaced by `co` 17 | ctx.runInBackground(async () => { 18 | await ctx.service.source.update(); 19 | app.lastUpdateBy = by; 20 | }); 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /ipc/app/controller/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Api extends Controller { 6 | index() { 7 | const { ctx, service } = this; 8 | ctx.body = { 9 | index: service.source.get('index'), 10 | lastUpdateBy: ctx.app.lastUpdateBy, 11 | }; 12 | } 13 | } 14 | 15 | module.exports = Api; 16 | -------------------------------------------------------------------------------- /ipc/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', 'api.index'); 5 | }; 6 | -------------------------------------------------------------------------------- /ipc/app/schedule/force_refresh.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.schedule = { 4 | interval: '10m', 5 | type: 'all', // run in all workers 6 | }; 7 | 8 | exports.task = async function(ctx) { 9 | await ctx.service.source.update(); 10 | ctx.app.lastUpdateBy = 'force'; 11 | }; 12 | -------------------------------------------------------------------------------- /ipc/app/schedule/pull_refresh.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.schedule = { 4 | interval: '10s', 5 | type: 'worker', // only run in one worker 6 | }; 7 | 8 | exports.task = async function(ctx) { 9 | const needRefresh = await ctx.service.source.checkUpdate(); 10 | if (!needRefresh) return; 11 | 12 | // notify all workers to update memory cache from `file` 13 | ctx.app.messenger.sendToApp('refresh', 'pull'); 14 | }; 15 | -------------------------------------------------------------------------------- /ipc/app/service/source.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sleep = require('mz-modules/sleep'); 4 | const Service = require('egg').Service; 5 | 6 | let memoryCache = {}; 7 | 8 | class Source extends Service { 9 | 10 | get(key) { 11 | return memoryCache[key]; 12 | } 13 | 14 | async checkUpdate() { 15 | // check if remote data source has changed 16 | const updated = await mockCheck(); 17 | this.ctx.logger.info('check update response %s', updated); 18 | return updated; 19 | } 20 | 21 | async update() { 22 | // update memory cache from remote 23 | memoryCache = await mockFetch(); 24 | this.ctx.logger.info('update memory cache from remote: %j', memoryCache); 25 | } 26 | } 27 | 28 | module.exports = Source; 29 | 30 | let index = 0; 31 | async function mockFetch() { 32 | await sleep(100); 33 | return { 34 | index: index++, 35 | }; 36 | } 37 | 38 | async function mockCheck() { 39 | await sleep(100); 40 | return Math.random() > 0.5; 41 | } 42 | -------------------------------------------------------------------------------- /ipc/lib/subscriber.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EventEmitter = require('events'); 4 | 5 | /** 6 | * A mock Subscriber 7 | */ 8 | module.exports = class Subscriber extends EventEmitter { 9 | constructor() { 10 | super(); 11 | 12 | this._start(); 13 | } 14 | 15 | _start() { 16 | const interval = Math.random() * 5000 + 5000; 17 | setTimeout(() => { 18 | this.emit('changed'); 19 | this._start(); 20 | }, interval); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /ipc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipc", 3 | "version": "1.0.0", 4 | "description": "egg ipc example", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^1.10.1", 8 | "mz-modules": "^2.0.0" 9 | }, 10 | "devDependencies": { 11 | "egg-bin": "^4.3.5" 12 | }, 13 | "engines": { 14 | "node": ">=8.9.0" 15 | }, 16 | "scripts": { 17 | "dev": "egg-bin dev", 18 | "lint": "eslint .", 19 | "autod": "autod" 20 | }, 21 | "author": "dead-horse", 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /middleware/app/middleware/hello.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | module.exports = (options, app) => { 6 | return async function(ctx, next) { 7 | assert.deepEqual(options, app.config.hello); 8 | ctx.body = options.text; 9 | await next(); 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /middleware/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my super cool keys'; 4 | 5 | exports.middleware = [ 6 | 'hello', 7 | ]; 8 | 9 | exports.hello = { 10 | text: 'Hello World', 11 | }; 12 | -------------------------------------------------------------------------------- /middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "middleware-example", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "dev": "egg-bin dev", 12 | "test": "egg-bin test", 13 | "cov": "egg-bin cov" 14 | }, 15 | "private": true 16 | } 17 | -------------------------------------------------------------------------------- /middleware/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('example middleware test', () => { 7 | let app; 8 | 9 | before(() => { 10 | app = mm.app(); 11 | return app.ready(); 12 | }); 13 | 14 | after(() => app.close()); 15 | 16 | it('should GET / 200', () => { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(200) 20 | .expect(app.config.hello.text); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /multipart-file-mode/.gitignore: -------------------------------------------------------------------------------- 1 | upload_dirs 2 | !upload_dirs/keepit 3 | app/public 4 | -------------------------------------------------------------------------------- /multipart-file-mode/app/controller/ajax.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const { pipeline } = require('stream/promises'); 4 | const Controller = require('egg').Controller; 5 | 6 | module.exports = class extends Controller { 7 | async show() { 8 | await this.ctx.render('page/ajax.html'); 9 | } 10 | 11 | async upload() { 12 | const { ctx } = this; 13 | const file = ctx.request.files[0]; 14 | if (!file) return ctx.throw(404); 15 | 16 | const filename = encodeURIComponent(ctx.request.body.name) + path.extname(file.filename).toLowerCase(); 17 | const targetPath = path.join(this.config.baseDir, 'app/public', filename); 18 | const source = fs.createReadStream(file.filepath); 19 | const target = fs.createWriteStream(targetPath); 20 | 21 | try { 22 | await pipeline(source, target); 23 | ctx.logger.warn('save %s to %s', file.filepath, targetPath); 24 | } finally { 25 | // delete those request tmp files 26 | await ctx.cleanupRequestFiles(); 27 | } 28 | 29 | ctx.body = { url: '/public/' + filename }; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /multipart-file-mode/app/controller/form.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const { pipeline } = require('stream/promises'); 4 | const Controller = require('egg').Controller; 5 | 6 | module.exports = class extends Controller { 7 | async show() { 8 | await this.ctx.render('page/form.html'); 9 | } 10 | 11 | async upload() { 12 | const { ctx } = this; 13 | const file = ctx.request.files[0]; 14 | if (!file) return ctx.throw(404); 15 | 16 | const filename = encodeURIComponent(ctx.request.body.name) + path.extname(file.filename).toLowerCase(); 17 | const targetPath = path.join(this.config.baseDir, 'app/public', filename); 18 | const source = fs.createReadStream(file.filepath); 19 | const target = fs.createWriteStream(targetPath); 20 | 21 | try { 22 | await pipeline(source, target); 23 | ctx.logger.warn('save %s to %s', file.filepath, targetPath); 24 | } finally { 25 | // delete those request tmp files 26 | await ctx.cleanupRequestFiles(); 27 | } 28 | 29 | ctx.redirect('/public/' + filename); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /multipart-file-mode/app/controller/home.js: -------------------------------------------------------------------------------- 1 | const Controller = require('egg').Controller; 2 | 3 | module.exports = class extends Controller { 4 | async render() { 5 | await this.ctx.render('index.html'); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /multipart-file-mode/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.router.get('/', 'home.render'); 3 | 4 | app.router.get('/ajax', app.controller.ajax.show); 5 | app.router.post('/ajax', app.controller.ajax.upload); 6 | 7 | app.router.get('/form', app.controller.form.show); 8 | app.router.post('/form', app.controller.form.upload); 9 | 10 | app.router.get('/multiple-file', app.controller.multiple.show); 11 | app.router.post('/multiple-file', app.controller.multiple.upload); 12 | }; 13 | -------------------------------------------------------------------------------- /multipart-file-mode/app/view/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |

Upload

4 | 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /multipart-file-mode/app/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% block content %} 8 | {% endblock %} 9 | 10 | 11 | -------------------------------------------------------------------------------- /multipart-file-mode/app/view/page/form.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |

Upload Image

5 |
6 |
    7 |
  • Image Name:
  • 8 |
  • 9 |
  • 10 |
11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /multipart-file-mode/app/view/page/multiple.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |

Upload Image

4 |
5 |
    6 |
  • 7 |
  • 8 |
  • 9 |
  • 10 |
  • 11 |
  • 12 |
13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /multipart-file-mode/app/view/page/multiple_result.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |

Upload Result

4 |
    5 | {% for field in fields %} 6 |
  • {{ field.key }}: {{ field.value }}
  • 7 | {% endfor %} 8 | 9 | {% for file in files %} 10 |
  • {{ file.filename }}
  • 11 | {% endfor %} 12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /multipart-file-mode/config/config.default.js: -------------------------------------------------------------------------------- 1 | exports.keys = 'my keys'; 2 | 3 | exports.view = { 4 | defaultViewEngine: 'nunjucks', 5 | }; 6 | 7 | exports.multipart = { 8 | mode: 'file', 9 | }; 10 | -------------------------------------------------------------------------------- /multipart-file-mode/config/plugin.js: -------------------------------------------------------------------------------- 1 | exports.nunjucks = { 2 | enable: true, 3 | package: 'egg-view-nunjucks', 4 | }; 5 | -------------------------------------------------------------------------------- /multipart-file-mode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multipart-file-mode-example", 3 | "dependencies": { 4 | "egg": "^3.11.0", 5 | "egg-view-nunjucks": "^2.3.0" 6 | }, 7 | "devDependencies": { 8 | "egg-bin": "^5.9.0", 9 | "egg-mock": "^5.5.0" 10 | }, 11 | "scripts": { 12 | "dev": "egg-bin dev", 13 | "test": "egg-bin test", 14 | "cov": "egg-bin cov" 15 | }, 16 | "private": true 17 | } 18 | -------------------------------------------------------------------------------- /multipart-file-mode/test/kfc.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/examples/5ea83ef36dede99ae057adfb6972c5086d8d2e4c/multipart-file-mode/test/kfc.jpeg -------------------------------------------------------------------------------- /multipart-file-mode/test/mc.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/examples/5ea83ef36dede99ae057adfb6972c5086d8d2e4c/multipart-file-mode/test/mc.jpeg -------------------------------------------------------------------------------- /multipart/.gitignore: -------------------------------------------------------------------------------- 1 | upload_dirs 2 | !upload_dirs/keepit 3 | app/public 4 | -------------------------------------------------------------------------------- /multipart/app/controller/ajax.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { pipeline } = require('stream/promises'); 4 | const Controller = require('egg').Controller; 5 | 6 | class UploadAjaxController extends Controller { 7 | async show() { 8 | await this.ctx.render('page/ajax.html'); 9 | } 10 | 11 | async upload() { 12 | const stream = await this.ctx.getFileStream(); 13 | const filename = encodeURIComponent(stream.fields.name) + path.extname(stream.filename).toLowerCase(); 14 | const target = path.join(this.config.baseDir, 'app/public', filename); 15 | const writeStream = fs.createWriteStream(target); 16 | await pipeline(stream, writeStream); 17 | 18 | this.ctx.body = { url: '/public/' + filename }; 19 | } 20 | } 21 | 22 | module.exports = UploadAjaxController; 23 | -------------------------------------------------------------------------------- /multipart/app/controller/buffer.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const Controller = require('egg').Controller; 4 | const toArray = require('stream-to-array'); 5 | const sendToWormhole = require('stream-wormhole'); 6 | 7 | class UploadBufferController extends Controller { 8 | async show() { 9 | await this.ctx.render('page/buffer.html'); 10 | } 11 | 12 | async upload() { 13 | const stream = await this.ctx.getFileStream(); 14 | let buf; 15 | try { 16 | const parts = await toArray(stream); 17 | buf = Buffer.concat(parts); 18 | } catch (err) { 19 | await sendToWormhole(stream); 20 | throw err; 21 | } 22 | 23 | const filename = encodeURIComponent(stream.fields.name) + path.extname(stream.filename).toLowerCase(); 24 | const target = path.join(this.config.baseDir, 'app/public', filename); 25 | await fs.writeFile(target, buf); 26 | 27 | this.ctx.redirect('/public/' + filename); 28 | } 29 | } 30 | 31 | module.exports = UploadBufferController; 32 | -------------------------------------------------------------------------------- /multipart/app/controller/form.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { pipeline } = require('stream/promises'); 4 | const Controller = require('egg').Controller; 5 | 6 | class UploadFormController extends Controller { 7 | async show() { 8 | await this.ctx.render('page/form.html'); 9 | } 10 | 11 | async upload() { 12 | const stream = await this.ctx.getFileStream(); 13 | const filename = encodeURIComponent(stream.fields.name) + path.extname(stream.filename).toLowerCase(); 14 | const target = path.join(this.config.baseDir, 'app/public', filename); 15 | const writeStream = fs.createWriteStream(target); 16 | await pipeline(stream, writeStream); 17 | this.ctx.redirect('/public/' + filename); 18 | } 19 | } 20 | 21 | module.exports = UploadFormController; 22 | -------------------------------------------------------------------------------- /multipart/app/controller/home.js: -------------------------------------------------------------------------------- 1 | const Controller = require('egg').Controller; 2 | 3 | class HomeController extends Controller { 4 | 5 | async render() { 6 | await this.ctx.render('index.html'); 7 | } 8 | 9 | } 10 | 11 | module.exports = HomeController; 12 | -------------------------------------------------------------------------------- /multipart/app/controller/multiple.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { pipeline } = require('stream/promises'); 4 | const Controller = require('egg').Controller; 5 | 6 | class UploadMultipleController extends Controller { 7 | async show() { 8 | await this.ctx.render('page/multiple.html'); 9 | } 10 | 11 | async upload() { 12 | const parts = this.ctx.multipart({ autoFields: true }); 13 | const files = []; 14 | 15 | let stream; 16 | while ((stream = await parts()) != null) { 17 | const filename = stream.filename.toLowerCase(); 18 | const target = path.join(this.config.baseDir, 'app/public', filename); 19 | const writeStream = fs.createWriteStream(target); 20 | await pipeline(stream, writeStream); 21 | files.push(filename); 22 | } 23 | 24 | await this.ctx.render('page/multiple_result.html', { 25 | fields: Object.keys(parts.field).map(key => ({ key, value: parts.field[key] })), 26 | files, 27 | }); 28 | } 29 | } 30 | 31 | module.exports = UploadMultipleController; 32 | -------------------------------------------------------------------------------- /multipart/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.router.get('/', 'home.render'); 3 | 4 | app.router.get('/ajax', app.controller.ajax.show); 5 | app.router.post('/ajax', app.controller.ajax.upload); 6 | 7 | app.router.get('/form', app.controller.form.show); 8 | app.router.post('/form', app.controller.form.upload); 9 | 10 | app.router.get('/multiple-file', app.controller.multiple.show); 11 | app.router.post('/multiple-file', app.controller.multiple.upload); 12 | 13 | app.router.get('/buffer', app.controller.buffer.show); 14 | app.router.post('/buffer', app.controller.buffer.upload); 15 | }; 16 | -------------------------------------------------------------------------------- /multipart/app/view/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |

Upload

4 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /multipart/app/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% block content %} 8 | {% endblock %} 9 | 10 | 11 | -------------------------------------------------------------------------------- /multipart/app/view/page/buffer.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |

Upload Image

5 |
6 |
    7 |
  • Image Name:
  • 8 |
  • 9 |
  • 10 |
11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /multipart/app/view/page/form.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |

Upload Image

5 |
6 |
    7 |
  • Image Name:
  • 8 |
  • 9 |
  • 10 |
11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /multipart/app/view/page/multiple.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |

Upload Image

4 |
5 |
    6 |
  • 7 |
  • 8 |
  • 9 |
  • 10 |
  • 11 |
  • 12 |
13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /multipart/app/view/page/multiple_result.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |

Upload Result

4 |
    5 | {% for field in fields %} 6 |
  • {{ field.key }}: {{ field.value }}
  • 7 | {% endfor %} 8 | 9 | {% for file in files %} 10 |
  • {{ file }}
  • 11 | {% endfor %} 12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /multipart/config/config.default.js: -------------------------------------------------------------------------------- 1 | exports.keys = 'my keys'; 2 | 3 | exports.view = { 4 | defaultViewEngine: 'nunjucks', 5 | }; 6 | -------------------------------------------------------------------------------- /multipart/config/plugin.js: -------------------------------------------------------------------------------- 1 | exports.nunjucks = { 2 | enable: true, 3 | package: 'egg-view-nunjucks', 4 | }; 5 | -------------------------------------------------------------------------------- /multipart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multipart-example", 3 | "dependencies": { 4 | "egg": "^3.11.0", 5 | "egg-view-nunjucks": "^2.3.0", 6 | "stream-to-array": "^2.3.0", 7 | "stream-wormhole": "^1.0.4" 8 | }, 9 | "devDependencies": { 10 | "egg-bin": "^5.9.0", 11 | "egg-mock": "^5.5.0" 12 | }, 13 | "scripts": { 14 | "dev": "egg-bin dev", 15 | "test": "egg-bin test", 16 | "cov": "egg-bin cov" 17 | }, 18 | "private": true 19 | } 20 | -------------------------------------------------------------------------------- /multipart/test/kfc.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/examples/5ea83ef36dede99ae057adfb6972c5086d8d2e4c/multipart/test/kfc.jpeg -------------------------------------------------------------------------------- /multipart/test/mc.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/examples/5ea83ef36dede99ae057adfb6972c5086d8d2e4c/multipart/test/mc.jpeg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egg-examples", 3 | "version": "1.0.0", 4 | "description": "Application examples for egg", 5 | "keywords": [ 6 | "application", 7 | "egg" 8 | ], 9 | "devDependencies": { 10 | "chalk": "^1.1.3", 11 | "common-bin": "^2.7.1", 12 | "globby": "^6.1.0", 13 | "npminstall": "^6.6.2", 14 | "runscript": "^1.5.3", 15 | "semver": "^5.3.0" 16 | }, 17 | "scripts": { 18 | "lint": "echo 'ignore'", 19 | "test": "./example.js test", 20 | "test-cn": "./example.js test -c", 21 | "ci": "npm run lint && npm test", 22 | "list": "./example.js list" 23 | }, 24 | "homepage": "https://github.com/eggjs/examples", 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/eggjs/examples.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/eggjs/egg/issues" 31 | }, 32 | "engines": { 33 | "node": ">= 14.17.0" 34 | }, 35 | "license": "MIT", 36 | "private": true 37 | } 38 | -------------------------------------------------------------------------------- /passport/.gitignore: -------------------------------------------------------------------------------- 1 | config/config.local.js 2 | -------------------------------------------------------------------------------- /passport/README.md: -------------------------------------------------------------------------------- 1 | # egg-passport example 2 | 3 | - Index: http://127.0.0.1:7001/ 4 | - Auth Strategies 5 | - Weibo: http://127.0.0.1:7001/passport/weibo 6 | - GitHub: http://127.0.0.1:7001/passport/github 7 | - Bitbucket: http://127.0.0.1:7001/passport/bitbucket 8 | - Twitter: http://127.0.0.1:7001/passport/twitter 9 | - YuQue: http://127.0.0.1:7001/passport/yuque 10 | -------------------------------------------------------------------------------- /passport/app/controller/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class UserController extends Controller { 6 | async logout() { 7 | const ctx = this.ctx; 8 | 9 | ctx.logout(); 10 | ctx.redirect(ctx.get('referer') || '/'); 11 | } 12 | } 13 | 14 | module.exports = UserController; 15 | -------------------------------------------------------------------------------- /passport/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', 'home.render'); 5 | app.router.get('/user', 'home.render'); 6 | 7 | app.passport.mount('weibo'); 8 | app.passport.mount('github'); 9 | app.passport.mount('bitbucket'); 10 | app.passport.mount('twitter'); 11 | app.passport.mount('yuque'); 12 | 13 | app.router.get('/logout', 'user.logout'); 14 | }; 15 | -------------------------------------------------------------------------------- /passport/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123456'; 4 | 5 | // secure keys on https://github.com/eggjs/egg/wiki#secure-env-for-travis-ci 6 | exports.passportWeibo = { 7 | key: 'a', 8 | secret: 'b', 9 | }; 10 | 11 | exports.passportGithub = { 12 | key: 'c', 13 | secret: 'd', 14 | }; 15 | 16 | exports.passportBitbucket = { 17 | key: 'e', 18 | secret: 'f', 19 | }; 20 | 21 | exports.passportTwitter = { 22 | key: 'g', 23 | secret: 'h', 24 | }; 25 | 26 | exports.passportYuque = { 27 | key: process.env.EGG_PASSPORT_YUQUE_CLIENT_ID || 'i', 28 | secret: process.env.EGG_PASSPORT_YUQUE_CLIENT_SECRET || 'j', 29 | }; 30 | -------------------------------------------------------------------------------- /passport/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.passport = { 4 | enable: true, 5 | package: 'egg-passport', 6 | }; 7 | 8 | exports.passportWeibo = { 9 | enable: true, 10 | package: 'egg-passport-weibo', 11 | }; 12 | 13 | exports.passportTwitter = { 14 | enable: true, 15 | package: 'egg-passport-twitter', 16 | }; 17 | 18 | exports.passportGithub = { 19 | enable: true, 20 | package: 'egg-passport-github', 21 | }; 22 | 23 | exports.passportBitbucket = { 24 | enable: true, 25 | package: 'egg-passport-bitbucket', 26 | }; 27 | 28 | exports.passportYuque = { 29 | enable: true, 30 | package: 'egg-passport-yuque', 31 | }; 32 | -------------------------------------------------------------------------------- /passport/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport", 3 | "dependencies": { 4 | "egg": "^1.10.1", 5 | "egg-passport": "^1.0.0", 6 | "egg-passport-bitbucket": "^1.0.0", 7 | "egg-passport-github": "^1.0.0", 8 | "egg-passport-twitter": "^1.0.0", 9 | "egg-passport-weibo": "^1.0.0", 10 | "egg-passport-yuque": "^1.0.0" 11 | }, 12 | "devDependencies": { 13 | "egg-bin": "^4.3.5" 14 | }, 15 | "scripts": { 16 | "dev": "egg-bin dev", 17 | "cov": "egg-bin cov" 18 | }, 19 | "private": true 20 | } 21 | -------------------------------------------------------------------------------- /progressive/step1/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async isIOS() { 7 | this.ctx.body = `isIOS: ${this.ctx.isIOS}`; 8 | } 9 | } 10 | 11 | module.exports = Home; 12 | -------------------------------------------------------------------------------- /progressive/step1/app/extend/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get isIOS() { 5 | const iosReg = /iphone|ipad|ipod/i; 6 | return iosReg.test(this.get('user-agent')); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /progressive/step1/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | router.get('/', controller.home.isIOS); 6 | }; 7 | -------------------------------------------------------------------------------- /progressive/step1/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progressive", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "egg": "^2.0.0" 7 | }, 8 | "devDependencies": { 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "scripts": { 13 | "dev": "egg-bin dev", 14 | "test": "egg-bin test", 15 | "cov": "egg-bin cov" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /progressive/step1/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { app } = require('egg-mock/bootstrap'); 5 | 6 | describe('test/index.test.js', () => { 7 | 8 | it('should GET / with iOS', () => { 9 | return app.httpRequest() 10 | .get('/') 11 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 12 | .expect(200) 13 | .expect('isIOS: true'); 14 | }); 15 | 16 | it('should GET / with non iOS', () => { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(200) 20 | .expect('isIOS: false'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /progressive/step2/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async isIOS() { 7 | this.ctx.body = `isIOS: ${this.ctx.isIOS}`; 8 | } 9 | } 10 | 11 | module.exports = Home; 12 | -------------------------------------------------------------------------------- /progressive/step2/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | router.get('/', controller.home.isIOS); 6 | }; 7 | -------------------------------------------------------------------------------- /progressive/step2/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step2/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | exports.ua = { 6 | enable: true, 7 | path: path.join(__dirname, '../lib/plugin/egg-ua'), 8 | }; 9 | -------------------------------------------------------------------------------- /progressive/step2/lib/plugin/egg-ua/app/extend/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get isIOS() { 5 | const iosReg = /iphone|ipad|ipod/i; 6 | return iosReg.test(this.get('user-agent')); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /progressive/step2/lib/plugin/egg-ua/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "eggPlugin": { 3 | "name": "ua" 4 | } 5 | } -------------------------------------------------------------------------------- /progressive/step2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progressive", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "egg": "^2.0.0" 7 | }, 8 | "devDependencies": { 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "scripts": { 13 | "dev": "egg-bin dev", 14 | "test": "egg-bin test", 15 | "cov": "egg-bin cov" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /progressive/step2/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { app } = require('egg-mock/bootstrap'); 5 | 6 | describe('test/index.test.js', () => { 7 | 8 | it('should GET / with iOS', () => { 9 | return app.httpRequest() 10 | .get('/') 11 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 12 | .expect(200) 13 | .expect('isIOS: true'); 14 | }); 15 | 16 | it('should GET / with non iOS', () => { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(200) 20 | .expect('isIOS: false'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /progressive/step3/egg-ua/app/extend/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get isIOS() { 5 | const iosReg = /iphone|ipad|ipod/i; 6 | return iosReg.test(this.get('user-agent')); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /progressive/step3/egg-ua/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egg-ua", 3 | "version": "1.0.0", 4 | "eggPlugin": { 5 | "name": "ua" 6 | }, 7 | "devDependencies": { 8 | "egg": "^2.0.0", 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "scripts": { 13 | "dev": "egg-bin dev", 14 | "test": "egg-bin test", 15 | "cov": "egg-bin cov" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /progressive/step3/egg-ua/test/fixtures/test-app/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', async function() { 5 | this.body = `isIOS: ${this.isIOS}`; 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /progressive/step3/egg-ua/test/fixtures/test-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step3/egg-ua/test/fixtures/test-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-app", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /progressive/step3/egg-ua/test/ua.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('test/ua.test.js', () => { 7 | let app; 8 | 9 | before(() => { 10 | app = mm.app({ 11 | baseDir: 'test-app', 12 | }); 13 | return app.ready(); 14 | }); 15 | 16 | after(() => app.close()); 17 | afterEach(mm.restore); 18 | 19 | it('should GET / with iOS', () => { 20 | return app.httpRequest() 21 | .get('/') 22 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 23 | .expect(200) 24 | .expect('isIOS: true'); 25 | }); 26 | 27 | it('should GET / with non iOS', () => { 28 | return app.httpRequest() 29 | .get('/') 30 | .expect(200) 31 | .expect('isIOS: false'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /progressive/step3/example-app/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async isIOS() { 7 | this.ctx.body = `isIOS: ${this.ctx.isIOS}`; 8 | } 9 | } 10 | 11 | module.exports = Home; 12 | -------------------------------------------------------------------------------- /progressive/step3/example-app/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | router.get('/', controller.home.isIOS); 6 | }; 7 | -------------------------------------------------------------------------------- /progressive/step3/example-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step3/example-app/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.ua = { 4 | enable: true, 5 | package: 'egg-ua', 6 | }; 7 | -------------------------------------------------------------------------------- /progressive/step3/example-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progressive", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "egg": "^1.0.0", 7 | "egg-ua": "../egg-ua" 8 | }, 9 | "devDependencies": { 10 | "egg-bin": "^4.3.5", 11 | "egg-mock": "^3.13.1" 12 | }, 13 | "scripts": { 14 | "dev": "egg-bin dev", 15 | "test": "egg-bin test", 16 | "cov": "egg-bin cov" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /progressive/step3/example-app/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { app } = require('egg-mock/bootstrap'); 5 | 6 | describe('test/index.test.js', () => { 7 | 8 | it('should GET / with iOS', () => { 9 | return app.httpRequest() 10 | .get('/') 11 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 12 | .expect(200) 13 | .expect('isIOS: true'); 14 | }); 15 | 16 | it('should GET / with non iOS', () => { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(200) 20 | .expect('isIOS: false'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /progressive/step4/egg-ua/app/extend/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get isIOS() { 5 | const iosReg = /iphone|ipad|ipod/i; 6 | return iosReg.test(this.get('user-agent')); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /progressive/step4/egg-ua/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egg-ua", 3 | "version": "1.0.0", 4 | "eggPlugin": { 5 | "name": "ua" 6 | }, 7 | "devDependencies": { 8 | "egg": "^1.0.0", 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "scripts": { 13 | "dev": "egg-bin dev", 14 | "test": "egg-bin test", 15 | "cov": "egg-bin cov" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /progressive/step4/egg-ua/test/fixtures/test-app/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', async function() { 5 | this.body = `isIOS: ${this.isIOS}`; 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /progressive/step4/egg-ua/test/fixtures/test-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step4/egg-ua/test/fixtures/test-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-app", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /progressive/step4/egg-ua/test/ua.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('test/ua.test.js', () => { 7 | let app; 8 | 9 | before(() => { 10 | app = mm.app({ 11 | baseDir: 'test-app', 12 | }); 13 | return app.ready(); 14 | }); 15 | 16 | after(() => app.close()); 17 | afterEach(mm.restore); 18 | 19 | it('should GET / with iOS', () => { 20 | return app.httpRequest() 21 | .get('/') 22 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 23 | .expect(200) 24 | .expect('isIOS: true'); 25 | }); 26 | 27 | it('should GET / with non iOS', () => { 28 | return app.httpRequest() 29 | .get('/') 30 | .expect(200) 31 | .expect('isIOS: false'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /progressive/step4/example-app/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('example-framework').Controller; 4 | 5 | class Home extends Controller { 6 | async isIOS() { 7 | this.ctx.body = `isIOS: ${this.ctx.isIOS}`; 8 | } 9 | 10 | async getFramework() { 11 | this.ctx.body = this.ctx.app.config.framework.name; 12 | } 13 | } 14 | 15 | module.exports = Home; 16 | -------------------------------------------------------------------------------- /progressive/step4/example-app/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | router.get('/', controller.home.isIOS); 6 | router.get('/framework', controller.home.getFramework); 7 | }; 8 | -------------------------------------------------------------------------------- /progressive/step4/example-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step4/example-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progressive", 3 | "version": "1.0.0", 4 | "private": true, 5 | "egg": { 6 | "framework": "example-framework" 7 | }, 8 | "dependencies": { 9 | "example-framework": "../example-framework" 10 | }, 11 | "devDependencies": { 12 | "egg-bin": "^4.3.5", 13 | "egg-mock": "^3.13.1" 14 | }, 15 | "scripts": { 16 | "dev": "egg-bin dev", 17 | "test": "egg-bin test", 18 | "cov": "egg-bin cov" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /progressive/step4/example-app/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { app } = require('egg-mock/bootstrap'); 5 | 6 | describe('test/index.test.js', () => { 7 | 8 | it('should use custom framework', () => { 9 | return app.httpRequest() 10 | .get('/framework') 11 | .expect(200) 12 | .expect('example-framework'); 13 | }); 14 | 15 | it('should GET / with iOS', () => { 16 | return app.httpRequest() 17 | .get('/') 18 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 19 | .expect(200) 20 | .expect('isIOS: true'); 21 | }); 22 | 23 | it('should GET / with non iOS', () => { 24 | return app.httpRequest() 25 | .get('/') 26 | .expect(200) 27 | .expect('isIOS: false'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.framework = { 4 | name: 'example-framework', 5 | }; 6 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.ua = { 4 | enable: true, 5 | package: 'egg-ua', 6 | }; 7 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Application = require('./lib/application'); 4 | const Agent = require('./lib/agent'); 5 | const egg = require('egg'); 6 | 7 | // clone egg API 8 | Object.assign(exports, egg); 9 | 10 | // override Application and Agent 11 | exports.Application = Application; 12 | exports.Agent = Agent; 13 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/lib/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const egg = require('egg'); 5 | const EGG_PATH = Symbol.for('egg#eggPath'); 6 | 7 | class Agent extends egg.Agent { 8 | get [EGG_PATH]() { 9 | return path.dirname(__dirname); 10 | } 11 | } 12 | 13 | module.exports = Agent; 14 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/lib/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const egg = require('egg'); 5 | const EGG_PATH = Symbol.for('egg#eggPath'); 6 | 7 | class Application extends egg.Application { 8 | get [EGG_PATH]() { 9 | return path.dirname(__dirname); 10 | } 11 | } 12 | 13 | module.exports = Application; 14 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-framework", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "egg": "^1.0.0", 6 | "egg-ua": "../egg-ua" 7 | }, 8 | "devDependencies": { 9 | "egg-bin": "^4.3.5", 10 | "egg-mock": "^3.13.1" 11 | }, 12 | "engines": { 13 | "node": ">=8.9.0" 14 | }, 15 | "scripts": { 16 | "dev": "egg-bin dev", 17 | "test": "egg-bin test", 18 | "cov": "egg-bin cov" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/test/fixtures/test-app/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', async function() { 5 | this.body = `framework: ${this.app.config.framework.name}, isIOS: ${this.isIOS}`; 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/test/fixtures/test-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/test/fixtures/test-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-app", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /progressive/step4/example-framework/test/framework.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { mock } = require('egg-mock/bootstrap'); 5 | 6 | describe('test/framework.test.js', () => { 7 | let app; 8 | 9 | before(() => { 10 | app = mock.app({ 11 | baseDir: 'test-app', 12 | framework: true, 13 | }); 14 | return app.ready(); 15 | }); 16 | 17 | after(() => app.close()); 18 | afterEach(mock.restore); 19 | 20 | it('should GET /', () => { 21 | return app.httpRequest() 22 | .get('/') 23 | .set('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1') 24 | .expect(200) 25 | .expect('framework: example-framework, isIOS: true'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /redefine-controller/app/controller/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('../core/controller'); 4 | 5 | class ApiController extends Controller { 6 | async successAction() { 7 | this.success({ foo: 'bar' }); 8 | } 9 | 10 | async failAction() { 11 | this.fail('something wrong'); 12 | } 13 | } 14 | 15 | module.exports = ApiController; 16 | -------------------------------------------------------------------------------- /redefine-controller/app/core/controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class CustomController extends Controller { 6 | success(result) { 7 | this.ctx.body = { 8 | success: true, 9 | result, 10 | }; 11 | } 12 | 13 | fail(message) { 14 | this.ctx.body = { 15 | success: false, 16 | message, 17 | }; 18 | } 19 | } 20 | 21 | module.exports = CustomController; 22 | -------------------------------------------------------------------------------- /redefine-controller/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/success', 'api.successAction'); 5 | app.router.get('/fail', 'api.failAction'); 6 | }; 7 | -------------------------------------------------------------------------------- /redefine-controller/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'keys'; 4 | -------------------------------------------------------------------------------- /redefine-controller/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redefine-controller", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "test": "egg-bin test", 12 | "dev": "egg-bin dev" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /redefine-controller/test/controller/api.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('test/controller/api.test.js', () => { 7 | let app; 8 | before(() => { 9 | app = mm.app(); 10 | return app.ready(); 11 | }); 12 | 13 | after(() => app.close()); 14 | 15 | it('should get /success ok', () => { 16 | return app.httpRequest() 17 | .get('/success') 18 | .expect(200) 19 | .expect({ success: true, result: { foo: 'bar' } }); 20 | }); 21 | 22 | it('should get /fail ok', () => { 23 | return app.httpRequest() 24 | .get('/fail') 25 | .expect(200) 26 | .expect({ success: false, message: 'something wrong' }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /schedule/app/schedule/all_cron.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Subscription = require('egg').Subscription; 4 | 5 | const now = new Date(); 6 | const start = (now.getSeconds() + 3) % 60; 7 | 8 | class AllCron extends Subscription { 9 | async subscribe() { 10 | this.ctx.logger.info('all&&cron'); 11 | } 12 | 13 | static get schedule() { 14 | return { 15 | cron: `${start}/30 * * * * *`, 16 | type: 'all', 17 | }; 18 | } 19 | } 20 | 21 | module.exports = AllCron; 22 | -------------------------------------------------------------------------------- /schedule/app/schedule/all_interval.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Subscription = require('egg').Subscription; 4 | 5 | class AllInterval extends Subscription { 6 | async subscribe() { 7 | this.ctx.logger.info('all&&interval'); 8 | } 9 | 10 | static get schedule() { 11 | return { 12 | interval: 3000, 13 | type: 'all', 14 | }; 15 | } 16 | } 17 | 18 | module.exports = AllInterval; 19 | -------------------------------------------------------------------------------- /schedule/app/schedule/worker_cron.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Subscription = require('egg').Subscription; 4 | 5 | const now = new Date(); 6 | const start = (now.getSeconds() + 3) % 60; 7 | 8 | class WorkerCron extends Subscription { 9 | async subscribe() { 10 | this.ctx.logger.info('worker&&cron'); 11 | } 12 | 13 | static get schedule() { 14 | return { 15 | cron: `${start}/30 * * * * *`, 16 | type: 'worker', 17 | }; 18 | } 19 | } 20 | 21 | module.exports = WorkerCron; 22 | -------------------------------------------------------------------------------- /schedule/app/schedule/worker_interval.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Subscription = require('egg').Subscription; 4 | 5 | class WorkerInterval extends Subscription { 6 | async subscribe() { 7 | this.ctx.logger.info('all&&interval'); 8 | } 9 | 10 | static get schedule() { 11 | return { 12 | interval: 3000, 13 | type: 'worker', 14 | }; 15 | } 16 | } 17 | 18 | module.exports = WorkerInterval; 19 | -------------------------------------------------------------------------------- /schedule/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "schedule-example", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "dev": "egg-bin dev", 12 | "test": "egg-bin test", 13 | "cov": "egg-bin cov" 14 | }, 15 | "private": true 16 | } 17 | -------------------------------------------------------------------------------- /schedule/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const mm = require('egg-mock'); 6 | 7 | describe.skip('egg schedule example', () => { 8 | it('should schedule run schedule success', async () => { 9 | const baseDir = path.dirname(__dirname); 10 | 11 | const app = mm.cluster({ 12 | workers: 2, 13 | }); 14 | 15 | await app.ready(); 16 | await sleep(3000); 17 | const log = fs.readFileSync(path.join(baseDir, 'logs/schedule-example/schedule-example-web.log'), 'utf8'); 18 | console.log(log); 19 | countLine(log, 'all&&cron').should.equal(2); 20 | countLine(log, 'all&&interval').should.equal(2); 21 | countLine(log, 'worker&&cron').should.equal(1); 22 | countLine(log, 'worker&&interval').should.equal(1); 23 | 24 | await app.close(); 25 | }); 26 | }); 27 | 28 | function sleep(time) { 29 | return new Promise(resolve => setTimeout(resolve, time)); 30 | } 31 | 32 | function countLine(content, key) { 33 | return content.split('\n').filter(line => line.indexOf(key) >= 0).length; 34 | } 35 | -------------------------------------------------------------------------------- /sequelize-ts/.gitignore: -------------------------------------------------------------------------------- 1 | typings/ -------------------------------------------------------------------------------- /sequelize-ts/.sequelizerc: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | config: path.join(__dirname, 'database/config.json'), 7 | 'migrations-path': path.join(__dirname, 'database/migrations'), 8 | 'seeders-path': path.join(__dirname, 'database/seeders'), 9 | }; 10 | -------------------------------------------------------------------------------- /sequelize-ts/app/extend/helper.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | parseInt(str: string | number) { 3 | if (typeof str === 'number') return str; 4 | if (!str) return 0; 5 | return parseInt(str) || 0; 6 | }, 7 | } -------------------------------------------------------------------------------- /sequelize-ts/app/model/post.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { Application } from 'egg'; 4 | 5 | export default function(app: Application) { 6 | const { STRING, INTEGER, DATE } = app.Sequelize; 7 | 8 | const Model = app.model.define('post', { 9 | id: { 10 | type: INTEGER, 11 | primaryKey: true, 12 | autoIncrement: true, 13 | }, 14 | title: STRING(30), 15 | content: STRING(255), 16 | user_id: INTEGER, 17 | created_at: DATE(6), 18 | updated_at: DATE(6), 19 | }); 20 | 21 | return class Post extends Model { 22 | static associate() { 23 | app.model.Post.belongsTo(app.model.User, { as: 'user', foreignKey: 'user_id' }); 24 | } 25 | 26 | static async findByIdWithUser(id: number, userId: number) { 27 | return await this.findOne({ 28 | where: { id, user_id: userId }, 29 | }); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /sequelize-ts/app/model/user.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { Application } from 'egg'; 4 | 5 | export default function(app: Application) { 6 | const { STRING, INTEGER, DATE } = app.Sequelize; 7 | const Model = app.model.define('user', { 8 | id: { 9 | type: INTEGER, 10 | primaryKey: true, 11 | autoIncrement: true, 12 | }, 13 | name: STRING(30), 14 | age: INTEGER, 15 | created_at: DATE(6), 16 | updated_at: DATE(6), 17 | }); 18 | 19 | return class User extends Model { 20 | static associate() { 21 | app.model.User.hasMany(app.model.Post, { as: 'posts' }); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /sequelize-ts/app/router.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { Application } from 'egg'; 4 | 5 | export default function(app: Application) { 6 | app.resources('users', '/users', app.controller.user); 7 | app.resources('posts', '/posts', app.controller.post); 8 | } 9 | -------------------------------------------------------------------------------- /sequelize-ts/config/config.default.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { EggAppConfig, PowerPartial } from 'egg'; 4 | 5 | export default function(appInfo: EggAppConfig) { 6 | const config = {} as PowerPartial; 7 | 8 | config.keys = appInfo.name + '123123'; 9 | 10 | config.sequelize = { 11 | database: 'egg-sequelize-ts-dev', 12 | }; 13 | 14 | const bizConfig = { 15 | // your biz config 16 | }; 17 | 18 | return { 19 | ...config as {}, 20 | ...bizConfig, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /sequelize-ts/config/config.unittest.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { EggAppConfig, PowerPartial } from 'egg'; 4 | 5 | export default function() { 6 | const config = {} as PowerPartial; 7 | 8 | config.sequelize = { 9 | database: 'egg-sequelize-ts-unittest', 10 | }; 11 | 12 | return config; 13 | } 14 | -------------------------------------------------------------------------------- /sequelize-ts/config/plugin.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { EggPlugin } from 'egg'; 4 | 5 | const plugin: EggPlugin = {}; 6 | 7 | plugin.sequelize = { 8 | package: 'egg-sequelize', 9 | enable: true, 10 | }; 11 | 12 | export default plugin; 13 | -------------------------------------------------------------------------------- /sequelize-ts/database/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "root", 4 | "password": null, 5 | "database": "egg-sequelize-ts-dev", 6 | "host": "127.0.0.1", 7 | "dialect": "mysql" 8 | }, 9 | "test": { 10 | "username": "root", 11 | "password": null, 12 | "database": "egg-sequelize-ts-unittest", 13 | "host": "127.0.0.1", 14 | "dialect": "mysql" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sequelize-ts/database/migrations/20180813112934-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | const { INTEGER, DATE, STRING } = Sequelize; 6 | await queryInterface.createTable('users', { 7 | id: { type: INTEGER, primaryKey: true, autoIncrement: true }, 8 | name: STRING(30), 9 | age: INTEGER, 10 | created_at: DATE(6), 11 | updated_at: DATE(6), 12 | }); 13 | }, 14 | 15 | down: async queryInterface => { 16 | await queryInterface.dropTable('users'); 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /sequelize-ts/database/migrations/20180813112942-post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | const { INTEGER, DATE, STRING } = Sequelize; 6 | await queryInterface.createTable('posts', { 7 | id: { type: INTEGER, primaryKey: true, autoIncrement: true }, 8 | title: STRING(30), 9 | content: STRING(255), 10 | user_id: INTEGER, 11 | created_at: DATE(6), 12 | updated_at: DATE(6), 13 | }); 14 | }, 15 | 16 | down: async queryInterface => { 17 | await queryInterface.dropTable('posts'); 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /sequelize-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sequelize-ts-example", 3 | "version": "1.0.0", 4 | "description": "The egg ts example project that uses egg-sequelize plugin.", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^2.10.0", 8 | "egg-sequelize": "^5.0.0", 9 | "mysql2": "^1.6.1" 10 | }, 11 | "devDependencies": { 12 | "@types/mocha": "^5.2.6", 13 | "autod": "^3.0.1", 14 | "egg-bin": "^4.8.1", 15 | "egg-mock": "^3.19.2", 16 | "factory-girl": "^5.0.2", 17 | "lodash": "^4.17.10", 18 | "sequelize-cli": "^5.0.0" 19 | }, 20 | "engines": { 21 | "node": ">=8.9.0" 22 | }, 23 | "egg": { 24 | "typescript": true, 25 | "declarations": true 26 | }, 27 | "scripts": { 28 | "dev": "egg-bin dev", 29 | "test": "NODE_ENV=test npm run sequelize -- db:migrate && egg-bin test", 30 | "cov": "egg-bin cov", 31 | "ci": "npm run cov", 32 | "autod": "autod", 33 | "sequelize": "sequelize --" 34 | }, 35 | "author": "Qi Yu ", 36 | "license": "MIT" 37 | } 38 | -------------------------------------------------------------------------------- /sequelize-ts/test/factories.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { MockApplication } from 'egg-mock'; 4 | import { factory } from 'factory-girl'; 5 | 6 | export default function(app: MockApplication) { 7 | app.factory = factory; 8 | factory.define('user', app.model.User, { 9 | name: factory.sequence('User.name', n => `name_${n}`), 10 | age: 18, 11 | }); 12 | 13 | factory.define('post', app.model.Post, { 14 | title: factory.sequence('Post.title', n => `title_${n}`), 15 | content: factory.chance('sentence', { word: 5 }), 16 | user_id: factory.assoc('user', 'id'), 17 | }); 18 | } 19 | 20 | declare module 'egg' { 21 | interface Application { 22 | factory: any; 23 | } 24 | } -------------------------------------------------------------------------------- /sequelize-ts/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --file ./test/setup.ts -------------------------------------------------------------------------------- /sequelize-ts/test/setup.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { app } from 'egg-mock/bootstrap'; 4 | import factories from './factories'; 5 | 6 | before(() => { 7 | // defined app.factory for build test data 8 | factories(app); 9 | }); 10 | 11 | afterEach(async () => { 12 | // clear database after each test case 13 | await Promise.all([ 14 | app.model.User.destroy({ truncate: true, force: true }), 15 | app.model.Post.destroy({ truncate: true, force: true }), 16 | ]); 17 | }); 18 | -------------------------------------------------------------------------------- /sequelize-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "strict": true, 7 | "noImplicitAny": false, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "allowSyntheticDefaultImports": true, 11 | "charset": "utf8", 12 | "allowJs": false, 13 | "pretty": true, 14 | "lib": ["es6", "dom"], 15 | "noEmitOnError": false, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "allowUnreachableCode": false, 19 | "allowUnusedLabels": false, 20 | "strictPropertyInitialization": false, 21 | "noFallthroughCasesInSwitch": true, 22 | "inlineSourceMap": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sequelize/.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'ues strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | prefix: '^', 6 | devprefix: '^', 7 | exclude: [ 8 | 'test/fixtures', 9 | 'run', 10 | ], 11 | devdep: [ 12 | 'egg-bin', 13 | 'sequelize-cli', 14 | ], 15 | dep: [ 16 | 'egg', 17 | 'egg-sequelize', 18 | 'mysql2', 19 | ], 20 | semver: [ 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /sequelize/.sequelizerc: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | config: path.join(__dirname, 'database/config.json'), 7 | 'migrations-path': path.join(__dirname, 'database/migrations'), 8 | 'seeders-path': path.join(__dirname, 'database/seeders'), 9 | }; 10 | -------------------------------------------------------------------------------- /sequelize/app/extend/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | parseInt(string) { 5 | if (typeof string === 'number') return string; 6 | if (!string) return string; 7 | return parseInt(string) || 0; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /sequelize/app/model/post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { STRING, INTEGER, DATE } = app.Sequelize; 5 | 6 | const Post = app.model.define('post', { 7 | id: { 8 | type: INTEGER, 9 | primaryKey: true, 10 | autoIncrement: true, 11 | }, 12 | title: STRING(30), 13 | content: STRING(255), 14 | user_id: INTEGER, 15 | created_at: DATE, 16 | updated_at: DATE, 17 | }); 18 | 19 | Post.associate = function() { 20 | app.model.Post.belongsTo(app.model.User, { as: 'user', foreignKey: 'user_id' }); 21 | }; 22 | 23 | Post.findByIdWithUser = async function(id, userId) { 24 | return await this.findOne({ 25 | where: { id, user_id: userId }, 26 | }); 27 | }; 28 | 29 | return Post; 30 | }; 31 | -------------------------------------------------------------------------------- /sequelize/app/model/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { STRING, INTEGER, DATE } = app.Sequelize; 5 | 6 | const User = app.model.define('user', { 7 | id: { 8 | type: INTEGER, 9 | primaryKey: true, 10 | autoIncrement: true, 11 | }, 12 | name: STRING(30), 13 | age: INTEGER, 14 | created_at: DATE, 15 | updated_at: DATE, 16 | }); 17 | 18 | User.prototype.associate = function() { 19 | app.model.User.hasMany(app.model.Post, { as: 'posts' }); 20 | }; 21 | 22 | return User; 23 | }; 24 | -------------------------------------------------------------------------------- /sequelize/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.resources('users', '/users', app.controller.user); 5 | app.resources('posts', '/posts', app.controller.post); 6 | }; 7 | -------------------------------------------------------------------------------- /sequelize/app/service/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Service = require('egg').Service; 4 | 5 | class User extends Service { 6 | async list({ offset = 0, limit = 10 }) { 7 | return this.ctx.model.User.findAndCountAll({ 8 | offset, 9 | limit, 10 | order: [[ 'created_at', 'desc' ], [ 'id', 'desc' ]], 11 | }); 12 | } 13 | 14 | async find(id) { 15 | const user = await this.ctx.model.User.findByPk(id); 16 | if (!user) { 17 | this.ctx.throw(404, 'user not found'); 18 | } 19 | return user; 20 | } 21 | 22 | async create(user) { 23 | return this.ctx.model.User.create(user); 24 | } 25 | 26 | async update({ id, updates }) { 27 | const user = await this.ctx.model.User.findByPk(id); 28 | if (!user) { 29 | this.ctx.throw(404, 'user not found'); 30 | } 31 | return user.update(updates); 32 | } 33 | 34 | async del(id) { 35 | const user = await this.ctx.model.User.findByPk(id); 36 | if (!user) { 37 | this.ctx.throw(404, 'user not found'); 38 | } 39 | return user.destroy(); 40 | } 41 | } 42 | 43 | module.exports = User; 44 | -------------------------------------------------------------------------------- /sequelize/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = {}; 5 | 6 | // should change to your own 7 | config.keys = appInfo.name + '_sequelize-example'; 8 | 9 | config.sequelize = { 10 | dialect: 'mysql', // support: mysql, mariadb, postgres, mssql 11 | database: 'egg-sequelize-example-dev', 12 | host: '127.0.0.1', 13 | port: 3306, 14 | }; 15 | 16 | return config; 17 | }; 18 | -------------------------------------------------------------------------------- /sequelize/config/config.unittest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.sequelize = { 4 | dialect: 'mysql', // support: mysql, mariadb, postgres, mssql 5 | database: 'egg-sequelize-example-unittest', 6 | }; 7 | -------------------------------------------------------------------------------- /sequelize/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // had enabled by egg 4 | // exports.static = true; 5 | exports.sequelize = { 6 | enable: true, 7 | package: 'egg-sequelize', 8 | }; 9 | -------------------------------------------------------------------------------- /sequelize/database/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "root", 4 | "password": null, 5 | "database": "egg-sequelize-example-dev", 6 | "host": "127.0.0.1", 7 | "dialect": "mysql" 8 | }, 9 | "test": { 10 | "username": "root", 11 | "password": null, 12 | "database": "egg-sequelize-example-unittest", 13 | "host": "127.0.0.1", 14 | "dialect": "mysql" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sequelize/database/migrations/20180813112934-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | const { INTEGER, DATE, STRING } = Sequelize; 6 | await queryInterface.createTable('users', { 7 | id: { type: INTEGER, primaryKey: true, autoIncrement: true }, 8 | name: STRING(30), 9 | age: INTEGER, 10 | created_at: DATE, 11 | updated_at: DATE, 12 | }); 13 | }, 14 | 15 | down: async queryInterface => { 16 | await queryInterface.dropTable('users'); 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /sequelize/database/migrations/20180813112942-post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | const { INTEGER, DATE, STRING } = Sequelize; 6 | await queryInterface.createTable('posts', { 7 | id: { type: INTEGER, primaryKey: true, autoIncrement: true }, 8 | title: STRING(30), 9 | content: STRING(255), 10 | user_id: INTEGER, 11 | created_at: DATE, 12 | updated_at: DATE, 13 | }); 14 | }, 15 | 16 | down: async queryInterface => { 17 | await queryInterface.dropTable('posts'); 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /sequelize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sequelize-example", 3 | "version": "1.0.0", 4 | "description": "The egg example project that uses egg-sequelize plugin.", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^2.10.0", 8 | "egg-sequelize": "^5.0.1", 9 | "mysql2": "^1.6.1" 10 | }, 11 | "devDependencies": { 12 | "autod": "^3.0.1", 13 | "egg-bin": "^4.8.1", 14 | "egg-mock": "^3.19.2", 15 | "factory-girl": "^5.0.2", 16 | "lodash": "^4.17.10", 17 | "sequelize-cli": "^5.0.0" 18 | }, 19 | "engines": { 20 | "node": ">=8.9.0" 21 | }, 22 | "scripts": { 23 | "dev": "egg-bin dev", 24 | "test": "NODE_ENV=test npm run sequelize -- db:migrate && egg-bin test", 25 | "cov": "egg-bin cov", 26 | "ci": "npm run cov", 27 | "autod": "autod", 28 | "sequelize": "sequelize --" 29 | }, 30 | "author": "Qi Yu ", 31 | "license": "MIT" 32 | } 33 | -------------------------------------------------------------------------------- /sequelize/test/.setup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app } = require('egg-mock/bootstrap'); 4 | const factories = require('./factories'); 5 | 6 | before(() => { 7 | // defined app.factory for build test data 8 | factories(app); 9 | }); 10 | 11 | afterEach(async () => { 12 | // clear database after each test case 13 | await Promise.all([ 14 | app.model.User.destroy({ truncate: true, force: true }), 15 | app.model.Post.destroy({ truncate: true, force: true }), 16 | ]); 17 | }); 18 | -------------------------------------------------------------------------------- /sequelize/test/factories.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { factory } = require('factory-girl'); 4 | 5 | module.exports = app => { 6 | app.factory = factory; 7 | factory.define('user', app.model.User, { 8 | name: factory.sequence('User.name', n => `name_${n}`), 9 | age: 18, 10 | }); 11 | 12 | factory.define('post', app.model.Post, { 13 | title: factory.sequence('Post.title', n => `title_${n}`), 14 | content: factory.chance('sentence', { word: 5 }), 15 | user_id: factory.assoc('user', 'id'), 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /sofa-rpc/.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | prefix: '^', 6 | plugin: 'autod-egg', 7 | test: [ 8 | 'test', 9 | 'benchmark', 10 | ], 11 | dep: [ 12 | 'egg', 13 | 'egg-scripts', 14 | ], 15 | devdep: [ 16 | 'egg-ci', 17 | 'egg-bin', 18 | 'egg-mock', 19 | 'autod', 20 | 'autod-egg', 21 | 'eslint', 22 | 'eslint-config-egg', 23 | 'webstorm-disable-index', 24 | ], 25 | exclude: [ 26 | './test/fixtures', 27 | './dist', 28 | ], 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /sofa-rpc/.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | app/proxy/* 3 | -------------------------------------------------------------------------------- /sofa-rpc/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-egg" 3 | } 4 | -------------------------------------------------------------------------------- /sofa-rpc/.gitignore: -------------------------------------------------------------------------------- 1 | app/proxy 2 | logs/ 3 | npm-debug.log 4 | yarn-error.log 5 | node_modules/ 6 | package-lock.json 7 | yarn.lock 8 | coverage/ 9 | .idea/ 10 | run/ 11 | .DS_Store 12 | *.sw* 13 | *.un~ 14 | -------------------------------------------------------------------------------- /sofa-rpc/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '8' 5 | install: 6 | - npm i npminstall && npminstall 7 | script: 8 | - npm run ci 9 | after_script: 10 | - npminstall codecov && codecov 11 | -------------------------------------------------------------------------------- /sofa-rpc/README.md: -------------------------------------------------------------------------------- 1 | # sofa-rpc 2 | 3 | sofa rpc demo 4 | 5 | ## QuickStart 6 | 7 | 8 | 9 | see [egg-sofa-rpc tutorial][https://github.com/eggjs/egg-sofa-rpc/wiki/Eggjs-%E5%92%8C-SOFA-%E7%9A%84%E8%B7%A8%E8%AF%AD%E8%A8%80%E4%BA%92%E8%B0%83] for more detail. 10 | 11 | ### Development 12 | 13 | ```bash 14 | $ npm i 15 | $ npm run dev 16 | $ open http://localhost:7001/ 17 | ``` 18 | 19 | ### Deploy 20 | 21 | ```bash 22 | $ npm start 23 | $ npm stop 24 | ``` 25 | 26 | ### npm scripts 27 | 28 | - Use `npm run lint` to check code style. 29 | - Use `npm test` to run unit test. 30 | - Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail. 31 | 32 | 33 | [egg]: https://eggjs.org 34 | -------------------------------------------------------------------------------- /sofa-rpc/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # sofa-rpc 2 | 3 | sofa rpc demo 4 | 5 | ## 快速入门 6 | 7 | 8 | 9 | 如需进一步了解,参见 [Eggjs 和 SOFA 的跨语言互调][https://github.com/eggjs/egg-sofa-rpc/wiki/Eggjs-%E5%92%8C-SOFA-%E7%9A%84%E8%B7%A8%E8%AF%AD%E8%A8%80%E4%BA%92%E8%B0%83]。 10 | 11 | ### 本地开发 12 | 13 | ```bash 14 | $ npm i 15 | $ npm run dev 16 | $ open http://localhost:7001/ 17 | ``` 18 | 19 | ### 部署 20 | 21 | ```bash 22 | $ npm start 23 | $ npm stop 24 | ``` 25 | 26 | ### 单元测试 27 | 28 | - [egg-bin] 内置了 [mocha], [thunk-mocha], [power-assert], [istanbul] 等框架,让你可以专注于写单元测试,无需理会配套工具。 29 | - 断言库非常推荐使用 [power-assert]。 30 | - 具体参见 [egg 文档 - 单元测试](https://eggjs.org/zh-cn/core/unittest)。 31 | 32 | ### 内置指令 33 | 34 | - 使用 `npm run lint` 来做代码风格检查。 35 | - 使用 `npm test` 来执行单元测试。 36 | - 使用 `npm run autod` 来自动检测依赖更新,详细参见 [autod](https://www.npmjs.com/package/autod) 。 37 | 38 | 39 | [egg]: https://eggjs.org 40 | -------------------------------------------------------------------------------- /sofa-rpc/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | async index() { 7 | const { ctx } = this; 8 | const res = await ctx.proxy.protoService.echoObj({ 9 | name: 'gxcsoccer', 10 | group: 'A', 11 | }); 12 | ctx.body = res; 13 | } 14 | } 15 | 16 | module.exports = HomeController; 17 | -------------------------------------------------------------------------------- /sofa-rpc/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {Egg.Application} app - egg application 5 | */ 6 | module.exports = app => { 7 | const { router, controller } = app; 8 | router.get('/', controller.home.index); 9 | }; 10 | -------------------------------------------------------------------------------- /sofa-rpc/app/rpc/ProtoService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.echoObj = async function(req) { 4 | return { 5 | code: 200, 6 | message: 'hello ' + req.name + ', you are in ' + req.group, 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /sofa-rpc/appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | 5 | install: 6 | - ps: Install-Product node $env:nodejs_version 7 | - npm i npminstall && node_modules\.bin\npminstall 8 | 9 | test_script: 10 | - node --version 11 | - npm --version 12 | - npm run test 13 | 14 | build: off 15 | -------------------------------------------------------------------------------- /sofa-rpc/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = exports = {}; 5 | 6 | // use for cookie sign key, should change to your own and keep security 7 | config.keys = appInfo.name + '_1528713428903_3456'; 8 | 9 | // add your config here 10 | config.middleware = []; 11 | 12 | config.rpc = { 13 | registry: { 14 | address: '127.0.0.1:2181', // 根据实际情况配置 15 | }, 16 | server: { 17 | namespace: 'com.alipay.sofa.rpc.protobuf', 18 | codecType: 'protobuf', 19 | }, 20 | }; 21 | 22 | return config; 23 | }; 24 | -------------------------------------------------------------------------------- /sofa-rpc/config/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | services: [{ 5 | appName: 'sofarpc', 6 | api: { 7 | ProtoService: 'com.alipay.sofa.rpc.protobuf.ProtoService', 8 | }, 9 | }], 10 | }; 11 | -------------------------------------------------------------------------------- /sofa-rpc/proto/ProtoService.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package com.alipay.sofa.rpc.protobuf; 4 | option java_multiple_files = true; // 可选 5 | option java_outer_classname = "ProtoServiceModels"; // 可选 6 | 7 | service ProtoService { 8 | rpc echoObj (EchoRequest) returns (EchoResponse) {} 9 | } 10 | 11 | message EchoRequest { 12 | string name = 1; 13 | Group group = 2; 14 | } 15 | 16 | message EchoResponse { 17 | int32 code = 1; 18 | string message = 2; 19 | } 20 | 21 | enum Group { 22 | A = 0; 23 | B = 1; 24 | } 25 | -------------------------------------------------------------------------------- /sofa-rpc/test/app/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app, assert } = require('egg-mock/bootstrap'); 4 | const sleep = require('mz-modules/sleep'); 5 | 6 | describe('test/app/controller/home.test.js', () => { 7 | 8 | it('should assert', function* () { 9 | const pkg = require('../../../package.json'); 10 | assert(app.config.keys.startsWith(pkg.name)); 11 | 12 | // const ctx = app.mockContext({}); 13 | // yield ctx.service.xx(); 14 | }); 15 | 16 | it('should GET /', async function() { 17 | await sleep(1000); 18 | return app.httpRequest() 19 | .get('/') 20 | .expect('{"code":200,"message":"hello gxcsoccer, you are in 0"}') 21 | .expect(200); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /sofa-rpc/test/server.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mm = require('egg-mock'); 4 | const sleep = require('mz-modules/sleep'); 5 | 6 | describe('test/server.test.js', () => { 7 | let app; 8 | before(async function() { 9 | app = mm.app(); 10 | await app.ready(); 11 | await sleep(3000); 12 | }); 13 | after(async function() { 14 | await app.close(); 15 | }); 16 | 17 | it('should invoke echoObj ok', done => { 18 | app.rpcRequest('ProtoService') 19 | .invoke('echoObj') 20 | .send([{ 21 | name: 'gxcsoccer', 22 | group: 'B', 23 | }]) 24 | .expect({ 25 | code: 200, 26 | message: 'hello gxcsoccer, you are in 1', 27 | }, done); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /static/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async render() { 7 | const ctx = this.ctx; 8 | ctx.body = ``; 14 | } 15 | } 16 | 17 | module.exports = Home; 18 | -------------------------------------------------------------------------------- /static/app/public/foo.js: -------------------------------------------------------------------------------- 1 | alert('hi egg'); 2 | -------------------------------------------------------------------------------- /static/app/public/hi.txt: -------------------------------------------------------------------------------- 1 | hi egg. 2 | 你好,蛋蛋。 3 | -------------------------------------------------------------------------------- /static/app/public/蛋蛋Web框架.txt: -------------------------------------------------------------------------------- 1 | test only. 2 | 测试专用。 3 | -------------------------------------------------------------------------------- /static/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', 'home.render'); 5 | }; 6 | -------------------------------------------------------------------------------- /static/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my keys'; 4 | -------------------------------------------------------------------------------- /static/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.static = true; 4 | -------------------------------------------------------------------------------- /static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "static-example", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1" 9 | }, 10 | "scripts": { 11 | "dev": "egg-bin dev", 12 | "test": "egg-bin test", 13 | "cov": "egg-bin cov" 14 | }, 15 | "private": true 16 | } 17 | -------------------------------------------------------------------------------- /static/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const mm = require('egg-mock'); 5 | 6 | describe('example static test', () => { 7 | let app; 8 | 9 | before(() => { 10 | app = mm.app(); 11 | return app.ready(); 12 | }); 13 | 14 | after(() => app.close()); 15 | 16 | it('should GET / 200', () => { 17 | return app.httpRequest() 18 | .get('/') 19 | .expect(200) 20 | .expect(/
  • Download hi\.txt<\/a>\.<\/li>/); 21 | }); 22 | 23 | it('should GET /public/hi.txt', () => { 24 | return app.httpRequest() 25 | .get('/public/hi.txt') 26 | .expect(200) 27 | .expect('hi egg.\n你好,蛋蛋。\n'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /todomvc/.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | -------------------------------------------------------------------------------- /todomvc/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint-config-egg", 4 | "parserOptions": { 5 | "ecmaVersion": 2022 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /todomvc/.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | yarn-error.log 4 | node_modules/ 5 | package-lock.json 6 | yarn.lock 7 | coverage/ 8 | .idea/ 9 | run/ 10 | .DS_Store 11 | *.sw* 12 | *.un~ 13 | typings/ 14 | .nyc_output/ 15 | *.sqlite3 16 | -------------------------------------------------------------------------------- /todomvc/README.md: -------------------------------------------------------------------------------- 1 | # TodoMVC powered by Egg 2 | 3 | - [TodoMVC](http://todomvc.com/) 4 | - [Egg](egg) 5 | 6 | ![](./todomvc.png) 7 | 8 | ### QuickStart 9 | 10 | ```bash 11 | $ npm i 12 | $ npm run dev 13 | $ open http://localhost:7001/ 14 | ``` 15 | 16 | [egg]: https://eggjs.org 17 | -------------------------------------------------------------------------------- /todomvc/app.js: -------------------------------------------------------------------------------- 1 | const initDatabase = require('./config/database/init'); 2 | 3 | class AppBootHook { 4 | constructor(app) { 5 | this.app = app; 6 | } 7 | 8 | async didLoad() { 9 | await initDatabase(this.app); 10 | } 11 | } 12 | 13 | module.exports = AppBootHook; 14 | -------------------------------------------------------------------------------- /todomvc/app/controller/home.js: -------------------------------------------------------------------------------- 1 | const { Controller } = require('egg'); 2 | 3 | class HomeController extends Controller { 4 | async index() { 5 | const { ctx } = this; 6 | await ctx.render('home.tpl'); 7 | } 8 | } 9 | 10 | module.exports = HomeController; 11 | -------------------------------------------------------------------------------- /todomvc/app/middleware/response_time.js: -------------------------------------------------------------------------------- 1 | module.exports = options => { 2 | return async function responseTime(ctx, next) { 3 | const start = Date.now(); 4 | await next(); 5 | const cost = Date.now() - start; 6 | ctx.set(options.headerKey, `${cost}ms`); 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /todomvc/app/model/todo.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | const { Bone, DataTypes: { DATE, STRING, BOOLEAN } } = app.model; 3 | 4 | class Todo extends Bone { 5 | static table = 'todos'; 6 | static attributes = { 7 | title: STRING(1000), 8 | createdAt: DATE, 9 | updatedAt: DATE, 10 | deletedAt: DATE, 11 | completed: BOOLEAN, 12 | }; 13 | } 14 | 15 | return Todo; 16 | }; 17 | -------------------------------------------------------------------------------- /todomvc/app/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {Egg.Application} app - egg application 3 | */ 4 | module.exports = app => { 5 | const { router, controller } = app; 6 | 7 | router.get('/', controller.home.index); 8 | router.resources('/api/todo', controller.todo); 9 | }; 10 | -------------------------------------------------------------------------------- /todomvc/config/config.unittest.js: -------------------------------------------------------------------------------- 1 | /* eslint valid-jsdoc: "off" */ 2 | 3 | const path = require('path'); 4 | 5 | module.exports = () => { 6 | /** 7 | * built-in config 8 | * @type {Egg.EggAppConfig} 9 | **/ 10 | const config = {}; 11 | 12 | config.orm = { 13 | database: path.join(__dirname, '..', 'todos_unittest.sqlite3'), 14 | }; 15 | 16 | return { 17 | ...config, 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /todomvc/config/database/init.js: -------------------------------------------------------------------------------- 1 | // init database here 2 | module.exports = async app => { 3 | // sync database defines 4 | await app.model.sync(); 5 | 6 | if (app.config.env === 'unittest') { 7 | await app.model.Todo.remove({}, true); 8 | await app.model.Todo.bulkCreate([ 9 | { title: 'Read history of Node.js', completed: true }, 10 | { title: 'Learn Koa', completed: true }, 11 | { title: 'Star Egg', completed: false }, 12 | ]); 13 | const rows = await app.model.Todo.findAll(); 14 | console.log(rows); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /todomvc/config/plugin.js: -------------------------------------------------------------------------------- 1 | /** @type Egg.EggPlugin */ 2 | module.exports = { 3 | nunjucks: { 4 | enable: true, 5 | package: 'egg-view-nunjucks', 6 | }, 7 | validate: { 8 | enable: true, 9 | package: 'egg-validate', 10 | }, 11 | orm: { 12 | enable: true, 13 | package: 'egg-orm', 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /todomvc/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "**/*" 4 | ] 5 | } -------------------------------------------------------------------------------- /todomvc/todomvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/examples/5ea83ef36dede99ae057adfb6972c5086d8d2e4c/todomvc/todomvc.png -------------------------------------------------------------------------------- /unittest-jest/README.md: -------------------------------------------------------------------------------- 1 | # unittest-jest 2 | 3 | Example use on https://github.com/eggjs/egg/blob/master/docs/source/zh-cn/core/unittest.md 4 | -------------------------------------------------------------------------------- /unittest-jest/__tests__/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app } = require('egg-mock/bootstrap'); 4 | 5 | describe('__tests__/controller/home.test.js', () => { 6 | it('should status 200 and get the body', () => { 7 | return app.httpRequest() 8 | .get('/') 9 | .expect(200) 10 | .expect('hello world'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /unittest-jest/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app } = require('egg-mock/bootstrap'); 4 | 5 | describe('__tests__/index.test.js', () => { 6 | it('should app exist', () => { 7 | expect(app.test).toBe('123'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /unittest-jest/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async index() { 7 | const ctx = this.ctx; 8 | ctx.body = 'hello world'; 9 | } 10 | } 11 | 12 | module.exports = Home; 13 | -------------------------------------------------------------------------------- /unittest-jest/app/extend/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /unittest-jest/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('homepage', '/', 'home.index'); 5 | }; 6 | -------------------------------------------------------------------------------- /unittest-jest/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my super cool keys'; 4 | -------------------------------------------------------------------------------- /unittest-jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unittest-jest-example", 3 | "dependencies": { 4 | "egg": "^1.10.1" 5 | }, 6 | "devDependencies": { 7 | "egg-bin": "^4.3.5", 8 | "egg-mock": "^3.13.1", 9 | "jest": "^23.6.0" 10 | }, 11 | "scripts": { 12 | "dev": "egg-bin dev", 13 | "test": "jest", 14 | "cov": "jest --coverage", 15 | "ci": "eslint . && npm run cov" 16 | }, 17 | "private": true 18 | } 19 | -------------------------------------------------------------------------------- /unittest/README.md: -------------------------------------------------------------------------------- 1 | # unittest 2 | 3 | Example use on https://github.com/eggjs/egg/blob/master/docs/source/zh-cn/core/unittest.md 4 | -------------------------------------------------------------------------------- /unittest/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | 5 | app.beforeStart(async () => { 6 | // 示例:启动的时候去读取 https://registry.npmjs.com/egg/latest 的版本信息 7 | const result = await app.curl('https://registry.npmjs.com/egg/latest', { 8 | dataType: 'json', 9 | }); 10 | app.logger.info('egg latest version: %s', result.data.version); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /unittest/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async index() { 7 | const ctx = this.ctx; 8 | ctx.body = 'hello world'; 9 | } 10 | 11 | async post() { 12 | const ctx = this.ctx; 13 | ctx.body = ctx.request.body; 14 | } 15 | 16 | async session() { 17 | const ctx = this.ctx; 18 | ctx.body = { 19 | session: ctx.session, 20 | }; 21 | } 22 | 23 | async user() { 24 | const ctx = this.ctx; 25 | const user = await ctx.service.user.get(ctx.query.name); 26 | ctx.body = { 27 | user, 28 | }; 29 | } 30 | 31 | async httpclient() { 32 | const ctx = this.ctx; 33 | const res = await ctx.curl('https://eggjs.org'); 34 | ctx.body = res.data.toString(); 35 | } 36 | } 37 | 38 | module.exports = Home; 39 | -------------------------------------------------------------------------------- /unittest/app/extend/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const LRU = Symbol('Application#lru'); 4 | const LRUCache = require('ylru'); 5 | 6 | module.exports = { 7 | get lru() { 8 | if (!this[LRU]) { 9 | this[LRU] = new LRUCache(1000); 10 | } 11 | return this[LRU]; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /unittest/app/extend/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get isXHR() { 5 | return this.get('X-Requested-With') === 'XMLHttpRequest'; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /unittest/app/extend/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | money(val) { 5 | const lang = this.ctx.get('Accept-Language'); 6 | if (lang.includes('zh-CN')) { 7 | return `¥ ${val}`; 8 | } 9 | return `$ ${val}`; 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /unittest/app/extend/request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const IS_CHROME = Symbol('Request#isChrome'); 4 | 5 | module.exports = { 6 | get isChrome() { 7 | if (!this[IS_CHROME]) { 8 | const ua = this.get('User-Agent').toLowerCase(); 9 | this[IS_CHROME] = ua.includes('chrome/'); 10 | } 11 | return this[IS_CHROME]; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /unittest/app/extend/response.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get isSuccess() { 5 | return this.status === 200; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /unittest/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('homepage', '/', 'home.index'); 5 | app.router.post('postdata', '/post', 'home.post'); 6 | app.router.get('session', '/session', 'home.session'); 7 | app.router.get('user', '/user', 'home.user'); 8 | app.router.get('httpclient', '/httpclient', 'home.httpclient'); 9 | }; 10 | -------------------------------------------------------------------------------- /unittest/app/service/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Service = require('egg').Service; 4 | 5 | const mockUsers = [ 6 | { 7 | name: 'fengmk2', 8 | home: 'HangZhou', 9 | url: 'https://fengmk2.com', 10 | }, 11 | ]; 12 | 13 | const userDatabase = { 14 | async get(name) { 15 | for (const user of mockUsers) { 16 | if (user.name === name) { 17 | return user; 18 | } 19 | } 20 | return null; 21 | }, 22 | }; 23 | 24 | class User extends Service { 25 | async get(name) { 26 | return userDatabase.get(name); 27 | } 28 | } 29 | 30 | module.exports = User; 31 | -------------------------------------------------------------------------------- /unittest/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my super cool keys'; 4 | -------------------------------------------------------------------------------- /unittest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unittest-example", 3 | "dependencies": { 4 | "egg": "^1.10.1", 5 | "ylru": "^1.2.0" 6 | }, 7 | "devDependencies": { 8 | "egg-bin": "^4.3.5", 9 | "egg-mock": "^3.13.1" 10 | }, 11 | "scripts": { 12 | "dev": "egg-bin dev", 13 | "test": "egg-bin test", 14 | "cov": "egg-bin cov", 15 | "ci": "eslint . && egg-bin cov" 16 | }, 17 | "private": true 18 | } 19 | -------------------------------------------------------------------------------- /unittest/test/extend/application.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const mock = require('egg-mock'); 5 | 6 | describe('test/extend/application.test.js', () => { 7 | let app; 8 | before(() => { 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | 13 | describe('get lru', () => { 14 | it('should get a lru and it work', () => { 15 | app.lru.set('foo', 'bar'); 16 | assert(app.lru.get('foo') === 'bar'); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /unittest/test/extend/context.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const mock = require('egg-mock'); 5 | 6 | describe('test/extend/context.test.js', () => { 7 | let app; 8 | before(() => { 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | 13 | describe('isXHR()', () => { 14 | it('should true', () => { 15 | const ctx = app.mockContext({ 16 | headers: { 17 | 'X-Requested-With': 'XMLHttpRequest', 18 | }, 19 | }); 20 | assert(ctx.isXHR === true); 21 | }); 22 | 23 | it('should false', () => { 24 | const ctx = app.mockContext({ 25 | headers: { 26 | 'X-Requested-With': 'SuperAgent', 27 | }, 28 | }); 29 | assert(ctx.isXHR === false); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /unittest/test/extend/helper.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const mock = require('egg-mock'); 5 | 6 | describe('test/extend/helper.test.js', () => { 7 | let app; 8 | before(() => { 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | 13 | describe('money()', () => { 14 | it('should RMB', () => { 15 | const ctx = app.mockContext({ 16 | // 模拟 ctx 的 headers 17 | headers: { 18 | 'Accept-Language': 'zh-CN,zh;q=0.5', 19 | }, 20 | }); 21 | assert(ctx.helper.money(100) === '¥ 100'); 22 | }); 23 | 24 | it('should US Dolar', () => { 25 | const ctx = app.mockContext(); 26 | assert(ctx.helper.money(100) === '$ 100'); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /unittest/test/extend/request.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const mock = require('egg-mock'); 5 | 6 | describe('test/extend/request.test.js', () => { 7 | let app; 8 | before(() => { 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | 13 | describe('isChrome()', () => { 14 | it('should true', () => { 15 | const ctx = app.mockContext({ 16 | headers: { 17 | 'User-Agent': 'Chrome/56.0.2924.51', 18 | }, 19 | }); 20 | assert(ctx.request.isChrome === true); 21 | }); 22 | 23 | it('should false', () => { 24 | const ctx = app.mockContext({ 25 | headers: { 26 | 'User-Agent': 'FireFox/1', 27 | }, 28 | }); 29 | assert(ctx.request.isChrome === false); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /unittest/test/extend/response.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const mock = require('egg-mock'); 5 | 6 | describe('test/extend/response.test.js', () => { 7 | let app; 8 | before(() => { 9 | app = mock.app(); 10 | return app.ready(); 11 | }); 12 | 13 | describe('isSuccess()', () => { 14 | it('should true', () => { 15 | const ctx = app.mockContext(); 16 | ctx.status = 200; 17 | assert(ctx.response.isSuccess === true); 18 | }); 19 | 20 | it('should false', () => { 21 | const ctx = app.mockContext(); 22 | ctx.status = 404; 23 | assert(ctx.response.isSuccess === false); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /unittest/test/hello.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | describe('test/hello.test.js', () => { 6 | it('should work', () => { 7 | assert(Date.now() > 0); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /view-nunjucks/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class Home extends Controller { 6 | async render() { 7 | const ctx = this.ctx; 8 | await ctx.render('home.html', { 9 | user: { 10 | name: 'foobar', 11 | }, 12 | title: 'egg view example', 13 | }); 14 | } 15 | } 16 | 17 | module.exports = Home; 18 | -------------------------------------------------------------------------------- /view-nunjucks/app/public/css/home.css: -------------------------------------------------------------------------------- 1 | body {} 2 | -------------------------------------------------------------------------------- /view-nunjucks/app/public/css/layout.css: -------------------------------------------------------------------------------- 1 | body {} 2 | -------------------------------------------------------------------------------- /view-nunjucks/app/public/js/home.js: -------------------------------------------------------------------------------- 1 | console.log('hello egg view'); 2 | -------------------------------------------------------------------------------- /view-nunjucks/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/', 'home.render'); 5 | }; 6 | -------------------------------------------------------------------------------- /view-nunjucks/app/view/home.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block body %} 4 | 5 |
    6 |

    egg view example here, welcome {{ user.name }}

    7 |
    8 | 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /view-nunjucks/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'my secret keys'; 4 | 5 | exports.view = { 6 | defaultViewEngine: 'nunjucks', 7 | }; 8 | exports.nunjucks = { 9 | // dir: 'path/to/template/dir', // default to `{app_root}/app/view` 10 | cache: true, // local env is false 11 | }; 12 | -------------------------------------------------------------------------------- /view-nunjucks/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.nunjucks = { 4 | enable: true, 5 | package: 'egg-view-nunjucks', 6 | }; 7 | -------------------------------------------------------------------------------- /view-nunjucks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "view-nunjucks-example", 3 | "dependencies": { 4 | "egg": "^1.10.1", 5 | "egg-view-nunjucks": "^2.1.4" 6 | }, 7 | "devDependencies": { 8 | "egg-bin": "^4.3.5", 9 | "egg-mock": "^3.13.1", 10 | "macaca-electron": "2", 11 | "macaca-wd": "2" 12 | }, 13 | "scripts": { 14 | "dev": "egg-bin dev", 15 | "test": "egg-bin test", 16 | "test:ui": "macaca run -d ./uitest --reporter macaca-reporter", 17 | "cov": "egg-bin cov" 18 | }, 19 | "private": true 20 | } 21 | -------------------------------------------------------------------------------- /view-nunjucks/test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mm = require('egg-mock'); 4 | 5 | describe('example view-nunjucks test', () => { 6 | let app; 7 | 8 | before(() => { 9 | app = mm.app(); 10 | return app.ready(); 11 | }); 12 | 13 | after(() => app.close()); 14 | 15 | it('should GET / 200', () => { 16 | return app.httpRequest() 17 | .get('/') 18 | .expect(200) 19 | .expect(/

    egg view example here, welcome foobar<\/h2>/) 20 | .expect(/egg view example<\/title>/); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /view-nunjucks/uitest/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const wd = require('macaca-wd'); 4 | const { 5 | extendsMixIn, 6 | } = require('macaca-wd/lib/helper'); 7 | 8 | extendsMixIn(wd); 9 | 10 | const port = 7001; 11 | 12 | const BASE_URL = `http://127.0.0.1:${port}`; 13 | 14 | wd.addPromiseChainMethod('initWindow', function(options = {}) { 15 | return this 16 | .init(Object.assign({ 17 | platformName: 'desktop', 18 | browserName: 'electron', 19 | deviceScaleFactor: 2, 20 | }, options)) 21 | .setWindowSize(options.width, options.height); 22 | }); 23 | 24 | const driver = wd.promiseChainRemote({ 25 | host: 'localhost', 26 | port: process.env.MACACA_SERVER_PORT || 3456, 27 | }); 28 | 29 | exports.driver = driver; 30 | exports.BASE_URL = BASE_URL; 31 | -------------------------------------------------------------------------------- /view-nunjucks/uitest/mocha.opts: -------------------------------------------------------------------------------- 1 | --require babel-register 2 | --recursive 3 | --timeout 60000 4 | --------------------------------------------------------------------------------