├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
└── workflows
│ ├── release.yml
│ ├── test.yml
│ └── validate.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .prettierignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __tests__
├── index.test.js
└── lib
│ └── utils.js
├── commitlint.config.js
├── docs
├── configure.md
├── output.md
└── upload.md
├── example
├── README.md
├── index.html
├── package.json
├── serverless.yml
├── sls.js
└── sls.upload.js
├── jest.config.js
├── package.json
├── prettier.config.js
├── release.config.js
├── serverless.component.yml
└── src
├── _shims
├── handler.js
├── package.json
└── sls.js
├── config.js
├── package.json
├── serverless.js
└── utils.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | charset = utf-8
8 | end_of_line = lf
9 | insert_final_newline = true
10 | indent_size = 2
11 | indent_style = space
12 | trim_trailing_whitespace = true
13 |
14 | [*.md]
15 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
4 | example
5 | *.test.js
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ['prettier'],
4 | plugins: ['import', 'prettier'],
5 | env: {
6 | es6: true,
7 | jest: true,
8 | node: true
9 | },
10 | parser: 'babel-eslint',
11 | parserOptions: {
12 | ecmaVersion: 2018,
13 | sourceType: 'module',
14 | ecmaFeatures: {
15 | jsx: true
16 | }
17 | },
18 | globals: {
19 | on: true // for the Socket file
20 | },
21 | rules: {
22 | 'array-bracket-spacing': [
23 | 'error',
24 | 'never',
25 | {
26 | objectsInArrays: false,
27 | arraysInArrays: false
28 | }
29 | ],
30 | 'arrow-parens': ['error', 'always'],
31 | 'arrow-spacing': ['error', { before: true, after: true }],
32 | 'comma-dangle': ['error', 'never'],
33 | curly: 'error',
34 | 'eol-last': 'error',
35 | 'func-names': 'off',
36 | 'id-length': [
37 | 'error',
38 | {
39 | min: 1,
40 | max: 50,
41 | properties: 'never',
42 | exceptions: ['e', 'i', 'n', 't', 'x', 'y', 'z', '_', '$']
43 | }
44 | ],
45 | 'no-alert': 'error',
46 | 'no-console': 'off',
47 | 'no-const-assign': 'error',
48 | 'no-else-return': 'error',
49 | 'no-empty': 'off',
50 | 'no-shadow': 'error',
51 | 'no-undef': 'error',
52 | 'no-unused-vars': 'error',
53 | 'no-use-before-define': 'error',
54 | 'no-useless-constructor': 'error',
55 | 'object-curly-newline': 'off',
56 | 'object-shorthand': 'off',
57 | 'prefer-const': 'error',
58 | 'prefer-destructuring': ['error', { object: true, array: false }],
59 | quotes: [
60 | 'error',
61 | 'single',
62 | {
63 | allowTemplateLiterals: true,
64 | avoidEscape: true
65 | }
66 | ],
67 | semi: ['error', 'never'],
68 | 'spaced-comment': 'error',
69 | strict: ['error', 'global'],
70 | 'prettier/prettier': 'error'
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches: [master]
6 |
7 | jobs:
8 | release:
9 | name: Release
10 | runs-on: ubuntu-latest
11 | env:
12 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
13 | steps:
14 | - name: Checkout repository
15 | uses: actions/checkout@v2
16 | with:
17 | persist-credentials: false
18 |
19 | - name: Install Node.js and npm
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: 14.x
23 | registry-url: https://registry.npmjs.org
24 |
25 | - name: Retrieve dependencies from cache
26 | id: cacheNpm
27 | uses: actions/cache@v2
28 | with:
29 | path: |
30 | ~/.npm
31 | node_modules
32 | key: npm-v14-${{ runner.os }}-refs/heads/master-${{ hashFiles('package.json') }}
33 | restore-keys: npm-v14-${{ runner.os }}-refs/heads/master-
34 |
35 | - name: Install dependencies
36 | if: steps.cacheNpm.outputs.cache-hit != 'true'
37 | run: |
38 | npm update --no-save
39 | npm update --save-dev --no-save
40 | - name: Releasing
41 | run: |
42 | npm run release
43 | env:
44 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
45 | GIT_AUTHOR_NAME: slsplus
46 | GIT_AUTHOR_EMAIL: slsplus.sz@gmail.com
47 | GIT_COMMITTER_NAME: slsplus
48 | GIT_COMMITTER_EMAIL: slsplus.sz@gmail.com
49 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | pull_request:
5 | branches: [master]
6 |
7 | jobs:
8 | test:
9 | name: Test
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout repository
13 | uses: actions/checkout@v2
14 | with:
15 | # Ensure connection with 'master' branch
16 | fetch-depth: 2
17 |
18 | - name: Install Node.js and npm
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: 14.x
22 | registry-url: https://registry.npmjs.org
23 |
24 | - name: Retrieve dependencies from cache
25 | id: cacheNpm
26 | uses: actions/cache@v2
27 | with:
28 | path: |
29 | ~/.npm
30 | node_modules
31 | key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
32 | restore-keys: |
33 | npm-v14-${{ runner.os }}-${{ github.ref }}-
34 | npm-v14-${{ runner.os }}-refs/heads/master-
35 |
36 | - name: Install dependencies
37 | if: steps.cacheNpm.outputs.cache-hit != 'true'
38 | run: |
39 | npm update --no-save
40 | npm update --save-dev --no-save
41 | - name: Running tests
42 | run: npm run test
43 | env:
44 | SERVERLESS_PLATFORM_VENDOR: tencent
45 | GLOBAL_ACCELERATOR_NA: true
46 | TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
47 | TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
48 |
--------------------------------------------------------------------------------
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | name: Validate
2 |
3 | on:
4 | pull_request:
5 | branches: [master]
6 |
7 | jobs:
8 | lintAndFormatting:
9 | name: Lint & Formatting
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout repository
13 | uses: actions/checkout@v2
14 | with:
15 | # Ensure connection with 'master' branch
16 | fetch-depth: 2
17 |
18 | - name: Install Node.js and npm
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: 14.x
22 | registry-url: https://registry.npmjs.org
23 |
24 | - name: Retrieve dependencies from cache
25 | id: cacheNpm
26 | uses: actions/cache@v2
27 | with:
28 | path: |
29 | ~/.npm
30 | node_modules
31 | key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
32 | restore-keys: |
33 | npm-v14-${{ runner.os }}-${{ github.ref }}-
34 | npm-v14-${{ runner.os }}-refs/heads/master-
35 |
36 | - name: Install dependencies
37 | if: steps.cacheNpm.outputs.cache-hit != 'true'
38 | run: |
39 | npm update --no-save
40 | npm update --save-dev --no-save
41 |
42 | - name: Validate Formatting
43 | run: npm run prettier:fix
44 | - name: Validate Lint rules
45 | run: npm run lint:fix
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.sublime-project
3 | *.sublime-workspace
4 | *.log
5 | .serverless
6 | v8-compile-cache-*
7 | jest/*
8 | coverage
9 | .serverless_plugins
10 | testProjects/*/package-lock.json
11 | testProjects/*/yarn.lock
12 | .serverlessUnzipped
13 | node_modules
14 | .vscode/
15 | .eslintcache
16 | dist
17 | .idea
18 | build/
19 | .env*
20 | env.js
21 | package-lock.json
22 | test
23 | yarn.lock
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test
2 | example
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
4 | CHANGELOG.md
5 | *.test.js
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [0.3.0](https://github.com/serverless-components/tencent-express/compare/v0.2.3...v0.3.0) (2021-01-26)
2 |
3 |
4 | ### Features
5 |
6 | * support apigw base64 encode ([78ca266](https://github.com/serverless-components/tencent-express/commit/78ca26613eec65558852ff1b6a130a841b0178f3))
7 |
8 | ## [0.2.3](https://github.com/serverless-components/tencent-express/compare/v0.2.2...v0.2.3) (2020-12-28)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * add sls initialize before creating server ([7f18f2c](https://github.com/serverless-components/tencent-express/commit/7f18f2c5e8e86cc0a9252e5bd036742251964e4d))
14 |
15 | ## [0.2.2](https://github.com/serverless-components/tencent-express/compare/v0.2.1...v0.2.2) (2020-12-15)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * update remove flow ([f766ee6](https://github.com/serverless-components/tencent-express/commit/f766ee69cf39cf410df0203fc71644297ef70693))
21 |
22 | ## [0.2.1](https://github.com/serverless-components/tencent-express/compare/v0.2.0...v0.2.1) (2020-12-15)
23 |
24 |
25 | ### Bug Fixes
26 |
27 | * update to deployment to serial flow ([a76fe58](https://github.com/serverless-components/tencent-express/commit/a76fe586846269132f3a9b9141302050b02a13c3))
28 |
29 | # [0.2.0](https://github.com/serverless-components/tencent-express/compare/v0.1.5...v0.2.0) (2020-10-12)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * support all parameters for apigw ([ec3ffc7](https://github.com/serverless-components/tencent-express/commit/ec3ffc7fb6a6959dbdb734b8a2137f5ac777d05c))
35 |
36 |
37 | ### Features
38 |
39 | * support custom entry file and all scf config ([80e2890](https://github.com/serverless-components/tencent-express/commit/80e28903422c5710a56a8267f64ffe47398ddcf8))
40 |
41 | ## [0.1.5](https://github.com/serverless-components/tencent-express/compare/v0.1.4...v0.1.5) (2020-09-03)
42 |
43 |
44 | ### Bug Fixes
45 |
46 | * update deploy flow for multi region ([33ae212](https://github.com/serverless-components/tencent-express/commit/33ae21248a7c191e3ceea2371fd2e69a6b1ebf67))
47 |
48 | ## [0.1.4](https://github.com/serverless-components/tencent-express/compare/v0.1.3...v0.1.4) (2020-09-02)
49 |
50 |
51 | ### Bug Fixes
52 |
53 | * update tencnet-component-toolkit for api mark ([7e25e8a](https://github.com/serverless-components/tencent-express/commit/7e25e8a86f539b2db449caf969cbbd58c5af4d1b))
54 |
55 | ## [0.1.3](https://github.com/serverless-components/tencent-express/compare/v0.1.2...v0.1.3) (2020-09-01)
56 |
57 |
58 | ### Bug Fixes
59 |
60 | * support cfs ([9d1a1e1](https://github.com/serverless-components/tencent-express/commit/9d1a1e1793d866f5fbe5bdce61090db8381bb103))
61 | * update deps for error message ([f0a8f89](https://github.com/serverless-components/tencent-express/commit/f0a8f892f469efcda67070ff940421aa83a100c0))
62 |
63 | ## [0.1.2](https://github.com/serverless-components/tencent-express/compare/v0.1.1...v0.1.2) (2020-08-26)
64 |
65 |
66 | ### Bug Fixes
67 |
68 | * add apigw/scf deployed state saving manually ([0fcc0b8](https://github.com/serverless-components/tencent-express/commit/0fcc0b84bdeb7119086cdbb050ae2c8ab734ff32))
69 | * add regionList state ([f66d8d8](https://github.com/serverless-components/tencent-express/commit/f66d8d8ee6564e343442bb59b9996bf23fd475b9))
70 | * add release config ([26c7e12](https://github.com/serverless-components/tencent-express/commit/26c7e1201caf79830843e860237f3ebf6efdf1e7))
71 | * apigw custom domain update bug ([1a53027](https://github.com/serverless-components/tencent-express/commit/1a53027effde871dc9f021f510fd7a13cc86794a))
72 | * apigw isDisabled ([f624171](https://github.com/serverless-components/tencent-express/commit/f624171b927f0efd4605566c6168d67a3f9b8302))
73 | * bind unexist role bug ([6cd8990](https://github.com/serverless-components/tencent-express/commit/6cd89900111c7afd1e073d95f81f99364debfb96))
74 | * cache http server ([fc5c824](https://github.com/serverless-components/tencent-express/commit/fc5c82453e2469d66837e1b525d317e811b3115a))
75 | * change entry to sls.js ([61bd482](https://github.com/serverless-components/tencent-express/commit/61bd4820dfcacefa1338580fa8d3b0f2609ffe4c))
76 | * cns ([65aa8b8](https://github.com/serverless-components/tencent-express/commit/65aa8b8b42880c9709ff583429899275978b007e))
77 | * enableCORS for apigw ([a0f59ef](https://github.com/serverless-components/tencent-express/commit/a0f59ef01d5dfe500758adfe2c4f05d7ffc2f22b))
78 | * handle usageplan & auth undefined ([2a257d5](https://github.com/serverless-components/tencent-express/commit/2a257d564e0968c8db1e0a4030412749cb19e847))
79 | * make default runtime to 10.15 ([a295bd4](https://github.com/serverless-components/tencent-express/commit/a295bd49a9b48f470a3416026531e840dd2177ac))
80 | * metics data bug ([6bdf89b](https://github.com/serverless-components/tencent-express/commit/6bdf89bbe3b3665012acee5ca66e08fdff9f05db))
81 | * metrics qps limit ([dddb9f6](https://github.com/serverless-components/tencent-express/commit/dddb9f607c96d86a3500eddeae248d600e2c9813))
82 | * monitor timeout bug ([de33311](https://github.com/serverless-components/tencent-express/commit/de333111a692b5bc8d14e9274b81b5b2e5e57e84))
83 | * optimize outputs for one region ([b3c6964](https://github.com/serverless-components/tencent-express/commit/b3c69646528edfdb8ff0d6455a4dc2b16e13cb81))
84 | * optimize traffic config outputs ([8545ead](https://github.com/serverless-components/tencent-express/commit/8545ead58183454536ca43c412d42d9be9c05580))
85 | * package version ([d682b66](https://github.com/serverless-components/tencent-express/commit/d682b66ab7858bb85c29548f90514056f8c34749))
86 | * prettier config ([e17bd6f](https://github.com/serverless-components/tencent-express/commit/e17bd6fe939666f7d8c5c89182ed24125409e068))
87 | * read bucket and object from srcOriginal ([1b2cf9d](https://github.com/serverless-components/tencent-express/commit/1b2cf9d7c890a94e7deaa830814b9758ac42cdf4))
88 | * release v0.0.1 for v2 ([52b2a37](https://github.com/serverless-components/tencent-express/commit/52b2a37a9f57550d48af868b6d53ac3a9c020fd5))
89 | * remove credential bug ([bca2d1f](https://github.com/serverless-components/tencent-express/commit/bca2d1f9c6ab79ee7b29844fe8bdf2085ddac94c))
90 | * role check error ([1a0b4bf](https://github.com/serverless-components/tencent-express/commit/1a0b4bf3ec1fed5492d72f07378ca7b5d26bbfe8))
91 | * support apigw endpoint timeout config ([01dd4c9](https://github.com/serverless-components/tencent-express/commit/01dd4c931235ec5547ce5f34e0997beb4e094fd1))
92 | * support eip config ([62c597f](https://github.com/serverless-components/tencent-express/commit/62c597fdef0a8e296b1943dba13baa60725afd53))
93 | * template url output ([88df485](https://github.com/serverless-components/tencent-express/commit/88df48503bd1f106e451ce102ec5bd16d6a1442a))
94 | * throw error when no temp secrets ([2a9167e](https://github.com/serverless-components/tencent-express/commit/2a9167e98a9648fb3ed7264956b10ea2808ce490))
95 | * traffic zero display bug ([0285406](https://github.com/serverless-components/tencent-express/commit/0285406c812284bd8b5847f0c201c9b69761c9d6))
96 | * uniform throw error ([c09b6f5](https://github.com/serverless-components/tencent-express/commit/c09b6f5b0071a2fb6a3251b0f8f31bc0dd614d3b))
97 | * update deploy code ([90a4339](https://github.com/serverless-components/tencent-express/commit/90a433968502f9d1499534cf852c6d4c3db12ba6))
98 | * update deps ([2e551db](https://github.com/serverless-components/tencent-express/commit/2e551db26dcfa02adf5aa2c63d6c08fea771ed00))
99 | * update deps ([c14e8f1](https://github.com/serverless-components/tencent-express/commit/c14e8f1492fa8ca7e3bedbc8018571556aa0c6ff))
100 | * update error message ([c4c7243](https://github.com/serverless-components/tencent-express/commit/c4c724358fe5be700e596929b8bddcfa94faa6fd))
101 | * update get credential error message ([ed47b3d](https://github.com/serverless-components/tencent-express/commit/ed47b3d759944e41b0c8fa7641594450d88704de))
102 | * update toolkit verison ([2e99bc7](https://github.com/serverless-components/tencent-express/commit/2e99bc73c438318d125d50d959bf3f9de4a6e85e))
103 | * update usageplan & auth logic ([afa8807](https://github.com/serverless-components/tencent-express/commit/afa8807792aa0e69c191391b1fb49b059add3c35))
104 | * upgrade deps ([6a1f7a8](https://github.com/serverless-components/tencent-express/commit/6a1f7a86d4838d02cef19de37a2f6e9714ee640b))
105 | * upgrade deps ([d5c013b](https://github.com/serverless-components/tencent-express/commit/d5c013b6a04487a29b9848442d31cc71c6230c5f))
106 | * upgrade tencent-component-toolkit ([4ba2edf](https://github.com/serverless-components/tencent-express/commit/4ba2edf8570e511b9fd5a8286ae81044b531c094))
107 | * upgrade tencent-component-toolkit ([af8c234](https://github.com/serverless-components/tencent-express/commit/af8c234622058253a21ebe0231796dd7a47902f8))
108 | * upgrade tencent-component-toolkit ([bdd5062](https://github.com/serverless-components/tencent-express/commit/bdd5062f6501fbe91e167ce3e7a91da2e8eb4acd))
109 | * upgrade tencent-component-toolkit ([00f4a09](https://github.com/serverless-components/tencent-express/commit/00f4a0993d04d4a7be3cf8ea15381a6d8ff04c98))
110 | * upgrade tencent-component-toolkit for deleting compatibility ([fa215bf](https://github.com/serverless-components/tencent-express/commit/fa215bfb66264c360c24a3aca68f4b63cb4c60fd))
111 | * wrong region parameter bug ([#31](https://github.com/serverless-components/tencent-express/issues/31)) ([32e519c](https://github.com/serverless-components/tencent-express/commit/32e519ca00170ed75f0bea19b80c0255288e8d37))
112 | * 增加cns ([b452285](https://github.com/serverless-components/tencent-express/commit/b45228501a381dd6ede9381ca8d05cd5f403d585))
113 | * 增加cns ([a2b87d9](https://github.com/serverless-components/tencent-express/commit/a2b87d92908935dc4299dfb0d5b01029c5fc8649))
114 |
115 |
116 | ### Features
117 |
118 | * add binary types support ([60603d6](https://github.com/serverless-components/tencent-express/commit/60603d61bbeb1cbdcf44d5bb6b9cd5a6f2b08fd6))
119 | * add integration test ([7c28271](https://github.com/serverless-components/tencent-express/commit/7c2827162f94b817f5e8f3c00e279263bff120e7))
120 | * add layers config support ([d971be9](https://github.com/serverless-components/tencent-express/commit/d971be985daf3bc4127c6648fd0e474c535ad1eb))
121 | * add metrics api ([#27](https://github.com/serverless-components/tencent-express/issues/27)) ([0d40154](https://github.com/serverless-components/tencent-express/commit/0d40154347b5db735fcac66a8a9b5ccf0e6ca035))
122 | * add state store, and remove method ([8815e40](https://github.com/serverless-components/tencent-express/commit/8815e4045cf7896e8843c017abb16b24720caa79))
123 | * optimize code zip flow ([f2d60ce](https://github.com/serverless-components/tencent-express/commit/f2d60ce03e96257f4aacda464ab83a4dc144fecb))
124 | * optimize deploy log ([80a2bc0](https://github.com/serverless-components/tencent-express/commit/80a2bc0a23a08051ede3c5d54657833499e0d2fc))
125 | * optimize metics and support disable apigw creating ([e722c80](https://github.com/serverless-components/tencent-express/commit/e722c80d3d1fce8315e08a86808c131402a7a81b))
126 | * support api gw metrics ([cc625ad](https://github.com/serverless-components/tencent-express/commit/cc625ad3e8a410b161b4744813685aa42a700898))
127 | * support role config ([f1d6ed6](https://github.com/serverless-components/tencent-express/commit/f1d6ed664b7354376f5791bd162113cdd44b7b5b))
128 | * support scf publish version and traffic setup ([8807d0e](https://github.com/serverless-components/tencent-express/commit/8807d0ef0fd8c560b3c7aaa8fa37165473a5f863))
129 | * update config and support usageplan+auth ([b067a14](https://github.com/serverless-components/tencent-express/commit/b067a149acec78d97f701aea893e95ad3833f7c3))
130 | * update event and context attach method ([31d0c1d](https://github.com/serverless-components/tencent-express/commit/31d0c1de42354103a800e0515f9a069971b357df))
131 | * 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))
132 | * using temporary secret for credentials ([cf61300](https://github.com/serverless-components/tencent-express/commit/cf61300f7d94f3149648a0d3378b8876a440aca1))
133 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Tencent Cloud, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ⚠️⚠️⚠️ 所有框架组件项目迁移到 [tencent-framework-components](https://github.com/serverless-components/tencent-framework-components).
2 |
3 | [](http://serverless.com)
4 |
5 |
6 |
7 | **腾讯云 Express 组件** ⎯⎯⎯ 通过使用 [Tencent Serverless Framework](https://github.com/serverless/components/tree/cloud),基于云上 Serverless 服务(如网关、云函数等),实现“0”配置,便捷开发,极速部署你的 Express 应用,Express 组件支持丰富的配置扩展,提供了目前最易用、低成本并且弹性伸缩的 Express 项目开发/托管能力。
8 |
9 |
10 | 特性介绍:
11 |
12 | - [x] **按需付费** - 按照请求的使用量进行收费,没有请求时无需付费
13 | - [x] **"0"配置** - 只需要关心项目代码,之后部署即可,Serverless Framework 会搞定所有配置。
14 | - [x] **极速部署** - 仅需几秒,部署你的整个 Express 应用。
15 | - [x] **实时日志** - 通过实时日志的输出查看业务状态,便于直接在云端开发应用。
16 | - [x] **云端调试** - 针对 Node.js 框架支持一键云端调试能力,屏蔽本地环境的差异。
17 | - [x] **便捷协作** - 通过云端的状态信息和部署日志,方便的进行多人协作开发。
18 | - [x] **自定义域名** - 支持配置自定义域名及 HTTPS 访问
19 |
20 |
21 |
22 | 快速开始:
23 |
24 | 1. [**安装**](#1-安装)
25 | 2. [**创建**](#2-创建)
26 | 3. [**部署**](#3-部署)
27 | 4. [**配置**](#4-配置)
28 | 5. [**开发调试**](#5-开发调试)
29 | 6. [**查看状态**](#6-查看状态)
30 | 7. [**移除**](#7-移除)
31 |
32 | 更多资源:
33 |
34 | - [**架构说明**](#架构说明)
35 | - [**账号配置**](#账号配置)
36 |
37 |
38 |
39 | ### 1. 安装
40 |
41 | 通过 npm 安装最新版本的 Serverless Framework
42 |
43 | ```bash
44 | $ npm install -g serverless
45 | ```
46 |
47 | ### 2. 创建
48 |
49 | 通过如下命令和模板链接,快速创建一个 Express 应用:
50 |
51 | ```bash
52 | $ serverless init express-starter --name example
53 | $ cd example
54 | ```
55 |
56 | ### 3. 部署
57 |
58 | 在 `serverless.yml` 文件所在的项目根目录,运行以下指令进行部署:
59 |
60 | ```bash
61 | $ serverless deploy
62 | ```
63 |
64 | 部署时需要进行身份验证,如您的账号未 [登陆](https://cloud.tencent.com/login) 或 [注册](https://cloud.tencent.com/register) 腾讯云,您可以直接通过 `微信` 扫描命令行中的二维码进行授权登陆和注册。
65 |
66 | > 注意: 如果希望查看更多部署过程的信息,可以通过`serverless deploy --debug` 命令查看部署过程中的实时日志信息。
67 |
68 | 部署完成后,控制台会打印相关的输出信息,您可以通过 `${output:${stage}:${app}:apigw.url}` 的形式在其他 `serverless` 组件中引用该组件的 API 网关访问链接(或通过类似的形式引用该组建其他输出结果),具体的,可以查看完成的输出文档:
69 |
70 | - [点击此处查看输出文档](https://github.com/serverless-components/tencent-express/tree/master/docs/output.md)
71 |
72 | ### 4. 配置
73 |
74 | Express 组件支持 0 配置部署,也就是可以直接通过配置文件中的默认值进行部署。但你依然可以修改更多可选配置来进一步开发该 Express 项目。
75 |
76 | 以下是 Express 组件的 `serverless.yml`配置示例:
77 |
78 | ```yml
79 | # serverless.yml
80 |
81 | component: express # (required) name of the component. In that case, it's express.
82 | name: expressDemo # (required) name of your express component instance.
83 | org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
84 | app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
85 | stage: dev # (optional) serverless dashboard stage. default is dev.
86 |
87 | inputs:
88 | src:
89 | src: ./ # (optional) path to the source folder. default is a hello world app.
90 | exclude:
91 | - .env
92 | functionName: expressDemo
93 | region: ap-guangzhou
94 | runtime: Nodejs10.15
95 | apigatewayConf:
96 | protocols:
97 | - http
98 | - https
99 | environment: release
100 | ```
101 |
102 | 点此查看[全量配置及配置说明](https://github.com/serverless-components/tencent-express/tree/master/docs/configure.md)
103 |
104 | 当你根据该配置文件更新配置字段后,再次运行 `serverless deploy` 或者 `serverless` 就可以更新配置到云端。
105 |
106 | ### 5. 开发调试
107 |
108 | 部署了 Express.js 应用后,可以通过开发调试能力对该项目进行二次开发,从而开发一个生产应用。在本地修改和更新代码后,不需要每次都运行 `serverless deploy` 命令来反复部署。你可以直接通过 `serverless dev` 命令对本地代码的改动进行检测和自动上传。
109 |
110 | 可以通过在 `serverless.yml`文件所在的目录下运行 `serverless dev` 命令开启开发调试能力。
111 |
112 | `serverless dev` 同时支持实时输出云端日志,每次部署完毕后,对项目进行访问,即可在命令行中实时输出调用日志,便于查看业务情况和排障。
113 |
114 | 除了实时日志输出之外,针对 Node.js 应用,当前也支持云端调试能力。在开启 `serverless dev` 命令之后,将会自动监听远端端口,并将函数的超时时间临时配置为 900s。此时你可以通过访问 chrome://inspect/#devices 查找远端的调试路径,并直接对云端代码进行断点等调试。在调试模式结束后,需要再次部署从而将代码更新并将超时时间设置为原来的值。详情参考[开发模式和云端调试](https://cloud.tencent.com/document/product/1154/43220)。
115 |
116 | ### 6. 查看状态
117 |
118 | 在`serverless.yml`文件所在的目录下,通过如下命令查看部署状态:
119 |
120 | ```
121 | $ serverless info
122 | ```
123 |
124 | ### 7. 移除
125 |
126 | 在`serverless.yml`文件所在的目录下,通过以下命令移除部署的 Express 服务。移除后该组件会对应删除云上部署时所创建的所有相关资源。
127 |
128 | ```
129 | $ serverless remove
130 | ```
131 |
132 | 和部署类似,支持通过 `serverless remove --debug` 命令查看移除过程中的实时日志信息。
133 |
134 | ## 架构说明
135 |
136 | Express 组件将在腾讯云账户中使用到如下 Serverless 服务:
137 |
138 | - [x] **API 网关** - API 网关将会接收外部请求并且转发到 SCF 云函数中。
139 | - [x] **SCF 云函数** - 云函数将承载 Express.js 应用。
140 | - [x] **CAM 访问控制** - 该组件会创建默认 CAM 角色用于授权访问关联资源。
141 | - [x] **COS 对象存储** - 为确保上传速度和质量,云函数压缩并上传代码时,会默认将代码包存储在特定命名的 COS 桶中。
142 | - [x] **SSL 证书服务** - 如果你在 yaml 文件中配置了 `apigatewayConf.customDomains` 字段,需要做自定义域名绑定并开启 HTTPS 时,也会用到证书管理服务和域名服务。Serverless Framework 会根据已经备案的域名自动申请并配置 SSL 证书。
143 |
144 | ## 账号配置
145 |
146 | 当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件
147 |
148 | ```console
149 | $ touch .env # 腾讯云的配置信息
150 | ```
151 |
152 | 在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存
153 |
154 | 如果没有腾讯云账号,可以在此[注册新账号](https://cloud.tencent.com/register)。
155 |
156 | 如果已有腾讯云账号,可以在[API 密钥管理](https://console.cloud.tencent.com/cam/capi)中获取 `SecretId` 和`SecretKey`.
157 |
158 | ```
159 | # .env
160 | TENCENT_SECRET_ID=123
161 | TENCENT_SECRET_KEY=123
162 | ```
163 |
164 | ## 静态资源服务
165 |
166 | 如果想要支持返回静态资源,比如图片之类的,需要在入口文件 `sls.js` 中指定相关 `MIME` 类型的文件为二进制,这样云函数在返回请求结果给 API 网关是,会对指定类型进行 `Base64` 编码,最终返回给客户端才能正常显示。如下:
167 |
168 | ```js
169 | const express = require('express')
170 | const app = express()
171 |
172 | // Routes
173 | // ...
174 |
175 | app.binaryTypes = ['*/*']
176 |
177 | module.exports = app
178 | ```
179 |
180 | `['*/*']` 代表所有文件类型将进行 `Base64` 编码,如果需要定制化,可以配置为 `['image/png']`,意思是指定 `png` 格式的图片类型。
181 |
182 | 更多文件类型的 `MIME` 类型,可参考 [mime-db](https://github.com/jshttp/mime-db/blob/master/db.json)。
183 |
184 | ### slsInitialize 应用初始化
185 |
186 | 有些时候,Express 服务在启动前,需要进行一个初始化操作,比如数据库建连,就可以通过在 Express 实例对象上添加 `slsInitialize` 函数来实现,如下:
187 |
188 | ```js
189 | const express = require('express')
190 | const mysql = require('mysql2/promise')
191 |
192 | const app = new express()
193 |
194 | // ...
195 |
196 | app.slsInitialize = async () => {
197 | app.db = await mysql.createConnection({
198 | host: 'localhost',
199 | user: 'root',
200 | database: 'test'
201 | })
202 | }
203 |
204 | // don't forget to export!
205 | module.exports = app
206 | ```
207 |
208 | 这样应用部署到云函数后,在函数服务逻辑执行前,会先执行 `slsInitialize()` 函数,来初始化数据库连接。
209 |
210 | ## License
211 |
212 | MIT License
213 |
214 | Copyright (c) 2020 Tencent Cloud, Inc.
215 |
--------------------------------------------------------------------------------
/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | const { join } = require('path');
2 | require('dotenv').config({ path: join(__dirname, '.env.test') });
3 |
4 | const { generateId, getServerlessSdk } = require('./lib/utils')
5 | const execSync = require('child_process').execSync
6 | const path = require('path')
7 | const axios = require('axios')
8 |
9 | const instanceYaml = {
10 | org: 'orgDemo',
11 | app: 'appDemo',
12 | component: 'express@dev',
13 | name: `express-integration-tests-${generateId()}`,
14 | stage: 'dev',
15 | inputs: {
16 | region: 'ap-guangzhou',
17 | runtime: 'Nodejs10.15',
18 | apigatewayConf: { environment: 'test' }
19 | }
20 | }
21 |
22 | const credentials = {
23 | tencent: {
24 | SecretId: process.env.TENCENT_SECRET_ID,
25 | SecretKey: process.env.TENCENT_SECRET_KEY,
26 | }
27 | }
28 |
29 | const sdk = getServerlessSdk(instanceYaml.org)
30 |
31 | it('should successfully deploy express app', async () => {
32 | const instance = await sdk.deploy(instanceYaml, credentials)
33 |
34 | expect(instance).toBeDefined()
35 | expect(instance.instanceName).toEqual(instanceYaml.name)
36 | expect(instance.outputs.templateUrl).toBeDefined()
37 | expect(instance.outputs.region).toEqual(instanceYaml.inputs.region)
38 | expect(instance.outputs.apigw).toBeDefined()
39 | expect(instance.outputs.apigw.environment).toEqual(instanceYaml.inputs.apigatewayConf.environment)
40 | expect(instance.outputs.scf).toBeDefined()
41 | expect(instance.outputs.scf.runtime).toEqual(instanceYaml.inputs.runtime)
42 | })
43 |
44 | it('should successfully update source code', async () => {
45 | // change source to own source './src' and need to install packages before deploy
46 | const srcPath = path.join(__dirname, '..', 'example')
47 | execSync('npm install', { cwd: srcPath })
48 | instanceYaml.inputs.src = srcPath
49 |
50 | const instance = await sdk.deploy(instanceYaml, credentials)
51 | const response = await axios.get(instance.outputs.apigw.url)
52 |
53 | expect(response.data.includes('Serverless Framework')).toBeTruthy()
54 | expect(instance.outputs.templateUrl).not.toBeDefined()
55 | })
56 |
57 | it('should successfully remove express app', async () => {
58 | await sdk.remove(instanceYaml, credentials)
59 | result = await sdk.getInstance(instanceYaml.org, instanceYaml.stage, instanceYaml.app, instanceYaml.name)
60 |
61 | expect(result.instance.instanceStatus).toEqual('inactive')
62 | })
63 |
--------------------------------------------------------------------------------
/__tests__/lib/utils.js:
--------------------------------------------------------------------------------
1 | const { ServerlessSDK } = require('@serverless/platform-client-china')
2 |
3 | /*
4 | * Generate random id
5 | */
6 | const generateId = () =>
7 | Math.random()
8 | .toString(36)
9 | .substring(6)
10 |
11 | /*
12 | * Initializes and returns an instance of the serverless sdk
13 | * @param ${string} orgName - the serverless org name.
14 | */
15 | const getServerlessSdk = (orgName) => {
16 | const sdk = new ServerlessSDK({
17 | context: {
18 | orgName
19 | }
20 | })
21 | return sdk
22 | }
23 |
24 | module.exports = { generateId, getServerlessSdk }
25 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | const Configuration = {
2 | /*
3 | * Resolve and load @commitlint/config-conventional from node_modules.
4 | * Referenced packages must be installed
5 | */
6 | extends: ['@commitlint/config-conventional']
7 | }
8 |
9 | module.exports = Configuration
10 |
--------------------------------------------------------------------------------
/docs/configure.md:
--------------------------------------------------------------------------------
1 | # 配置文档
2 |
3 | ## 全部配置
4 |
5 | ```yml
6 | # serverless.yml
7 |
8 | component: express # (必选) 组件名称,在该实例中为express
9 | name: expressDemo # 必选) 组件实例名称.
10 | org: orgDemo # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid,必须为字符串
11 | app: appDemo # (可选) 用于记录组织信息. 默认与name相同,必须为字符串
12 | stage: dev # (可选) 用于区分环境信息,默认值是 dev
13 |
14 | inputs:
15 | region: ap-guangzhou # 云函数所在区域
16 | functionName: expressDemo # 云函数名称
17 | serviceName: mytest # api网关服务名称
18 | runtime: Nodejs10.15 # 运行环境
19 | serviceId: service-np1uloxw # api网关服务ID
20 | entryFile: sls.js # 自定义 server 的入口文件名,默认为 sls.js,如果不想修改文件名为 sls.js 可以自定义
21 | src: ./src # 第一种为string时,会打包src对应目录下的代码上传到默认cos上。
22 | # src: # 第二种,部署src下的文件代码,并打包成zip上传到bucket上
23 | # src: ./src # 本地需要打包的文件目录
24 | # bucket: bucket01 # bucket name,当前会默认在bucket name后增加 appid 后缀, 本例中为 bucket01-appid
25 | # exclude: # 被排除的文件或目录
26 | # - .env
27 | # - node_modules
28 | # src: # 第三种,在指定存储桶bucket中已经存在了object代码,直接部署
29 | # bucket: bucket01 # bucket name,当前会默认在bucket name后增加 appid 后缀, 本例中为 bucket01-appid
30 | # object: cos.zip # bucket key 指定存储桶内的文件
31 | layers:
32 | - name: layerName # layer名称
33 | version: 1 # 版本
34 | functionConf: # 函数配置相关
35 | timeout: 10 # 超时时间,单位秒
36 | eip: false # 是否固定出口IP
37 | memorySize: 128 # 内存大小,单位MB
38 | environment: # 环境变量
39 | variables: # 环境变量数组
40 | TEST: vale
41 | vpcConfig: # 私有网络配置
42 | vpcId: '' # 私有网络的Id
43 | subnetId: '' # 子网ID
44 | apigatewayConf: # api网关配置
45 | isDisabled: false # 是否禁用自动创建 API 网关功能
46 | enableCORS: true # 允许跨域
47 | customDomains: # 自定义域名绑定
48 | - domain: abc.com # 待绑定的自定义的域名
49 | certificateId: abcdefg # 待绑定自定义域名的证书唯一 ID
50 | # 如要设置自定义路径映射,请设置为 false
51 | isDefaultMapping: false
52 | # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。
53 | pathMappingSet:
54 | - path: /
55 | environment: release
56 | protocols: # 绑定自定义域名的协议类型,默认与服务的前端协议一致。
57 | - http # 支持http协议
58 | - https # 支持https协议
59 | protocols:
60 | - http
61 | - https
62 | environment: test
63 | serviceTimeout: 15
64 | isBase64Encoded: false
65 | usagePlan: # 用户使用计划
66 | usagePlanId: 1111
67 | usagePlanName: slscmp
68 | usagePlanDesc: sls create
69 | maxRequestNum: 1000
70 | auth: # 密钥
71 | secretName: secret
72 | secretIds:
73 | - xxx
74 | ```
75 |
76 | ## 配置描述
77 |
78 | 主要的参数
79 |
80 | | 参数名称 | 必选 | 默认值 | 描述 |
81 | | ------------------------------------ | :--: | :-------------: | :------------------------------------------------------------------ |
82 | | runtime | 否 | `Nodejs10.15` | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 |
83 | | region | 否 | `ap-guangzhou` | 项目部署所在区域,默认广州区 |
84 | | functionName | 否 | | 云函数名称 |
85 | | serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 |
86 | | serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 |
87 | | entryFile | 否 | `sls.js` | 自定义 server 的入口文件名 |
88 | | src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) |
89 | | layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) |
90 | | [functionConf](#函数配置) | 否 | | 函数配置 |
91 | | [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 |
92 | | [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 |
93 | | [Region special config](#指定区配置) | 否 | | 指定区配置 |
94 |
95 | ## 执行目录
96 |
97 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
98 | | -------- | :------: | :-------------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
99 | | src | 否 | String | | 代码路径。与 object 不能同时存在。 |
100 | | exclude | 否 | Array of String | | 不包含的文件或路径, 遵守 [glob 语法](https://github.com/isaacs/node-glob) |
101 | | bucket | 否 | String | | bucket 名称。如果配置了 src,表示部署 src 的代码并压缩成 zip 后上传到 bucket-appid 对应的存储桶中;如果配置了 object,表示获取 bucket-appid 对应存储桶中 object 对应的代码进行部署。 |
102 | | object | 否 | String | | 部署的代码在存储桶中的路径。 |
103 |
104 | ## 层配置
105 |
106 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
107 | | -------- | :------: | :----: | :----: | :------- |
108 | | name | 否 | String | | 层名称 |
109 | | version | 否 | String | | 层版本号 |
110 |
111 | ### DNS 配置
112 |
113 | 参考: https://cloud.tencent.com/document/product/302/8516
114 |
115 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
116 | | ---------- | :------: | -------- | :----: | :---------------------------------------------- |
117 | | ttl | 否 | Number | `600` | TTL 值,范围 1 - 604800,不同等级域名最小值不同 |
118 | | recordLine | 否 | String[] | | 记录的线路名称 |
119 |
120 | ### 指定区配置
121 |
122 | | 参数名称 | 是否必选 | 类型 | 默认值 | 函数 |
123 | | ------------------------------- | :------: | ------ | ------ | ------------ |
124 | | [functionConf](#函数配置) | 否 | Object | | 函数配置 |
125 | | [apigatewayConf](#API-网关配置) | 否 | Object | | API 网关配置 |
126 | | [cloudDNSConf](#DNS-配置) | 否 | Object | | DNS 配置 |
127 |
128 | ### 函数配置
129 |
130 | 参考: https://cloud.tencent.com/document/product/583/18586
131 |
132 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
133 | | ----------- | :------: | :-----: | :-----: | :------------------------------------------------------------------------------ |
134 | | timeout | 否 | Number | `3` | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒 |
135 | | memorySize | 否 | Number | `128` | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 |
136 | | environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) |
137 | | vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) |
138 | | eip | 否 | Boolean | `false` | 是否固定出口 IP |
139 |
140 | ##### 环境变量
141 |
142 | | 参数名称 | 类型 | 描述 |
143 | | --------- | ---- | :---------------------------------------- |
144 | | variables | | 环境变量参数, 包含多对 key-value 的键值对 |
145 |
146 | ##### VPC 配置
147 |
148 | | 参数名称 | 类型 | 描述 |
149 | | -------- | ------ | :------ |
150 | | subnetId | String | 子网 ID |
151 | | vpcId | String | VPC ID |
152 |
153 | ### API 网关配置
154 |
155 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
156 | | --------------- | :------: | :------- | :--------- | :--------------------------------------------------------------------------------- |
157 | | protocols | 否 | String[] | `['http']` | 前端请求的类型,如 http,https,http 与 https |
158 | | environment | 否 | String | `release` | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). |
159 | | usagePlan | 否 | | | 使用计划配置, 参考 [使用计划](#使用计划) |
160 | | auth | 否 | | | API 密钥配置, 参考 [API 密钥](#API-密钥配置) |
161 | | customDomain | 否 | Object[] | | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名) |
162 | | enableCORS | 否 | Boolean | `false` | 开启跨域。默认值为否。 |
163 | | serviceTimeout | 否 | Number | `15` | Api 超时时间,单位: 秒 |
164 | | isDisabled | 否 | Boolean | `false` | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。 |
165 | | isBase64Encoded | 否 | Boolean | `false` | 是否开启 Base64 编码,如果需要文件上传,请配置为 `true` |
166 |
167 | ##### 使用计划
168 |
169 | 参考: https://cloud.tencent.com/document/product/628/14947
170 |
171 | | 参数名称 | 是否必选 | 类型 | 描述 |
172 | | ------------- | :------: | ------ | :------------------------------------------------------ |
173 | | usagePlanId | 否 | String | 用户自定义使用计划 ID |
174 | | usagePlanName | 否 | String | 用户自定义的使用计划名称 |
175 | | usagePlanDesc | 否 | String | 用户自定义的使用计划描述 |
176 | | maxRequestNum | 否 | Number | 请求配额总数,如果为空,将使用-1 作为默认值,表示不开启 |
177 |
178 | ##### API 密钥配置
179 |
180 | 参考: https://cloud.tencent.com/document/product/628/14916
181 |
182 | | 参数名称 | 类型 | 描述 |
183 | | ---------- | :----- | :------- |
184 | | secretName | String | 密钥名称 |
185 | | secretIds | String | 密钥 ID |
186 |
187 | ##### 自定义域名
188 |
189 | Refer to: https://cloud.tencent.com/document/product/628/14906
190 |
191 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
192 | | ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
193 | | domain | 是 | String | | 待绑定的自定义的域名。 |
194 | | certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 `https`,则为必选 |
195 | | isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射。为 `false` 时,表示自定义路径映射,此时 pathMappingSet 必填。 |
196 | | pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 |
197 | | protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 |
198 |
199 | - 自定义路径映射
200 |
201 | | 参数名称 | 是否必选 | 类型 | Description |
202 | | ----------- | :------: | :----- | :------------- |
203 | | path | 是 | String | 自定义映射路径 |
204 | | environment | 是 | String | 自定义映射环境 |
205 |
--------------------------------------------------------------------------------
/docs/output.md:
--------------------------------------------------------------------------------
1 | # 部署 `output` 参数介绍
2 |
3 | > 组件输出可以在别的组件中通过 `${output:${stage}:${app}:.}` 获取
4 | >
5 | > 例如,如果该组件名称是 `test_name`, ·且只部署于一个地域,则可以通过 `${output:${stage}:${app}:test_name.apigw.url}` 在别的组件中获取该组件的 API 网关的 `url`。
6 |
7 | | 名称 | 类型 | 描述 |
8 | | :---------- | :------------------------------------------------------------------------------: | :------------------------------- |
9 | | templateUrl | string | 未提供代码时的模板代码 url |
10 | | region | string | 地域信息(只有一个地域时才提供) |
11 | | scf | [`FunctionOutput | Record`](#云函数输出-`FunctionOutput`) | 云函数输出信息 |
12 | | apigw | [`ApigwOutput | Record`](#API-网关输出-`ApigwOutput`) | API 网关输出信息 |
13 |
14 | ## 云函数输出 `FunctionOutput`
15 |
16 | | 名称 | 类型 | 描述 |
17 | | :------------------- | :------------: | :--------------------- |
18 | | functionName | string | 云函数名称 |
19 | | runtime | string | 云运行环境 |
20 | | namespace | string | 云函数名称空间 |
21 | | lastVersion | string | 云函数版本 |
22 | | traffic | `number (0~1)` | 将多少流量导向该云函数 |
23 | | configTrafficVersion | string | |
24 |
25 | ## API 网关输出 `ApigwOutput`
26 |
27 | | 名称 | 类型 | 描述 |
28 | | :------------ | :------------------------------------------------------------------: | :------------------------- |
29 | | serviceId | string | API 网关 ID |
30 | | subDomain | string | API 网关子域名 |
31 | | enviroment | `"release" | "prepub" | "test"` | API 网关 |
32 | | url | string | API 网关对外的完整 URL |
33 | | traffic | number (0~1) | 将多少流量导向该云函数 |
34 | | customDomains | [CustomDomain[]](#API-网关自定义域名输出-`ApigwOutput.CustomDomain`) | API 网关自定义域名输出列表 |
35 |
36 | ## API 网关自定义域名输出 `ApigwOutput.CustomDomain`
37 |
38 | | 名称 | 类型 | 描述 |
39 | | :--------------- | :----------------------------------------------------------------: | :------------------------- |
40 | | domain | string | 自定义域名 |
41 | | certificateId | string | 域名证书 ID |
42 | | isDefaultMapping | boolean | 该自定义域名是否为默认域名 |
43 | | pathMappingSet | [PathMapping[]](#-API-网关域名映射规则-`CustomDomain.PathMapping`) | 该域名的路径映射规则列表 |
44 | | protocols | `"http" | "https"` | 启用的协议 |
45 |
46 | ## API 网关域名映射规则 `CustomDomain.PathMapping`
47 |
48 | | 名称 | 类型 | 描述 |
49 | | :--------- | :----: | :--------------- |
50 | | path | string | 路径 |
51 | | enviroment | string | 路径映射到的环境 |
52 |
--------------------------------------------------------------------------------
/docs/upload.md:
--------------------------------------------------------------------------------
1 | ## 文件上传说明
2 |
3 | 项目中如果涉及到文件上传,需要依赖 API 网关提供的 [Base64 编码能力](https://cloud.tencent.com/document/product/628/51799),使用时只需要 `serverless.yml` 中配置 `isBase64Encoded` 为 `true`,如下:
4 |
5 | ```yaml
6 | app: appDemo
7 | stage: dev
8 | component: express
9 | name: expressDemo
10 |
11 | inputs:
12 | # 省略...
13 | apigatewayConf:
14 | isBase64Encoded: true
15 | # 省略...
16 | # 省略...
17 | ```
18 |
19 | 当前 API 网关支持上传最大文件大小为 `2M`,如果文件过大,请修改为前端直传对象存储方案。
20 |
21 | ## Base64 示例
22 |
23 | 此 Github 项目的 `example` 目录下存在模板文件:
24 |
25 | - [sls.upload.js](../example/sls.upload.js)
26 |
27 | 开发者可根据个人项目需要参考修改,使用时需要复制文件名为 `sls.js`。
28 |
29 | 文件中实现了文件上传接口 `POST /upload`,如果要支持文件上传,需要安装 `multer` 包。
30 |
31 | 同时需要在 `serverless.yml` 的 `apigatewayConf` 中配置 `isBase64Encoded` 为 `true`。
32 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | [](http://serverless.com)
2 |
3 |
4 |
5 | **腾讯云 Express 组件** ⎯⎯⎯ 通过使用 [Tencent Serverless Framework](https://github.com/serverless/components/tree/cloud),基于云上 Serverless 服务(如网关、云函数等),实现“0”配置,便捷开发,极速部署你的 Express 应用,Express 组件支持丰富的配置扩展,提供了目前最易用、低成本并且弹性伸缩的 Express 项目开发/托管能力。
6 |
7 |
8 | 特性介绍:
9 |
10 | - [x] **按需付费** - 按照请求的使用量进行收费,没有请求时无需付费
11 | - [x] **"0"配置** - 只需要关心项目代码,之后部署即可,Serverless Framework 会搞定所有配置。
12 | - [x] **极速部署** - 仅需几秒,部署你的整个 Express 应用。
13 | - [x] **实时日志** - 通过实时日志的输出查看业务状态,便于直接在云端开发应用。
14 | - [x] **云端调试** - 针对 Node.js 框架支持一键云端调试能力,屏蔽本地环境的差异。
15 | - [x] **便捷协作** - 通过云端的状态信息和部署日志,方便的进行多人协作开发。
16 | - [x] **自定义域名** - 支持配置自定义域名及 HTTPS 访问
17 |
18 |
19 |
20 |
21 |
22 | 快速开始:
23 |
24 | 1. [**安装**](#1-安装)
25 | 2. [**创建**](#2-创建)
26 | 3. [**部署**](#3-部署)
27 | 4. [**配置**](#4-配置)
28 | 5. [**开发调试**](#5-开发调试)
29 | 6. [**查看状态**](#6-查看状态)
30 | 7. [**移除**](#7-移除)
31 |
32 | 更多资源:
33 |
34 | - [**架构说明**](#架构说明)
35 | - [**账号配置**](#账号配置)
36 |
37 |
38 |
39 | ### 1. 安装
40 |
41 | 通过 npm 安装最新版本的 Serverless Framework
42 |
43 | ```bash
44 | $ npm install -g serverless
45 | ```
46 |
47 | ### 2. 创建
48 |
49 | 通过如下命令和模板链接,快速创建一个 Express 应用:
50 |
51 | ```bash
52 | $ serverless create --template-url https://github.com/serverless-components/tencent-express/tree/master/example
53 | $ cd example
54 | ```
55 |
56 | 执行如下命令,安装 Express 应用的对应依赖
57 |
58 | ```
59 | $ npm install
60 | ```
61 |
62 | ### 3. 部署
63 |
64 | 在 `serverless.yml` 文件下的目录中运行 `serverless deploy` 进行 Express 项目的部署。第一次部署可能耗时相对较久,但后续的二次部署会在几秒钟之内完成。部署完毕后,你可以在命令行的输出中查看到你 Express 应用的 URL 地址,点击地址即可访问你的 Express 项目。
65 |
66 | **注意:**
67 |
68 | 如您的账号未[登陆](https://cloud.tencent.com/login)或[注册](https://cloud.tencent.com/register)腾讯云,您可以直接通过`微信`扫描命令行中的二维码进行授权登陆和注册。
69 |
70 | 如果出现了 `internal server error` 的报错,请检查是否在创建模板后没有运行 `npm install`。
71 |
72 | 如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
73 |
74 |
75 |
76 | ### 4. 配置
77 |
78 | Express 组件支持 0 配置部署,也就是可以直接通过配置文件中的默认值进行部署。但你依然可以修改更多可选配置来进一步开发该 Express 项目。
79 |
80 | 以下是 Express 组件的 `serverless.yml`完整配置说明:
81 |
82 | ```yml
83 | # serverless.yml
84 |
85 | component: express # (required) name of the component. In that case, it's express.
86 | name: expressDemo # (required) name of your express component instance.
87 | org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
88 | app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
89 | stage: dev # (optional) serverless dashboard stage. default is dev.
90 |
91 | inputs:
92 | src:
93 | src: ./ # (optional) path to the source folder. default is a hello world app.
94 | exclude:
95 | - .env
96 | functionName: expressDemo
97 | region: ap-guangzhou
98 | runtime: Nodejs10.15
99 | apigatewayConf:
100 | protocols:
101 | - http
102 | - https
103 | environment: release
104 | ```
105 |
106 | 点此查看[全量配置及配置说明](https://github.com/serverless-components/tencent-express/tree/master/docs/configure.md)
107 |
108 | 当你根据该配置文件更新配置字段后,再次运行 `serverless deploy` 或者 `serverless` 就可以更新配置到云端。
109 |
110 | ### 5. 开发调试
111 |
112 | 部署了 Express.js 应用后,可以通过开发调试能力对该项目进行二次开发,从而开发一个生产应用。在本地修改和更新代码后,不需要每次都运行 `serverless deploy` 命令来反复部署。你可以直接通过 `serverless dev` 命令对本地代码的改动进行检测和自动上传。
113 |
114 | 可以通过在 `serverless.yml`文件所在的目录下运行 `serverless dev` 命令开启开发调试能力。
115 |
116 | `serverless dev` 同时支持实时输出云端日志,每次部署完毕后,对项目进行访问,即可在命令行中实时输出调用日志,便于查看业务情况和排障。
117 |
118 | 除了实时日志输出之外,针对 Node.js 应用,当前也支持云端调试能力。在开启 `serverless dev` 命令之后,将会自动监听远端端口,并将函数的超时时间临时配置为 900s。此时你可以通过访问 chrome://inspect/#devices 查找远端的调试路径,并直接对云端代码进行断点等调试。在调试模式结束后,需要再次部署从而将代码更新并将超时时间设置为原来的值。详情参考[开发模式和云端调试](https://cloud.tencent.com/document/product/1154/43220)。
119 |
120 | ### 6. 查看状态
121 |
122 | 在`serverless.yml`文件所在的目录下,通过如下命令查看部署状态:
123 |
124 | ```
125 | $ serverless info
126 | ```
127 |
128 | ### 7. 移除
129 |
130 | 在`serverless.yml`文件所在的目录下,通过以下命令移除部署的 Express 服务。移除后该组件会对应删除云上部署时所创建的所有相关资源。
131 |
132 | ```
133 | $ serverless remove
134 | ```
135 |
136 | 和部署类似,支持通过 `sls remove --debug` 命令查看移除过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。
137 |
138 | ## 架构说明
139 |
140 | Express 组件将在腾讯云账户中使用到如下 Serverless 服务:
141 |
142 | - [x] **API 网关** - API 网关将会接收外部请求并且转发到 SCF 云函数中。
143 | - [x] **SCF 云函数** - 云函数将承载 Express.js 应用。
144 | - [x] **CAM 访问控制** - 该组件会创建默认 CAM 角色用于授权访问关联资源。
145 | - [x] **COS 对象存储** - 为确保上传速度和质量,云函数压缩并上传代码时,会默认将代码包存储在特定命名的 COS 桶中。
146 | - [x] **SSL 证书服务** - 如果你在 yaml 文件中配置了 `apigatewayConf.customDomains` 字段,需要做自定义域名绑定并开启 HTTPS 时,也会用到证书管理服务和域名服务。Serverless Framework 会根据已经备案的域名自动申请并配置 SSL 证书。
147 |
148 | ## 账号配置
149 |
150 | 当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件
151 |
152 | ```console
153 | $ touch .env # 腾讯云的配置信息
154 | ```
155 |
156 | 在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存
157 |
158 | 如果没有腾讯云账号,可以在此[注册新账号](https://cloud.tencent.com/register)。
159 |
160 | 如果已有腾讯云账号,可以在[API 密钥管理](https://console.cloud.tencent.com/cam/capi)中获取 `SecretId` 和`SecretKey`.
161 |
162 | ```
163 | # .env
164 | TENCENT_SECRET_ID=123
165 | TENCENT_SECRET_KEY=123
166 | ```
167 |
168 | ## License
169 |
170 | MIT License
171 |
172 | Copyright (c) 2020 Tencent Cloud, Inc.
173 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Serverless Component - Express.js
7 |
14 |
15 |
16 |
17 | Welcome to Express.js application created by
18 | Serverless Framework.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "sls.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "yugasun",
10 | "license": "MIT",
11 | "dependencies": {
12 | "express": "^4.17.1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example/serverless.yml:
--------------------------------------------------------------------------------
1 | org: orgDemo
2 | app: appDemo
3 | stage: dev
4 | component: express
5 | name: expressDemo
6 |
7 | inputs:
8 | src:
9 | src: ./
10 | exclude:
11 | - .env
12 | apigatewayConf:
13 | protocols:
14 | - http
15 | - https
16 |
--------------------------------------------------------------------------------
/example/sls.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const path = require('path')
3 | const app = express()
4 | const isServerless = process.env.SERVERLESS
5 |
6 | // Routes
7 | app.get(`/`, (req, res) => {
8 | res.sendFile(path.join(__dirname, 'index.html'))
9 | })
10 |
11 | app.get('/user', (req, res) => {
12 | res.send([
13 | {
14 | title: 'serverless framework',
15 | link: 'https://serverless.com'
16 | }
17 | ])
18 | })
19 |
20 | app.get('/user/:id', (req, res) => {
21 | const id = req.params.id
22 | res.send({
23 | id: id,
24 | title: 'serverless framework',
25 | link: 'https://serverless.com'
26 | })
27 | })
28 |
29 | app.get('/404', (req, res) => {
30 | res.status(404).send('Not found')
31 | })
32 |
33 | app.get('/500', (req, res) => {
34 | res.status(500).send('Server Error')
35 | })
36 |
37 | // Error handler
38 | app.use(function(err, req, res, next) {
39 | console.error(err)
40 | res.status(500).send('Internal Serverless Error')
41 | })
42 |
43 | if (isServerless) {
44 | module.exports = app
45 | } else {
46 | app.listen(3000, () => {
47 | console.log(`Server start on http://localhost:3000`)
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/example/sls.upload.js:
--------------------------------------------------------------------------------
1 | const multer = require('multer')
2 | const express = require('express')
3 | const path = require('path')
4 |
5 | const app = express()
6 | const isServerless = process.env.SERVERLESS
7 | const upload = multer({ dest: isServerless ? '/tmp/upload' : './upload' })
8 |
9 | // Routes
10 | app.post('/upload', upload.single('file'), (req, res) => {
11 | res.send({
12 | success: true,
13 | data: req.file
14 | })
15 | })
16 |
17 | app.get(`/`, (req, res) => {
18 | res.sendFile(path.join(__dirname, 'index.html'))
19 | })
20 |
21 | app.get('/user', (req, res) => {
22 | res.send([
23 | {
24 | title: 'serverless framework',
25 | link: 'https://serverless.com'
26 | }
27 | ])
28 | })
29 |
30 | app.get('/user/:id', (req, res) => {
31 | const id = req.params.id
32 | res.send({
33 | id: id,
34 | title: 'serverless framework',
35 | link: 'https://serverless.com'
36 | })
37 | })
38 |
39 | app.get('/404', (req, res) => {
40 | res.status(404).send('Not found')
41 | })
42 |
43 | app.get('/500', (req, res) => {
44 | res.status(500).send('Server Error')
45 | })
46 |
47 | // Error handler
48 | app.use(function(err, req, res, next) {
49 | console.error(err)
50 | res.status(500).send('Internal Serverless Error')
51 | })
52 |
53 | if (isServerless) {
54 | module.exports = app
55 | } else {
56 | app.listen(3000, () => {
57 | console.log(`Server start on http://localhost:3000`)
58 | })
59 | }
60 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const { join } = require('path')
2 | require('dotenv').config({ path: join(__dirname, '.env.test') })
3 |
4 | const config = {
5 | verbose: true,
6 | silent: false,
7 | testTimeout: 600000,
8 | testEnvironment: 'node',
9 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$',
10 | testPathIgnorePatterns: ['/node_modules/', '/__tests__/lib/'],
11 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
12 | }
13 |
14 | module.exports = config
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@serverless/express",
3 | "main": "src/serverless.js",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "scripts": {
8 | "test": "jest",
9 | "commitlint": "commitlint -f HEAD@{15}",
10 | "lint": "eslint --ext .js,.ts,.tsx .",
11 | "lint:fix": "eslint --fix --ext .js,.ts,.tsx .",
12 | "prettier": "prettier --check '**/*.{css,html,js,json,md,yaml,yml}'",
13 | "prettier:fix": "prettier --write '**/*.{css,html,js,json,md,yaml,yml}'",
14 | "release": "semantic-release",
15 | "release-local": "node -r dotenv/config node_modules/semantic-release/bin/semantic-release --no-ci --dry-run",
16 | "check-dependencies": "npx npm-check --skip-unused --update"
17 | },
18 | "husky": {
19 | "hooks": {
20 | "pre-commit": "ygsec && lint-staged",
21 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
22 | "pre-push": "ygsec && npm run lint:fix && npm run prettier:fix"
23 | }
24 | },
25 | "lint-staged": {
26 | "**/*.{js,ts,tsx}": [
27 | "npm run lint:fix",
28 | "git add ."
29 | ],
30 | "**/*.{css,html,js,json,md,yaml,yml}": [
31 | "npm run prettier:fix",
32 | "git add ."
33 | ]
34 | },
35 | "author": "Tencent Cloud, Inc.",
36 | "license": "MIT",
37 | "dependencies": {},
38 | "devDependencies": {
39 | "@commitlint/cli": "^8.3.5",
40 | "@commitlint/config-conventional": "^8.3.4",
41 | "@semantic-release/changelog": "^5.0.0",
42 | "@semantic-release/commit-analyzer": "^8.0.1",
43 | "@semantic-release/git": "^9.0.0",
44 | "@semantic-release/npm": "^7.0.4",
45 | "@semantic-release/release-notes-generator": "^9.0.1",
46 | "@serverless/platform-client-china": "^1.0.19",
47 | "@ygkit/secure": "0.0.3",
48 | "axios": "^0.19.2",
49 | "babel-eslint": "^10.1.0",
50 | "dotenv": "^8.2.0",
51 | "eslint": "^6.8.0",
52 | "eslint-config-prettier": "^6.10.0",
53 | "eslint-plugin-import": "^2.20.1",
54 | "eslint-plugin-prettier": "^3.1.2",
55 | "husky": "^4.2.5",
56 | "jest": "^25.0.1",
57 | "lint-staged": "^10.0.8",
58 | "prettier": "^1.19.1",
59 | "semantic-release": "^17.0.4"
60 | },
61 | "description": "Easily deploy serverless Express.js applications to Tencent Cloud with the Serverless Framework",
62 | "directories": {
63 | "doc": "docs",
64 | "example": "example",
65 | "test": "tests"
66 | },
67 | "repository": {
68 | "type": "git",
69 | "url": "git+https://github.com/serverless-components/tencent-express.git"
70 | },
71 | "keywords": [
72 | "serverless-express",
73 | "serverless",
74 | "express",
75 | "serverless-framework",
76 | "serverless-components",
77 | "tencent-cloud"
78 | ],
79 | "bugs": {
80 | "url": "https://github.com/serverless-components/tencent-express/issues"
81 | },
82 | "homepage": "https://github.com/serverless-components/tencent-express#readme"
83 | }
84 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'always',
3 | printWidth: 100,
4 | semi: false,
5 | singleQuote: true,
6 | tabWidth: 2,
7 | trailingComma: 'none'
8 | }
9 |
--------------------------------------------------------------------------------
/release.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | verifyConditions: [
3 | '@semantic-release/changelog',
4 | '@semantic-release/git',
5 | '@semantic-release/github'
6 | ],
7 | plugins: [
8 | [
9 | '@semantic-release/commit-analyzer',
10 | {
11 | preset: 'angular',
12 | parserOpts: {
13 | noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING']
14 | }
15 | }
16 | ],
17 | [
18 | '@semantic-release/release-notes-generator',
19 | {
20 | preset: 'angular',
21 | parserOpts: {
22 | noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING']
23 | },
24 | writerOpts: {
25 | commitsSort: ['subject', 'scope']
26 | }
27 | }
28 | ],
29 | [
30 | '@semantic-release/changelog',
31 | {
32 | changelogFile: 'CHANGELOG.md'
33 | }
34 | ],
35 | [
36 | '@semantic-release/git',
37 | {
38 | assets: ['package.json', 'src/**', 'CHANGELOG.md'],
39 | message: 'chore(release): version ${nextRelease.version} \n\n${nextRelease.notes}'
40 | }
41 | ],
42 | [
43 | '@semantic-release/github',
44 | {
45 | assets: ['!.env']
46 | }
47 | ]
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/serverless.component.yml:
--------------------------------------------------------------------------------
1 | name: express
2 | version: 0.3.0
3 | author: Tencent Cloud, Inc.
4 | org: Tencent Cloud, Inc.
5 | description: Deploy a serverless Express.js application on Tencent SCF and API Gateway.
6 | keywords: tencent, serverless, express
7 | repo: https://github.com/serverless-components/tencent-express
8 | readme: https://github.com/serverless-components/tencent-express/tree/master/README.md
9 | license: MIT
10 | main: ./src
11 | webDeployable: true
12 |
--------------------------------------------------------------------------------
/src/_shims/handler.js:
--------------------------------------------------------------------------------
1 | require('tencent-component-monitor')
2 | const fs = require('fs')
3 | const path = require('path')
4 | const { createServer, proxy } = require('tencent-serverless-http')
5 |
6 | let server
7 | let app
8 |
9 | exports.handler = async (event, context) => {
10 | const userSls = path.join(__dirname, '..', process.env.SLS_ENTRY_FILE)
11 | if (fs.existsSync(userSls)) {
12 | // eslint-disable-next-line
13 | console.log(`Using user custom entry file ${process.env.SLS_ENTRY_FILE}`)
14 | app = require(userSls)
15 | } else {
16 | app = require('./sls.js')
17 | }
18 |
19 | // attach event and context to request
20 | app.request.__SLS_EVENT__ = event
21 | app.request.__SLS_CONTEXT__ = context
22 |
23 | // provide sls intialize hooks
24 | if (app.slsInitialize && typeof app.slsInitialize === 'function') {
25 | await app.slsInitialize()
26 | }
27 |
28 | // cache server, not create repeatly
29 | if (!server) {
30 | server = createServer(app, null, app.binaryTypes || [])
31 | }
32 |
33 | context.callbackWaitsForEmptyEventLoop = app.callbackWaitsForEmptyEventLoop === true
34 |
35 | const result = await proxy(server, event, context, 'PROMISE')
36 | return result.promise
37 | }
38 |
--------------------------------------------------------------------------------
/src/_shims/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@serverless/express",
3 | "main": "./handler.js",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "lint": "eslint . --fix --cache"
10 | },
11 | "author": "Serverless, Inc.",
12 | "license": "Apache",
13 | "dependencies": {
14 | "tencent-component-monitor": "^1.1.0",
15 | "tencent-serverless-http": "^1.2.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/_shims/sls.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const app = express()
3 |
4 | // Routes
5 | app.get(`/`, (req, res) => {
6 | res.send({
7 | msg: `Hello Express, Request received: ${req.method} - ${req.path}`
8 | })
9 | })
10 |
11 | // Error handler
12 | // eslint-disable-next-line
13 | app.use(function(err, req, res, next) {
14 | console.error(err)
15 | res.status(500).send('Internal Serverless Error')
16 | })
17 |
18 | module.exports = app
19 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const CONFIGS = {
2 | templateUrl:
3 | 'https://serverless-templates-1300862921.cos.ap-beijing.myqcloud.com/express-demo.zip',
4 | compName: 'express',
5 | compFullname: 'Express.js',
6 | defaultEntryFile: 'sls.js',
7 | handler: 'sl_handler.handler',
8 | runtime: 'Nodejs10.15',
9 | timeout: 3,
10 | memorySize: 128,
11 | namespace: 'default',
12 | description: 'Created by Serverless Component'
13 | }
14 |
15 | module.exports = CONFIGS
16 |
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "download": "^8.0.0",
4 | "tencent-component-toolkit": "^1.20.10",
5 | "type": "^2.0.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/serverless.js:
--------------------------------------------------------------------------------
1 | const { Component } = require('@serverless/core')
2 | const { Scf, Apigw, Cns, Cam, Metrics } = require('tencent-component-toolkit')
3 | const { TypeError } = require('tencent-component-toolkit/src/utils/error')
4 | const { uploadCodeToCos, getDefaultProtocol, prepareInputs, deepClone } = require('./utils')
5 | const CONFIGS = require('./config')
6 |
7 | class ServerlessComponent extends Component {
8 | getCredentials() {
9 | const { tmpSecrets } = this.credentials.tencent
10 |
11 | if (!tmpSecrets || !tmpSecrets.TmpSecretId) {
12 | throw new TypeError(
13 | 'CREDENTIAL',
14 | 'Cannot get secretId/Key, your account could be sub-account and does not have the access to use SLS_QcsRole, please make sure the role exists first, then visit https://cloud.tencent.com/document/product/1154/43006, follow the instructions to bind the role to your account.'
15 | )
16 | }
17 |
18 | return {
19 | SecretId: tmpSecrets.TmpSecretId,
20 | SecretKey: tmpSecrets.TmpSecretKey,
21 | Token: tmpSecrets.Token
22 | }
23 | }
24 |
25 | getAppId() {
26 | return this.credentials.tencent.tmpSecrets.appId
27 | }
28 |
29 | async deployFunction(credentials, inputs, regionList) {
30 | if (!inputs.role) {
31 | try {
32 | const camClient = new Cam(credentials)
33 | const roleExist = await camClient.CheckSCFExcuteRole()
34 | if (roleExist) {
35 | inputs.role = 'QCS_SCFExcuteRole'
36 | }
37 | } catch (e) {
38 | // no op
39 | }
40 | }
41 |
42 | const outputs = {}
43 | const appId = this.getAppId()
44 |
45 | const funcDeployer = async (curRegion) => {
46 | const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
47 | const scf = new Scf(credentials, curRegion)
48 | const tempInputs = {
49 | ...inputs,
50 | code
51 | }
52 | const scfOutput = await scf.deploy(deepClone(tempInputs))
53 | outputs[curRegion] = {
54 | functionName: scfOutput.FunctionName,
55 | runtime: scfOutput.Runtime,
56 | namespace: scfOutput.Namespace
57 | }
58 |
59 | this.state[curRegion] = {
60 | ...(this.state[curRegion] ? this.state[curRegion] : {}),
61 | ...outputs[curRegion]
62 | }
63 |
64 | // default version is $LATEST
65 | outputs[curRegion].lastVersion = scfOutput.LastVersion
66 | ? scfOutput.LastVersion
67 | : this.state.lastVersion || '$LATEST'
68 |
69 | // default traffic is 1.0, it can also be 0, so we should compare to undefined
70 | outputs[curRegion].traffic =
71 | scfOutput.Traffic !== undefined
72 | ? scfOutput.Traffic
73 | : this.state.traffic !== undefined
74 | ? this.state.traffic
75 | : 1
76 |
77 | if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
78 | outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
79 | this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
80 | }
81 |
82 | this.state.lastVersion = outputs[curRegion].lastVersion
83 | this.state.traffic = outputs[curRegion].traffic
84 | }
85 |
86 | for (let i = 0; i < regionList.length; i++) {
87 | const curRegion = regionList[i]
88 | await funcDeployer(curRegion)
89 | }
90 | this.save()
91 | return outputs
92 | }
93 |
94 | // try to add dns record
95 | async tryToAddDnsRecord(credentials, customDomains) {
96 | try {
97 | const cns = new Cns(credentials)
98 | for (let i = 0; i < customDomains.length; i++) {
99 | const item = customDomains[i]
100 | if (item.domainPrefix) {
101 | await cns.deploy({
102 | domain: item.subDomain.replace(`${item.domainPrefix}.`, ''),
103 | records: [
104 | {
105 | subDomain: item.domainPrefix,
106 | recordType: 'CNAME',
107 | recordLine: '默认',
108 | value: item.cname,
109 | ttl: 600,
110 | mx: 10,
111 | status: 'enable'
112 | }
113 | ]
114 | })
115 | }
116 | }
117 | } catch (e) {
118 | console.log('METHOD_tryToAddDnsRecord', e.message)
119 | }
120 | }
121 |
122 | async deployApigateway(credentials, inputs, regionList) {
123 | if (inputs.isDisabled) {
124 | return {}
125 | }
126 |
127 | const getServiceId = (instance, region) => {
128 | const regionState = instance.state[region]
129 | return inputs.serviceId || (regionState && regionState.serviceId)
130 | }
131 |
132 | const deployTasks = []
133 | const outputs = {}
134 | regionList.forEach((curRegion) => {
135 | const apigwDeployer = async () => {
136 | const apigw = new Apigw(credentials, curRegion)
137 |
138 | const oldState = this.state[curRegion] || {}
139 | const apigwInputs = {
140 | ...inputs,
141 | oldState: {
142 | apiList: oldState.apiList || [],
143 | customDomains: oldState.customDomains || []
144 | }
145 | }
146 | // different region deployment has different service id
147 | apigwInputs.serviceId = getServiceId(this, curRegion)
148 | const apigwOutput = await apigw.deploy(deepClone(apigwInputs))
149 | outputs[curRegion] = {
150 | serviceId: apigwOutput.serviceId,
151 | subDomain: apigwOutput.subDomain,
152 | environment: apigwOutput.environment,
153 | url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${
154 | apigwOutput.environment
155 | }${apigwInputs.endpoints[0].path}`
156 | }
157 |
158 | if (apigwOutput.customDomains) {
159 | // TODO: need confirm add cns authentication
160 | if (inputs.autoAddDnsRecord === true) {
161 | // await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains)
162 | }
163 | outputs[curRegion].customDomains = apigwOutput.customDomains
164 | }
165 | this.state[curRegion] = {
166 | created: true,
167 | ...(this.state[curRegion] ? this.state[curRegion] : {}),
168 | ...outputs[curRegion],
169 | apiList: apigwOutput.apiList
170 | }
171 | }
172 | deployTasks.push(apigwDeployer())
173 | })
174 |
175 | await Promise.all(deployTasks)
176 |
177 | this.save()
178 | return outputs
179 | }
180 |
181 | async deploy(inputs) {
182 | console.log(`Deploying ${CONFIGS.compFullname} App...`)
183 |
184 | const credentials = this.getCredentials()
185 |
186 | // 对Inputs内容进行标准化
187 | const { regionList, functionConf, apigatewayConf } = await prepareInputs(
188 | this,
189 | credentials,
190 | inputs
191 | )
192 |
193 | // 部署函数 + API网关
194 | const outputs = {}
195 | if (!functionConf.code.src) {
196 | outputs.templateUrl = CONFIGS.templateUrl
197 | }
198 |
199 | let apigwOutputs
200 | const functionOutputs = await this.deployFunction(
201 | credentials,
202 | functionConf,
203 | regionList,
204 | outputs
205 | )
206 | // support apigatewayConf.isDisabled
207 | if (apigatewayConf.isDisabled !== true) {
208 | apigwOutputs = await this.deployApigateway(credentials, apigatewayConf, regionList, outputs)
209 | } else {
210 | this.state.apigwDisabled = true
211 | }
212 |
213 | // optimize outputs for one region
214 | if (regionList.length === 1) {
215 | const [oneRegion] = regionList
216 | outputs.region = oneRegion
217 | outputs['scf'] = functionOutputs[oneRegion]
218 | if (apigwOutputs) {
219 | outputs['apigw'] = apigwOutputs[oneRegion]
220 | }
221 | } else {
222 | outputs['scf'] = functionOutputs
223 | if (apigwOutputs) {
224 | outputs['apigw'] = apigwOutputs
225 | }
226 | }
227 |
228 | this.state.region = regionList[0]
229 | this.state.regionList = regionList
230 | this.state.lambdaArn = functionConf.name
231 |
232 | return outputs
233 | }
234 |
235 | async remove() {
236 | console.log(`Removing ${CONFIGS.compFullname} App...`)
237 |
238 | const { state } = this
239 | const { regionList = [] } = state
240 |
241 | const credentials = this.getCredentials()
242 |
243 | const removeHandlers = []
244 | for (let i = 0; i < regionList.length; i++) {
245 | const curRegion = regionList[i]
246 | const curState = state[curRegion]
247 | const scf = new Scf(credentials, curRegion)
248 | const apigw = new Apigw(credentials, curRegion)
249 | const handler = async () => {
250 | // if disable apigw, no need to remove
251 | if (state.apigwDisabled !== true) {
252 | await apigw.remove({
253 | created: curState.created,
254 | environment: curState.environment,
255 | serviceId: curState.serviceId,
256 | apiList: curState.apiList,
257 | customDomains: curState.customDomains
258 | })
259 | }
260 | await scf.remove({
261 | functionName: curState.functionName,
262 | namespace: curState.namespace
263 | })
264 | }
265 | removeHandlers.push(handler())
266 | }
267 |
268 | await Promise.all(removeHandlers)
269 |
270 | if (this.state.cns) {
271 | const cns = new Cns(credentials)
272 | for (let i = 0; i < this.state.cns.length; i++) {
273 | await cns.remove({ deleteList: this.state.cns[i].records })
274 | }
275 | }
276 |
277 | this.state = {}
278 | }
279 |
280 | async metrics(inputs = {}) {
281 | console.log(`Get ${CONFIGS.compFullname} Metrics Datas...`)
282 | if (!inputs.rangeStart || !inputs.rangeEnd) {
283 | throw new TypeError(
284 | `PARAMETER_${CONFIGS.compName.toUpperCase()}_METRICS`,
285 | 'rangeStart and rangeEnd are require inputs'
286 | )
287 | }
288 | const { region } = this.state
289 | if (!region) {
290 | throw new TypeError(
291 | `PARAMETER_${CONFIGS.compName.toUpperCase()}_METRICS`,
292 | 'No region property in state'
293 | )
294 | }
295 | const { functionName, namespace, functionVersion } = this.state[region] || {}
296 | if (functionName) {
297 | const options = {
298 | funcName: functionName,
299 | namespace: namespace,
300 | version: functionVersion,
301 | region,
302 | timezone: inputs.tz
303 | }
304 |
305 | const curState = this.state[region]
306 | if (curState.serviceId) {
307 | options.apigwServiceId = curState.serviceId
308 | options.apigwEnvironment = curState.environment || 'release'
309 | }
310 | const credentials = this.getCredentials()
311 | const mertics = new Metrics(credentials, options)
312 | const metricResults = await mertics.getDatas(
313 | inputs.rangeStart,
314 | inputs.rangeEnd,
315 | Metrics.Type.All
316 | )
317 | return metricResults
318 | }
319 | throw new TypeError(
320 | `PARAMETER_${CONFIGS.compName.toUpperCase()}_METRICS`,
321 | 'Function name not define'
322 | )
323 | }
324 | }
325 |
326 | module.exports = ServerlessComponent
327 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const { Cos } = require('tencent-component-toolkit')
3 | const ensureObject = require('type/object/ensure')
4 | const ensureIterable = require('type/iterable/ensure')
5 | const ensureString = require('type/string/ensure')
6 | const download = require('download')
7 | const { TypeError } = require('tencent-component-toolkit/src/utils/error')
8 | const CONFIGS = require('./config')
9 |
10 | /*
11 | * Generates a random id
12 | */
13 | const generateId = () =>
14 | Math.random()
15 | .toString(36)
16 | .substring(6)
17 |
18 | const deepClone = (obj) => {
19 | return JSON.parse(JSON.stringify(obj))
20 | }
21 |
22 | const getType = (obj) => {
23 | return Object.prototype.toString.call(obj).slice(8, -1)
24 | }
25 |
26 | const mergeJson = (sourceJson, targetJson) => {
27 | Object.entries(sourceJson).forEach(([key, val]) => {
28 | targetJson[key] = deepClone(val)
29 | })
30 | return targetJson
31 | }
32 |
33 | const capitalString = (str) => {
34 | if (str.length < 2) {
35 | return str.toUpperCase()
36 | }
37 |
38 | return `${str[0].toUpperCase()}${str.slice(1)}`
39 | }
40 |
41 | const getDefaultProtocol = (protocols) => {
42 | return String(protocols).includes('https') ? 'https' : 'http'
43 | }
44 |
45 | const getDefaultFunctionName = () => {
46 | return `${CONFIGS.compName}_component_${generateId()}`
47 | }
48 |
49 | const getDefaultServiceName = () => {
50 | return 'serverless'
51 | }
52 |
53 | const getDefaultServiceDescription = () => {
54 | return 'Created by Serverless Component'
55 | }
56 |
57 | const validateTraffic = (num) => {
58 | if (getType(num) !== 'Number') {
59 | throw new TypeError(
60 | `PARAMETER_${CONFIGS.compName.toUpperCase()}_TRAFFIC`,
61 | 'traffic must be a number'
62 | )
63 | }
64 | if (num < 0 || num > 1) {
65 | throw new TypeError(
66 | `PARAMETER_${CONFIGS.compName.toUpperCase()}_TRAFFIC`,
67 | 'traffic must be a number between 0 and 1'
68 | )
69 | }
70 | return true
71 | }
72 |
73 | const getCodeZipPath = async (instance, inputs) => {
74 | console.log(`Packaging ${CONFIGS.compFullname} application...`)
75 |
76 | // unzip source zip file
77 | let zipPath
78 | if (!inputs.code.src) {
79 | // add default template
80 | const downloadPath = `/tmp/${generateId()}`
81 | const filename = 'template'
82 |
83 | console.log(`Installing Default ${CONFIGS.compFullname} App...`)
84 | try {
85 | await download(CONFIGS.templateUrl, downloadPath, {
86 | filename: `${filename}.zip`
87 | })
88 | } catch (e) {
89 | throw new TypeError(`DOWNLOAD_TEMPLATE`, 'Download default template failed.')
90 | }
91 | zipPath = `${downloadPath}/${filename}.zip`
92 | } else {
93 | zipPath = inputs.code.src
94 | }
95 |
96 | return zipPath
97 | }
98 |
99 | /**
100 | * Upload code to COS
101 | * @param {Component} instance serverless component instance
102 | * @param {string} appId app id
103 | * @param {object} credentials credentials
104 | * @param {object} inputs component inputs parameters
105 | * @param {string} region region
106 | */
107 | const uploadCodeToCos = async (instance, appId, credentials, inputs, region) => {
108 | const bucketName = inputs.code.bucket || `sls-cloudfunction-${region}-code`
109 | const objectName = inputs.code.object || `${inputs.name}-${Math.floor(Date.now() / 1000)}.zip`
110 | // if set bucket and object not pack code
111 | if (!inputs.code.bucket || !inputs.code.object) {
112 | const zipPath = await getCodeZipPath(instance, inputs)
113 | console.log(`Code zip path ${zipPath}`)
114 |
115 | // save the zip path to state for lambda to use it
116 | instance.state.zipPath = zipPath
117 |
118 | const cos = new Cos(credentials, region)
119 |
120 | if (!inputs.code.bucket) {
121 | // create default bucket
122 | await cos.deploy({
123 | bucket: bucketName + '-' + appId,
124 | force: true,
125 | lifecycle: [
126 | {
127 | status: 'Enabled',
128 | id: 'deleteObject',
129 | filter: '',
130 | expiration: { days: '10' },
131 | abortIncompleteMultipartUpload: { daysAfterInitiation: '10' }
132 | }
133 | ]
134 | })
135 | }
136 |
137 | // upload code to cos
138 | if (!inputs.code.object) {
139 | console.log(`Getting cos upload url for bucket ${bucketName}`)
140 | const uploadUrl = await cos.getObjectUrl({
141 | bucket: bucketName + '-' + appId,
142 | object: objectName,
143 | method: 'PUT'
144 | })
145 |
146 | // if shims and sls sdk entries had been injected to zipPath, no need to injected again
147 | console.log(`Uploading code to bucket ${bucketName}`)
148 | if (instance.codeInjected === true) {
149 | await instance.uploadSourceZipToCOS(zipPath, uploadUrl, {}, {})
150 | } else {
151 | const slsSDKEntries = instance.getSDKEntries('_shims/handler.handler')
152 | await instance.uploadSourceZipToCOS(zipPath, uploadUrl, slsSDKEntries, {
153 | _shims: path.join(__dirname, '_shims')
154 | })
155 | instance.codeInjected = true
156 | }
157 | console.log(`Upload ${objectName} to bucket ${bucketName} success`)
158 | }
159 | }
160 |
161 | // save bucket state
162 | instance.state.bucket = bucketName
163 | instance.state.object = objectName
164 |
165 | return {
166 | bucket: bucketName,
167 | object: objectName
168 | }
169 | }
170 |
171 | const prepareInputs = async (instance, credentials, inputs = {}) => {
172 | // 对function inputs进行标准化
173 | const tempFunctionConf = inputs.functionConf
174 | ? inputs.functionConf
175 | : inputs.functionConfig
176 | ? inputs.functionConfig
177 | : {}
178 | const fromClientRemark = `tencent-${CONFIGS.compName}`
179 | const regionList = inputs.region
180 | ? typeof inputs.region == 'string'
181 | ? [inputs.region]
182 | : inputs.region
183 | : ['ap-guangzhou']
184 |
185 | // chenck state function name
186 | const stateFunctionName =
187 | instance.state[regionList[0]] && instance.state[regionList[0]].functionName
188 | const functionConf = Object.assign(tempFunctionConf, {
189 | code: {
190 | src: inputs.src,
191 | bucket: inputs.srcOriginal && inputs.srcOriginal.bucket,
192 | object: inputs.srcOriginal && inputs.srcOriginal.object
193 | },
194 | name:
195 | ensureString(inputs.functionName, { isOptional: true }) ||
196 | stateFunctionName ||
197 | getDefaultFunctionName(),
198 | region: regionList,
199 | role: ensureString(tempFunctionConf.role ? tempFunctionConf.role : inputs.role, {
200 | default: ''
201 | }),
202 | handler: ensureString(tempFunctionConf.handler ? tempFunctionConf.handler : inputs.handler, {
203 | default: CONFIGS.handler
204 | }),
205 | runtime: ensureString(tempFunctionConf.runtime ? tempFunctionConf.runtime : inputs.runtime, {
206 | default: CONFIGS.runtime
207 | }),
208 | namespace: ensureString(
209 | tempFunctionConf.namespace ? tempFunctionConf.namespace : inputs.namespace,
210 | { default: CONFIGS.namespace }
211 | ),
212 | description: ensureString(
213 | tempFunctionConf.description ? tempFunctionConf.description : inputs.description,
214 | {
215 | default: CONFIGS.description
216 | }
217 | ),
218 | fromClientRemark,
219 | layers: ensureIterable(tempFunctionConf.layers ? tempFunctionConf.layers : inputs.layers, {
220 | default: []
221 | }),
222 | cfs: ensureIterable(tempFunctionConf.cfs ? tempFunctionConf.cfs : inputs.cfs, {
223 | default: []
224 | }),
225 | publish: inputs.publish,
226 | traffic: inputs.traffic,
227 | lastVersion: instance.state.lastVersion,
228 | timeout: tempFunctionConf.timeout ? tempFunctionConf.timeout : CONFIGS.timeout,
229 | memorySize: tempFunctionConf.memorySize ? tempFunctionConf.memorySize : CONFIGS.memorySize,
230 | tags: ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, {
231 | default: null
232 | })
233 | })
234 |
235 | // validate traffic
236 | if (inputs.traffic !== undefined) {
237 | validateTraffic(inputs.traffic)
238 | }
239 | functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion
240 |
241 | if (tempFunctionConf.environment) {
242 | functionConf.environment = tempFunctionConf.environment
243 | functionConf.environment.variables = functionConf.environment.variables || {}
244 | functionConf.environment.variables.SERVERLESS = '1'
245 | functionConf.environment.variables.SLS_ENTRY_FILE = inputs.entryFile || CONFIGS.defaultEntryFile
246 | } else {
247 | functionConf.environment = {
248 | variables: {
249 | SERVERLESS: '1',
250 | SLS_ENTRY_FILE: inputs.entryFile || CONFIGS.defaultEntryFile
251 | }
252 | }
253 | }
254 |
255 | if (tempFunctionConf.vpcConfig) {
256 | functionConf.vpcConfig = tempFunctionConf.vpcConfig
257 | }
258 |
259 | // 对apigw inputs进行标准化
260 | const tempApigwConf = inputs.apigatewayConf
261 | ? inputs.apigatewayConf
262 | : inputs.apigwConfig
263 | ? inputs.apigwConfig
264 | : {}
265 | const apigatewayConf = Object.assign(tempApigwConf, {
266 | serviceId: inputs.serviceId || tempApigwConf.serviceId,
267 | region: regionList,
268 | isDisabled: tempApigwConf.isDisabled === true,
269 | fromClientRemark: fromClientRemark,
270 | serviceName: inputs.serviceName || tempApigwConf.serviceName || getDefaultServiceName(instance),
271 | serviceDesc: tempApigwConf.serviceDesc || getDefaultServiceDescription(instance),
272 | protocols: tempApigwConf.protocols || ['http'],
273 | environment: tempApigwConf.environment ? tempApigwConf.environment : 'release',
274 | customDomains: tempApigwConf.customDomains || []
275 | })
276 | if (!apigatewayConf.endpoints) {
277 | apigatewayConf.endpoints = [
278 | {
279 | path: tempApigwConf.path || '/',
280 | enableCORS: tempApigwConf.enableCORS,
281 | serviceTimeout: tempApigwConf.serviceTimeout,
282 | method: 'ANY',
283 | apiName: tempApigwConf.apiName || 'index',
284 | isBase64Encoded: tempApigwConf.isBase64Encoded,
285 | isBase64Trigger: tempApigwConf.isBase64Trigger,
286 | base64EncodedTriggerRules: tempApigwConf.base64EncodedTriggerRules,
287 | function: {
288 | isIntegratedResponse: true,
289 | functionName: functionConf.name,
290 | functionNamespace: functionConf.namespace,
291 | functionQualifier:
292 | (tempApigwConf.function && tempApigwConf.function.functionQualifier) || '$LATEST'
293 | }
294 | }
295 | ]
296 | }
297 | if (tempApigwConf.usagePlan) {
298 | apigatewayConf.endpoints[0].usagePlan = {
299 | usagePlanId: tempApigwConf.usagePlan.usagePlanId,
300 | usagePlanName: tempApigwConf.usagePlan.usagePlanName,
301 | usagePlanDesc: tempApigwConf.usagePlan.usagePlanDesc,
302 | maxRequestNum: tempApigwConf.usagePlan.maxRequestNum
303 | }
304 | }
305 | if (tempApigwConf.auth) {
306 | apigatewayConf.endpoints[0].auth = {
307 | secretName: tempApigwConf.auth.secretName,
308 | secretIds: tempApigwConf.auth.secretIds
309 | }
310 | }
311 |
312 | regionList.forEach((curRegion) => {
313 | const curRegionConf = inputs[curRegion]
314 | if (curRegionConf && curRegionConf.functionConf) {
315 | functionConf[curRegion] = curRegionConf.functionConf
316 | }
317 | if (curRegionConf && curRegionConf.apigatewayConf) {
318 | apigatewayConf[curRegion] = curRegionConf.apigatewayConf
319 | }
320 | })
321 |
322 | return {
323 | regionList,
324 | functionConf,
325 | apigatewayConf
326 | }
327 | }
328 |
329 | module.exports = {
330 | deepClone,
331 | generateId,
332 | uploadCodeToCos,
333 | mergeJson,
334 | capitalString,
335 | getDefaultProtocol,
336 | prepareInputs
337 | }
338 |
--------------------------------------------------------------------------------