├── .npmignore
├── .npmrc
├── .eslintignore
├── .prettierignore
├── src
├── _shims
│ ├── package.json
│ ├── sls.js
│ └── handler.js
├── package.json
├── config.js
├── serverless.js
└── utils.js
├── prettier.config.js
├── commitlint.config.js
├── .editorconfig
├── .gitignore
├── example
├── package.json
├── serverless.yml
├── index.html
├── sls.js
├── sls.upload.js
└── README.md
├── serverless.component.yml
├── jest.config.js
├── __tests__
├── lib
│ └── utils.js
└── index.test.js
├── docs
├── upload.md
├── output.md
└── configure.md
├── LICENSE
├── release.config.js
├── .github
└── workflows
│ ├── validate.yml
│ ├── release.yml
│ └── test.yml
├── .eslintrc.js
├── package.json
├── README.md
└── CHANGELOG.md
/.npmignore:
--------------------------------------------------------------------------------
1 | test
2 | example
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
4 | example
5 | *.test.js
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
4 | CHANGELOG.md
5 | *.test.js
6 |
--------------------------------------------------------------------------------
/src/_shims/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "tencent-serverless-http": "^1.3.1"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "download": "^8.0.0",
4 | "tencent-component-toolkit": "1.20.10",
5 | "type": "^2.1.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/_shims/sls.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa')
2 |
3 | const app = new Koa()
4 |
5 | app.use(async (ctx, next) => {
6 | if (ctx.path !== '/') {
7 | return next()
8 | }
9 | ctx.body = 'Hello from Koa'
10 | })
11 |
12 | // don't forget to export!
13 | module.exports = app
14 |
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "koa-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 | "@koa/router": "^10.0.0",
13 | "koa": "^2.13.1",
14 | "koa-sendfile": "^2.0.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example/serverless.yml:
--------------------------------------------------------------------------------
1 | app: appDemo
2 | stage: dev
3 | component: koa
4 | name: koaDemo
5 |
6 | inputs:
7 | src:
8 | src: ./ # (optional) path to the source folder. default is a hello world app.
9 | exclude:
10 | - .env
11 | region: ap-guangzhou
12 | runtime: Nodejs10.15
13 | apigatewayConf:
14 | protocols:
15 | - http
16 | - https
17 | environment: release
18 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const CONFIGS = {
2 | templateUrl: 'https://serverless-templates-1300862921.cos.ap-beijing.myqcloud.com/koa-demo.zip',
3 | compName: 'koa',
4 | compFullname: 'Koa.js',
5 | defaultEntryFile: 'sls.js',
6 | handler: 'sl_handler.handler',
7 | runtime: 'Nodejs10.15',
8 | timeout: 3,
9 | memorySize: 128,
10 | namespace: 'default',
11 | description: 'Created by Serverless Component'
12 | }
13 |
14 | module.exports = CONFIGS
15 |
--------------------------------------------------------------------------------
/serverless.component.yml:
--------------------------------------------------------------------------------
1 | name: koa
2 | version: 0.4.0
3 | author: 'Tencent Cloud, Inc.'
4 | org: 'Tencent Cloud, Inc.'
5 | description: Deploy a serverless Koa.js application onto Tencent SCF and API Gateway.
6 | keywords: 'tencent, serverless, koa'
7 | repo: 'https://github.com/serverless-components/tencent-koa/'
8 | readme: 'https://github.com/serverless-components/tencent-koa/tree/master/README.md'
9 | license: MIT
10 | main: ./src
11 | webDeployable: true
12 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/__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 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Serverless Component - Express.js
7 |
14 |
15 |
16 |
17 | Welcome to Koa.js application created by
18 | Serverless Framework.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/example/sls.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa')
2 | const KoaRouter = require('@koa/router')
3 | const sendFile = require('koa-sendfile')
4 | const path = require('path')
5 |
6 | const app = new Koa()
7 | const router = new KoaRouter()
8 | const isServerless = process.env.SERVERLESS
9 | const PORT = 3000
10 |
11 | // Routes
12 | router.get(`/`, async (ctx) => {
13 | await sendFile(ctx, path.join(__dirname, 'index.html'))
14 | })
15 |
16 | app.use(router.allowedMethods()).use(router.routes())
17 |
18 | // don't forget to export!
19 | if (isServerless) {
20 | module.exports = app
21 | } else {
22 | app.listen(PORT, () => {
23 | console.log(`Server start on http://localhost:${PORT}`)
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/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: koa
9 | name: koaDemo
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`,如果要支持文件上传,需要安装 `@koajs/multer` 和 `multer` 包。
30 |
31 | 同时需要在 `serverless.yml` 的 `apigatewayConf` 中配置 `isBase64Encoded` 为 `true`。
32 |
--------------------------------------------------------------------------------
/example/sls.upload.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa')
2 | const KoaRouter = require('@koa/router')
3 | const multer = require('@koa/multer')
4 | const sendFile = require('koa-sendfile')
5 | const path = require('path')
6 |
7 | const isServerless = process.env.SERVERLESS
8 | const app = new Koa()
9 | const router = new KoaRouter()
10 | const upload = multer({ dest: isServerless ? '/tmp/upload' : './upload' })
11 |
12 | router.get(`/`, async (ctx) => {
13 | await sendFile(ctx, path.join(__dirname, 'index.html'))
14 | })
15 | router.post('/upload', upload.single('file'), (ctx) => {
16 | ctx.body = {
17 | success: true,
18 | data: ctx.file
19 | }
20 | })
21 |
22 | app.use(router.routes()).use(router.allowedMethods())
23 |
24 | if (isServerless) {
25 | module.exports = app
26 | } else {
27 | app.listen(3000, () => {
28 | console.log(`Server start on http://localhost:3000`)
29 | })
30 | }
31 |
--------------------------------------------------------------------------------
/src/_shims/handler.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const { createServer, proxy } = require('tencent-serverless-http')
4 |
5 | let server
6 | let app
7 |
8 | exports.handler = async (event, context) => {
9 | const userSls = path.join(__dirname, '..', process.env.SLS_ENTRY_FILE)
10 | if (fs.existsSync(userSls)) {
11 | // eslint-disable-next-line
12 | console.log(`Using user custom entry file ${process.env.SLS_ENTRY_FILE}`)
13 | app = require(userSls)
14 | } else {
15 | app = require('./sls.js')
16 | }
17 |
18 | app.request.__SLS_EVENT__ = event
19 | app.request.__SLS_CONTEXT__ = context
20 |
21 | if (app.slsInitialize && typeof app.slsInitialize === 'function') {
22 | await app.slsInitialize()
23 | }
24 |
25 | // cache server, not create repeatly
26 | if (!server) {
27 | server = createServer(app.callback(), null, app.binaryTypes || [])
28 | }
29 |
30 | context.callbackWaitsForEmptyEventLoop =
31 | app.callbackWaitsForEmptyEventLoop === true ? true : false
32 |
33 | const result = await proxy(server, event, context, 'PROMISE')
34 | return result.promise
35 | }
36 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@serverless/koa",
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 | }
62 |
--------------------------------------------------------------------------------
/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | const { generateId, getServerlessSdk } = require('./lib/utils')
2 | const execSync = require('child_process').execSync
3 | const path = require('path')
4 | const axios = require('axios')
5 |
6 | const instanceYaml = {
7 | org: 'orgDemo',
8 | app: 'appDemo',
9 | component: 'koa@dev',
10 | name: `koa-integration-tests-${generateId()}`,
11 | stage: 'dev',
12 | inputs: {
13 | region: 'ap-guangzhou',
14 | runtime: 'Nodejs10.15',
15 | apigatewayConf: { environment: 'test' }
16 | }
17 | }
18 |
19 | const credentials = {
20 | tencent: {
21 | SecretId: process.env.TENCENT_SECRET_ID,
22 | SecretKey: process.env.TENCENT_SECRET_KEY,
23 | }
24 | }
25 |
26 | // get serverless construct sdk
27 | const sdk = getServerlessSdk(instanceYaml.org)
28 |
29 | it('deploy koa app by template', async () => {
30 | const instance = await sdk.deploy(instanceYaml, credentials)
31 | expect(instance).toBeDefined()
32 | expect(instance.instanceName).toEqual(instanceYaml.name)
33 | // get src from template by default
34 | expect(instance.outputs.templateUrl).toBeDefined()
35 | expect(instance.outputs.region).toEqual(instanceYaml.inputs.region)
36 | expect(instance.outputs.scf).toBeDefined()
37 | expect(instance.outputs.scf.runtime).toEqual(instanceYaml.inputs.runtime)
38 | expect(instance.outputs.apigw).toBeDefined()
39 | expect(instance.outputs.apigw.environment).toEqual(instanceYaml.inputs.apigatewayConf.environment)
40 | })
41 |
42 | it('deploy with src code', async () => {
43 | const srcPath = path.join(__dirname, '..', 'example')
44 | execSync('npm install', { cwd: srcPath })
45 | instanceYaml.inputs.src = srcPath
46 |
47 | const instance = await sdk.deploy(instanceYaml, credentials)
48 | const response = await axios.get(instance.outputs.apigw.url)
49 |
50 | expect(response.data).toContain('Serverless Framework')
51 | expect(instance.outputs.templateUrl).not.toBeDefined()
52 | })
53 |
54 | it('remove koa app', async () => {
55 | await sdk.remove(instanceYaml, credentials)
56 | result = await sdk.getInstance(instanceYaml.org, instanceYaml.stage, instanceYaml.app, instanceYaml.name)
57 |
58 | expect(result.instance.instanceStatus).toEqual('inactive')
59 | })
60 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | [](http://serverless.com)
2 |
3 | # 腾讯云 Koa 组件
4 |
5 | 简体中文 | [English](https://github.com/serverless-components/tencent-koa/tree/master/README.en.md)
6 |
7 | ## 简介
8 |
9 | 使用腾讯云 Koa 组件,可快速的在腾讯云创建,配置和管理一个 [Koa 框架](https://koajs.com/) 服务。
10 |
11 | ## 目录
12 |
13 | 1. [安装](#1-安装)
14 | 2. [创建](#2-创建)
15 | 3. [配置](#3-配置)
16 | 4. [部署](#4-部署)
17 | 5. [移除](#5-移除)
18 |
19 | ### 1. 安装
20 |
21 | 通过 npm 安装 serverless
22 |
23 | ```bash
24 | $ npm install -g serverless
25 | ```
26 |
27 | ### 2. 创建
28 |
29 | 本地创建 `serverless.yml` 文件:
30 |
31 | ```bash
32 | $ touch serverless.yml
33 | ```
34 |
35 | 初始化一个新的 npm 包,并安装 koa:
36 |
37 | ```bash
38 | # 创建后持续回车
39 | $ npm init
40 |
41 | # 安装 koa
42 | $ npm i --save koa
43 | ```
44 |
45 | 创建一个 `sls.js`文件,并在其中创建您的 koa App:
46 |
47 | ```bash
48 | $ touch sls.js
49 | ```
50 |
51 | ```js
52 | const koa = require('koa')
53 | const app = new koa()
54 |
55 | app.use(async (ctx, next) => {
56 | if (ctx.path !== '/') return next()
57 | ctx.body = 'Hello from Koa'
58 | })
59 |
60 | // set binary types
61 | // app.binaryTypes = [*/*];
62 |
63 | // don't forget to export!
64 | module.exports = app
65 | ```
66 |
67 | ### 3. 配置
68 |
69 | 在 serverless.yml 中进行如下配置
70 |
71 | ```yml
72 | # serverless.yml
73 |
74 | org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
75 | app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
76 | stage: dev # (optional) serverless dashboard stage. default is dev.
77 | component: koa # (required) name of the component. In that case, it's koa.
78 | name: koaDemo # (required) name of your koa component instance.
79 |
80 | inputs:
81 | src:
82 | src: ./ # (optional) path to the source folder. default is a hello world app.
83 | exclude:
84 | - .env
85 | region: ap-guangzhou
86 | runtime: Nodejs10.15
87 | apigatewayConf:
88 | protocols:
89 | - http
90 | - https
91 | environment: release
92 | ```
93 |
94 | - [点击此处查看配置文档](https://github.com/serverless-components/tencent-koa/tree/master/docs/configure.md)
95 |
96 | ### 4. 部署
97 |
98 | 如您的账号未[登陆](https://cloud.tencent.com/login)或[注册](https://cloud.tencent.com/register)腾讯云,您可以直接通过`微信`扫描命令行中的二维码进行授权登陆和注册。
99 |
100 | 通过`sls`命令进行部署,并可以添加`--debug`参数查看部署过程中的信息
101 |
102 | > 注:`sls`命令是`serverless`命令的缩写
103 |
104 | ```
105 | $ sls deploy
106 | ```
107 |
108 | 部署完毕后,可以在浏览器中访问部署成功地 url。
109 |
110 | ### 5. 移除
111 |
112 | 通过以下命令移除部署的 Koa 服务。
113 |
114 | ```
115 | $ sls remove
116 | ```
117 |
118 | ### 账号配置(可选)
119 |
120 | 当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件
121 |
122 | ```bash
123 | $ touch .env # 腾讯云的配置信息
124 | ```
125 |
126 | 在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存
127 |
128 | 如果没有腾讯云账号,可以在此[注册新账号](https://cloud.tencent.com/register)。
129 |
130 | 如果已有腾讯云账号,可以在[API 密钥管理](https://bash.cloud.tencent.com/cam/capi)中获取 `SecretId` 和`SecretKey`.
131 |
132 | ```
133 | # .env
134 | TENCENT_SECRET_ID=123
135 | TENCENT_SECRET_KEY=123
136 | ```
137 |
138 | ### 还支持哪些组件?
139 |
140 | 可以在 [Serverless Components](https://github.com/serverless/components) repo 中查询更多组件的信息。
141 |
142 | ## License
143 |
144 | MIT License
145 |
146 | Copyright (c) 2020 Tencent Cloud, Inc.
147 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ⚠️⚠️⚠️ 所有框架组件项目迁移到 [tencent-framework-components](https://github.com/serverless-components/tencent-framework-components).
2 |
3 | [](http://serverless.com)
4 |
5 | # 腾讯云 Koa 组件
6 |
7 | ## 简介
8 |
9 | 使用腾讯云 Koa 组件,可快速的在腾讯云创建,配置和管理一个 [Koa 框架](https://koajs.com/) 服务。
10 |
11 | ## 目录
12 |
13 | 1. [安装](#1-安装)
14 | 2. [创建](#2-创建)
15 | 3. [配置](#3-配置)
16 | 4. [部署](#4-部署)
17 | 5. [移除](#5-移除)
18 |
19 | ### 1. 安装
20 |
21 | 通过 npm 全局安装 [serverless cli](https://github.com/serverless/serverless)
22 |
23 | ```bash
24 | $ npm install -g serverless
25 | ```
26 |
27 | ### 2. 创建
28 |
29 | 通过如下命令和模板链接,快速创建一个 koa 应用:
30 |
31 | ```bash
32 | $ serverless init koa-starter --name example
33 | $ cd example
34 | ```
35 |
36 | ### 3. 部署
37 |
38 | 在 `serverless.yml` 文件所在的项目根目录,运行以下指令,将会弹出二维码,直接扫码授权进行部署:
39 |
40 | ```
41 | serverless deploy
42 | ```
43 |
44 | > **说明**:如果鉴权失败,请参考 [权限配置](https://cloud.tencent.com/document/product/1154/43006) 进行授权。
45 |
46 | 部署完成后,控制台会打印相关的输出信息,您可以通过 `${output:${stage}:${app}:apigw.url}` 的形式在其他 `serverless` 组件中引用该组件的 API 网关访问链接(或通过类似的形式引用该组建其他输出结果),具体的,可以查看完成的输出文档:
47 |
48 | - [点击此处查看输出文档](https://github.com/serverless-components/tencent-koa/tree/master/docs/output.md)
49 |
50 | ### 4. 配置
51 |
52 | koa 组件支持 0 配置部署,也就是可以直接通过配置文件中的默认值进行部署。但你依然可以修改更多可选配置来进一步开发该 koa 项目。
53 |
54 | 以下是 koa 组件的 `serverless.yml`配置示例:
55 |
56 | ```yml
57 | # serverless.yml
58 |
59 | org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
60 | app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
61 | stage: dev # (optional) serverless dashboard stage. default is dev.
62 | component: koa # (required) name of the component. In that case, it's koa.
63 | name: koaDemo # (required) name of your koa component instance.
64 |
65 | inputs:
66 | src:
67 | src: ./ # (optional) path to the source folder. default is a hello world app.
68 | exclude:
69 | - .env
70 | region: ap-guangzhou
71 | runtime: Nodejs10.15
72 | apigatewayConf:
73 | protocols:
74 | - http
75 | - https
76 | environment: release
77 | ```
78 |
79 | - [点击此处查看配置文档](https://github.com/serverless-components/tencent-koa/tree/master/docs/configure.md)
80 |
81 | ### 5. 移除
82 |
83 | 通过以下命令移除部署的 Koa 服务。
84 |
85 | ```
86 | $ serverless remove
87 | ```
88 |
89 | ### 账号配置(可选)
90 |
91 | 当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件
92 |
93 | ```bash
94 | $ touch .env # 腾讯云的配置信息
95 | ```
96 |
97 | 在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存
98 |
99 | 如果没有腾讯云账号,可以在此[注册新账号](https://cloud.tencent.com/register)。
100 |
101 | 如果已有腾讯云账号,可以在[API 密钥管理](https://console.cloud.tencent.com/cam/capi)中获取 `SecretId` 和`SecretKey`.
102 |
103 | ```
104 | # .env
105 | TENCENT_SECRET_ID=123
106 | TENCENT_SECRET_KEY=123
107 | ```
108 |
109 | ## 静态资源服务
110 |
111 | 如果想要支持返回静态资源,比如图片之类的,需要在入口文件 `sls.js` 中指定相关 `MIME` 类型的文件为二进制,这样云函数在返回请求结果给 API 网关是,会对指定类型进行 `Base64` 编码,最终返回给客户端才能正常显示。如下:
112 |
113 | ```js
114 | const koa = require('koa')
115 | const app = koa()
116 |
117 | // Routes
118 | // ...
119 |
120 | app.binaryTypes = ['*/*']
121 |
122 | module.exports = app
123 | ```
124 |
125 | `['*/*']` 代表所有文件类型将进行 `Base64` 编码,如果需要定制化,可以配置为 `['image/png']`,意思是指定 `png` 格式的图片类型。
126 |
127 | 更多文件类型的 `MIME` 类型,可参考 [mime-db](https://github.com/jshttp/mime-db/blob/master/db.json)。
128 |
129 | ### slsInitialize 应用初始化
130 |
131 | 有些时候,Koa 服务在启动前,需要进行一个初始化操作,比如数据库建连,就可以通过在 Koa 实例对象上添加 `slsInitialize` 函数来实现,如下:
132 |
133 | ```js
134 | const Koa = require('koa')
135 | const mysql = require('mysql2/promise')
136 |
137 | const app = new Koa()
138 |
139 | // ...
140 |
141 | app.slsInitialize = async () => {
142 | app.db = await mysql.createConnection({
143 | host: 'localhost',
144 | user: 'root',
145 | database: 'test'
146 | })
147 | }
148 |
149 | // don't forget to export!
150 | module.exports = app
151 | ```
152 |
153 | 这样应用部署到云函数后,在函数服务逻辑执行前,会先执行 `slsInitialize()` 函数,来初始化数据库连接。
154 |
155 | ## 文件上传
156 |
157 | [文件上传教程](https://github.com/serverless-components/tencent-koa/tree/master/docs/upload.md)
158 |
159 | ## License
160 |
161 | MIT License
162 |
163 | Copyright (c) 2020 Tencent Cloud, Inc.
164 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [0.4.0](https://github.com/serverless-components/tencent-koa/compare/v0.3.2...v0.4.0) (2021-01-26)
2 |
3 |
4 | ### Features
5 |
6 | * support apigw base64 encode ([#46](https://github.com/serverless-components/tencent-koa/issues/46)) ([d18d9b7](https://github.com/serverless-components/tencent-koa/commit/d18d9b787438d5a9430f6b8d66296cf98fd4b31e))
7 |
8 | ## [0.3.2](https://github.com/serverless-components/tencent-koa/compare/v0.3.1...v0.3.2) (2020-12-28)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * add sls initialize before creating server ([9ce2692](https://github.com/serverless-components/tencent-koa/commit/9ce26922f15c904e3b6e8fc7061b823c36cb0f33))
14 |
15 | ## [0.3.1](https://github.com/serverless-components/tencent-koa/compare/v0.3.0...v0.3.1) (2020-12-15)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * update deploy and remove flow ([#42](https://github.com/serverless-components/tencent-koa/issues/42)) ([97e70ae](https://github.com/serverless-components/tencent-koa/commit/97e70aedf2818696d9409b180671e7fcb61a8169))
21 |
22 | # [0.3.0](https://github.com/serverless-components/tencent-koa/compare/v0.2.0...v0.3.0) (2020-11-05)
23 |
24 |
25 | ### Features
26 |
27 | * add entryFile doc ([660d31c](https://github.com/serverless-components/tencent-koa/commit/660d31c7ffbbd909c2cfce291e22ea1ec5e89cd8))
28 |
29 | # [0.2.0](https://github.com/serverless-components/tencent-koa/compare/v0.1.0...v0.2.0) (2020-10-12)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * support all parameters for apigw ([c3efa96](https://github.com/serverless-components/tencent-koa/commit/c3efa96be05122a62d31ece57cdcaa315fdfb228))
35 |
36 |
37 | ### Features
38 |
39 | * support custom entry file ([38a93c4](https://github.com/serverless-components/tencent-koa/commit/38a93c44814f69f4f48a3512f617bb4a379a0515))
40 |
41 | # [0.1.0](https://github.com/serverless-components/tencent-koa/compare/v0.0.12...v0.1.0) (2020-09-28)
42 |
43 |
44 | ### Features
45 |
46 | * support multi region and cls config ([3dd0c8f](https://github.com/serverless-components/tencent-koa/commit/3dd0c8fd4a0a074c858df1d151f9131ad5d14620))
47 |
48 | ## [0.0.12](https://github.com/serverless-components/tencent-koa/compare/v0.0.11...v0.0.12) (2020-09-04)
49 |
50 |
51 | ### Bug Fixes
52 |
53 | * update deps ([9854a88](https://github.com/serverless-components/tencent-koa/commit/9854a889676e3558538bd6b30defe05600a3c3ec))
54 |
55 | ## [0.0.11](https://github.com/serverless-components/tencent-koa/compare/v0.0.10...v0.0.11) (2020-09-03)
56 |
57 |
58 | ### Bug Fixes
59 |
60 | * update deps ([31dbc3e](https://github.com/serverless-components/tencent-koa/commit/31dbc3e321a14648de0ef0918f1a0a5af0905336))
61 | * update tencnet-component-toolkit for api mark ([9c79124](https://github.com/serverless-components/tencent-koa/commit/9c79124a18a9da3dea18157d69c952ed0f1cdacc))
62 |
63 | ## [0.0.10](https://github.com/serverless-components/tencent-koa/compare/v0.0.9...v0.0.10) (2020-09-01)
64 |
65 |
66 | ### Bug Fixes
67 |
68 | * support cfs ([b7f7d9e](https://github.com/serverless-components/tencent-koa/commit/b7f7d9e7070ebe3dd3e0190eb66f56e6686aa7a7))
69 |
70 | ## [0.0.9](https://github.com/serverless-components/tencent-koa/compare/v0.0.8...v0.0.9) (2020-08-26)
71 |
72 |
73 | ### Bug Fixes
74 |
75 | * apigw custom domain update bug ([8d6de6f](https://github.com/serverless-components/tencent-koa/commit/8d6de6fe3fc9d4933fcce1cd052d46bc942e9908))
76 | * l5 enable config ([cc7f2f8](https://github.com/serverless-components/tencent-koa/commit/cc7f2f82f4efa1c5304dde748ac80127fb758311))
77 | * support eip config ([d629f87](https://github.com/serverless-components/tencent-koa/commit/d629f878a2a9f0e80eeceb9043045e34fb1e75a2))
78 | * traffic zero display bug ([5c2c0cc](https://github.com/serverless-components/tencent-koa/commit/5c2c0cc9b72d8b0e26ca112184c2bdd0a15bde8d))
79 | * update default function description ([e9a95b0](https://github.com/serverless-components/tencent-koa/commit/e9a95b0346fafec33ab08d95bd0dd9a0e9520cc1))
80 | * update error message ([8d14b37](https://github.com/serverless-components/tencent-koa/commit/8d14b37e4805710f701385d0bd01ee4628d2b434))
81 | * update get credential error message ([764287e](https://github.com/serverless-components/tencent-koa/commit/764287ee5cd977c23da047a11149c91a1086fb73))
82 | * update toolkit verison ([d859fb6](https://github.com/serverless-components/tencent-koa/commit/d859fb677159e79b2eb535822fd40e252f437eb3))
83 | * upgrade deps ([0142d69](https://github.com/serverless-components/tencent-koa/commit/0142d6938aeafa4c489b8def1119b50bef8553ef))
84 | * upgrade deps ([eae66b2](https://github.com/serverless-components/tencent-koa/commit/eae66b231df8737a5c249072dd4dbb2f269639be))
85 | * upgrade deps ([12bc6b0](https://github.com/serverless-components/tencent-koa/commit/12bc6b03b84a8c8e0ebd3f9802b9b66555d5db40))
86 | * upgrade deps ([b0501fe](https://github.com/serverless-components/tencent-koa/commit/b0501fe5591e8aaba0d4fc95d3564a54400012cc))
87 | * upgrade tencent-component-toolkit for delete compatibility ([05ce942](https://github.com/serverless-components/tencent-koa/commit/05ce9428e78b572219d00048316279c80628bba9))
88 | * upgrade tencent-serverless-http ([ef8d10f](https://github.com/serverless-components/tencent-koa/commit/ef8d10fde3271d61d6cff9062742a803827377ad))
89 |
90 |
91 | ### Features
92 |
93 | * optmize code zip flow ([6fa5126](https://github.com/serverless-components/tencent-koa/commit/6fa5126532f85d976007e1442fcf0198179ef273))
94 | * support disable creating apigw ([5975396](https://github.com/serverless-components/tencent-koa/commit/5975396dcdff734c1ac4447696b3b305a5875f13))
95 | * support scf publish version and traffic setup ([47522b4](https://github.com/serverless-components/tencent-koa/commit/47522b4edc6c14f6cc89bb379aed885899cfac46))
96 | * update config & support usageplan+auth ([fa4eebd](https://github.com/serverless-components/tencent-koa/commit/fa4eebdbf24eb0d8f2dc949444f15da6176154ae))
97 |
--------------------------------------------------------------------------------
/src/serverless.js:
--------------------------------------------------------------------------------
1 | const { Component } = require('@serverless/core')
2 | const { Scf, Apigw, Cns } = 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 | const outputs = {}
31 | const appId = this.getAppId()
32 |
33 | const funcDeployer = async (curRegion) => {
34 | const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion)
35 | const scf = new Scf(credentials, curRegion)
36 | const tempInputs = {
37 | ...inputs,
38 | code
39 | }
40 | const scfOutput = await scf.deploy(deepClone(tempInputs))
41 | outputs[curRegion] = {
42 | functionName: scfOutput.FunctionName,
43 | runtime: scfOutput.Runtime,
44 | namespace: scfOutput.Namespace
45 | }
46 |
47 | this.state[curRegion] = {
48 | ...(this.state[curRegion] ? this.state[curRegion] : {}),
49 | ...outputs[curRegion]
50 | }
51 |
52 | // default version is $LATEST
53 | outputs[curRegion].lastVersion = scfOutput.LastVersion
54 | ? scfOutput.LastVersion
55 | : this.state.lastVersion || '$LATEST'
56 |
57 | // default traffic is 1.0, it can also be 0, so we should compare to undefined
58 | outputs[curRegion].traffic =
59 | scfOutput.Traffic !== undefined
60 | ? scfOutput.Traffic
61 | : this.state.traffic !== undefined
62 | ? this.state.traffic
63 | : 1
64 |
65 | if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) {
66 | outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion
67 | this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion
68 | }
69 |
70 | this.state.lastVersion = outputs[curRegion].lastVersion
71 | this.state.traffic = outputs[curRegion].traffic
72 | }
73 |
74 | for (let i = 0; i < regionList.length; i++) {
75 | const curRegion = regionList[i]
76 | await funcDeployer(curRegion)
77 | }
78 | this.save()
79 | return outputs
80 | }
81 |
82 | // try to add dns record
83 | async tryToAddDnsRecord(credentials, customDomains) {
84 | try {
85 | const cns = new Cns(credentials)
86 | for (let i = 0; i < customDomains.length; i++) {
87 | const item = customDomains[i]
88 | if (item.domainPrefix) {
89 | await cns.deploy({
90 | domain: item.subDomain.replace(`${item.domainPrefix}.`, ''),
91 | records: [
92 | {
93 | subDomain: item.domainPrefix,
94 | recordType: 'CNAME',
95 | recordLine: '默认',
96 | value: item.cname,
97 | ttl: 600,
98 | mx: 10,
99 | status: 'enable'
100 | }
101 | ]
102 | })
103 | }
104 | }
105 | } catch (e) {
106 | console.log('METHOD_tryToAddDnsRecord', e.message)
107 | }
108 | }
109 |
110 | async deployApigateway(credentials, inputs, regionList) {
111 | if (inputs.isDisabled) {
112 | return {}
113 | }
114 |
115 | const getServiceId = (instance, region) => {
116 | const regionState = instance.state[region]
117 | return inputs.serviceId || (regionState && regionState.serviceId)
118 | }
119 |
120 | const deployTasks = []
121 | const outputs = {}
122 | regionList.forEach((curRegion) => {
123 | const apigwDeployer = async () => {
124 | const apigw = new Apigw(credentials, curRegion)
125 |
126 | const oldState = this.state[curRegion] || {}
127 | const apigwInputs = {
128 | ...inputs,
129 | oldState: {
130 | apiList: oldState.apiList || [],
131 | customDomains: oldState.customDomains || []
132 | }
133 | }
134 | // different region deployment has different service id
135 | apigwInputs.serviceId = getServiceId(this, curRegion)
136 | const apigwOutput = await apigw.deploy(deepClone(apigwInputs))
137 | outputs[curRegion] = {
138 | serviceId: apigwOutput.serviceId,
139 | subDomain: apigwOutput.subDomain,
140 | environment: apigwOutput.environment,
141 | url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${
142 | apigwOutput.environment
143 | }${apigwInputs.endpoints[0].path}`
144 | }
145 |
146 | if (apigwOutput.customDomains) {
147 | // TODO: need confirm add cns authentication
148 | if (inputs.autoAddDnsRecord === true) {
149 | // await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains)
150 | }
151 | outputs[curRegion].customDomains = apigwOutput.customDomains
152 | }
153 | this.state[curRegion] = {
154 | created: true,
155 | ...(this.state[curRegion] ? this.state[curRegion] : {}),
156 | ...outputs[curRegion],
157 | apiList: apigwOutput.apiList
158 | }
159 | }
160 | deployTasks.push(apigwDeployer())
161 | })
162 |
163 | await Promise.all(deployTasks)
164 |
165 | this.save()
166 | return outputs
167 | }
168 |
169 | async deploy(inputs) {
170 | console.log(`Deploying ${CONFIGS.compFullname} App...`)
171 |
172 | const credentials = this.getCredentials()
173 |
174 | // 对Inputs内容进行标准化
175 | const { regionList, functionConf, apigatewayConf } = await prepareInputs(
176 | this,
177 | credentials,
178 | inputs
179 | )
180 |
181 | // 部署函数 + API网关
182 | const outputs = {}
183 | if (!functionConf.code.src) {
184 | outputs.templateUrl = CONFIGS.templateUrl
185 | }
186 |
187 | let apigwOutputs
188 | const functionOutputs = await this.deployFunction(
189 | credentials,
190 | functionConf,
191 | regionList,
192 | outputs
193 | )
194 | // support apigatewayConf.isDisabled
195 | if (apigatewayConf.isDisabled !== true) {
196 | apigwOutputs = await this.deployApigateway(credentials, apigatewayConf, regionList, outputs)
197 | } else {
198 | this.state.apigwDisabled = true
199 | }
200 |
201 | // optimize outputs for one region
202 | if (regionList.length === 1) {
203 | const [oneRegion] = regionList
204 | outputs.region = oneRegion
205 | outputs['scf'] = functionOutputs[oneRegion]
206 | if (apigwOutputs) {
207 | outputs['apigw'] = apigwOutputs[oneRegion]
208 | }
209 | } else {
210 | outputs['scf'] = functionOutputs
211 | if (apigwOutputs) {
212 | outputs['apigw'] = apigwOutputs
213 | }
214 | }
215 |
216 | this.state.region = regionList[0]
217 | this.state.regionList = regionList
218 | this.state.lambdaArn = functionConf.name
219 |
220 | return outputs
221 | }
222 |
223 | async remove() {
224 | console.log(`Removing ${CONFIGS.compFullname} App...`)
225 |
226 | const { state } = this
227 | const { regionList = [] } = state
228 |
229 | const credentials = this.getCredentials()
230 |
231 | const removeHandlers = []
232 | for (let i = 0; i < regionList.length; i++) {
233 | const curRegion = regionList[i]
234 | const curState = state[curRegion]
235 | const scf = new Scf(credentials, curRegion)
236 | const apigw = new Apigw(credentials, curRegion)
237 | const handler = async () => {
238 | // if disable apigw, no need to remove
239 | if (state.apigwDisabled !== true) {
240 | await apigw.remove({
241 | created: curState.created,
242 | environment: curState.environment,
243 | serviceId: curState.serviceId,
244 | apiList: curState.apiList,
245 | customDomains: curState.customDomains
246 | })
247 | }
248 | await scf.remove({
249 | functionName: curState.functionName,
250 | namespace: curState.namespace
251 | })
252 | }
253 | removeHandlers.push(handler())
254 | }
255 |
256 | await Promise.all(removeHandlers)
257 |
258 | if (this.state.cns) {
259 | const cns = new Cns(credentials)
260 | for (let i = 0; i < this.state.cns.length; i++) {
261 | await cns.remove({ deleteList: this.state.cns[i].records })
262 | }
263 | }
264 |
265 | this.state = {}
266 | }
267 | }
268 |
269 | module.exports = ServerlessComponent
270 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/docs/configure.md:
--------------------------------------------------------------------------------
1 | # 配置文档
2 |
3 | ## 全部配置
4 |
5 | ```yml
6 | # serverless.yml
7 |
8 | component: koa # (必选) 组件名称,在该实例中为koa
9 | name: koaDemo # 必选) 组件实例名称.
10 | org: orgDemo # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid,必须为字符串
11 | app: appDemo # (可选) 用于记录组织信息. 默认与name相同,必须为字符串
12 | stage: dev # (可选) 用于区分环境信息,默认值是 dev
13 |
14 | inputs:
15 | region: ap-guangzhou # 云函数所在区域
16 | functionName: koaDemo # 云函数名称
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 | cls: # CLS 日志投递配置
38 | logsetId: abcdef # 所属日志集ID
39 | topicId: abcdef # 日志主题ID
40 | memorySize: 128 # 内存大小,单位MB
41 | environment: # 环境变量
42 | variables: # 环境变量数组
43 | TEST: vale
44 | vpcConfig: # 私有网络配置
45 | vpcId: '' # 私有网络的Id
46 | subnetId: '' # 子网ID
47 | apigatewayConf: # api网关配置
48 | isDisabled: false # 是否禁用自动创建 API 网关功能
49 | enableCORS: true # 允许跨域
50 | customDomains: # 自定义域名绑定
51 | - domain: abc.com # 待绑定的自定义的域名
52 | certificateId: abcdefg # 待绑定自定义域名的证书唯一 ID
53 | # 如要设置自定义路径映射,请设置为 false
54 | isDefaultMapping: false
55 | # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。
56 | pathMappingSet:
57 | - path: /
58 | environment: release
59 | protocols: # 绑定自定义域名的协议类型,默认与服务的前端协议一致。
60 | - http # 支持http协议
61 | - https # 支持https协议
62 | protocols:
63 | - http
64 | - https
65 | environment: test
66 | serviceTimeout: 15
67 | usagePlan: # 用户使用计划
68 | usagePlanId: 1111
69 | usagePlanName: slscmp
70 | usagePlanDesc: sls create
71 | maxRequestNum: 1000
72 | auth: # 密钥
73 | secretName: secret
74 | secretIds:
75 | - xxx
76 | ```
77 |
78 | ## 配置描述
79 |
80 | 主要的参数
81 |
82 | | 参数名称 | 是否必选 | 默认值 | 描述 |
83 | | ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------ |
84 | | runtime | 否 | Nodejs10.15 | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 |
85 | | region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 |
86 | | functionName | 否 | | 云函数名称 |
87 | | serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 |
88 | | serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 |
89 | | entryFile | 否 | `sls.js` | 自定义 server 的入口文件名 |
90 | | src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) |
91 | | layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) |
92 | | [functionConf](#函数配置) | 否 | | 函数配置 |
93 | | [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 |
94 | | [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 |
95 | | [Region special config](#指定区配置) | 否 | | 指定区配置 |
96 |
97 | ## 执行目录
98 |
99 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
100 | | -------- | :------: | :-------------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101 | | src | 否 | String | | 代码路径。与 object 不能同时存在。 |
102 | | exclude | 否 | Array of String | | 不包含的文件或路径, 遵守 [glob 语法](https://github.com/isaacs/node-glob) |
103 | | bucket | 否 | String | | bucket 名称。如果配置了 src,表示部署 src 的代码并压缩成 zip 后上传到 bucket-appid 对应的存储桶中;如果配置了 object,表示获取 bucket-appid 对应存储桶中 object 对应的代码进行部署。 |
104 | | object | 否 | String | | 部署的代码在存储桶中的路径。 |
105 |
106 | ## 层配置
107 |
108 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
109 | | -------- | :------: | :----: | :----: | :------- |
110 | | name | 否 | String | | 层名称 |
111 | | version | 否 | String | | 层版本号 |
112 |
113 | ### DNS 配置
114 |
115 | 参考: https://cloud.tencent.com/document/product/302/8516
116 |
117 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
118 | | ---------- | :------: | -------- | :----: | :---------------------------------------------- |
119 | | ttl | 否 | Number | 600 | TTL 值,范围 1 - 604800,不同等级域名最小值不同 |
120 | | recordLine | 否 | String[] | | 记录的线路名称 |
121 |
122 | ### 指定区配置
123 |
124 | | 参数名称 | 是否必选 | 类型 | 默认值 | 函数 |
125 | | ------------------------------- | :------: | ------ | ------ | ------------ |
126 | | [functionConf](#函数配置) | 否 | Object | | 函数配置 |
127 | | [apigatewayConf](#API-网关配置) | 否 | Object | | API 网关配置 |
128 | | [cloudDNSConf](#DNS-配置) | 否 | Object | | DNS 配置 |
129 |
130 | ### 函数配置
131 |
132 | 参考: https://cloud.tencent.com/document/product/583/18586
133 |
134 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
135 | | ----------- | :------: | :-----: | :-----: | :------------------------------------------------------------------------------ |
136 | | timeout | 否 | Number | 3 | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒 |
137 | | memorySize | 否 | Number | 128 | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 |
138 | | environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) |
139 | | vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) |
140 | | eip | 否 | Boolean | `false` | 是否固定出口 IP |
141 | | cls | 否 | Object | | CLS 日志投递配置,参考 [CLS 配置](#CLS-配置) |
142 |
143 | ##### 环境变量
144 |
145 | | 参数名称 | 类型 | 描述 |
146 | | --------- | ---- | :---------------------------------------- |
147 | | variables | | 环境变量参数, 包含多对 key-value 的键值对 |
148 |
149 | ##### VPC 配置
150 |
151 | | 参数名称 | 类型 | 描述 |
152 | | -------- | ------ | :------ |
153 | | subnetId | String | 子网 ID |
154 | | vpcId | String | VPC ID |
155 |
156 | ##### CLS 配置
157 |
158 | | 参数名称 | 类型 | 描述 |
159 | | -------- | ------ | :------------ |
160 | | logsetId | String | 所属日志集 ID |
161 | | topicId | String | 日志主题 ID |
162 |
163 | ### API 网关配置
164 |
165 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
166 | | -------------- | :------: | :------- | :------- | :--------------------------------------------------------------------------------- |
167 | | protocols | 否 | String[] | ['http'] | 前端请求的类型,如 http,https,http 与 https |
168 | | environment | 否 | String | release | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). |
169 | | usagePlan | 否 | | | 使用计划配置, 参考 [使用计划](#使用计划) |
170 | | auth | 否 | | | API 密钥配置, 参考 [API 密钥](#API-密钥配置) |
171 | | customDomain | 否 | Object[] | | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名) |
172 | | enableCORS | 否 | Boolean | `false` | 开启跨域。默认值为否。 |
173 | | serviceTimeout | 否 | Number | `15` | Api 超时时间,单位: 秒 |
174 | | isDisabled | 否 | Boolean | `false` | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。 |
175 |
176 | ##### 使用计划
177 |
178 | 参考: https://cloud.tencent.com/document/product/628/14947
179 |
180 | | 参数名称 | 是否必选 | 类型 | 描述 |
181 | | ------------- | :------: | ------ | :------------------------------------------------------ |
182 | | usagePlanId | 否 | String | 用户自定义使用计划 ID |
183 | | usagePlanName | 否 | String | 用户自定义的使用计划名称 |
184 | | usagePlanDesc | 否 | String | 用户自定义的使用计划描述 |
185 | | maxRequestNum | 否 | Int | 请求配额总数,如果为空,将使用-1 作为默认值,表示不开启 |
186 |
187 | ##### API 密钥配置
188 |
189 | 参考: https://cloud.tencent.com/document/product/628/14916
190 |
191 | | 参数名称 | 类型 | 描述 |
192 | | ---------- | :----- | :------- |
193 | | secretName | String | 密钥名称 |
194 | | secretIds | String | 密钥 ID |
195 |
196 | ##### 自定义域名
197 |
198 | Refer to: https://cloud.tencent.com/document/product/628/14906
199 |
200 | | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 |
201 | | ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
202 | | domain | 是 | String | | 待绑定的自定义的域名。 |
203 | | certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 |
204 | | isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射,默认为 true。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 |
205 | | pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 |
206 | | protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 |
207 |
208 | - 自定义路径映射
209 |
210 | | 参数名称 | 是否必选 | 类型 | Description |
211 | | ----------- | :------: | :----- | :------------- |
212 | | path | 是 | String | 自定义映射路径 |
213 | | environment | 是 | String | 自定义映射环境 |
214 |
--------------------------------------------------------------------------------