diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..11c8ac8
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,48 @@
+name: Release
+
+on:
+  push:
+    branches: [master]
+
+jobs:
+  release:
+    name: Release
+    runs-on: ubuntu-latest
+    env:
+      GH_TOKEN: ${{ secrets.GH_TOKEN }}
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+        with:
+          persist-credentials: false
+
+      - name: Install Node.js and npm
+        uses: actions/setup-node@v1
+        with:
+          node-version: 14.x
+          registry-url: https://registry.npmjs.org
+
+      - name: Retrieve dependencies from cache
+        id: cacheNpm
+        uses: actions/cache@v2
+        with:
+          path: |
+            ~/.npm
+            node_modules
+          key: npm-v14-${{ runner.os }}-refs/heads/master-${{ hashFiles('package.json') }}
+          restore-keys: npm-v14-${{ runner.os }}-refs/heads/master-
+
+      - name: Install dependencies
+        if: steps.cacheNpm.outputs.cache-hit != 'true'
+        run: |
+          npm update --no-save
+          npm update --save-dev --no-save
+      - name: Releasing
+        run: |
+          npm run release
+        env:
+          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
+          GIT_AUTHOR_NAME: slsplus
+          GIT_AUTHOR_EMAIL: slsplus.sz@gmail.com
+          GIT_COMMITTER_NAME: slsplus
+          GIT_COMMITTER_EMAIL: slsplus.sz@gmail.com
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..c966e94
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,47 @@
+name: Test
+
+on:
+  pull_request:
+    branches: [master]
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+        with:
+          # Ensure connection with 'master' branch
+          fetch-depth: 2
+
+      - name: Install Node.js and npm
+        uses: actions/setup-node@v1
+        with:
+          node-version: 14.x
+          registry-url: https://registry.npmjs.org
+
+      - name: Retrieve dependencies from cache
+        id: cacheNpm
+        uses: actions/cache@v2
+        with:
+          path: |
+            ~/.npm
+            node_modules
+          key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
+          restore-keys: |
+            npm-v14-${{ runner.os }}-${{ github.ref }}-
+            npm-v14-${{ runner.os }}-refs/heads/master-
+
+      - name: Install dependencies
+        if: steps.cacheNpm.outputs.cache-hit != 'true'
+        run: |
+          npm update --no-save
+          npm update --save-dev --no-save
+      - name: Running tests
+        run: npm run test
+        env:
+          SERVERLESS_PLATFORM_VENDOR: tencent
+          GLOBAL_ACCELERATOR_NA: true
+          TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
+          TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
new file mode 100644
index 0000000..3840792
--- /dev/null
+++ b/.github/workflows/validate.yml
@@ -0,0 +1,45 @@
+name: Validate
+
+on:
+  pull_request:
+    branches: [master]
+
+jobs:
+  lintAndFormatting:
+    name: Lint & Formatting
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+        with:
+          # Ensure connection with 'master' branch
+          fetch-depth: 2
+
+      - name: Install Node.js and npm
+        uses: actions/setup-node@v1
+        with:
+          node-version: 14.x
+          registry-url: https://registry.npmjs.org
+
+      - name: Retrieve dependencies from cache
+        id: cacheNpm
+        uses: actions/cache@v2
+        with:
+          path: |
+            ~/.npm
+            node_modules
+          key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
+          restore-keys: |
+            npm-v14-${{ runner.os }}-${{ github.ref }}-
+            npm-v14-${{ runner.os }}-refs/heads/master-
+
+      - name: Install dependencies
+        if: steps.cacheNpm.outputs.cache-hit != 'true'
+        run: |
+          npm update --no-save
+          npm update --save-dev --no-save
+
+      - name: Validate Formatting
+        run: npm run prettier:fix
+      - name: Validate Lint rules
+        run: npm run lint:fix
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b9f070c..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: node_js
-
-node_js:
-  - 8
-  - 10
-
-install:
-  - npm install
-
-# should change to serverless registry publish
-jobs:
-  include:
-    # Define the release stage that runs semantic-release
-    - stage: release
-      node_js: 10.18
-      # Advanced: optionally overwrite your default `script` step to skip the tests
-      # script: skip
-      deploy:
-        provider: script
-        skip_cleanup: true
-        script:
-          - npm run release
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..96dfed8
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,132 @@
+# [0.3.0](https://github.com/serverless-components/tencent-express/compare/v0.2.3...v0.3.0) (2021-01-26)
+
+
+### Features
+
+* support apigw base64 encode ([78ca266](https://github.com/serverless-components/tencent-express/commit/78ca26613eec65558852ff1b6a130a841b0178f3))
+
+## [0.2.3](https://github.com/serverless-components/tencent-express/compare/v0.2.2...v0.2.3) (2020-12-28)
+
+
+### Bug Fixes
+
+* add sls initialize before creating server ([7f18f2c](https://github.com/serverless-components/tencent-express/commit/7f18f2c5e8e86cc0a9252e5bd036742251964e4d))
+
+## [0.2.2](https://github.com/serverless-components/tencent-express/compare/v0.2.1...v0.2.2) (2020-12-15)
+
+
+### Bug Fixes
+
+* update remove flow ([f766ee6](https://github.com/serverless-components/tencent-express/commit/f766ee69cf39cf410df0203fc71644297ef70693))
+
+## [0.2.1](https://github.com/serverless-components/tencent-express/compare/v0.2.0...v0.2.1) (2020-12-15)
+
+
+### Bug Fixes
+
+* update to deployment to serial flow ([a76fe58](https://github.com/serverless-components/tencent-express/commit/a76fe586846269132f3a9b9141302050b02a13c3))
+
+# [0.2.0](https://github.com/serverless-components/tencent-express/compare/v0.1.5...v0.2.0) (2020-10-12)
+
+
+### Bug Fixes
+
+* support all parameters for apigw ([ec3ffc7](https://github.com/serverless-components/tencent-express/commit/ec3ffc7fb6a6959dbdb734b8a2137f5ac777d05c))
+
+
+### Features
+
+* support custom entry file and all scf config ([80e2890](https://github.com/serverless-components/tencent-express/commit/80e28903422c5710a56a8267f64ffe47398ddcf8))
+
+## [0.1.5](https://github.com/serverless-components/tencent-express/compare/v0.1.4...v0.1.5) (2020-09-03)
+
+
+### Bug Fixes
+
+* update deploy flow for multi region ([33ae212](https://github.com/serverless-components/tencent-express/commit/33ae21248a7c191e3ceea2371fd2e69a6b1ebf67))
+
+## [0.1.4](https://github.com/serverless-components/tencent-express/compare/v0.1.3...v0.1.4) (2020-09-02)
+
+
+### Bug Fixes
+
+* update tencnet-component-toolkit for api mark ([7e25e8a](https://github.com/serverless-components/tencent-express/commit/7e25e8a86f539b2db449caf969cbbd58c5af4d1b))
+
+## [0.1.3](https://github.com/serverless-components/tencent-express/compare/v0.1.2...v0.1.3) (2020-09-01)
+
+
+### Bug Fixes
+
+* support cfs ([9d1a1e1](https://github.com/serverless-components/tencent-express/commit/9d1a1e1793d866f5fbe5bdce61090db8381bb103))
+* update deps for error message ([f0a8f89](https://github.com/serverless-components/tencent-express/commit/f0a8f892f469efcda67070ff940421aa83a100c0))
+
+## [0.1.2](https://github.com/serverless-components/tencent-express/compare/v0.1.1...v0.1.2) (2020-08-26)
+
+
+### Bug Fixes
+
+* add apigw/scf deployed state saving manually ([0fcc0b8](https://github.com/serverless-components/tencent-express/commit/0fcc0b84bdeb7119086cdbb050ae2c8ab734ff32))
+* add regionList state ([f66d8d8](https://github.com/serverless-components/tencent-express/commit/f66d8d8ee6564e343442bb59b9996bf23fd475b9))
+* add release config ([26c7e12](https://github.com/serverless-components/tencent-express/commit/26c7e1201caf79830843e860237f3ebf6efdf1e7))
+* apigw custom domain update bug ([1a53027](https://github.com/serverless-components/tencent-express/commit/1a53027effde871dc9f021f510fd7a13cc86794a))
+* apigw isDisabled ([f624171](https://github.com/serverless-components/tencent-express/commit/f624171b927f0efd4605566c6168d67a3f9b8302))
+* bind unexist role bug ([6cd8990](https://github.com/serverless-components/tencent-express/commit/6cd89900111c7afd1e073d95f81f99364debfb96))
+* cache http server ([fc5c824](https://github.com/serverless-components/tencent-express/commit/fc5c82453e2469d66837e1b525d317e811b3115a))
+* change entry to sls.js ([61bd482](https://github.com/serverless-components/tencent-express/commit/61bd4820dfcacefa1338580fa8d3b0f2609ffe4c))
+* cns ([65aa8b8](https://github.com/serverless-components/tencent-express/commit/65aa8b8b42880c9709ff583429899275978b007e))
+* enableCORS for apigw ([a0f59ef](https://github.com/serverless-components/tencent-express/commit/a0f59ef01d5dfe500758adfe2c4f05d7ffc2f22b))
+* handle usageplan & auth undefined ([2a257d5](https://github.com/serverless-components/tencent-express/commit/2a257d564e0968c8db1e0a4030412749cb19e847))
+* make default runtime to 10.15 ([a295bd4](https://github.com/serverless-components/tencent-express/commit/a295bd49a9b48f470a3416026531e840dd2177ac))
+* metics data bug ([6bdf89b](https://github.com/serverless-components/tencent-express/commit/6bdf89bbe3b3665012acee5ca66e08fdff9f05db))
+* metrics qps limit ([dddb9f6](https://github.com/serverless-components/tencent-express/commit/dddb9f607c96d86a3500eddeae248d600e2c9813))
+* monitor timeout bug ([de33311](https://github.com/serverless-components/tencent-express/commit/de333111a692b5bc8d14e9274b81b5b2e5e57e84))
+* optimize outputs for one region ([b3c6964](https://github.com/serverless-components/tencent-express/commit/b3c69646528edfdb8ff0d6455a4dc2b16e13cb81))
+* optimize traffic config outputs ([8545ead](https://github.com/serverless-components/tencent-express/commit/8545ead58183454536ca43c412d42d9be9c05580))
+* package version ([d682b66](https://github.com/serverless-components/tencent-express/commit/d682b66ab7858bb85c29548f90514056f8c34749))
+* prettier config ([e17bd6f](https://github.com/serverless-components/tencent-express/commit/e17bd6fe939666f7d8c5c89182ed24125409e068))
+* read bucket and object from srcOriginal ([1b2cf9d](https://github.com/serverless-components/tencent-express/commit/1b2cf9d7c890a94e7deaa830814b9758ac42cdf4))
+* release v0.0.1 for v2 ([52b2a37](https://github.com/serverless-components/tencent-express/commit/52b2a37a9f57550d48af868b6d53ac3a9c020fd5))
+* remove credential bug ([bca2d1f](https://github.com/serverless-components/tencent-express/commit/bca2d1f9c6ab79ee7b29844fe8bdf2085ddac94c))
+* role check error ([1a0b4bf](https://github.com/serverless-components/tencent-express/commit/1a0b4bf3ec1fed5492d72f07378ca7b5d26bbfe8))
+* support apigw endpoint timeout config ([01dd4c9](https://github.com/serverless-components/tencent-express/commit/01dd4c931235ec5547ce5f34e0997beb4e094fd1))
+* support eip config ([62c597f](https://github.com/serverless-components/tencent-express/commit/62c597fdef0a8e296b1943dba13baa60725afd53))
+* template url output ([88df485](https://github.com/serverless-components/tencent-express/commit/88df48503bd1f106e451ce102ec5bd16d6a1442a))
+* throw error when no temp secrets ([2a9167e](https://github.com/serverless-components/tencent-express/commit/2a9167e98a9648fb3ed7264956b10ea2808ce490))
+* traffic zero display bug ([0285406](https://github.com/serverless-components/tencent-express/commit/0285406c812284bd8b5847f0c201c9b69761c9d6))
+* uniform throw error ([c09b6f5](https://github.com/serverless-components/tencent-express/commit/c09b6f5b0071a2fb6a3251b0f8f31bc0dd614d3b))
+* update deploy code ([90a4339](https://github.com/serverless-components/tencent-express/commit/90a433968502f9d1499534cf852c6d4c3db12ba6))
+* update deps ([2e551db](https://github.com/serverless-components/tencent-express/commit/2e551db26dcfa02adf5aa2c63d6c08fea771ed00))
+* update deps ([c14e8f1](https://github.com/serverless-components/tencent-express/commit/c14e8f1492fa8ca7e3bedbc8018571556aa0c6ff))
+* update error message ([c4c7243](https://github.com/serverless-components/tencent-express/commit/c4c724358fe5be700e596929b8bddcfa94faa6fd))
+* update get credential error message ([ed47b3d](https://github.com/serverless-components/tencent-express/commit/ed47b3d759944e41b0c8fa7641594450d88704de))
+* update toolkit verison ([2e99bc7](https://github.com/serverless-components/tencent-express/commit/2e99bc73c438318d125d50d959bf3f9de4a6e85e))
+* update usageplan & auth logic ([afa8807](https://github.com/serverless-components/tencent-express/commit/afa8807792aa0e69c191391b1fb49b059add3c35))
+* upgrade deps ([6a1f7a8](https://github.com/serverless-components/tencent-express/commit/6a1f7a86d4838d02cef19de37a2f6e9714ee640b))
+* upgrade deps ([d5c013b](https://github.com/serverless-components/tencent-express/commit/d5c013b6a04487a29b9848442d31cc71c6230c5f))
+* upgrade tencent-component-toolkit ([4ba2edf](https://github.com/serverless-components/tencent-express/commit/4ba2edf8570e511b9fd5a8286ae81044b531c094))
+* upgrade tencent-component-toolkit ([af8c234](https://github.com/serverless-components/tencent-express/commit/af8c234622058253a21ebe0231796dd7a47902f8))
+* upgrade tencent-component-toolkit ([bdd5062](https://github.com/serverless-components/tencent-express/commit/bdd5062f6501fbe91e167ce3e7a91da2e8eb4acd))
+* upgrade tencent-component-toolkit ([00f4a09](https://github.com/serverless-components/tencent-express/commit/00f4a0993d04d4a7be3cf8ea15381a6d8ff04c98))
+* upgrade tencent-component-toolkit for deleting compatibility ([fa215bf](https://github.com/serverless-components/tencent-express/commit/fa215bfb66264c360c24a3aca68f4b63cb4c60fd))
+* wrong region parameter bug ([#31](https://github.com/serverless-components/tencent-express/issues/31)) ([32e519c](https://github.com/serverless-components/tencent-express/commit/32e519ca00170ed75f0bea19b80c0255288e8d37))
+* 增加cns ([b452285](https://github.com/serverless-components/tencent-express/commit/b45228501a381dd6ede9381ca8d05cd5f403d585))
+* 增加cns ([a2b87d9](https://github.com/serverless-components/tencent-express/commit/a2b87d92908935dc4299dfb0d5b01029c5fc8649))
+
+
+### Features
+
+* add binary types support ([60603d6](https://github.com/serverless-components/tencent-express/commit/60603d61bbeb1cbdcf44d5bb6b9cd5a6f2b08fd6))
+* add integration test ([7c28271](https://github.com/serverless-components/tencent-express/commit/7c2827162f94b817f5e8f3c00e279263bff120e7))
+* add layers config support ([d971be9](https://github.com/serverless-components/tencent-express/commit/d971be985daf3bc4127c6648fd0e474c535ad1eb))
+* add metrics api ([#27](https://github.com/serverless-components/tencent-express/issues/27)) ([0d40154](https://github.com/serverless-components/tencent-express/commit/0d40154347b5db735fcac66a8a9b5ccf0e6ca035))
+* add state store, and remove method ([8815e40](https://github.com/serverless-components/tencent-express/commit/8815e4045cf7896e8843c017abb16b24720caa79))
+* optimize code zip flow ([f2d60ce](https://github.com/serverless-components/tencent-express/commit/f2d60ce03e96257f4aacda464ab83a4dc144fecb))
+* optimize deploy log ([80a2bc0](https://github.com/serverless-components/tencent-express/commit/80a2bc0a23a08051ede3c5d54657833499e0d2fc))
+* optimize metics and support disable apigw creating ([e722c80](https://github.com/serverless-components/tencent-express/commit/e722c80d3d1fce8315e08a86808c131402a7a81b))
+* support api gw metrics ([cc625ad](https://github.com/serverless-components/tencent-express/commit/cc625ad3e8a410b161b4744813685aa42a700898))
+* support role config ([f1d6ed6](https://github.com/serverless-components/tencent-express/commit/f1d6ed664b7354376f5791bd162113cdd44b7b5b))
+* support scf publish version and traffic setup ([8807d0e](https://github.com/serverless-components/tencent-express/commit/8807d0ef0fd8c560b3c7aaa8fa37165473a5f863))
+* update config and support usageplan+auth ([b067a14](https://github.com/serverless-components/tencent-express/commit/b067a149acec78d97f701aea893e95ad3833f7c3))
+* update event and context attach method ([31d0c1d](https://github.com/serverless-components/tencent-express/commit/31d0c1de42354103a800e0515f9a069971b357df))
+* use metrics api from toolkit library([#32](https://github.com/serverless-components/tencent-express/issues/32)) ([4b4742d](https://github.com/serverless-components/tencent-express/commit/4b4742d7e53a04fbbd7845073a0ebd2031a1dcd6))
+* using temporary secret for credentials ([cf61300](https://github.com/serverless-components/tencent-express/commit/cf61300f7d94f3149648a0d3378b8876a440aca1))
diff --git a/README.md b/README.md
index 651a98e..9fb1b9d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+⚠️⚠️⚠️ 所有框架组件项目迁移到 [tencent-framework-components](https://github.com/serverless-components/tencent-framework-components).
+
 [](http://serverless.com)
 
 
@@ -17,8 +19,6 @@
 
 
 
- -
 快速开始:
 
 1. [**安装**](#1-安装)
@@ -49,35 +49,31 @@ $ npm install -g serverless
 通过如下命令和模板链接,快速创建一个 Express 应用:
 
 ```bash
-$ serverless create --template-url https://github.com/serverless-components/tencent-express/tree/v2/serverless-express
-$ cd serverless-express
-```
-
-执行如下命令,安装 Express 应用的对应依赖
-
-```
-$ npm install
+$ serverless init express-starter --name example
+$ cd example
 ```
 
 ### 3. 部署
 
-在 `serverless.yml` 文件下的目录中运行 `serverless deploy` 进行 Express 项目的部署。第一次部署可能耗时相对较久,但后续的二次部署会在几秒钟之内完成。部署完毕后,你可以在命令行的输出中查看到你 Express 应用的 URL 地址,点击地址即可访问你的 Express 项目。
+在 `serverless.yml` 文件所在的项目根目录,运行以下指令进行部署:
 
-**注意:**
+```bash
+$ serverless deploy
+```
 
-如您的账号未[登陆](https://cloud.tencent.com/login)或[注册](https://cloud.tencent.com/register)腾讯云,您可以直接通过`微信`扫描命令行中的二维码进行授权登陆和注册。
+部署时需要进行身份验证,如您的账号未 [登陆](https://cloud.tencent.com/login) 或 [注册](https://cloud.tencent.com/register) 腾讯云,您可以直接通过 `微信` 扫描命令行中的二维码进行授权登陆和注册。
 
-如果出现了 `internal server error` 的报错,请检查是否在创建模板后没有运行 `npm install`。
+> 注意: 如果希望查看更多部署过程的信息,可以通过`serverless deploy --debug` 命令查看部署过程中的实时日志信息。
 
-如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
+部署完成后,控制台会打印相关的输出信息,您可以通过 `${output:${stage}:${app}:apigw.url}` 的形式在其他 `serverless` 组件中引用该组件的 API 网关访问链接(或通过类似的形式引用该组建其他输出结果),具体的,可以查看完成的输出文档:
 
-
-
 快速开始:
 
 1. [**安装**](#1-安装)
@@ -49,35 +49,31 @@ $ npm install -g serverless
 通过如下命令和模板链接,快速创建一个 Express 应用:
 
 ```bash
-$ serverless create --template-url https://github.com/serverless-components/tencent-express/tree/v2/serverless-express
-$ cd serverless-express
-```
-
-执行如下命令,安装 Express 应用的对应依赖
-
-```
-$ npm install
+$ serverless init express-starter --name example
+$ cd example
 ```
 
 ### 3. 部署
 
-在 `serverless.yml` 文件下的目录中运行 `serverless deploy` 进行 Express 项目的部署。第一次部署可能耗时相对较久,但后续的二次部署会在几秒钟之内完成。部署完毕后,你可以在命令行的输出中查看到你 Express 应用的 URL 地址,点击地址即可访问你的 Express 项目。
+在 `serverless.yml` 文件所在的项目根目录,运行以下指令进行部署:
 
-**注意:**
+```bash
+$ serverless deploy
+```
 
-如您的账号未[登陆](https://cloud.tencent.com/login)或[注册](https://cloud.tencent.com/register)腾讯云,您可以直接通过`微信`扫描命令行中的二维码进行授权登陆和注册。
+部署时需要进行身份验证,如您的账号未 [登陆](https://cloud.tencent.com/login) 或 [注册](https://cloud.tencent.com/register) 腾讯云,您可以直接通过 `微信` 扫描命令行中的二维码进行授权登陆和注册。
 
-如果出现了 `internal server error` 的报错,请检查是否在创建模板后没有运行 `npm install`。
+> 注意: 如果希望查看更多部署过程的信息,可以通过`serverless deploy --debug` 命令查看部署过程中的实时日志信息。
 
-如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
+部署完成后,控制台会打印相关的输出信息,您可以通过 `${output:${stage}:${app}:apigw.url}` 的形式在其他 `serverless` 组件中引用该组件的 API 网关访问链接(或通过类似的形式引用该组建其他输出结果),具体的,可以查看完成的输出文档:
 
-
+- [点击此处查看输出文档](https://github.com/serverless-components/tencent-express/tree/master/docs/output.md)
 
 ### 4. 配置
 
 Express 组件支持 0 配置部署,也就是可以直接通过配置文件中的默认值进行部署。但你依然可以修改更多可选配置来进一步开发该 Express 项目。
 
-以下是 Express 组件的 `serverless.yml`完整配置说明:
+以下是 Express 组件的 `serverless.yml`配置示例:
 
 ```yml
 # serverless.yml
@@ -89,12 +85,13 @@ app: appDemo # (optional) serverless dashboard app. default is the same as the n
 stage: dev # (optional) serverless dashboard stage. default is dev.
 
 inputs:
-  src: ./ # (optional) path to the source folder. default is a hello world app.
+  src:
+    src: ./ # (optional) path to the source folder. default is a hello world app.
+    exclude:
+      - .env
   functionName: expressDemo
   region: ap-guangzhou
   runtime: Nodejs10.15
-  exclude:
-    - .env
   apigatewayConf:
     protocols:
       - http
@@ -102,7 +99,7 @@ inputs:
     environment: release
 ```
 
-点此查看[全量配置及配置说明](https://github.com/serverless-components/tencent-express/blob/v2/docs/configure.md)
+点此查看[全量配置及配置说明](https://github.com/serverless-components/tencent-express/tree/master/docs/configure.md)
 
 当你根据该配置文件更新配置字段后,再次运行 `serverless deploy` 或者 `serverless` 就可以更新配置到云端。
 
@@ -132,7 +129,7 @@ $ serverless info
 $ serverless remove
 ```
 
-和部署类似,支持通过 `sls remove --debug` 命令查看移除过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
+和部署类似,支持通过 `serverless remove --debug` 命令查看移除过程中的实时日志信息。
 
 ## 架构说明
 
@@ -164,6 +161,52 @@ TENCENT_SECRET_ID=123
 TENCENT_SECRET_KEY=123
 ```
 
+## 静态资源服务
+
+如果想要支持返回静态资源,比如图片之类的,需要在入口文件 `sls.js` 中指定相关 `MIME` 类型的文件为二进制,这样云函数在返回请求结果给 API 网关是,会对指定类型进行 `Base64` 编码,最终返回给客户端才能正常显示。如下:
+
+```js
+const express = require('express')
+const app = express()
+
+// Routes
+// ...
+
+app.binaryTypes = ['*/*']
+
+module.exports = app
+```
+
+`['*/*']` 代表所有文件类型将进行 `Base64` 编码,如果需要定制化,可以配置为 `['image/png']`,意思是指定 `png` 格式的图片类型。
+
+更多文件类型的 `MIME` 类型,可参考 [mime-db](https://github.com/jshttp/mime-db/blob/master/db.json)。
+
+### slsInitialize 应用初始化
+
+有些时候,Express 服务在启动前,需要进行一个初始化操作,比如数据库建连,就可以通过在 Express 实例对象上添加 `slsInitialize` 函数来实现,如下:
+
+```js
+const express = require('express')
+const mysql = require('mysql2/promise')
+
+const app = new express()
+
+// ...
+
+app.slsInitialize = async () => {
+  app.db = await mysql.createConnection({
+    host: 'localhost',
+    user: 'root',
+    database: 'test'
+  })
+}
+
+// don't forget to export!
+module.exports = app
+```
+
+这样应用部署到云函数后,在函数服务逻辑执行前,会先执行 `slsInitialize()` 函数,来初始化数据库连接。
+
 ## License
 
 MIT License
diff --git a/__tests__/index.test.js b/__tests__/index.test.js
new file mode 100644
index 0000000..2e67e22
--- /dev/null
+++ b/__tests__/index.test.js
@@ -0,0 +1,62 @@
+const { join } = require('path');
+require('dotenv').config({ path: join(__dirname, '.env.test') });
+
+const { generateId, getServerlessSdk } = require('./lib/utils')
+const execSync = require('child_process').execSync
+const path = require('path')
+const axios = require('axios')
+
+const instanceYaml = {
+  org: 'orgDemo',
+  app: 'appDemo',
+  component: 'express@dev',
+  name: `express-integration-tests-${generateId()}`,
+  stage: 'dev',
+  inputs: {
+    region: 'ap-guangzhou',
+    runtime: 'Nodejs10.15',
+    apigatewayConf: { environment: 'test' }
+  }
+}
+
+const credentials = {
+  tencent: {
+    SecretId: process.env.TENCENT_SECRET_ID,
+    SecretKey: process.env.TENCENT_SECRET_KEY,
+  }
+}
+
+const sdk = getServerlessSdk(instanceYaml.org)
+
+it('should successfully deploy express app', async () => {
+  const instance = await sdk.deploy(instanceYaml, credentials)
+
+  expect(instance).toBeDefined()
+  expect(instance.instanceName).toEqual(instanceYaml.name)
+  expect(instance.outputs.templateUrl).toBeDefined()
+  expect(instance.outputs.region).toEqual(instanceYaml.inputs.region)
+  expect(instance.outputs.apigw).toBeDefined()
+  expect(instance.outputs.apigw.environment).toEqual(instanceYaml.inputs.apigatewayConf.environment)
+  expect(instance.outputs.scf).toBeDefined()
+  expect(instance.outputs.scf.runtime).toEqual(instanceYaml.inputs.runtime)
+})
+
+it('should successfully update source code', async () => {
+  // change source to own source './src' and need to install packages before deploy
+  const srcPath = path.join(__dirname, '..', 'example')
+  execSync('npm install', { cwd: srcPath })
+  instanceYaml.inputs.src = srcPath
+
+  const instance = await sdk.deploy(instanceYaml, credentials)
+  const response = await axios.get(instance.outputs.apigw.url)
+
+  expect(response.data.includes('Serverless Framework')).toBeTruthy()
+  expect(instance.outputs.templateUrl).not.toBeDefined()
+})
+
+it('should successfully remove express app', async () => {
+  await sdk.remove(instanceYaml, credentials)
+  result = await sdk.getInstance(instanceYaml.org, instanceYaml.stage, instanceYaml.app, instanceYaml.name)
+
+  expect(result.instance.instanceStatus).toEqual('inactive')
+})
diff --git a/__tests__/lib/utils.js b/__tests__/lib/utils.js
new file mode 100644
index 0000000..d047afa
--- /dev/null
+++ b/__tests__/lib/utils.js
@@ -0,0 +1,24 @@
+const { ServerlessSDK } = require('@serverless/platform-client-china')
+
+/*
+ * Generate random id
+ */
+const generateId = () =>
+  Math.random()
+    .toString(36)
+    .substring(6)
+
+/*
+ * Initializes and returns an instance of the serverless sdk
+ * @param ${string} orgName - the serverless org name.
+ */
+const getServerlessSdk = (orgName) => {
+  const sdk = new ServerlessSDK({
+    context: {
+      orgName
+    }
+  })
+  return sdk
+}
+
+module.exports = { generateId, getServerlessSdk }
diff --git a/docs/configure.md b/docs/configure.md
index 28f4c0e..392f030 100644
--- a/docs/configure.md
+++ b/docs/configure.md
@@ -17,6 +17,7 @@ inputs:
   serviceName: mytest # api网关服务名称
   runtime: Nodejs10.15 # 运行环境
   serviceId: service-np1uloxw # api网关服务ID
+  entryFile: sls.js # 自定义 server 的入口文件名,默认为 sls.js,如果不想修改文件名为 sls.js 可以自定义
   src: ./src # 第一种为string时,会打包src对应目录下的代码上传到默认cos上。
   # src:  # 第二种,部署src下的文件代码,并打包成zip上传到bucket上
   #   src: ./src  # 本地需要打包的文件目录
@@ -30,9 +31,9 @@ inputs:
   layers:
     - name: layerName #  layer名称
       version: 1 #  版本
-  traffic: 0.9 # 配置默认流量中 $LATEST 版本比重:0 - 1
   functionConf: # 函数配置相关
     timeout: 10 # 超时时间,单位秒
+    eip: false # 是否固定出口IP
     memorySize: 128 # 内存大小,单位MB
     environment: #  环境变量
       variables: #  环境变量数组
@@ -46,6 +47,8 @@ inputs:
     customDomains: # 自定义域名绑定
       - domain: abc.com # 待绑定的自定义的域名
         certificateId: abcdefg # 待绑定自定义域名的证书唯一 ID
+        # 如要设置自定义路径映射,请设置为 false
+        isDefaultMapping: false
         # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。
         pathMappingSet:
           - path: /
@@ -58,6 +61,7 @@ inputs:
       - https
     environment: test
     serviceTimeout: 15
+    isBase64Encoded: false
     usagePlan: #  用户使用计划
       usagePlanId: 1111
       usagePlanName: slscmp
@@ -73,20 +77,20 @@ inputs:
 
 主要的参数
 
-| 参数名称                             | 是否必选 |     默认值      | 描述                                                                                                                                                                                                       |
-| ------------------------------------ | :------: | :-------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| runtime                              |    否    |   Nodejs10.15   | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16.16                                                                                                                                     |
-| region                               |    否    |  ap-guangzhou   | 项目部署所在区域,默认广州区                                                                                                                                                                               |
-| functionName                         |    否    |                 | 云函数名称                                                                                                                                                                                                 |
-| serviceName                          |    否    |                 | API 网关服务名称, 默认创建一个新的服务名称                                                                                                                                                                 |
-| serviceId                            |    否    |                 | API 网关服务 ID,如果存在将使用这个 API 网关服务                                                                                                                                                            |
-| src                                  |    否    | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录)                                                                                                                                             |
-| layers                               |    否    |                 | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置)                                                                                                                                                         |
-| traffic                              |    否    |        1        | 配置默认流量中 `$LATEST` 版本比重,取值范围:0 ~ 1,比如 80%,可配置成 0.8。注意如果皮遏制灰度流量,需要配置对应的 API 网关触发器的 endpoints 的 `function.functionQualifier` 参数为 `$DEFAULT` (默认流量) |
-| [functionConf](#函数配置)            |    否    |                 | 函数配置                                                                                                                                                                                                   |
-| [apigatewayConf](#API-网关配置)      |    否    |                 | API 网关配置                                                                                                                                                                                               |
-| [cloudDNSConf](#DNS-配置)            |    否    |                 | DNS 配置                                                                                                                                                                                                   |
-| [Region special config](#指定区配置) |    否    |                 | 指定区配置                                                                                                                                                                                                 |
+| 参数名称                             | 必选 |     默认值      | 描述                                                                |
+| ------------------------------------ | :--: | :-------------: | :------------------------------------------------------------------ |
+| runtime                              |  否  |  `Nodejs10.15`  | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 |
+| region                               |  否  | `ap-guangzhou`  | 项目部署所在区域,默认广州区                                        |
+| functionName                         |  否  |                 | 云函数名称                                                          |
+| serviceName                          |  否  |                 | API 网关服务名称, 默认创建一个新的服务名称                          |
+| serviceId                            |  否  |                 | API 网关服务 ID,如果存在将使用这个 API 网关服务                     |
+| entryFile                            |  否  |    `sls.js`     | 自定义 server 的入口文件名                                          |
+| src                                  |  否  | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录)      |
+| layers                               |  否  |                 | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置)                  |
+| [functionConf](#函数配置)            |  否  |                 | 函数配置                                                            |
+| [apigatewayConf](#API-网关配置)      |  否  |                 | API 网关配置                                                        |
+| [cloudDNSConf](#DNS-配置)            |  否  |                 | DNS 配置                                                            |
+| [Region special config](#指定区配置) |  否  |                 | 指定区配置                                                          |
 
 ## 执行目录
 
@@ -110,7 +114,7 @@ inputs:
 
 | 参数名称   | 是否必选 | 类型     | 默认值 | 描述                                            |
 | ---------- | :------: | -------- | :----: | :---------------------------------------------- |
-| ttl        |    否    | Number   |  600   | TTL 值,范围 1 - 604800,不同等级域名最小值不同 |
+| ttl        |    否    | Number   | `600`  | TTL 值,范围 1 - 604800,不同等级域名最小值不同 |
 | recordLine |    否    | String[] |        | 记录的线路名称                                  |
 
 ### 指定区配置
@@ -125,12 +129,13 @@ inputs:
 
 参考: https://cloud.tencent.com/document/product/583/18586
 
-| 参数名称    | 是否必选 |  类型  | 默认值 | 描述                                                                            |
-| ----------- | :------: | :----: | :----: | :------------------------------------------------------------------------------ |
-| timeout     |    否    | Number |   3    | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒                    |
-| memorySize  |    否    | Number |  128   | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 |
-| environment |    否    | Object |        | 函数的环境变量, 参考 [环境变量](#环境变量)                                      |
-| vpcConfig   |    否    | Object |        | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置)                                     |
+| 参数名称    | 是否必选 |  类型   | 默认值  | 描述                                                                            |
+| ----------- | :------: | :-----: | :-----: | :------------------------------------------------------------------------------ |
+| timeout     |    否    | Number  |   `3`   | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒                    |
+| memorySize  |    否    | Number  |  `128`  | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 |
+| environment |    否    | Object  |         | 函数的环境变量, 参考 [环境变量](#环境变量)                                      |
+| vpcConfig   |    否    | Object  |         | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置)                                     |
+| eip         |    否    | Boolean | `false` | 是否固定出口 IP                                                                 |
 
 ##### 环境变量
 
@@ -147,16 +152,17 @@ inputs:
 
 ### API 网关配置
 
-| 参数名称       | 是否必选 | 类型     | 默认值   | 描述                                                                               |
-| -------------- | :------: | :------- | :------- | :--------------------------------------------------------------------------------- |
-| protocols      |    否    | String[] | ['http'] | 前端请求的类型,如 http,https,http 与 https                                      |
-| environment    |    否    | String   | release  | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). |
-| usagePlan      |    否    |          |          | 使用计划配置, 参考 [使用计划](#使用计划)                                           |
-| auth           |    否    |          |          | API 密钥配置, 参考 [API 密钥](#API-密钥配置)                                       |
-| customDomain   |    否    | Object[] |          | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名)                                |
-| enableCORS     |    否    | Boolean  | `false`  | 开启跨域。默认值为否。                                                             |
-| serviceTimeout |    否    | Number   | `15`     | Api 超时时间,单位: 秒                                                             |
-| isDisabled     |    否    | Boolean  | `false`  | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。                   |
+| 参数名称        | 是否必选 | 类型     | 默认值     | 描述                                                                               |
+| --------------- | :------: | :------- | :--------- | :--------------------------------------------------------------------------------- |
+| protocols       |    否    | String[] | `['http']` | 前端请求的类型,如 http,https,http 与 https                                      |
+| environment     |    否    | String   | `release`  | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). |
+| usagePlan       |    否    |          |            | 使用计划配置, 参考 [使用计划](#使用计划)                                           |
+| auth            |    否    |          |            | API 密钥配置, 参考 [API 密钥](#API-密钥配置)                                       |
+| customDomain    |    否    | Object[] |            | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名)                                |
+| enableCORS      |    否    | Boolean  | `false`    | 开启跨域。默认值为否。                                                             |
+| serviceTimeout  |    否    | Number   | `15`       | Api 超时时间,单位: 秒                                                             |
+| isDisabled      |    否    | Boolean  | `false`    | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。                   |
+| isBase64Encoded |    否    | Boolean  | `false`    | 是否开启 Base64 编码,如果需要文件上传,请配置为 `true`                            |
 
 ##### 使用计划
 
@@ -167,7 +173,7 @@ inputs:
 | usagePlanId   |    否    | String | 用户自定义使用计划 ID                                   |
 | usagePlanName |    否    | String | 用户自定义的使用计划名称                                |
 | usagePlanDesc |    否    | String | 用户自定义的使用计划描述                                |
-| maxRequestNum |    否    | Int    | 请求配额总数,如果为空,将使用-1 作为默认值,表示不开启 |
+| maxRequestNum |    否    | Number | 请求配额总数,如果为空,将使用-1 作为默认值,表示不开启 |
 
 ##### API 密钥配置
 
@@ -182,13 +188,13 @@ inputs:
 
 Refer to: https://cloud.tencent.com/document/product/628/14906
 
-| 参数名称         | 是否必选 |   类型   |  默认值  | 描述                                                                                                                                                                                 |
-| ---------------- | :------: | :------: | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| domain           |    是    |  String  |          | 待绑定的自定义的域名。                                                                                                                                                               |
-| certificateId    |    否    |  String  |          | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选                                                                                                                    |
-| isDefaultMapping |    否    |  String  | `'TRUE'` | 是否使用默认路径映射,默认为 TRUE。为 FALSE 时,表示自定义路径映射,此时 pathMappingSet 必填。                                                                                       |
-| pathMappingSet   |    否    | Object[] |   `[]`   | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 |
-| protocol         |    否    | String[] |          | 绑定自定义域名的协议类型,默认与服务的前端协议一致。                                                                                                                                 |
+| 参数名称         | 是否必选 |   类型   | 默认值 | 描述                                                                                                                                                                                 |
+| ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| domain           |    是    |  String  |        | 待绑定的自定义的域名。                                                                                                                                                               |
+| certificateId    |    否    |  String  |        | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 `https`,则为必选                                                                                                                  |
+| isDefaultMapping |    否    |  String  | `true` | 是否使用默认路径映射。为 `false` 时,表示自定义路径映射,此时 pathMappingSet 必填。                                                                                                  |
+| pathMappingSet   |    否    | Object[] |  `[]`  | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 |
+| protocol         |    否    | String[] |        | 绑定自定义域名的协议类型,默认与服务的前端协议一致。                                                                                                                                 |
 
 - 自定义路径映射
 
diff --git a/docs/output.md b/docs/output.md
new file mode 100644
index 0000000..f9fdd99
--- /dev/null
+++ b/docs/output.md
@@ -0,0 +1,51 @@
+# 部署 `output` 参数介绍
+
+> 组件输出可以在别的组件中通过 `${output:${stage}:${app}:.}` 获取
+>
+> 例如,如果该组件名称是 `test_name`, ·且只部署于一个地域,则可以通过 `${output:${stage}:${app}:test_name.apigw.url}` 在别的组件中获取该组件的 API 网关的 `url`。
+
+| 名称        |                                       类型                                       | 描述                             |
+| :---------- | :------------------------------------------------------------------------------: | :------------------------------- |
+| templateUrl |                                      string                                      | 未提供代码时的模板代码 url       |
+| region      |                                      string                                      | 地域信息(只有一个地域时才提供) |
+| scf         | [`FunctionOutput | Record`](#云函数输出-`FunctionOutput`) | 云函数输出信息                   |
+| apigw       |    [`ApigwOutput | Record`](#API-网关输出-`ApigwOutput`)     | API 网关输出信息                 |
+
+## 云函数输出 `FunctionOutput`
+
+| 名称                 |      类型      | 描述                   |
+| :------------------- | :------------: | :--------------------- |
+| functionName         |     string     | 云函数名称             |
+| runtime              |     string     | 云运行环境             |
+| namespace            |     string     | 云函数名称空间         |
+| lastVersion          |     string     | 云函数版本             |
+| traffic              | `number (0~1)` | 将多少流量导向该云函数 |
+| configTrafficVersion |     string     |                        |
+
+## API 网关输出 `ApigwOutput`
+
+| 名称          |                                 类型                                 | 描述                       |
+| :------------ | :------------------------------------------------------------------: | :------------------------- |
+| serviceId     |                                string                                | API 网关 ID                |
+| subDomain     |                                string                                | API 网关子域名             |
+| enviroment    |                   `"release" | "prepub" | "test"`                    | API 网关                   |
+| url           |                                string                                | API 网关对外的完整 URL     |
+| traffic       |                             number (0~1)                             | 将多少流量导向该云函数     |
+| customDomains | [CustomDomain[]](#API-网关自定义域名输出-`ApigwOutput.CustomDomain`) | API 网关自定义域名输出列表 |
+
+## API 网关自定义域名输出 `ApigwOutput.CustomDomain`
+
+| 名称             |                                类型                                | 描述                       |
+| :--------------- | :----------------------------------------------------------------: | :------------------------- |
+| domain           |                               string                               | 自定义域名                 |
+| certificateId    |                               string                               | 域名证书 ID                |
+| isDefaultMapping |                              boolean                               | 该自定义域名是否为默认域名 |
+| pathMappingSet   | [PathMapping[]](#-API-网关域名映射规则-`CustomDomain.PathMapping`) | 该域名的路径映射规则列表   |
+| protocols        |                         `"http" | "https"`                         | 启用的协议                 |
+
+## API 网关域名映射规则 `CustomDomain.PathMapping`
+
+| 名称       |  类型  | 描述             |
+| :--------- | :----: | :--------------- |
+| path       | string | 路径             |
+| enviroment | string | 路径映射到的环境 |
diff --git a/docs/upload.md b/docs/upload.md
new file mode 100644
index 0000000..2e5e68d
--- /dev/null
+++ b/docs/upload.md
@@ -0,0 +1,31 @@
+## 文件上传说明
+
+项目中如果涉及到文件上传,需要依赖 API 网关提供的 [Base64 编码能力](https://cloud.tencent.com/document/product/628/51799),使用时只需要 `serverless.yml` 中配置 `isBase64Encoded` 为 `true`,如下:
+
+```yaml
+app: appDemo
+stage: dev
+component: express
+name: expressDemo
+
+inputs:
+  # 省略...
+  apigatewayConf:
+    isBase64Encoded: true
+    # 省略...
+  # 省略...
+```
+
+当前 API 网关支持上传最大文件大小为 `2M`,如果文件过大,请修改为前端直传对象存储方案。
+
+## Base64 示例
+
+此 Github 项目的 `example` 目录下存在模板文件:
+
+- [sls.upload.js](../example/sls.upload.js)
+
+开发者可根据个人项目需要参考修改,使用时需要复制文件名为 `sls.js`。
+
+文件中实现了文件上传接口 `POST /upload`,如果要支持文件上传,需要安装 `multer` 包。
+
+同时需要在 `serverless.yml` 的 `apigatewayConf` 中配置 `isBase64Encoded` 为 `true`。
diff --git a/example/README.md b/example/README.md
new file mode 100644
index 0000000..225e894
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,172 @@
+[](http://serverless.com)
+
+
+
+**腾讯云 Express 组件** ⎯⎯⎯ 通过使用 [Tencent Serverless Framework](https://github.com/serverless/components/tree/cloud),基于云上 Serverless 服务(如网关、云函数等),实现“0”配置,便捷开发,极速部署你的 Express 应用,Express 组件支持丰富的配置扩展,提供了目前最易用、低成本并且弹性伸缩的 Express 项目开发/托管能力。
+
+
+特性介绍:
+
+- [x] **按需付费** - 按照请求的使用量进行收费,没有请求时无需付费
+- [x] **"0"配置** - 只需要关心项目代码,之后部署即可,Serverless Framework 会搞定所有配置。
+- [x] **极速部署** - 仅需几秒,部署你的整个 Express 应用。
+- [x] **实时日志** - 通过实时日志的输出查看业务状态,便于直接在云端开发应用。
+- [x] **云端调试** - 针对 Node.js 框架支持一键云端调试能力,屏蔽本地环境的差异。
+- [x] **便捷协作** - 通过云端的状态信息和部署日志,方便的进行多人协作开发。
+- [x] **自定义域名** - 支持配置自定义域名及 HTTPS 访问
+
+
+
+ +
+快速开始:
+
+1. [**安装**](#1-安装)
+2. [**创建**](#2-创建)
+3. [**部署**](#3-部署)
+4. [**配置**](#4-配置)
+5. [**开发调试**](#5-开发调试)
+6. [**查看状态**](#6-查看状态)
+7. [**移除**](#7-移除)
+
+更多资源:
+
+- [**架构说明**](#架构说明)
+- [**账号配置**](#账号配置)
+
+ 
+
+### 1. 安装
+
+通过 npm 安装最新版本的 Serverless Framework
+
+```bash
+$ npm install -g serverless
+```
+
+### 2. 创建
+
+通过如下命令和模板链接,快速创建一个 Express 应用:
+
+```bash
+$ serverless create --template-url https://github.com/serverless-components/tencent-express/tree/master/example
+$ cd example
+```
+
+执行如下命令,安装 Express 应用的对应依赖
+
+```
+$ npm install
+```
+
+### 3. 部署
+
+在 `serverless.yml` 文件下的目录中运行 `serverless deploy` 进行 Express 项目的部署。第一次部署可能耗时相对较久,但后续的二次部署会在几秒钟之内完成。部署完毕后,你可以在命令行的输出中查看到你 Express 应用的 URL 地址,点击地址即可访问你的 Express 项目。
+
+**注意:**
+
+如您的账号未[登陆](https://cloud.tencent.com/login)或[注册](https://cloud.tencent.com/register)腾讯云,您可以直接通过`微信`扫描命令行中的二维码进行授权登陆和注册。
+
+如果出现了 `internal server error` 的报错,请检查是否在创建模板后没有运行 `npm install`。
+
+如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
+
+
+
+快速开始:
+
+1. [**安装**](#1-安装)
+2. [**创建**](#2-创建)
+3. [**部署**](#3-部署)
+4. [**配置**](#4-配置)
+5. [**开发调试**](#5-开发调试)
+6. [**查看状态**](#6-查看状态)
+7. [**移除**](#7-移除)
+
+更多资源:
+
+- [**架构说明**](#架构说明)
+- [**账号配置**](#账号配置)
+
+ 
+
+### 1. 安装
+
+通过 npm 安装最新版本的 Serverless Framework
+
+```bash
+$ npm install -g serverless
+```
+
+### 2. 创建
+
+通过如下命令和模板链接,快速创建一个 Express 应用:
+
+```bash
+$ serverless create --template-url https://github.com/serverless-components/tencent-express/tree/master/example
+$ cd example
+```
+
+执行如下命令,安装 Express 应用的对应依赖
+
+```
+$ npm install
+```
+
+### 3. 部署
+
+在 `serverless.yml` 文件下的目录中运行 `serverless deploy` 进行 Express 项目的部署。第一次部署可能耗时相对较久,但后续的二次部署会在几秒钟之内完成。部署完毕后,你可以在命令行的输出中查看到你 Express 应用的 URL 地址,点击地址即可访问你的 Express 项目。
+
+**注意:**
+
+如您的账号未[登陆](https://cloud.tencent.com/login)或[注册](https://cloud.tencent.com/register)腾讯云,您可以直接通过`微信`扫描命令行中的二维码进行授权登陆和注册。
+
+如果出现了 `internal server error` 的报错,请检查是否在创建模板后没有运行 `npm install`。
+
+如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
+
+
+
+### 4. 配置
+
+Express 组件支持 0 配置部署,也就是可以直接通过配置文件中的默认值进行部署。但你依然可以修改更多可选配置来进一步开发该 Express 项目。
+
+以下是 Express 组件的 `serverless.yml`完整配置说明:
+
+```yml
+# serverless.yml
+
+component: express # (required) name of the component. In that case, it's express.
+name: expressDemo # (required) name of your express component instance.
+org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
+app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
+stage: dev # (optional) serverless dashboard stage. default is dev.
+
+inputs:
+  src:
+    src: ./ # (optional) path to the source folder. default is a hello world app.
+    exclude:
+      - .env
+  functionName: expressDemo
+  region: ap-guangzhou
+  runtime: Nodejs10.15
+  apigatewayConf:
+    protocols:
+      - http
+      - https
+    environment: release
+```
+
+点此查看[全量配置及配置说明](https://github.com/serverless-components/tencent-express/tree/master/docs/configure.md)
+
+当你根据该配置文件更新配置字段后,再次运行 `serverless deploy` 或者 `serverless` 就可以更新配置到云端。
+
+### 5. 开发调试
+
+部署了 Express.js 应用后,可以通过开发调试能力对该项目进行二次开发,从而开发一个生产应用。在本地修改和更新代码后,不需要每次都运行 `serverless deploy` 命令来反复部署。你可以直接通过 `serverless dev` 命令对本地代码的改动进行检测和自动上传。
+
+可以通过在 `serverless.yml`文件所在的目录下运行 `serverless dev` 命令开启开发调试能力。
+
+`serverless dev` 同时支持实时输出云端日志,每次部署完毕后,对项目进行访问,即可在命令行中实时输出调用日志,便于查看业务情况和排障。
+
+除了实时日志输出之外,针对 Node.js 应用,当前也支持云端调试能力。在开启 `serverless dev` 命令之后,将会自动监听远端端口,并将函数的超时时间临时配置为 900s。此时你可以通过访问 chrome://inspect/#devices 查找远端的调试路径,并直接对云端代码进行断点等调试。在调试模式结束后,需要再次部署从而将代码更新并将超时时间设置为原来的值。详情参考[开发模式和云端调试](https://cloud.tencent.com/document/product/1154/43220)。
+
+### 6. 查看状态
+
+在`serverless.yml`文件所在的目录下,通过如下命令查看部署状态:
+
+```
+$ serverless info
+```
+
+### 7. 移除
+
+在`serverless.yml`文件所在的目录下,通过以下命令移除部署的 Express 服务。移除后该组件会对应删除云上部署时所创建的所有相关资源。
+
+```
+$ serverless remove
+```
+
+和部署类似,支持通过 `sls remove --debug` 命令查看移除过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
+
+## 架构说明
+
+Express 组件将在腾讯云账户中使用到如下 Serverless 服务:
+
+- [x] **API 网关** - API 网关将会接收外部请求并且转发到 SCF 云函数中。
+- [x] **SCF 云函数** - 云函数将承载 Express.js 应用。
+- [x] **CAM 访问控制** - 该组件会创建默认 CAM 角色用于授权访问关联资源。
+- [x] **COS 对象存储** - 为确保上传速度和质量,云函数压缩并上传代码时,会默认将代码包存储在特定命名的 COS 桶中。
+- [x] **SSL 证书服务** - 如果你在 yaml 文件中配置了 `apigatewayConf.customDomains` 字段,需要做自定义域名绑定并开启 HTTPS 时,也会用到证书管理服务和域名服务。Serverless Framework 会根据已经备案的域名自动申请并配置 SSL 证书。
+
+## 账号配置
+
+当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件
+
+```console
+$ touch .env # 腾讯云的配置信息
+```
+
+在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存
+
+如果没有腾讯云账号,可以在此[注册新账号](https://cloud.tencent.com/register)。
+
+如果已有腾讯云账号,可以在[API 密钥管理](https://console.cloud.tencent.com/cam/capi)中获取 `SecretId` 和`SecretKey`.
+
+```
+# .env
+TENCENT_SECRET_ID=123
+TENCENT_SECRET_KEY=123
+```
+
+## License
+
+MIT License
+
+Copyright (c) 2020 Tencent Cloud, Inc.
diff --git a/example/serverless.yml b/example/serverless.yml
index 9eae45b..fe13841 100644
--- a/example/serverless.yml
+++ b/example/serverless.yml
@@ -1,18 +1,15 @@
-org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
-app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
-stage: dev # (optional) serverless dashboard stage. default is dev.
-component: express # (required) name of the component. In that case, it's express.
-name: expressDemo # (required) name of your express component instance.
+org: orgDemo
+app: appDemo
+stage: dev
+component: express
+name: expressDemo
 
 inputs:
   src:
-    src: ./ # (optional) path to the source folder. default is a hello world app.
+    src: ./
     exclude:
       - .env
-  region: ap-guangzhou
-  runtime: Nodejs10.15
   apigatewayConf:
     protocols:
       - http
       - https
-    environment: release
diff --git a/example/sls.js b/example/sls.js
index ce89135..b0d9090 100644
--- a/example/sls.js
+++ b/example/sls.js
@@ -1,6 +1,7 @@
 const express = require('express')
 const path = require('path')
 const app = express()
+const isServerless = process.env.SERVERLESS
 
 // Routes
 app.get(`/`, (req, res) => {
@@ -39,6 +40,10 @@ app.use(function(err, req, res, next) {
   res.status(500).send('Internal Serverless Error')
 })
 
-app.listen(8080)
-
-module.exports = app
+if (isServerless) {
+  module.exports = app
+} else {
+  app.listen(3000, () => {
+    console.log(`Server start on http://localhost:3000`)
+  })
+}
diff --git a/example/sls.upload.js b/example/sls.upload.js
new file mode 100644
index 0000000..14b8552
--- /dev/null
+++ b/example/sls.upload.js
@@ -0,0 +1,59 @@
+const multer = require('multer')
+const express = require('express')
+const path = require('path')
+
+const app = express()
+const isServerless = process.env.SERVERLESS
+const upload = multer({ dest: isServerless ? '/tmp/upload' : './upload' })
+
+// Routes
+app.post('/upload', upload.single('file'), (req, res) => {
+  res.send({
+    success: true,
+    data: req.file
+  })
+})
+
+app.get(`/`, (req, res) => {
+  res.sendFile(path.join(__dirname, 'index.html'))
+})
+
+app.get('/user', (req, res) => {
+  res.send([
+    {
+      title: 'serverless framework',
+      link: 'https://serverless.com/'
+    }
+  ])
+})
+
+app.get('/user/:id', (req, res) => {
+  const id = req.params.id
+  res.send({
+    id: id,
+    title: 'serverless framework',
+    link: 'https://serverless.com/'
+  })
+})
+
+app.get('/404', (req, res) => {
+  res.status(404).send('Not found')
+})
+
+app.get('/500', (req, res) => {
+  res.status(500).send('Server Error')
+})
+
+// Error handler
+app.use(function(err, req, res, next) {
+  console.error(err)
+  res.status(500).send('Internal Serverless Error')
+})
+
+if (isServerless) {
+  module.exports = app
+} else {
+  app.listen(3000, () => {
+    console.log(`Server start on http://localhost:3000`)
+  })
+}
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000..a70dd57
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,14 @@
+const { join } = require('path')
+require('dotenv').config({ path: join(__dirname, '.env.test') })
+
+const config = {
+  verbose: true,
+  silent: false,
+  testTimeout: 600000,
+  testEnvironment: 'node',
+  testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$',
+  testPathIgnorePatterns: ['/node_modules/', '/__tests__/lib/'],
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
+}
+
+module.exports = config
diff --git a/package.json b/package.json
index b06f08f..4b0d707 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,11 @@
 {
   "name": "@serverless/express",
-  "version": "0.0.17",
   "main": "src/serverless.js",
   "publishConfig": {
     "access": "public"
   },
   "scripts": {
-    "test": "npm run lint && npm run prettier",
+    "test": "jest",
     "commitlint": "commitlint -f HEAD@{15}",
     "lint": "eslint --ext .js,.ts,.tsx .",
     "lint:fix": "eslint --fix --ext .js,.ts,.tsx .",
@@ -18,9 +17,9 @@
   },
   "husky": {
     "hooks": {
-      "pre-commit": "lint-staged",
+      "pre-commit": "ygsec && lint-staged",
       "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
-      "pre-push": "npm run lint:fix && npm run prettier:fix"
+      "pre-push": "ygsec && npm run lint:fix && npm run prettier:fix"
     }
   },
   "lint-staged": {
@@ -44,15 +43,41 @@
     "@semantic-release/git": "^9.0.0",
     "@semantic-release/npm": "^7.0.4",
     "@semantic-release/release-notes-generator": "^9.0.1",
+    "@serverless/platform-client-china": "^1.0.19",
+    "@ygkit/secure": "0.0.3",
+    "axios": "^0.19.2",
     "babel-eslint": "^10.1.0",
     "dotenv": "^8.2.0",
     "eslint": "^6.8.0",
     "eslint-config-prettier": "^6.10.0",
     "eslint-plugin-import": "^2.20.1",
     "eslint-plugin-prettier": "^3.1.2",
-    "husky": "^4.2.3",
+    "husky": "^4.2.5",
+    "jest": "^25.0.1",
     "lint-staged": "^10.0.8",
     "prettier": "^1.19.1",
     "semantic-release": "^17.0.4"
-  }
+  },
+  "description": "Easily deploy serverless Express.js applications to Tencent Cloud with the Serverless Framework",
+  "directories": {
+    "doc": "docs",
+    "example": "example",
+    "test": "tests"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/serverless-components/tencent-express.git"
+  },
+  "keywords": [
+    "serverless-express",
+    "serverless",
+    "express",
+    "serverless-framework",
+    "serverless-components",
+    "tencent-cloud"
+  ],
+  "bugs": {
+    "url": "https://github.com/serverless-components/tencent-express/issues"
+  },
+  "homepage": "https://github.com/serverless-components/tencent-express#readme"
 }
diff --git a/release.config.js b/release.config.js
index 53f3398..98b3864 100644
--- a/release.config.js
+++ b/release.config.js
@@ -1,7 +1,6 @@
 module.exports = {
   verifyConditions: [
     '@semantic-release/changelog',
-    '@semantic-release/npm',
     '@semantic-release/git',
     '@semantic-release/github'
   ],
@@ -33,14 +32,6 @@ module.exports = {
         changelogFile: 'CHANGELOG.md'
       }
     ],
-    [
-      '@semantic-release/npm',
-      {
-        pkgRoot: '.',
-        npmPublish: false,
-        tarballDir: false
-      }
-    ],
     [
       '@semantic-release/git',
       {
diff --git a/serverless.component.yml b/serverless.component.yml
index c9a84d4..34269df 100644
--- a/serverless.component.yml
+++ b/serverless.component.yml
@@ -1,10 +1,11 @@
 name: express
-version: 0.0.17
+version: 0.3.0
 author: Tencent Cloud, Inc.
 org: Tencent Cloud, Inc.
-description: Deploys a serverless Express.js application onto Tencent SCF and Tencent APIGateway.
+description: Deploy a serverless Express.js application on Tencent SCF and API Gateway.
 keywords: tencent, serverless, express
-repo: https://github.com/serverless-components/tencent-express/tree/v2
-readme: https://github.com/serverless-components/tencent-express/tree/v2/README.md
+repo: https://github.com/serverless-components/tencent-express
+readme: https://github.com/serverless-components/tencent-express/tree/master/README.md
 license: MIT
 main: ./src
+webDeployable: true
diff --git a/src/_shims/handler.js b/src/_shims/handler.js
index f132ec2..5863add 100644
--- a/src/_shims/handler.js
+++ b/src/_shims/handler.js
@@ -4,15 +4,15 @@ const path = require('path')
 const { createServer, proxy } = require('tencent-serverless-http')
 
 let server
+let app
 
 exports.handler = async (event, context) => {
-  const userSls = path.join(__dirname, '..', 'sls.js')
-  let app
+  const userSls = path.join(__dirname, '..', process.env.SLS_ENTRY_FILE)
   if (fs.existsSync(userSls)) {
-    // load the user provided app
+    // eslint-disable-next-line
+    console.log(`Using user custom entry file ${process.env.SLS_ENTRY_FILE}`)
     app = require(userSls)
   } else {
-    // load the built-in default app
     app = require('./sls.js')
   }
 
@@ -20,6 +20,11 @@ exports.handler = async (event, context) => {
   app.request.__SLS_EVENT__ = event
   app.request.__SLS_CONTEXT__ = context
 
+  // provide sls intialize hooks
+  if (app.slsInitialize && typeof app.slsInitialize === 'function') {
+    await app.slsInitialize()
+  }
+
   // cache server, not create repeatly
   if (!server) {
     server = createServer(app, null, app.binaryTypes || [])
@@ -27,11 +32,6 @@ exports.handler = async (event, context) => {
 
   context.callbackWaitsForEmptyEventLoop = app.callbackWaitsForEmptyEventLoop === true
 
-  // provide sls intialize hooks
-  if (app.slsInitialize && typeof app.slsInitialize === 'function') {
-    await app.slsInitialize()
-  }
-
   const result = await proxy(server, event, context, 'PROMISE')
   return result.promise
 }
diff --git a/src/config.js b/src/config.js
index 51f414a..35901dd 100644
--- a/src/config.js
+++ b/src/config.js
@@ -3,13 +3,13 @@ const CONFIGS = {
     'https://serverless-templates-1300862921.cos.ap-beijing.myqcloud.com/express-demo.zip',
   compName: 'express',
   compFullname: 'Express.js',
+  defaultEntryFile: 'sls.js',
   handler: 'sl_handler.handler',
   runtime: 'Nodejs10.15',
-  exclude: ['.git/**', '.gitignore', '.DS_Store'],
   timeout: 3,
   memorySize: 128,
   namespace: 'default',
-  description: 'This is a function created by serverless component'
+  description: 'Created by Serverless Component'
 }
 
 module.exports = CONFIGS
diff --git a/src/package.json b/src/package.json
index f99537f..8722896 100644
--- a/src/package.json
+++ b/src/package.json
@@ -1,18 +1,7 @@
 {
-  "name": "@serverless/express",
-  "main": "./serverless.js",
-  "publishConfig": {
-    "access": "public"
-  },
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
-    "lint": "eslint . --fix --cache"
-  },
-  "author": "Tencent Cloud, Inc.",
-  "license": "MIT",
   "dependencies": {
     "download": "^8.0.0",
-    "tencent-component-toolkit": "^1.12.7",
+    "tencent-component-toolkit": "^1.20.10",
     "type": "^2.0.0"
   }
 }
diff --git a/src/serverless.js b/src/serverless.js
index 5b69ee9..f76e037 100644
--- a/src/serverless.js
+++ b/src/serverless.js
@@ -1,7 +1,7 @@
 const { Component } = require('@serverless/core')
-const { MultiApigw, Scf, Apigw, Cns, Cam, Metrics } = require('tencent-component-toolkit')
+const { Scf, Apigw, Cns, Cam, Metrics } = require('tencent-component-toolkit')
 const { TypeError } = require('tencent-component-toolkit/src/utils/error')
-const { uploadCodeToCos, getDefaultProtocol, deleteRecord, prepareInputs } = require('./utils')
+const { uploadCodeToCos, getDefaultProtocol, prepareInputs, deepClone } = require('./utils')
 const CONFIGS = require('./config')
 
 class ServerlessComponent extends Component {
@@ -39,134 +39,141 @@ class ServerlessComponent extends Component {
       }
     }
 
-    const uploadCodeHandler = []
     const outputs = {}
     const appId = this.getAppId()
 
-    for (let eveRegionIndex = 0; eveRegionIndex < regionList.length; eveRegionIndex++) {
-      const curRegion = regionList[eveRegionIndex]
-      const funcDeployer = async () => {
-        const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
-        const scf = new Scf(credentials, curRegion)
-        const tempInputs = {
-          ...inputs,
-          code
-        }
-        const scfOutput = await scf.deploy(tempInputs)
-        outputs[curRegion] = {
-          functionName: scfOutput.FunctionName,
-          runtime: scfOutput.Runtime,
-          namespace: scfOutput.Namespace
-        }
+    const funcDeployer = async (curRegion) => {
+      const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
+      const scf = new Scf(credentials, curRegion)
+      const tempInputs = {
+        ...inputs,
+        code
+      }
+      const scfOutput = await scf.deploy(deepClone(tempInputs))
+      outputs[curRegion] = {
+        functionName: scfOutput.FunctionName,
+        runtime: scfOutput.Runtime,
+        namespace: scfOutput.Namespace
+      }
 
-        this.state[curRegion] = {
-          ...(this.state[curRegion] ? this.state[curRegion] : {}),
-          ...outputs[curRegion]
-        }
+      this.state[curRegion] = {
+        ...(this.state[curRegion] ? this.state[curRegion] : {}),
+        ...outputs[curRegion]
+      }
 
-        // default version is $LATEST
-        outputs[curRegion].lastVersion = scfOutput.LastVersion
-          ? scfOutput.LastVersion
-          : this.state.lastVersion || '$LATEST'
+      // default version is $LATEST
+      outputs[curRegion].lastVersion = scfOutput.LastVersion
+        ? scfOutput.LastVersion
+        : this.state.lastVersion || '$LATEST'
 
-        // default traffic is 1.0, it can also be 0, so we should compare to undefined
-        outputs[curRegion].traffic = scfOutput.Traffic
+      // default traffic is 1.0, it can also be 0, so we should compare to undefined
+      outputs[curRegion].traffic =
+        scfOutput.Traffic !== undefined
           ? scfOutput.Traffic
           : this.state.traffic !== undefined
           ? this.state.traffic
           : 1
 
-        if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
-          outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
-          this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
-        }
-
-        this.state.lastVersion = outputs[curRegion].lastVersion
-        this.state.traffic = outputs[curRegion].traffic
+      if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
+        outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
+        this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
       }
-      uploadCodeHandler.push(funcDeployer())
+
+      this.state.lastVersion = outputs[curRegion].lastVersion
+      this.state.traffic = outputs[curRegion].traffic
+    }
+
+    for (let i = 0; i < regionList.length; i++) {
+      const curRegion = regionList[i]
+      await funcDeployer(curRegion)
     }
-    await Promise.all(uploadCodeHandler)
     this.save()
     return outputs
   }
 
+  // try to add dns record
+  async tryToAddDnsRecord(credentials, customDomains) {
+    try {
+      const cns = new Cns(credentials)
+      for (let i = 0; i < customDomains.length; i++) {
+        const item = customDomains[i]
+        if (item.domainPrefix) {
+          await cns.deploy({
+            domain: item.subDomain.replace(`${item.domainPrefix}.`, ''),
+            records: [
+              {
+                subDomain: item.domainPrefix,
+                recordType: 'CNAME',
+                recordLine: '默认',
+                value: item.cname,
+                ttl: 600,
+                mx: 10,
+                status: 'enable'
+              }
+            ]
+          })
+        }
+      }
+    } catch (e) {
+      console.log('METHOD_tryToAddDnsRecord', e.message)
+    }
+  }
+
   async deployApigateway(credentials, inputs, regionList) {
     if (inputs.isDisabled) {
       return {}
     }
-    const apigw = new MultiApigw(credentials, regionList)
-    const oldState = this.state[regionList[0]] || {}
-    inputs.oldState = {
-      apiList: oldState.apiList || [],
-      customDomains: oldState.customDomains || []
+
+    const getServiceId = (instance, region) => {
+      const regionState = instance.state[region]
+      return inputs.serviceId || (regionState && regionState.serviceId)
     }
-    const apigwOutputs = await apigw.deploy(inputs)
-    const outputs = {}
-    Object.keys(apigwOutputs).forEach((curRegion) => {
-      const curOutput = apigwOutputs[curRegion]
-      outputs[curRegion] = {
-        serviceId: curOutput.serviceId,
-        subDomain: curOutput.subDomain,
-        environment: curOutput.environment,
-        url: `${getDefaultProtocol(inputs.protocols)}://${curOutput.subDomain}/${
-          curOutput.environment
-        }/`
-      }
-      if (curOutput.customDomains) {
-        outputs[curRegion].customDomains = curOutput.customDomains
-      }
-      this.state[curRegion] = {
-        created: curOutput.created,
-        ...(this.state[curRegion] ? this.state[curRegion] : {}),
-        ...outputs[curRegion],
-        apiList: curOutput.apiList
-      }
-    })
-    this.save()
-    return outputs
-  }
 
-  async deployCns(credentials, inputs, regionList, apigwOutputs) {
-    const cns = new Cns(credentials)
-    const cnsRegion = {}
+    const deployTasks = []
+    const outputs = {}
     regionList.forEach((curRegion) => {
-      const curApigwOutput = apigwOutputs[curRegion]
-      cnsRegion[curRegion] = curApigwOutput.subDomain
-    })
+      const apigwDeployer = async () => {
+        const apigw = new Apigw(credentials, curRegion)
 
-    const state = []
-    const outputs = {}
-    const tempJson = {}
-    for (let i = 0; i < inputs.length; i++) {
-      const curCns = inputs[i]
-      for (let j = 0; j < curCns.records.length; j++) {
-        curCns.records[j].value =
-          cnsRegion[curCns.records[j].value.replace('temp_value_about_', '')]
-      }
-      const tencentCnsOutputs = await cns.deploy(curCns)
-      outputs[curCns.domain] = tencentCnsOutputs.DNS
-        ? tencentCnsOutputs.DNS
-        : 'The domain name has already been added.'
-      tencentCnsOutputs.domain = curCns.domain
-      state.push(tencentCnsOutputs)
-    }
+        const oldState = this.state[curRegion] || {}
+        const apigwInputs = {
+          ...inputs,
+          oldState: {
+            apiList: oldState.apiList || [],
+            customDomains: oldState.customDomains || []
+          }
+        }
+        // different region deployment has different service id
+        apigwInputs.serviceId = getServiceId(this, curRegion)
+        const apigwOutput = await apigw.deploy(deepClone(apigwInputs))
+        outputs[curRegion] = {
+          serviceId: apigwOutput.serviceId,
+          subDomain: apigwOutput.subDomain,
+          environment: apigwOutput.environment,
+          url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${
+            apigwOutput.environment
+          }${apigwInputs.endpoints[0].path}`
+        }
 
-    // 删除serverless创建的但是不在本次列表中
-    try {
-      for (let i = 0; i < state.length; i++) {
-        tempJson[state[i].domain] = state[i].records
-      }
-      const recordHistory = this.state.cns || []
-      for (let i = 0; i < recordHistory.length; i++) {
-        const delList = deleteRecord(tempJson[recordHistory[i].domain], recordHistory[i].records)
-        if (delList && delList.length > 0) {
-          await cns.remove({ deleteList: delList })
+        if (apigwOutput.customDomains) {
+          // TODO: need confirm add cns authentication
+          if (inputs.autoAddDnsRecord === true) {
+            // await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains)
+          }
+          outputs[curRegion].customDomains = apigwOutput.customDomains
+        }
+        this.state[curRegion] = {
+          created: true,
+          ...(this.state[curRegion] ? this.state[curRegion] : {}),
+          ...outputs[curRegion],
+          apiList: apigwOutput.apiList
         }
       }
-    } catch (e) {}
+      deployTasks.push(apigwDeployer())
+    })
+
+    await Promise.all(deployTasks)
 
-    this.state['cns'] = state
     this.save()
     return outputs
   }
@@ -177,7 +184,7 @@ class ServerlessComponent extends Component {
     const credentials = this.getCredentials()
 
     // 对Inputs内容进行标准化
-    const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs(
+    const { regionList, functionConf, apigatewayConf } = await prepareInputs(
       this,
       credentials,
       inputs
@@ -189,29 +196,33 @@ class ServerlessComponent extends Component {
       outputs.templateUrl = CONFIGS.templateUrl
     }
 
-    const deployTasks = [this.deployFunction(credentials, functionConf, regionList, outputs)]
+    let apigwOutputs
+    const functionOutputs = await this.deployFunction(
+      credentials,
+      functionConf,
+      regionList,
+      outputs
+    )
     // support apigatewayConf.isDisabled
     if (apigatewayConf.isDisabled !== true) {
-      deployTasks.push(this.deployApigateway(credentials, apigatewayConf, regionList, outputs))
+      apigwOutputs = await this.deployApigateway(credentials, apigatewayConf, regionList, outputs)
     } else {
       this.state.apigwDisabled = true
     }
-    const [functionOutputs, apigwOutputs = {}] = await Promise.all(deployTasks)
 
     // optimize outputs for one region
     if (regionList.length === 1) {
       const [oneRegion] = regionList
       outputs.region = oneRegion
-      outputs['apigw'] = apigwOutputs[oneRegion]
       outputs['scf'] = functionOutputs[oneRegion]
+      if (apigwOutputs) {
+        outputs['apigw'] = apigwOutputs[oneRegion]
+      }
     } else {
-      outputs['apigw'] = apigwOutputs
       outputs['scf'] = functionOutputs
-    }
-
-    // cns depends on apigw, so if disabled apigw, just ignore it.
-    if (cnsConf.length > 0 && apigatewayConf.isDisabled !== true) {
-      outputs['cns'] = await this.deployCns(credentials, cnsConf, regionList, apigwOutputs)
+      if (apigwOutputs) {
+        outputs['apigw'] = apigwOutputs
+      }
     }
 
     this.state.region = regionList[0]
@@ -236,10 +247,6 @@ class ServerlessComponent extends Component {
       const scf = new Scf(credentials, curRegion)
       const apigw = new Apigw(credentials, curRegion)
       const handler = async () => {
-        await scf.remove({
-          functionName: curState.functionName,
-          namespace: curState.namespace
-        })
         // if disable apigw, no need to remove
         if (state.apigwDisabled !== true) {
           await apigw.remove({
@@ -250,6 +257,10 @@ class ServerlessComponent extends Component {
             customDomains: curState.customDomains
           })
         }
+        await scf.remove({
+          functionName: curState.functionName,
+          namespace: curState.namespace
+        })
       }
       removeHandlers.push(handler())
     }
@@ -290,6 +301,12 @@ class ServerlessComponent extends Component {
         region,
         timezone: inputs.tz
       }
+
+      const curState = this.state[region]
+      if (curState.serviceId) {
+        options.apigwServiceId = curState.serviceId
+        options.apigwEnvironment = curState.environment || 'release'
+      }
       const credentials = this.getCredentials()
       const mertics = new Metrics(credentials, options)
       const metricResults = await mertics.getDatas(
diff --git a/src/utils.js b/src/utils.js
index 5403265..b8b6b6d 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,5 +1,5 @@
 const path = require('path')
-const { Domain, Cos } = require('tencent-component-toolkit')
+const { Cos } = require('tencent-component-toolkit')
 const ensureObject = require('type/object/ensure')
 const ensureIterable = require('type/iterable/ensure')
 const ensureString = require('type/string/ensure')
@@ -15,10 +15,45 @@ const generateId = () =>
     .toString(36)
     .substring(6)
 
+const deepClone = (obj) => {
+  return JSON.parse(JSON.stringify(obj))
+}
+
 const getType = (obj) => {
   return Object.prototype.toString.call(obj).slice(8, -1)
 }
 
+const mergeJson = (sourceJson, targetJson) => {
+  Object.entries(sourceJson).forEach(([key, val]) => {
+    targetJson[key] = deepClone(val)
+  })
+  return targetJson
+}
+
+const capitalString = (str) => {
+  if (str.length < 2) {
+    return str.toUpperCase()
+  }
+
+  return `${str[0].toUpperCase()}${str.slice(1)}`
+}
+
+const getDefaultProtocol = (protocols) => {
+  return String(protocols).includes('https') ? 'https' : 'http'
+}
+
+const getDefaultFunctionName = () => {
+  return `${CONFIGS.compName}_component_${generateId()}`
+}
+
+const getDefaultServiceName = () => {
+  return 'serverless'
+}
+
+const getDefaultServiceDescription = () => {
+  return 'Created by Serverless Component'
+}
+
 const validateTraffic = (num) => {
   if (getType(num) !== 'Number') {
     throw new TypeError(
@@ -107,12 +142,18 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) =>
         object: objectName,
         method: 'PUT'
       })
-      const slsSDKEntries = instance.getSDKEntries('_shims/handler.handler')
 
+      // if shims and sls sdk entries had been injected to zipPath, no need to injected again
       console.log(`Uploading code to bucket ${bucketName}`)
-      await instance.uploadSourceZipToCOS(zipPath, uploadUrl, slsSDKEntries, {
-        _shims: path.join(__dirname, '_shims')
-      })
+      if (instance.codeInjected === true) {
+        await instance.uploadSourceZipToCOS(zipPath, uploadUrl, {}, {})
+      } else {
+        const slsSDKEntries = instance.getSDKEntries('_shims/handler.handler')
+        await instance.uploadSourceZipToCOS(zipPath, uploadUrl, slsSDKEntries, {
+          _shims: path.join(__dirname, '_shims')
+        })
+        instance.codeInjected = true
+      }
       console.log(`Upload ${objectName} to bucket ${bucketName} success`)
     }
   }
@@ -127,72 +168,13 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) =>
   }
 }
 
-const mergeJson = (sourceJson, targetJson) => {
-  for (const eveKey in sourceJson) {
-    if (targetJson.hasOwnProperty(eveKey)) {
-      if (['protocols', 'endpoints', 'customDomain'].indexOf(eveKey) != -1) {
-        for (let i = 0; i < sourceJson[eveKey].length; i++) {
-          const sourceEvents = JSON.stringify(sourceJson[eveKey][i])
-          const targetEvents = JSON.stringify(targetJson[eveKey])
-          if (targetEvents.indexOf(sourceEvents) == -1) {
-            targetJson[eveKey].push(sourceJson[eveKey][i])
-          }
-        }
-      } else {
-        if (typeof sourceJson[eveKey] != 'string') {
-          mergeJson(sourceJson[eveKey], targetJson[eveKey])
-        } else {
-          targetJson[eveKey] = sourceJson[eveKey]
-        }
-      }
-    } else {
-      targetJson[eveKey] = sourceJson[eveKey]
-    }
-  }
-  return targetJson
-}
-
-const capitalString = (str) => {
-  if (str.length < 2) {
-    return str.toUpperCase()
-  }
-
-  return `${str[0].toUpperCase()}${str.slice(1)}`
-}
-
-const getDefaultProtocol = (protocols) => {
-  if (protocols.map((i) => i.toLowerCase()).includes('https')) {
-    return 'https'
-  }
-  return 'http'
-}
-
-const deleteRecord = (newRecords, historyRcords) => {
-  const deleteList = []
-  for (let i = 0; i < historyRcords.length; i++) {
-    let temp = false
-    for (let j = 0; j < newRecords.length; j++) {
-      if (
-        newRecords[j].domain == historyRcords[i].domain &&
-        newRecords[j].subDomain == historyRcords[i].subDomain &&
-        newRecords[j].recordType == historyRcords[i].recordType &&
-        newRecords[j].value == historyRcords[i].value &&
-        newRecords[j].recordLine == historyRcords[i].recordLine
-      ) {
-        temp = true
-        break
-      }
-    }
-    if (!temp) {
-      deleteList.push(historyRcords[i])
-    }
-  }
-  return deleteList
-}
-
 const prepareInputs = async (instance, credentials, inputs = {}) => {
   // 对function inputs进行标准化
-  const tempFunctionConf = inputs.functionConf ? inputs.functionConf : {}
+  const tempFunctionConf = inputs.functionConf
+    ? inputs.functionConf
+    : inputs.functionConfig
+    ? inputs.functionConfig
+    : {}
   const fromClientRemark = `tencent-${CONFIGS.compName}`
   const regionList = inputs.region
     ? typeof inputs.region == 'string'
@@ -203,10 +185,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => {
   // chenck state function name
   const stateFunctionName =
     instance.state[regionList[0]] && instance.state[regionList[0]].functionName
-  // check state service id
-  const stateServiceId = instance.state[regionList[0]] && instance.state[regionList[0]].serviceId
-
-  const functionConf = {
+  const functionConf = Object.assign(tempFunctionConf, {
     code: {
       src: inputs.src,
       bucket: inputs.srcOriginal && inputs.srcOriginal.bucket,
@@ -215,7 +194,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => {
     name:
       ensureString(inputs.functionName, { isOptional: true }) ||
       stateFunctionName ||
-      `${CONFIGS.compName}_component_${generateId()}`,
+      getDefaultFunctionName(),
     region: regionList,
     role: ensureString(tempFunctionConf.role ? tempFunctionConf.role : inputs.role, {
       default: ''
@@ -240,10 +219,18 @@ const prepareInputs = async (instance, credentials, inputs = {}) => {
     layers: ensureIterable(tempFunctionConf.layers ? tempFunctionConf.layers : inputs.layers, {
       default: []
     }),
+    cfs: ensureIterable(tempFunctionConf.cfs ? tempFunctionConf.cfs : inputs.cfs, {
+      default: []
+    }),
     publish: inputs.publish,
     traffic: inputs.traffic,
-    lastVersion: instance.state.lastVersion
-  }
+    lastVersion: instance.state.lastVersion,
+    timeout: tempFunctionConf.timeout ? tempFunctionConf.timeout : CONFIGS.timeout,
+    memorySize: tempFunctionConf.memorySize ? tempFunctionConf.memorySize : CONFIGS.memorySize,
+    tags: ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, {
+      default: null
+    })
+  })
 
   // validate traffic
   if (inputs.traffic !== undefined) {
@@ -251,82 +238,78 @@ const prepareInputs = async (instance, credentials, inputs = {}) => {
   }
   functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion
 
-  functionConf.tags = ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, {
-    default: null
-  })
-
-  functionConf.include = ensureIterable(
-    tempFunctionConf.include ? tempFunctionConf.include : inputs.include,
-    { default: [], ensureItem: ensureString }
-  )
-  functionConf.exclude = ensureIterable(
-    tempFunctionConf.exclude ? tempFunctionConf.exclude : inputs.exclude,
-    { default: [], ensureItem: ensureString }
-  )
-  functionConf.exclude.push('.git/**', '.gitignore', '.serverless', '.DS_Store')
-  if (inputs.functionConf) {
-    functionConf.timeout = inputs.functionConf.timeout
-      ? inputs.functionConf.timeout
-      : CONFIGS.timeout
-    functionConf.memorySize = inputs.functionConf.memorySize
-      ? inputs.functionConf.memorySize
-      : CONFIGS.memorySize
-    if (inputs.functionConf.environment) {
-      functionConf.environment = inputs.functionConf.environment
-    }
-    if (inputs.functionConf.vpcConfig) {
-      functionConf.vpcConfig = inputs.functionConf.vpcConfig
+  if (tempFunctionConf.environment) {
+    functionConf.environment = tempFunctionConf.environment
+    functionConf.environment.variables = functionConf.environment.variables || {}
+    functionConf.environment.variables.SERVERLESS = '1'
+    functionConf.environment.variables.SLS_ENTRY_FILE = inputs.entryFile || CONFIGS.defaultEntryFile
+  } else {
+    functionConf.environment = {
+      variables: {
+        SERVERLESS: '1',
+        SLS_ENTRY_FILE: inputs.entryFile || CONFIGS.defaultEntryFile
+      }
     }
   }
 
+  if (tempFunctionConf.vpcConfig) {
+    functionConf.vpcConfig = tempFunctionConf.vpcConfig
+  }
+
   // 对apigw inputs进行标准化
-  const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {}
-  apigatewayConf.isDisabled = inputs.apigatewayConf === true
-  apigatewayConf.fromClientRemark = fromClientRemark
-  apigatewayConf.serviceName = inputs.serviceName
-  apigatewayConf.description = `Serverless Framework Tencent-${capitalString(
-    CONFIGS.compName
-  )} Component`
-  apigatewayConf.serviceId = inputs.serviceId || stateServiceId
-  apigatewayConf.region = functionConf.region
-  apigatewayConf.protocols = apigatewayConf.protocols || ['http']
-  apigatewayConf.environment = apigatewayConf.environment ? apigatewayConf.environment : 'release'
-  apigatewayConf.endpoints = [
-    {
-      path: '/',
-      enableCORS: apigatewayConf.enableCORS,
-      serviceTimeout: apigatewayConf.serviceTimeout,
-      method: 'ANY',
-      function: {
-        isIntegratedResponse: apigatewayConf.isIntegratedResponse === false ? false : true,
-        functionName: functionConf.name,
-        functionNamespace: functionConf.namespace,
-        functionQualifier: functionConf.needSetTraffic ? '$DEFAULT' : '$LATEST'
+  const tempApigwConf = inputs.apigatewayConf
+    ? inputs.apigatewayConf
+    : inputs.apigwConfig
+    ? inputs.apigwConfig
+    : {}
+  const apigatewayConf = Object.assign(tempApigwConf, {
+    serviceId: inputs.serviceId || tempApigwConf.serviceId,
+    region: regionList,
+    isDisabled: tempApigwConf.isDisabled === true,
+    fromClientRemark: fromClientRemark,
+    serviceName: inputs.serviceName || tempApigwConf.serviceName || getDefaultServiceName(instance),
+    serviceDesc: tempApigwConf.serviceDesc || getDefaultServiceDescription(instance),
+    protocols: tempApigwConf.protocols || ['http'],
+    environment: tempApigwConf.environment ? tempApigwConf.environment : 'release',
+    customDomains: tempApigwConf.customDomains || []
+  })
+  if (!apigatewayConf.endpoints) {
+    apigatewayConf.endpoints = [
+      {
+        path: tempApigwConf.path || '/',
+        enableCORS: tempApigwConf.enableCORS,
+        serviceTimeout: tempApigwConf.serviceTimeout,
+        method: 'ANY',
+        apiName: tempApigwConf.apiName || 'index',
+        isBase64Encoded: tempApigwConf.isBase64Encoded,
+        isBase64Trigger: tempApigwConf.isBase64Trigger,
+        base64EncodedTriggerRules: tempApigwConf.base64EncodedTriggerRules,
+        function: {
+          isIntegratedResponse: true,
+          functionName: functionConf.name,
+          functionNamespace: functionConf.namespace,
+          functionQualifier:
+            (tempApigwConf.function && tempApigwConf.function.functionQualifier) || '$LATEST'
+        }
       }
-    }
-  ]
-  if (apigatewayConf.usagePlan) {
+    ]
+  }
+  if (tempApigwConf.usagePlan) {
     apigatewayConf.endpoints[0].usagePlan = {
-      usagePlanId: apigatewayConf.usagePlan.usagePlanId,
-      usagePlanName: apigatewayConf.usagePlan.usagePlanName,
-      usagePlanDesc: apigatewayConf.usagePlan.usagePlanDesc,
-      maxRequestNum: apigatewayConf.usagePlan.maxRequestNum
+      usagePlanId: tempApigwConf.usagePlan.usagePlanId,
+      usagePlanName: tempApigwConf.usagePlan.usagePlanName,
+      usagePlanDesc: tempApigwConf.usagePlan.usagePlanDesc,
+      maxRequestNum: tempApigwConf.usagePlan.maxRequestNum
     }
   }
-  if (apigatewayConf.auth) {
+  if (tempApigwConf.auth) {
     apigatewayConf.endpoints[0].auth = {
-      secretName: apigatewayConf.auth.secretName,
-      secretIds: apigatewayConf.auth.secretIds
+      secretName: tempApigwConf.auth.secretName,
+      secretIds: tempApigwConf.auth.secretIds
     }
   }
 
-  // 对cns inputs进行标准化
-  const tempCnsConf = {}
-  const tempCnsBaseConf = inputs.cloudDNSConf ? inputs.cloudDNSConf : {}
-
-  // 分地域处理functionConf/apigatewayConf/cnsConf
-  for (let i = 0; i < functionConf.region.length; i++) {
-    const curRegion = functionConf.region[i]
+  regionList.forEach((curRegion) => {
     const curRegionConf = inputs[curRegion]
     if (curRegionConf && curRegionConf.functionConf) {
       functionConf[curRegion] = curRegionConf.functionConf
@@ -334,62 +317,21 @@ const prepareInputs = async (instance, credentials, inputs = {}) => {
     if (curRegionConf && curRegionConf.apigatewayConf) {
       apigatewayConf[curRegion] = curRegionConf.apigatewayConf
     }
-
-    const tempRegionCnsConf = mergeJson(
-      tempCnsBaseConf,
-      curRegionConf && curRegionConf.cloudDNSConf ? curRegionConf.cloudDNSConf : {}
-    )
-
-    tempCnsConf[functionConf.region[i]] = {
-      recordType: 'CNAME',
-      recordLine: tempRegionCnsConf.recordLine ? tempRegionCnsConf.recordLine : undefined,
-      ttl: tempRegionCnsConf.ttl,
-      mx: tempRegionCnsConf.mx,
-      status: tempRegionCnsConf.status ? tempRegionCnsConf.status : 'enable'
-    }
-  }
-
-  const cnsConf = []
-  // 对cns inputs进行检查和赋值
-  if (apigatewayConf.customDomain && apigatewayConf.customDomain.length > 0) {
-    const domain = new Domain(credentials)
-    for (let domianNum = 0; domianNum < apigatewayConf.customDomain.length; domianNum++) {
-      const domainData = await domain.check(apigatewayConf.customDomain[domianNum].domain)
-      const tempInputs = {
-        domain: domainData.domain,
-        records: []
-      }
-      for (let eveRecordNum = 0; eveRecordNum < functionConf.region.length; eveRecordNum++) {
-        if (tempCnsConf[functionConf.region[eveRecordNum]].recordLine) {
-          tempInputs.records.push({
-            subDomain: domainData.subDomain || '@',
-            recordType: 'CNAME',
-            recordLine: tempCnsConf[functionConf.region[eveRecordNum]].recordLine,
-            value: `temp_value_about_${functionConf.region[eveRecordNum]}`,
-            ttl: tempCnsConf[functionConf.region[eveRecordNum]].ttl,
-            mx: tempCnsConf[functionConf.region[eveRecordNum]].mx,
-            status: tempCnsConf[functionConf.region[eveRecordNum]].status || 'enable'
-          })
-        }
-      }
-      cnsConf.push(tempInputs)
-    }
-  }
+  })
 
   return {
     regionList,
     functionConf,
-    apigatewayConf,
-    cnsConf
+    apigatewayConf
   }
 }
 
 module.exports = {
+  deepClone,
   generateId,
   uploadCodeToCos,
   mergeJson,
   capitalString,
   getDefaultProtocol,
-  deleteRecord,
   prepareInputs
 }