From 3860cc815bb8cd9a609188e524f472d07015f759 Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 6 Jul 2020 17:28:46 +0800 Subject: [PATCH 01/65] feat: support scf publish version and traffic setup --- package.json | 4 +- serverless.component.yml | 4 +- src/_shims/handler.js | 4 +- src/_shims/package.json | 6 --- src/_shims/sls.js | 8 ---- src/config.js | 7 ++-- src/package.json | 7 +--- src/serverless.js | 26 ++++++++++++- src/utils.js | 84 +++++++++++++++++++++++++++++++--------- 9 files changed, 99 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index dbb645f..8565a44 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "commitlint": "commitlint -f HEAD@{15}", "lint": "eslint --ext .js,.ts,.tsx .", "lint:fix": "eslint --fix --ext .js,.ts,.tsx .", - "prettier": "prettier --check **/*.{css,html,js,json,md,yaml,yml}", - "prettier:fix": "prettier --write **/*.{css,html,js,json,md,yaml,yml}", + "prettier": "prettier --check '**/*.{css,html,js,json,md,yaml,yml}'", + "prettier:fix": "prettier --write '**/*.{css,html,js,json,md,yaml,yml}'", "release": "semantic-release", "release-local": "node -r dotenv/config node_modules/semantic-release/bin/semantic-release --no-ci --dry-run", "check-dependencies": "npx npm-check --skip-unused --update" diff --git a/serverless.component.yml b/serverless.component.yml index b9ec9c2..ae98635 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -4,7 +4,7 @@ author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploys a serverless Nuxt.js application onto Tencent SCF and Tencent APIGateway. keywords: tencent, serverless, nuxt.js -repo: https://github.com/serverless-components/tencent-nuxtjs/tree/v2 -readme: https://github.com/serverless-components/tencent-nuxtjs/tree/v2/README.md +repo: https://github.com/serverless-components/tencent-nuxtjs +readme: https://github.com/serverless-components/tencent-nuxtjs/tree/master/README.md license: MIT main: ./src diff --git a/src/_shims/handler.js b/src/_shims/handler.js index d739fef..5719666 100644 --- a/src/_shims/handler.js +++ b/src/_shims/handler.js @@ -1,4 +1,3 @@ -require('tencent-component-monitor') const fs = require('fs') const path = require('path') const { createServer, proxy } = require('tencent-serverless-http') @@ -24,8 +23,7 @@ module.exports.handler = async (event, context) => { server = createServer(app, null, app.binaryTypes || []) } - context.callbackWaitsForEmptyEventLoop = - app.callbackWaitsForEmptyEventLoop === true ? true : false + context.callbackWaitsForEmptyEventLoop = app.callbackWaitsForEmptyEventLoop === true if (app.slsInitialize && typeof app.slsInitialize === 'function') { await app.slsInitialize() diff --git a/src/_shims/package.json b/src/_shims/package.json index 48a333f..58e017b 100644 --- a/src/_shims/package.json +++ b/src/_shims/package.json @@ -1,12 +1,6 @@ { - "name": "@serverless/nuxtjs", - "main": "./handler.js", - "scripts": {}, - "author": "Tencent Cloud, Inc.", - "license": "MIT", "dependencies": { "express": "^4.17.1", - "tencent-component-monitor": "^1.1.0", "tencent-serverless-http": "^1.2.0" } } diff --git a/src/_shims/sls.js b/src/_shims/sls.js index 0479615..f4568bd 100644 --- a/src/_shims/sls.js +++ b/src/_shims/sls.js @@ -2,9 +2,6 @@ const path = require('path') const express = require('express') const { Nuxt } = require('nuxt') -// not report route for custom monitor -const noReportRoutes = ['/_next', '/static'] - async function createServer(custom) { const server = express() // get next config @@ -22,11 +19,6 @@ async function createServer(custom) { // Give nuxt middleware to express // app.use(nuxt.render) server.all('*', (req, res, next) => { - noReportRoutes.forEach((route) => { - if (req.path.indexOf(route) === 0) { - req.__SLS_NO_REPORT__ = true - } - }) return nuxt.render(req, res, next) }) diff --git a/src/config.js b/src/config.js index ce4722e..eea1803 100644 --- a/src/config.js +++ b/src/config.js @@ -1,14 +1,15 @@ const CONFIGS = { templateUrl: 'https://serverless-templates-1300862921.cos.ap-beijing.myqcloud.com/nuxtjs-demo.zip', - framework: 'nuxtjs', - frameworkFullname: 'Nuxt.js', + compName: 'nuxtjs', + compFullname: 'Nuxt.js', handler: 'sl_handler.handler', runtime: 'Nodejs10.15', exclude: ['.git/**', '.gitignore', '.DS_Store'], timeout: 3, memorySize: 128, - namespace: 'default' + namespace: 'default', + description: 'Created by Serverless Component' } module.exports = CONFIGS diff --git a/src/package.json b/src/package.json index 894beda..1d27541 100644 --- a/src/package.json +++ b/src/package.json @@ -1,13 +1,8 @@ { - "name": "@serverless/nuxtjs", - "main": "./serverless.js", - "scripts": {}, - "author": "Tencent Cloud, Inc.", - "license": "MIT", "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.8.3", + "tencent-component-toolkit": "^1.12.9", "type": "^2.0.0" } } diff --git a/src/serverless.js b/src/serverless.js index cb4c3e9..4a3d06a 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -1,5 +1,6 @@ const { Component } = require('@serverless/core') const { MultiApigw, Scf, Apigw, Cos, Cns, Cam } = require('tencent-component-toolkit') +const { TypeError } = require('tencent-component-toolkit/src/utils/error') const { uploadCodeToCos, getDefaultProtocol, deleteRecord, prepareInputs } = require('./utils') const CONFIGS = require('./config') @@ -8,8 +9,9 @@ class ServerlessComopnent extends Component { const { tmpSecrets } = this.credentials.tencent if (!tmpSecrets || !tmpSecrets.TmpSecretId) { - throw new Error( - 'Cannot get secretId/Key, your account could be sub-account or does not have access, please check if SLS_QcsRole role exists in your account, and visit https://console.cloud.tencent.com/cam to bind this role to your account.' + throw new TypeError( + 'CREDENTIAL', + '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.' ) } @@ -103,6 +105,26 @@ class ServerlessComopnent extends Component { ...(this.state[curRegion] ? this.state[curRegion] : {}), ...outputs[curRegion] } + + // default version is $LATEST + outputs[curRegion].lastVersion = scfOutput.LastVersion + ? scfOutput.LastVersion + : this.state.lastVersion || '$LATEST' + + // default traffic is 1.0, it can also be 0, so we should compare to undefined + outputs[curRegion].traffic = scfOutput.Traffic + ? scfOutput.Traffic + : this.state.traffic !== undefined + ? this.state.traffic + : 1 + + if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { + outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion + this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion + } + + this.state.lastVersion = outputs[curRegion].lastVersion + this.state.traffic = outputs[curRegion].traffic } uploadCodeHandler.push(funcDeployer()) } diff --git a/src/utils.js b/src/utils.js index 60a30ad..210bf46 100644 --- a/src/utils.js +++ b/src/utils.js @@ -7,13 +7,6 @@ const download = require('download') const AdmZip = require('adm-zip') const CONFIGS = require('./config') -/* - * Pauses execution for the provided miliseconds - * - * @param ${number} wait - number of miliseconds to wait - */ -const sleep = async (wait) => new Promise((resolve) => setTimeout(() => resolve(), wait)) - /* * Generates a random id */ @@ -22,6 +15,26 @@ const generateId = () => .toString(36) .substring(6) +const getType = (obj) => { + return Object.prototype.toString.call(obj).slice(8, -1) +} + +const validateTraffic = (num) => { + if (getType(num) !== 'Number') { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUpperCase()}_TRAFFIC`, + 'traffic must be a number' + ) + } + if (num < 0 || num > 1) { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUpperCase()}_TRAFFIC`, + 'traffic must be a number between 0 and 1' + ) + } + return true +} + const transformNextConfig = (zipPath) => { const zip = new AdmZip(zipPath) const entries = zip.getEntries() @@ -38,7 +51,7 @@ const transformNextConfig = (zipPath) => { } const getCodeZipPath = async (instance, inputs) => { - console.log(`Packaging ${CONFIGS.frameworkFullname} application...`) + console.log(`Packaging ${CONFIGS.compFullname} application...`) // unzip source zip file let zipPath @@ -47,10 +60,14 @@ const getCodeZipPath = async (instance, inputs) => { const downloadPath = `/tmp/${generateId()}` const filename = 'template' - console.log(`Installing Default ${CONFIGS.frameworkFullname} App...`) - await download(CONFIGS.templateUrl, downloadPath, { - filename: `${filename}.zip` - }) + console.log(`Installing Default ${CONFIGS.compFullname} App...`) + try { + await download(CONFIGS.templateUrl, downloadPath, { + filename: `${filename}.zip` + }) + } catch (e) { + throw new TypeError(`DOWNLOAD_TEMPLATE`, 'Download default template failed.') + } zipPath = `${downloadPath}/${filename}.zip` } else { zipPath = inputs.code.src @@ -195,7 +212,7 @@ const deleteRecord = (newRecords, historyRcords) => { const prepareInputs = async (instance, credentials, inputs = {}) => { // 对function inputs进行标准化 const tempFunctionConf = inputs.functionConf ? inputs.functionConf : {} - const fromClientRemark = `tencent-${CONFIGS.framework}` + const fromClientRemark = `tencent-${CONFIGS.compName}` const regionList = inputs.region ? typeof inputs.region == 'string' ? [inputs.region] @@ -217,8 +234,11 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { name: ensureString(inputs.functionName, { isOptional: true }) || stateFunctionName || - `${CONFIGS.framework}_component_${generateId()}`, + `${CONFIGS.compName}_component_${generateId()}`, region: regionList, + role: ensureString(tempFunctionConf.role ? tempFunctionConf.role : inputs.role, { + default: '' + }), handler: ensureString(tempFunctionConf.handler ? tempFunctionConf.handler : inputs.handler, { default: CONFIGS.handler }), @@ -238,8 +258,18 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { fromClientRemark, layers: ensureIterable(tempFunctionConf.layers ? tempFunctionConf.layers : inputs.layers, { default: [] - }) + }), + publish: inputs.publish, + traffic: inputs.traffic, + lastVersion: instance.state.lastVersion + } + + // validate traffic + if (inputs.traffic !== undefined) { + validateTraffic(inputs.traffic) } + functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion + functionConf.tags = ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { default: null }) @@ -270,10 +300,11 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // 对apigw inputs进行标准化 const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} + apigatewayConf.isDisabled = inputs.apigatewayConf === true apigatewayConf.fromClientRemark = fromClientRemark apigatewayConf.serviceName = inputs.serviceName apigatewayConf.description = `Serverless Framework Tencent-${capitalString( - CONFIGS.framework + CONFIGS.compName )} Component` apigatewayConf.serviceId = inputs.serviceId || stateServiceId apigatewayConf.region = functionConf.region @@ -283,14 +314,30 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { { path: '/', enableCORS: apigatewayConf.enableCORS, + serviceTimeout: apigatewayConf.serviceTimeout, method: 'ANY', function: { - isIntegratedResponse: true, + isIntegratedResponse: apigatewayConf.isIntegratedResponse === false ? false : true, functionName: functionConf.name, - functionNamespace: functionConf.namespace + functionNamespace: functionConf.namespace, + functionQualifier: functionConf.needSetTraffic ? '$DEFAULT' : '$LATEST' } } ] + if (apigatewayConf.usagePlan) { + apigatewayConf.endpoints[0].usagePlan = { + usagePlanId: apigatewayConf.usagePlan.usagePlanId, + usagePlanName: apigatewayConf.usagePlan.usagePlanName, + usagePlanDesc: apigatewayConf.usagePlan.usagePlanDesc, + maxRequestNum: apigatewayConf.usagePlan.maxRequestNum + } + } + if (apigatewayConf.auth) { + apigatewayConf.endpoints[0].auth = { + secretName: apigatewayConf.auth.secretName, + secretIds: apigatewayConf.auth.secretIds + } + } // 对cns inputs进行标准化 const tempCnsConf = {} @@ -358,7 +405,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { module.exports = { generateId, - sleep, uploadCodeToCos, mergeJson, capitalString, From 06fc608984b9bf927809d28f0e78e79cbb207bfe Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 6 Jul 2020 17:33:52 +0800 Subject: [PATCH 02/65] chore: release v0.0.5 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8565a44..dca041f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.4", + "version": "0.0.5", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index ae98635..17d6617 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.4 +version: 0.0.5 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploys a serverless Nuxt.js application onto Tencent SCF and Tencent APIGateway. From 2139fba3f149bb1a7c3ec820d08fa29133bfb2d5 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 7 Jul 2020 13:03:30 +0800 Subject: [PATCH 03/65] docs: fix typo --- docs/configure.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index a70365c..8a286fd 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -30,6 +30,7 @@ inputs: layers: - name: layerName # layer名称 version: 1 # 版本 + traffic: 0.9 # 配置默认流量中 $LATEST 版本比重:0 - 1 functionConf: # 函数配置相关 timeout: 10 # 超时时间,单位秒 memorySize: 128 # 内存大小,单位MB @@ -72,21 +73,20 @@ inputs: 主要的参数 -| 参数名称 | 是否必选 | 默认值 | 描述 | -| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------ | -| runtime | 否 | Nodejs10.15 | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 | -| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | -| functionName | 否 | | 云函数名称 | -| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | -| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | -| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | -| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | -| exclude | 否 | | 不包含的文件 | -| include | 否 | | 包含的文件, 如果是相对路径,是相对于 `serverless.yml`的路径 | -| [functionConf](#函数配置) | 否 | | 函数配置 | -| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | -| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | -| [Region special config](#指定区配置) | 否 | | 指定区配置 | +| 参数名称 | 是否必选 | 默认值 | 描述 | +| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| runtime | 否 | Nodejs10.15 | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 | +| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | +| functionName | 否 | | 云函数名称 | +| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | +| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | +| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | +| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | +| traffic | 否 | 1 | 配置默认流量中 `$LATEST` 版本比重,取值范围:0 ~ 1,比如 80%,可配置成 0.8。注意如果配置灰度流量,需要配置对应的 API 网关触发器的 endpoints 的 `function.functionQualifier` 参数为 `$DEFAULT` (默认流量) | +| [functionConf](#函数配置) | 否 | | 函数配置 | +| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | +| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | +| [Region special config](#指定区配置) | 否 | | 指定区配置 | ## 执行目录 From 1c4d4f83d829ff8c031e467dc22a4457c7a4e698 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 9 Jul 2020 20:57:34 +0800 Subject: [PATCH 04/65] fix: upgrade deps --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 1d27541..e28953b 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.9", + "tencent-component-toolkit": "^1.12.10", "type": "^2.0.0" } } From d24b94168f17d39c1281804b18f864bdf9304dff Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 14 Jul 2020 10:54:10 +0800 Subject: [PATCH 05/65] feat: support static cdn deployment --- docs/configure.md | 14 ++-- example/serverless.yml | 7 +- serverless.component.yml | 2 +- src/config.js | 18 ++++- src/package.json | 2 +- src/serverless.js | 151 ++++++++++++++++++++++++++------------- src/utils.js | 136 ++++++++++++++++++++++++++++++----- 7 files changed, 249 insertions(+), 81 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 8a286fd..6c4e64d 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -182,13 +182,13 @@ inputs: Refer to: https://cloud.tencent.com/document/product/628/14906 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| ---------------- | :------: | :------: | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| domain | 是 | String | | 待绑定的自定义的域名。 | -| certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | -| isDefaultMapping | 否 | String | `'TRUE'` | 是否使用默认路径映射,默认为 TRUE。为 FALSE 时,表示自定义路径映射,此时 pathMappingSet 必填。 | -| pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | -| protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| domain | 是 | String | | 待绑定的自定义的域名。 | +| certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | +| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射。为 `false` 时,表示自定义路径映射,此时 pathMappingSet 必填。 | +| pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | +| protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | - 自定义路径映射 diff --git a/example/serverless.yml b/example/serverless.yml index d2f10a4..a5f2aed 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -1,12 +1,13 @@ -component: nuxtjs -name: nuxtjsDemo org: orgDemo app: appDemo stage: dev +component: nuxtjs +name: nuxtjsDemo inputs: src: - src: ./ + hook: npm run build + dist: ./ exclude: - .env region: ap-guangzhou diff --git a/serverless.component.yml b/serverless.component.yml index 17d6617..0c102ce 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -2,7 +2,7 @@ name: nuxtjs version: 0.0.5 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. -description: Deploys a serverless Nuxt.js application onto Tencent SCF and Tencent APIGateway. +description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. keywords: tencent, serverless, nuxt.js repo: https://github.com/serverless-components/tencent-nuxtjs readme: https://github.com/serverless-components/tencent-nuxtjs/tree/master/README.md diff --git a/src/config.js b/src/config.js index eea1803..0e095f1 100644 --- a/src/config.js +++ b/src/config.js @@ -9,7 +9,23 @@ const CONFIGS = { timeout: 3, memorySize: 128, namespace: 'default', - description: 'Created by Serverless Component' + description: 'Created by Serverless Component', + defaultStatics: [ + { src: '.nuxt/dist/client', targetDir: '/' }, + { src: 'static', targetDir: '/' } + ], + defaultCdnConf: { + autoRefresh: true, + forceRedirect: { + switch: 'on', + redirectType: 'https', + redirectStatusCode: 301 + }, + https: { + switch: 'on', + http2: 'on' + } + } } module.exports = CONFIGS diff --git a/src/package.json b/src/package.json index e28953b..9425ec0 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.10", + "tencent-component-toolkit": "^1.12.12", "type": "^2.0.0" } } diff --git a/src/serverless.js b/src/serverless.js index 4a3d06a..e7a1f10 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -1,7 +1,14 @@ const { Component } = require('@serverless/core') -const { MultiApigw, Scf, Apigw, Cos, Cns, Cam } = require('tencent-component-toolkit') +const { MultiApigw, Scf, Apigw, Cns, Cam, Cos, Cdn } = require('tencent-component-toolkit') const { TypeError } = require('tencent-component-toolkit/src/utils/error') -const { uploadCodeToCos, getDefaultProtocol, deleteRecord, prepareInputs } = require('./utils') +const { + uploadCodeToCos, + getDefaultProtocol, + deleteRecord, + prepareInputs, + prepareStaticCosInputs, + prepareStaticCdnInputs +} = require('./utils') const CONFIGS = require('./config') class ServerlessComopnent extends Component { @@ -26,47 +33,6 @@ class ServerlessComopnent extends Component { return this.credentials.tencent.tmpSecrets.appId } - async uploadCodeToCos(credentials, inputs, region, filePath) { - const { appId } = this.credentials.tencent.tmpSecrets - // 创建cos对象 - const cos = new Cos(credentials, region) - // 创建存储桶 + 设置生命周期 - if (!inputs.code.bucket) { - inputs.code.bucket = `sls-cloudfunction-${region}-code` - await cos.deploy({ - bucket: inputs.code.bucket + '-' + appId, - force: true, - lifecycle: [ - { - status: 'Enabled', - id: 'deleteObject', - filter: '', - expiration: { days: '10' }, - abortIncompleteMultipartUpload: { daysAfterInitiation: '10' } - } - ] - }) - } - - // 上传代码 - if (!inputs.code.object) { - const object = `${inputs.name}-${Math.floor(Date.now() / 1000)}.zip` - inputs.code.object = object - await cos.upload({ - bucket: inputs.code.bucket + '-' + appId, - file: filePath, - key: inputs.code.object - }) - } - this.state.bucket = inputs.code.bucket - this.state.object = inputs.code.object - - return { - bucket: inputs.code.bucket, - object: inputs.code.object - } - } - async deployFunction(credentials, inputs, regionList) { if (!inputs.role) { try { @@ -80,13 +46,11 @@ class ServerlessComopnent extends Component { } } - // 上传代码到COS const uploadCodeHandler = [] const outputs = {} const appId = this.getAppId() - for (let eveRegionIndex = 0; eveRegionIndex < regionList.length; eveRegionIndex++) { - const curRegion = regionList[eveRegionIndex] + regionList.forEach((curRegion) => { const funcDeployer = async () => { const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion) const scf = new Scf(credentials, curRegion) @@ -127,7 +91,7 @@ class ServerlessComopnent extends Component { this.state.traffic = outputs[curRegion].traffic } uploadCodeHandler.push(funcDeployer()) - } + }) await Promise.all(uploadCodeHandler) this.save() return outputs @@ -213,8 +177,58 @@ class ServerlessComopnent extends Component { return outputs } + // deploy static to cos, and setup cdn + async deployStatic(credentials, inputs, region) { + const { zipPath } = this.state + const appId = this.getAppId() + const deployStaticOutpus = {} + + if (zipPath) { + console.log(`Deploy static for ${CONFIGS.compFullname} application`) + // 1. deploy to cos + const staticCosInputs = await prepareStaticCosInputs(this, inputs, appId, zipPath) + + const cos = new Cos(credentials, region) + const cosOutput = { + region + } + for (let i = 0; i < staticCosInputs.length; i++) { + const curInputs = staticCosInputs[i] + console.log(`Starting deploy directory ${curInputs.src} to cos bucket ${curInputs.bucket}`) + const deployRes = await cos.deploy(curInputs) + cosOutput.cosOrigin = `${curInputs.bucket}.cos.${region}.myqcloud.com` + cosOutput.bucket = deployRes.bucket + console.log(`Deploy directory ${curInputs.src} to cos bucket ${curInputs.bucket} success`) + } + deployStaticOutpus.cos = cosOutput + + // 2. deploy cdn + if (inputs.cdnConf) { + const cdn = new Cdn(credentials) + const cdnInputs = await prepareStaticCdnInputs(this, inputs, cosOutput.cosOrigin) + console.log(`Starting deploy cdn ${cdnInputs.domain}`) + const cdnDeployRes = await cdn.deploy(cdnInputs) + const protocol = cdnInputs.https ? 'https' : 'http' + const cdnOutput = { + domain: cdnDeployRes.domain, + url: `${protocol}://${cdnDeployRes.domain}`, + cname: cdnDeployRes.cname + } + deployStaticOutpus.cdn = cdnOutput + + console.log(`Deploy cdn ${cdnInputs.domain} success`) + } + + console.log(`Deployed static for ${CONFIGS.compFullname} application successfully`) + + return deployStaticOutpus + } + + return null + } + async deploy(inputs) { - console.log(`Deploying ${CONFIGS.frameworkFullname} App...`) + console.log(`Deploying ${CONFIGS.compFullname} App...`) const credentials = this.getCredentials() @@ -256,15 +270,49 @@ class ServerlessComopnent extends Component { outputs['cns'] = await this.deployCns(credentials, cnsConf, regionList, apigwOutputs) } + // start deploy static cdn + if (inputs.staticConf) { + const staticDeployRes = await this.deployStatic(credentials, inputs.staticConf, regionList[0]) + if (staticDeployRes) { + this.state.staticConf = staticDeployRes + outputs.staticConf = staticDeployRes + } + } + this.state.region = regionList[0] this.state.regionList = regionList + this.state.lambdaArn = functionConf.name return outputs } + async removeStatic() { + // remove static + const { region, staticConf } = this.state + if (staticConf) { + console.log(`Removing static config`) + const credentials = this.getCredentials() + // 1. remove cos + if (staticConf.cos) { + const cos = new Cos(credentials, region) + await cos.remove(staticConf.cos) + } + // 2. remove cdn + if (staticConf.cdn) { + const cdn = new Cdn(credentials) + try { + await cdn.remove(staticConf.cdn) + } catch (e) { + // no op + } + } + console.log(`Remove static config success`) + } + } + async remove() { - console.log(`Removing ${CONFIGS.frameworkFullname} App...`) + console.log(`Removing ${CONFIGS.compFullname} App...`) const { state } = this const { regionList = [] } = state @@ -298,13 +346,16 @@ class ServerlessComopnent extends Component { await Promise.all(removeHandlers) - if (this.state.cns) { + if (state.cns) { const cns = new Cns(credentials) for (let i = 0; i < this.state.cns.length; i++) { await cns.remove({ deleteList: this.state.cns[i].records }) } } + // remove static + await this.removeStatic() + this.state = {} } } diff --git a/src/utils.js b/src/utils.js index 210bf46..5b4c192 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,6 @@ const path = require('path') const { Domain, Cos } = require('tencent-component-toolkit') +const { TypeError } = require('tencent-component-toolkit/src/utils/error') const ensureObject = require('type/object/ensure') const ensureIterable = require('type/iterable/ensure') const ensureString = require('type/string/ensure') @@ -35,21 +36,6 @@ const validateTraffic = (num) => { return true } -const transformNextConfig = (zipPath) => { - const zip = new AdmZip(zipPath) - const entries = zip.getEntries() - const [entry] = entries.filter((e) => e.name === 'nuxt.config.js') - entry.setData( - Buffer.from( - entry - .getData() - .toString('utf-8') - .replace('export default', 'module.exports =') - ) - ) - zip.writeZip() -} - const getCodeZipPath = async (instance, inputs) => { console.log(`Packaging ${CONFIGS.compFullname} application...`) @@ -76,6 +62,119 @@ const getCodeZipPath = async (instance, inputs) => { return zipPath } +const prepareStaticCosInputs = async (instance, inputs, appId, codeZipPath) => { + const staticCosInputs = [] + const { cosConf } = inputs + const sources = cosConf.sources || CONFIGS.defaultStatics + const { bucket } = cosConf + const staticPath = `/tmp/${generateId()}` + const codeZip = new AdmZip(codeZipPath) + const entries = codeZip.getEntries() + + // traverse sources, generate static directory and deploy to cos + for (let i = 0; i < sources.length; i++) { + const curSource = sources[i] + const entryName = `${curSource.src}` + let exist = false + entries.forEach((et) => { + if (et.entryName.indexOf(entryName) === 0) { + codeZip.extractEntryTo(et, staticPath, true, true) + exist = true + } + }) + if (exist) { + const cosInputs = { + force: true, + protocol: cosConf.protocol, + bucket: `${bucket}-${appId}`, + src: `${staticPath}/${entryName}`, + keyPrefix: curSource.targetDir || '/', + acl: { + permissions: 'public-read', + grantRead: '', + grantWrite: '', + grantFullControl: '' + } + } + + if (cosConf.acl) { + cosInputs.acl = { + permissions: cosConf.acl.permissions || 'public-read', + grantRead: cosConf.acl.grantRead || '', + grantWrite: cosConf.acl.grantWrite || '', + grantFullControl: cosConf.acl.grantFullControl || '' + } + } + + staticCosInputs.push(cosInputs) + } + } + return staticCosInputs +} + +const prepareStaticCdnInputs = async (instance, inputs, origin) => { + const { cdnConf } = inputs + const cdnInputs = { + async: true, + area: cdnConf.area || 'mainland', + domain: cdnConf.domain, + serviceType: 'web', + origin: { + origins: [origin], + originType: 'cos', + originPullProtocol: 'https' + }, + autoRefresh: true, + ...cdnConf + } + if (cdnConf.https) { + // using these default configs, for making user's config more simple + cdnInputs.forceRedirect = cdnConf.https.forceRedirect || CONFIGS.defaultCdnConf.forceRedirect + if (!cdnConf.https.certId) { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUpperCase()}_HTTPS`, + 'https.certId is required' + ) + } + cdnInputs.https = { + ...CONFIGS.defaultCdnConf.https, + ...{ + http2: cdnConf.https.http2 || 'on', + certInfo: { + certId: cdnConf.https.certId + } + } + } + } + if (cdnInputs.autoRefresh) { + cdnInputs.refreshCdn = { + flushType: cdnConf.refreshType || 'delete', + urls: [`http://${cdnInputs.domain}`, `https://${cdnInputs.domain}`] + } + } + + return cdnInputs +} + +const transformNextConfig = (zipPath) => { + const zip = new AdmZip(zipPath) + const entries = zip.getEntries() + const [entry] = entries.filter((e) => e.name === 'nuxt.config.js') + if (entry) { + entry.setData( + Buffer.from( + entry + .getData() + .toString('utf-8') + .replace('export default', 'module.exports =') + ) + ) + } else { + console.log('TransformNextConfig: nuxt.config.js not exist.') + } + zip.writeZip() +} + /** * Upload code to COS * @param {Component} instance serverless component instance @@ -300,7 +399,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // 对apigw inputs进行标准化 const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} - apigatewayConf.isDisabled = inputs.apigatewayConf === true apigatewayConf.fromClientRemark = fromClientRemark apigatewayConf.serviceName = inputs.serviceName apigatewayConf.description = `Serverless Framework Tencent-${capitalString( @@ -317,7 +415,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { serviceTimeout: apigatewayConf.serviceTimeout, method: 'ANY', function: { - isIntegratedResponse: apigatewayConf.isIntegratedResponse === false ? false : true, + isIntegratedResponse: true, functionName: functionConf.name, functionNamespace: functionConf.namespace, functionQualifier: functionConf.needSetTraffic ? '$DEFAULT' : '$LATEST' @@ -410,5 +508,7 @@ module.exports = { capitalString, getDefaultProtocol, deleteRecord, - prepareInputs + prepareInputs, + prepareStaticCosInputs, + prepareStaticCdnInputs } From 2fb7f7917f72eeaefcb6242d4ebf43d154706595 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 14 Jul 2020 11:06:28 +0800 Subject: [PATCH 06/65] chore: release v0.0.6 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dca041f..a0ef3ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.5", + "version": "0.0.6", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 0c102ce..de0863d 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.5 +version: 0.0.6 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From 5d45e22b2290628e2cc085c29a1c225c05ea6f23 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 14 Jul 2020 14:21:59 +0800 Subject: [PATCH 07/65] fix: optimize static cos output --- src/serverless.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serverless.js b/src/serverless.js index e7a1f10..24e5e81 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -198,6 +198,7 @@ class ServerlessComopnent extends Component { const deployRes = await cos.deploy(curInputs) cosOutput.cosOrigin = `${curInputs.bucket}.cos.${region}.myqcloud.com` cosOutput.bucket = deployRes.bucket + cosOutput.url = `https://${curInputs.bucket}.cos.${region}.myqcloud.com` console.log(`Deploy directory ${curInputs.src} to cos bucket ${curInputs.bucket} success`) } deployStaticOutpus.cos = cosOutput From b0dd2b34d56049266fab4862aaee5597a7ef66e6 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 21 Jul 2020 17:43:44 +0800 Subject: [PATCH 08/65] fix: upgrade deps --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 9425ec0..2e67e80 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.12", + "tencent-component-toolkit": "^1.12.15", "type": "^2.0.0" } } From dcd8c39441ff284f523152e87f451a4d9f69ddf8 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 21 Jul 2020 19:37:16 +0800 Subject: [PATCH 09/65] chore: release v0.0.7 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a0ef3ac..b2ad3b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.6", + "version": "0.0.7", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index de0863d..3b3fa8a 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.6 +version: 0.0.7 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From 10a10f706ad6fd114584de359b0eaa0812f9a49c Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 21 Jul 2020 20:10:03 +0800 Subject: [PATCH 10/65] docs: update configure --- docs/configure.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 6c4e64d..4b21126 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -30,7 +30,6 @@ inputs: layers: - name: layerName # layer名称 version: 1 # 版本 - traffic: 0.9 # 配置默认流量中 $LATEST 版本比重:0 - 1 functionConf: # 函数配置相关 timeout: 10 # 超时时间,单位秒 memorySize: 128 # 内存大小,单位MB @@ -73,20 +72,19 @@ inputs: 主要的参数 -| 参数名称 | 是否必选 | 默认值 | 描述 | -| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| runtime | 否 | Nodejs10.15 | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 | -| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | -| functionName | 否 | | 云函数名称 | -| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | -| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | -| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | -| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | -| traffic | 否 | 1 | 配置默认流量中 `$LATEST` 版本比重,取值范围:0 ~ 1,比如 80%,可配置成 0.8。注意如果配置灰度流量,需要配置对应的 API 网关触发器的 endpoints 的 `function.functionQualifier` 参数为 `$DEFAULT` (默认流量) | -| [functionConf](#函数配置) | 否 | | 函数配置 | -| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | -| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | -| [Region special config](#指定区配置) | 否 | | 指定区配置 | +| 参数名称 | 是否必选 | 默认值 | 描述 | +| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------ | +| runtime | 否 | Nodejs10.15 | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 | +| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | +| functionName | 否 | | 云函数名称 | +| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | +| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | +| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | +| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | +| [functionConf](#函数配置) | 否 | | 函数配置 | +| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | +| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | +| [Region special config](#指定区配置) | 否 | | 指定区配置 | ## 执行目录 From 0e3eae68c182db70e003107374a2d682acab9a03 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 22 Jul 2020 16:12:18 +0800 Subject: [PATCH 11/65] docs: update configure isDefaultMapping --- docs/configure.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/configure.md b/docs/configure.md index 4b21126..8d2ee7f 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -45,6 +45,8 @@ inputs: customDomains: # 自定义域名绑定 - domain: abc.com # 待绑定的自定义的域名 certificateId: abcdefg # 待绑定自定义域名的证书唯一 ID + # 如要设置自定义路径映射,请设置为 false + isDefaultMapping: false # 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 pathMappingSet: - path: / @@ -184,7 +186,7 @@ Refer to: https://cloud.tencent.com/document/product/628/14906 | ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | domain | 是 | String | | 待绑定的自定义的域名。 | | certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | -| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射。为 `false` 时,表示自定义路径映射,此时 pathMappingSet 必填。 | +| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射,默认为 true。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 | | pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | | protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | From f0ebc4dd747314aaadff0e5c799460e844971b99 Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 27 Jul 2020 16:41:52 +0800 Subject: [PATCH 12/65] fix: upgrade deps --- .travis.yml | 18 +++++++++--------- src/package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index b9f070c..f1ee195 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,12 @@ install: jobs: include: # Define the release stage that runs semantic-release - - stage: release - node_js: 10.18 - # Advanced: optionally overwrite your default `script` step to skip the tests - # script: skip - deploy: - provider: script - skip_cleanup: true - script: - - npm run release + # - stage: release + # node_js: 10.18 + # # Advanced: optionally overwrite your default `script` step to skip the tests + # # script: skip + # deploy: + # provider: script + # skip_cleanup: true + # script: + # - npm run release diff --git a/src/package.json b/src/package.json index 2e67e80..389b0b7 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.12.15", + "tencent-component-toolkit": "^1.13.0", "type": "^2.0.0" } } From 1c191c38fb213f2cde88f9b9cc476cd12495bd55 Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 27 Jul 2020 16:45:47 +0800 Subject: [PATCH 13/65] chore: release v0.0.8 --- package.json | 2 +- serverless.component.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b2ad3b2..6915ebf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.7", + "version": "0.0.8", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 3b3fa8a..436d99c 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.7 +version: 0.0.8 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. @@ -8,3 +8,4 @@ repo: https://github.com/serverless-components/tencent-nuxtjs readme: https://github.com/serverless-components/tencent-nuxtjs/tree/master/README.md license: MIT main: ./src +webDeployable: true From e63042a2195415c73d900ec3c438e2cfe1366c10 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 28 Jul 2020 15:09:41 +0800 Subject: [PATCH 14/65] docs: update configure --- docs/configure.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/docs/configure.md b/docs/configure.md index 8d2ee7f..93fb1c1 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -68,6 +68,28 @@ inputs: secretName: secret secretIds: - xxx + staticConf: + cosConf: + bucket: static-bucket + acl: + permissions: public-read + sources: + - src: .nuxt/dist/client + targetDir: / + - src: static + targetDir: / + cdnConf: + area: mainland + domain: cnode.yuga.chat + autoRefresh: true + refreshType: delete + forceRedirect: + switch: on + redirectType: https + redirectStatusCode: 301 + https: + http2: on + certId: 'abc' ``` ## 配置描述 @@ -87,6 +109,7 @@ inputs: | [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | | [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | | [Region special config](#指定区配置) | 否 | | 指定区配置 | +| [staticConf](#静态资源-CDN-配置) | 否 | | 静态资源 CDN 配置 | ## 执行目录 @@ -196,3 +219,54 @@ Refer to: https://cloud.tencent.com/document/product/628/14906 | ----------- | :------: | :----- | :------------- | | path | 是 | String | 自定义映射路径 | | environment | 是 | String | 自定义映射环境 | + +### 静态资源 CDN 配置 + +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------- | :------: | :----: | :----: | :-------------------- | +| cosConf | 是 | Object | | [COS 配置](#cos-配置) | +| cdnConf | 否 | Object | | [CDN 配置](#cdn-配置) | + +##### COS 配置 + +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------- | :------: | :------: | :-----------------------------------------------------------------------------: | :------------------------------- | +| bucket | 是 | string | | COS 存储同名称,没有将自动创建 | +| acl | 否 | Object | | 存储桶权限配置,参考 [acl](#acl) | +| sources | 否 | Object[] | `[{src: '.nuxt/dist/client', targetDir: '/'}, {src: 'static', targetDir: '/'}]` | 需要托管到 COS 的静态资源目录 | + +###### acl + +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ----------- | :------: | :----: | :-----------: | :----------- | +| permissions | 是 | string | `public-read` | 公共权限配置 | + +##### CDN 配置 + +area: mainland domain: cnode.yuga.chat autoRefresh: true refreshType: delete +forceRedirect: switch: on redirectType: https redirectStatusCode: 301 https: +http2: on certId: 'eGkM75xv' + +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ------------- | :------: | :-----: | :--------: | :--------------------------------------------------------- | +| domain | 是 | string | | CDN 域名 | +| area | 否 | string | `mainland` | 加速区域,mainland: 大陆,overseas:海外,global:全球加速 | +| autoRefresh | 否 | boolean | `true` | 是否自动刷新 CDN | +| refreshType | 否 | boolean | `delete` | CDN 刷新类型,delete:刷新全部资源,flush:刷新变更资源 | +| forceRedirect | 否 | Object | | 访问协议强制跳转配置,参考 [forceRedirect](#forceRedirect) | +| https | 否 | Object | | https 配置,参考 [https](#https) | + +###### forceRedirect + +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ------------------ | :------: | :----: | :----: | :------------------------------------------------------------- | +| switch | 是 | string | `on` | 访问强制跳转配置开关, on:开启,off:关闭 | +| redirectType | 是 | string | `http` | 访问强制跳转类型,http:强制 http 跳转,https:强制 https 跳转 | +| redirectStatusCode | 是 | number | `301` | 强制跳转时返回状态码,支持 301、302 | + +###### https + +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------- | :------: | :----: | :----: | :------------------------------------ | +| certId | 是 | string | | 腾讯云托管域名证书 ID | +| http2 | 是 | string | | 是否开启 HTTP2,on: 开启,off: 关闭 | From 82e9970e19658aa637dcf30ad477d3cb1a033a02 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 30 Jul 2020 20:30:52 +0800 Subject: [PATCH 15/65] feat: support metrics --- src/_shims/handler.js | 1 + src/_shims/package.json | 1 + src/_shims/sls.js | 24 ++++++++++-------------- src/package.json | 2 +- src/serverless.js | 41 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/_shims/handler.js b/src/_shims/handler.js index 5719666..a6b2146 100644 --- a/src/_shims/handler.js +++ b/src/_shims/handler.js @@ -1,3 +1,4 @@ +require('tencent-component-monitor') const fs = require('fs') const path = require('path') const { createServer, proxy } = require('tencent-serverless-http') diff --git a/src/_shims/package.json b/src/_shims/package.json index 58e017b..6290a44 100644 --- a/src/_shims/package.json +++ b/src/_shims/package.json @@ -1,6 +1,7 @@ { "dependencies": { "express": "^4.17.1", + "tencent-component-monitor": "^1.1.0", "tencent-serverless-http": "^1.2.0" } } diff --git a/src/_shims/sls.js b/src/_shims/sls.js index f4568bd..d94a2b1 100644 --- a/src/_shims/sls.js +++ b/src/_shims/sls.js @@ -1,24 +1,20 @@ -const path = require('path') const express = require('express') const { Nuxt } = require('nuxt') -async function createServer(custom) { - const server = express() - // get next config - let configPath = path.join(__dirname, '..', 'nuxt.config.js') - if (custom) { - configPath = path.join(__dirname, 'nuxt.config.js') - } - const config = require(configPath) - config.dev = false +async function createServer() { + // not report route for custom monitor + const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] - // Init Nuxt.js - const nuxt = new Nuxt(config) + const server = express() + const nuxt = new Nuxt({ dev: false }) await nuxt.ready() - // Give nuxt middleware to express - // app.use(nuxt.render) server.all('*', (req, res, next) => { + noReportRoutes.forEach((route) => { + if (req.path.indexOf(route) === 0) { + req.__SLS_NO_REPORT__ = true + } + }) return nuxt.render(req, res, next) }) diff --git a/src/package.json b/src/package.json index 389b0b7..ebb3f2d 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.13.0", + "tencent-component-toolkit": "^1.13.1", "type": "^2.0.0" } } diff --git a/src/serverless.js b/src/serverless.js index 24e5e81..92fb3aa 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -1,5 +1,5 @@ const { Component } = require('@serverless/core') -const { MultiApigw, Scf, Apigw, Cns, Cam, Cos, Cdn } = require('tencent-component-toolkit') +const { MultiApigw, Scf, Apigw, Cns, Cam, Metrics, Cos, Cdn } = require('tencent-component-toolkit') const { TypeError } = require('tencent-component-toolkit/src/utils/error') const { uploadCodeToCos, @@ -359,6 +359,45 @@ class ServerlessComopnent extends Component { this.state = {} } + + async metrics(inputs = {}) { + console.log(`Get ${CONFIGS.compFullname} Metrics Datas...`) + if (!inputs.rangeStart || !inputs.rangeEnd) { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUppoerCase()}_METRICS`, + 'rangeStart and rangeEnd are require inputs' + ) + } + const { region } = this.state + if (!region) { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUppoerCase()}_METRICS`, + 'No region property in state' + ) + } + const { functionName, namespace, functionVersion } = this.state[region] || {} + if (functionName) { + const options = { + funcName: functionName, + namespace: namespace, + version: functionVersion, + region, + timezone: inputs.tz + } + const credentials = this.getCredentials() + const mertics = new Metrics(credentials, options) + const metricResults = await mertics.getDatas( + inputs.rangeStart, + inputs.rangeEnd, + Metrics.Type.All + ) + return metricResults + } + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUppoerCase()}_METRICS`, + 'Function name not define' + ) + } } module.exports = ServerlessComopnent From 1c8b313a1ffc132b2ab1e3e564a4d0b4e7625f19 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 30 Jul 2020 20:43:19 +0800 Subject: [PATCH 16/65] chore: release v0.0.9 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6915ebf..94f57e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.8", + "version": "0.0.9", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 436d99c..0eb000e 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.8 +version: 0.0.9 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From b57e7348d2891f7188489ab97be3c16b7eda5c77 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 5 Aug 2020 17:04:03 +0800 Subject: [PATCH 17/65] feat: update default entry file sls.js --- README.en.md | 25 ++++++------------------- README.md | 25 ++++++------------------- src/_shims/sls.js | 5 ++--- 3 files changed, 14 insertions(+), 41 deletions(-) diff --git a/README.en.md b/README.en.md index 735a47f..be22ee7 100755 --- a/README.en.md +++ b/README.en.md @@ -112,29 +112,16 @@ Checkout the [Serverless Components](https://github.com/serverless/components) r If you had used `express` for you server, you should create entry file `sls.js`, please change depand on your server entry file, below is a template: ```js -const path = require('path') const express = require('express') -const { Nuxt } = require('nuxt') +const { loadNuxt } = require('nuxt') -// not report route for custom monitor -const noReportRoutes = ['/_next', '/static'] +async function createServer() { + // not report route for custom monitor + const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] -async function createServer(custom) { const server = express() - // get next config - let configPath = path.join(__dirname, '..', 'nuxt.config.js') - if (custom) { - configPath = path.join(__dirname, 'nuxt.config.js') - } - const config = require(configPath) - config.dev = false - - // Init Nuxt.js - const nuxt = new Nuxt(config) - await nuxt.ready() - - // Give nuxt middleware to express - // app.use(nuxt.render) + const nuxt = await loadNuxt('start') + server.all('*', (req, res, next) => { noReportRoutes.forEach((route) => { if (req.path.indexOf(route) === 0) { diff --git a/README.md b/README.md index 59fd39e..3ffb19d 100644 --- a/README.md +++ b/README.md @@ -183,29 +183,16 @@ TENCENT_SECRET_KEY=123 如果你的 Nuxt.js 项目本身运行就是基于 `express` 自定义服务的,那么你需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: ```js -const path = require('path') const express = require('express') -const { Nuxt } = require('nuxt') +const { loadNuxt } = require('nuxt') -// not report route for custom monitor -const noReportRoutes = ['/_next', '/static'] +async function createServer() { + // not report route for custom monitor + const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] -async function createServer(custom) { const server = express() - // get next config - let configPath = path.join(__dirname, '..', 'nuxt.config.js') - if (custom) { - configPath = path.join(__dirname, 'nuxt.config.js') - } - const config = require(configPath) - config.dev = false - - // Init Nuxt.js - const nuxt = new Nuxt(config) - await nuxt.ready() - - // Give nuxt middleware to express - // app.use(nuxt.render) + const nuxt = await loadNuxt('start') + server.all('*', (req, res, next) => { noReportRoutes.forEach((route) => { if (req.path.indexOf(route) === 0) { diff --git a/src/_shims/sls.js b/src/_shims/sls.js index d94a2b1..45afad9 100644 --- a/src/_shims/sls.js +++ b/src/_shims/sls.js @@ -1,13 +1,12 @@ const express = require('express') -const { Nuxt } = require('nuxt') +const { loadNuxt } = require('nuxt') async function createServer() { // not report route for custom monitor const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] const server = express() - const nuxt = new Nuxt({ dev: false }) - await nuxt.ready() + const nuxt = await loadNuxt('start') server.all('*', (req, res, next) => { noReportRoutes.forEach((route) => { From c02155ea77a02d2335262a71b97e4e879cd4fe58 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 5 Aug 2020 17:16:52 +0800 Subject: [PATCH 18/65] chore: release v0.0.10 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 94f57e5..4f8bd47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.9", + "version": "0.0.10", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 0eb000e..7ba617b 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.9 +version: 0.0.10 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From e5034af0b6ebb1ce929c7fe6cc37597d7ef8096b Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 6 Aug 2020 17:36:08 +0800 Subject: [PATCH 19/65] fix: support eip config --- docs/configure.md | 48 +++++----- example/README.md | 236 +++++++++++++++++++++++++++++++++++++++++++--- src/config.js | 1 - src/package.json | 2 +- src/utils.js | 13 +-- 5 files changed, 252 insertions(+), 48 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 93fb1c1..21b30e6 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -32,6 +32,7 @@ inputs: version: 1 # 版本 functionConf: # 函数配置相关 timeout: 10 # 超时时间,单位秒 + eip: false # 是否固定出口IP memorySize: 128 # 内存大小,单位MB environment: # 环境变量 variables: # 环境变量数组 @@ -148,12 +149,13 @@ inputs: 参考: https://cloud.tencent.com/document/product/583/18586 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| ----------- | :------: | :----: | :----: | :------------------------------------------------------------------------------ | -| timeout | 否 | Number | 3 | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒 | -| memorySize | 否 | Number | 128 | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 | -| environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) | -| vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| ----------- | :------: | :-----: | :-----: | :------------------------------------------------------------------------------ | +| timeout | 否 | Number | `3` | 函数最长执行时间,单位为秒,可选值范围 1-900 秒,默认为 3 秒 | +| memorySize | 否 | Number | `128` | 函数运行时内存大小,默认为 128M,可选范围 64、128MB-3072MB,并且以 128MB 为阶梯 | +| environment | 否 | Object | | 函数的环境变量, 参考 [环境变量](#环境变量) | +| vpcConfig | 否 | Object | | 函数的 VPC 配置, 参考 [VPC 配置](#VPC-配置) | +| eip | 否 | Boolean | `false` | 是否固定出口 IP | ##### 环境变量 @@ -170,27 +172,27 @@ inputs: ### API 网关配置 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| -------------- | :------: | :------- | :------- | :--------------------------------------------------------------------------------- | -| protocols | 否 | String[] | ['http'] | 前端请求的类型,如 http,https,http 与 https | -| environment | 否 | String | release | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). | -| usagePlan | 否 | | | 使用计划配置, 参考 [使用计划](#使用计划) | -| auth | 否 | | | API 密钥配置, 参考 [API 密钥](#API-密钥配置) | -| customDomain | 否 | Object[] | | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名) | -| enableCORS | 否 | Boolean | `false` | 开启跨域。默认值为否。 | -| serviceTimeout | 否 | Number | `15` | Api 超时时间,单位: 秒 | -| isDisabled | 否 | Boolean | `false` | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。 | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------------- | :------: | :------- | :--------- | :--------------------------------------------------------------------------------- | +| protocols | 否 | String[] | `['http']` | 前端请求的类型,如 http,https,http 与 https | +| environment | 否 | String | `release` | 发布环境. 目前支持三种发布环境: test(测试), prepub(预发布) 与 release(发布). | +| usagePlan | 否 | | | 使用计划配置, 参考 [使用计划](#使用计划) | +| auth | 否 | | | API 密钥配置, 参考 [API 密钥](#API-密钥配置) | +| customDomain | 否 | Object[] | | 自定义 API 域名配置, 参考 [自定义域名](#自定义域名) | +| enableCORS | 否 | Boolean | `false` | 开启跨域。默认值为否。 | +| serviceTimeout | 否 | Number | `15` | Api 超时时间,单位: 秒 | +| isDisabled | 否 | Boolean | `false` | 关闭自动创建 API 网关功能。默认值为否,即默认自动创建 API 网关。 | ##### 使用计划 参考: https://cloud.tencent.com/document/product/628/14947 -| 参数名称 | 是否必选 | 类型 | 描述 | -| ------------- | :------: | ------ | :------------------------------------------------------ | -| usagePlanId | 否 | String | 用户自定义使用计划 ID | -| usagePlanName | 否 | String | 用户自定义的使用计划名称 | -| usagePlanDesc | 否 | String | 用户自定义的使用计划描述 | -| maxRequestNum | 否 | Int | 请求配额总数,如果为空,将使用-1 作为默认值,表示不开启 | +| 参数名称 | 是否必选 | 类型 | 描述 | +| ------------- | :------: | ------ | :--------------------------------------------------------- | +| usagePlanId | 否 | String | 用户自定义使用计划 ID | +| usagePlanName | 否 | String | 用户自定义的使用计划名称 | +| usagePlanDesc | 否 | String | 用户自定义的使用计划描述 | +| maxRequestNum | 否 | Number | 请求配额总数,如果为空,将使用 `-1` 作为默认值,表示不开启 | ##### API 密钥配置 @@ -209,7 +211,7 @@ Refer to: https://cloud.tencent.com/document/product/628/14906 | ---------------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | domain | 是 | String | | 待绑定的自定义的域名。 | | certificateId | 否 | String | | 待绑定自定义域名的证书唯一 ID,如果设置了 type 为 https,则为必选 | -| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射,默认为 true。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 | +| isDefaultMapping | 否 | String | `true` | 是否使用默认路径映射。为 false 时,表示自定义路径映射,此时 pathMappingSet 必填。 | | pathMappingSet | 否 | Object[] | `[]` | 自定义路径映射的路径。使用自定义映射时,可一次仅映射一个 path 到一个环境,也可映射多个 path 到多个环境。并且一旦使用自定义映射,原本的默认映射规则不再生效,只有自定义映射路径生效。 | | protocol | 否 | String[] | | 绑定自定义域名的协议类型,默认与服务的前端协议一致。 | diff --git a/example/README.md b/example/README.md index 6abdb50..3ffb19d 100644 --- a/example/README.md +++ b/example/README.md @@ -1,22 +1,232 @@ -# nuxt-demo +[![Serverless Nuxtjs Tencent Cloud](https://img.serverlesscloud.cn/2020310/1583829094342-nuxt.js%20_%E9%95%BF.png)](http://serverless.com) -> My superb Nuxt.js project +# 腾讯云 Nuxt.js Serverless Component -## Build Setup +简体中文 | [English](https://github.com/serverless-components/tencent-nuxtjs/blob/v2/README.en.md) + +## 简介 + +**腾讯云[Nuxt.js](https://github.com/nuxt/nuxt.js)组件** - 通过使用[**Tencent Serverless Framework**](https://github.com/serverless/components/tree/cloud) , 基于云上 Serverless 服务(如 API 网关、云函数等),实现“0”配置,便捷开发,极速部署采用 Nuxt.js 框架的网页应用,Nuxt.js 组件支持丰富的配置扩展,提供了目前便捷实用,开发成本低的网页应用项目的开发/托管能力。 + +特性介绍: + +- [x] **按需付费** - 按照请求的使用量进行收费,没有请求时无需付费 +- [x] **"0"配置** - 只需要关心项目代码,之后部署即可,Serverless Framework 会搞定所有配置。 +- [x] **极速部署** - 部署速度快,仅需几秒,部署你的整个应用。 +- [x] **实时日志** - 通过实时日志的输出查看业务状态,便于直接在云端开发应用。 +- [x] **云端调试** - 可在云端直接进行项目调试,从而避免本地环境的差异。 +- [x] **便捷协作** - 通过云端控制台的状态信息和部署日志,方便进行多人协作开发。 + +## 快速开始 + +0. [**准备**](#0-准备) +1. [**安装**](#1-安装) +1. [**配置**](#2-配置) +1. [**部署**](#3-部署) +1. [**开发调试**](#4-开发调试) +1. [**查看状态**](#5-查看部署状态) +1. [**移除**](#6-移除) + +更多资源: + +- [**账号配置**](#账号配置) +- [**更多组件**](#更多组件) +- [**FAQ**](#FAQ) + +### 0. 准备 + +#### 初始化 Nuxt.js 项目 + +首先,在本地创建根目录,并初始化一个 Nuxt.js 项目 + +```bash +$ npx create-nuxt-app serverless-nuxtjs +$ cd serverless-nuxtjs +``` + +> 注意:本教程中的 Nuxt 项目使用 JavaScript 与 Npm 安装包进行构建,初始化项目的时候请选择相应的选项 + +### 1. 安装 + +通过 npm 全局安装 [serverless cli](https://github.com/serverless/serverless) + +```bash +$ npm install -g serverless +``` + +### 2. 配置 + +在项目根目录创建 `serverless.yml` 文件,在其中进行如下配置 + +```bash +$ touch serverless.yml +``` + +```yml +# serverless.yml +component: nuxtjs # (必填) 组件名称,此处为nuxtjs +name: nuxtjsDemo # (必填) 实例名称 +org: orgDemo # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid +app: appDemo # (可选) 该 nuxt.js 项目名称 +stage: dev # (可选) 用于区分环境信息,默认值是 dev + +inputs: + src: + src: ./ + exclude: + - .env + region: ap-guangzhou + runtime: Nodejs10.15 + apigatewayConf: + protocols: + - http + - https + environment: release +``` + +- 点此查看[更多配置及说明](https://github.com/serverless-components/tencent-nextjs/tree/v2/docs/configure.md) + +### 3. 部署 + +#### 3.1 构建静态资源 + +进入到 nuxt 项目目录下,构建静态资源 + +```bash +$ cd src && npm run build +``` + +#### 3.2 部署到云端 + +回到在 serverless.yml 文件所在的项目根目录,运行以下指令进行部署: + +```bash +# 进入项目根目录 serverless-nuxtjs +$ sls deploy + +serverless ⚡ framework +Action: "deploy" - Stage: "dev" - App: "appDemo" - Instance: "nuxtjsDemo" + +region: ap-guangzhou +apigw: + serviceId: service-4v2jx72g + subDomain: service-4v2jx72g-1258834142.gz.apigw.tencentcs.com + environment: release + url: https://xxxxxx.gz.apigw.tencentcs.com/release/ +scf: + functionName: nuxtjs_component_mm518kl + runtime: Nodejs10.15 + namespace: default + +139s › nuxtjsDemo › Success +``` + +部署时需要进行身份验证,如您的账号未 [登陆](https://cloud.tencent.com/login) 或 [注册](https://cloud.tencent.com/register) 腾讯云,您可以直接通过 `微信` 扫描命令行中的二维码进行授权登陆和注册。 + +> 注意: 如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。 +> `sls` 是 `serverless` 命令的简写。 + +### 4. 开发调试 + +部署了 Nuxt.js 应用后,可以通过开发调试能力对该项目进行二次开发,从而开发一个生产应用。在本地修改和更新代码后,不需要每次都运行 `serverless deploy` 命令来反复部署。你可以直接通过 `serverless dev` 命令对本地代码的改动进行检测和自动上传。 + +可以通过在 `serverless.yml`文件所在的目录下运行 `serverless dev` 命令开启开发调试能力。 + +`serverless dev` 同时支持实时输出云端日志,每次部署完毕后,对项目进行访问,即可在命令行中实时输出调用日志,便于查看业务情况和排障。 + +### 5. 查看部署状态 + +在`serverless.yml`文件所在的目录下,通过如下命令查看部署状态: + +``` +$ serverless info +``` + +### 6. 移除 + +在`serverless.yml`文件所在的目录下,通过以下命令移除部署通过以下命令移除部署的 API 网关,移除后该组件会对应删除云上部署时所创建的所有相关资源。 + +```bash +$ sls remove +``` + +和部署类似,支持通过 `sls remove --debug` 命令查看移除过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。 + +### 账号配置 + +当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件 ```bash -# install dependencies -$ npm install +$ touch .env # 腾讯云的配置信息 +``` -# serve with hot reload at localhost:3000 -$ npm run dev +在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存 -# build for production and launch server -$ npm run build -$ npm run start +如果没有腾讯云账号,可以在此 [注册新账号](https://cloud.tencent.com/register)。 -# generate static project -$ npm run generate +如果已有腾讯云账号,可以在 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 中获取 `SecretId` 和`SecretKey`. + +```text +# .env +TENCENT_SECRET_ID=123 +TENCENT_SECRET_KEY=123 ``` -For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). +> 注意:海外 ip 登录时,需要在`.env`文件中添加`SERVERLESS_PLATFORM_VENDOR=tencent` ,使 sls 默认使用 tencent 组件 + +## 更多组件 + +可以在 [Serverless Components](https://github.com/serverless/components) repo 中查询更多组件的信息。 + +## 项目迁移 - 自定义 express 服务 + +如果你的 Nuxt.js 项目本身运行就是基于 `express` 自定义服务的,那么你需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: + +```js +const express = require('express') +const { loadNuxt } = require('nuxt') + +async function createServer() { + // not report route for custom monitor + const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] + + const server = express() + const nuxt = await loadNuxt('start') + + server.all('*', (req, res, next) => { + noReportRoutes.forEach((route) => { + if (req.path.indexOf(route) === 0) { + req.__SLS_NO_REPORT__ = true + } + }) + return nuxt.render(req, res, next) + }) + + // define binary type for response + // if includes, will return base64 encoded, very useful for images + server.binaryTypes = ['*/*'] + + return server +} + +module.exports = createServer +``` + +## 自定义监控 + +当在部署 Next.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_next` 和 `/static` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: + +```js +server.get('/no-report', (req, res) => { + req.__SLS_NO_REPORT__ = true + return handle(req, res) +}) +``` + +那么用户在访问 `GET /no-report` 路由时,就不会上报自定义监控指标。 + +## License + +MIT License + +Copyright (c) 2020 Tencent Cloud, Inc. diff --git a/src/config.js b/src/config.js index 0e095f1..8ac7bac 100644 --- a/src/config.js +++ b/src/config.js @@ -5,7 +5,6 @@ const CONFIGS = { compFullname: 'Nuxt.js', handler: 'sl_handler.handler', runtime: 'Nodejs10.15', - exclude: ['.git/**', '.gitignore', '.DS_Store'], timeout: 3, memorySize: 128, namespace: 'default', diff --git a/src/package.json b/src/package.json index ebb3f2d..a0d541c 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.13.1", + "tencent-component-toolkit": "^1.13.2", "type": "^2.0.0" } } diff --git a/src/utils.js b/src/utils.js index 5b4c192..c569749 100644 --- a/src/utils.js +++ b/src/utils.js @@ -360,7 +360,9 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { }), publish: inputs.publish, traffic: inputs.traffic, - lastVersion: instance.state.lastVersion + lastVersion: instance.state.lastVersion, + eip: tempFunctionConf.eip === true, + l5Enable: tempFunctionConf.l5Enable === true } // validate traffic @@ -373,15 +375,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { default: null }) - functionConf.include = ensureIterable( - tempFunctionConf.include ? tempFunctionConf.include : inputs.include, - { default: [], ensureItem: ensureString } - ) - functionConf.exclude = ensureIterable( - tempFunctionConf.exclude ? tempFunctionConf.exclude : inputs.exclude, - { default: [], ensureItem: ensureString } - ) - functionConf.exclude.push('.git/**', '.gitignore', '.serverless', '.DS_Store') if (inputs.functionConf) { functionConf.timeout = inputs.functionConf.timeout ? inputs.functionConf.timeout From 5d644d575e2686fc1a66477e6d70d9e52529a229 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 6 Aug 2020 17:37:18 +0800 Subject: [PATCH 20/65] chore: release v0.0.11 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4f8bd47..a1ab05e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.10", + "version": "0.0.11", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index 7ba617b..bc48893 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.10 +version: 0.0.11 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From 584aa55bff4b929275dff011b921d4c4d7437307 Mon Sep 17 00:00:00 2001 From: yugasun Date: Fri, 7 Aug 2020 17:08:47 +0800 Subject: [PATCH 21/65] fix: typo --- src/serverless.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serverless.js b/src/serverless.js index 92fb3aa..9cd2d1c 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -364,14 +364,14 @@ class ServerlessComopnent extends Component { console.log(`Get ${CONFIGS.compFullname} Metrics Datas...`) if (!inputs.rangeStart || !inputs.rangeEnd) { throw new TypeError( - `PARAMETER_${CONFIGS.compName.toUppoerCase()}_METRICS`, + `PARAMETER_${CONFIGS.compName.toUpperCase()}_METRICS`, 'rangeStart and rangeEnd are require inputs' ) } const { region } = this.state if (!region) { throw new TypeError( - `PARAMETER_${CONFIGS.compName.toUppoerCase()}_METRICS`, + `PARAMETER_${CONFIGS.compName.toUpperCase()}_METRICS`, 'No region property in state' ) } @@ -394,7 +394,7 @@ class ServerlessComopnent extends Component { return metricResults } throw new TypeError( - `PARAMETER_${CONFIGS.compName.toUppoerCase()}_METRICS`, + `PARAMETER_${CONFIGS.compName.toUpperCase()}_METRICS`, 'Function name not define' ) } From 1a14b2447b3cbabe57b238bf6adf6b37ca8d0fbf Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 10 Aug 2020 19:04:08 +0800 Subject: [PATCH 22/65] feat: support api gw metrics --- src/package.json | 2 +- src/serverless.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index a0d541c..c769236 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.13.2", + "tencent-component-toolkit": "^1.14.1", "type": "^2.0.0" } } diff --git a/src/serverless.js b/src/serverless.js index 9cd2d1c..d00a95b 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -384,6 +384,11 @@ class ServerlessComopnent extends Component { region, timezone: inputs.tz } + const curState = this.state[region] + if (curState.serviceId) { + options.apigwServiceId = curState.serviceId + options.apigwEnvironment = curState.environment || 'release' + } const credentials = this.getCredentials() const mertics = new Metrics(credentials, options) const metricResults = await mertics.getDatas( From 09b59847941a6b95082d6776d3ea2cc3f0076e5a Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 10 Aug 2020 19:12:40 +0800 Subject: [PATCH 23/65] chore: release v0.0.12 --- package.json | 2 +- serverless.component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a1ab05e..2034bb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.11", + "version": "0.0.12", "main": "src/serverless.js", "publishConfig": { "access": "public" diff --git a/serverless.component.yml b/serverless.component.yml index bc48893..c081ebd 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.11 +version: 0.0.12 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From af359f735c42d866f96ef13bb6fc68b29f05c021 Mon Sep 17 00:00:00 2001 From: yugasun Date: Mon, 10 Aug 2020 20:41:36 +0800 Subject: [PATCH 24/65] fix: update deps --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index c769236..5c7441f 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.14.1", + "tencent-component-toolkit": "^1.14.2", "type": "^2.0.0" } } From f95f479662f1ce385832d30031f4b0981e6d4958 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 19 Aug 2020 16:23:45 +0800 Subject: [PATCH 25/65] fix: traffic zero display bug --- src/serverless.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/serverless.js b/src/serverless.js index d00a95b..f6e5a40 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -76,11 +76,12 @@ class ServerlessComopnent extends Component { : this.state.lastVersion || '$LATEST' // default traffic is 1.0, it can also be 0, so we should compare to undefined - outputs[curRegion].traffic = scfOutput.Traffic - ? scfOutput.Traffic - : this.state.traffic !== undefined - ? this.state.traffic - : 1 + outputs[curRegion].traffic = + scfOutput.Traffic !== undefined + ? scfOutput.Traffic + : this.state.traffic !== undefined + ? this.state.traffic + : 1 if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion From 9ff78a98d87f574e38bcd8d1fa393d83ae7bc0be Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 19 Aug 2020 16:54:45 +0800 Subject: [PATCH 26/65] fix: static cos bucket has appid suffix --- src/package.json | 2 +- src/utils.js | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/package.json b/src/package.json index 5c7441f..f7e2d70 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.14.2", + "tencent-component-toolkit": "^1.15.1", "type": "^2.0.0" } } diff --git a/src/utils.js b/src/utils.js index c569749..5432b03 100644 --- a/src/utils.js +++ b/src/utils.js @@ -20,6 +20,14 @@ const getType = (obj) => { return Object.prototype.toString.call(obj).slice(8, -1) } +const removeAppid = (str, appid) => { + const suffix = `-${appid}` + if (!str || str.indexOf(suffix) === -1) { + return str + } + return str.slice(0, -suffix.length) +} + const validateTraffic = (num) => { if (getType(num) !== 'Number') { throw new TypeError( @@ -67,6 +75,8 @@ const prepareStaticCosInputs = async (instance, inputs, appId, codeZipPath) => { const { cosConf } = inputs const sources = cosConf.sources || CONFIGS.defaultStatics const { bucket } = cosConf + // remove user append appid + const bucketName = removeAppid(bucket, appId) const staticPath = `/tmp/${generateId()}` const codeZip = new AdmZip(codeZipPath) const entries = codeZip.getEntries() @@ -86,7 +96,7 @@ const prepareStaticCosInputs = async (instance, inputs, appId, codeZipPath) => { const cosInputs = { force: true, protocol: cosConf.protocol, - bucket: `${bucket}-${appId}`, + bucket: `${bucketName}-${appId}`, src: `${staticPath}/${entryName}`, keyPrefix: curSource.targetDir || '/', acl: { From 0b8d96e4abbde1f028d2808e5ef0288d21fd919f Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 19 Aug 2020 16:56:59 +0800 Subject: [PATCH 27/65] chore: release v0.0.13 --- package.json | 3 ++- serverless.component.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2034bb3..684e8d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.12", + "version": "0.0.13", "main": "src/serverless.js", "publishConfig": { "access": "public" @@ -31,6 +31,7 @@ "**/*.{css,html,js,json,md,yaml,yml}": [ "npm run prettier:fix", "git add ." + ] }, "author": "Tencent Cloud, Inc.", diff --git a/serverless.component.yml b/serverless.component.yml index c081ebd..123ffc7 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.12 +version: 0.0.13 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. From e59531689c7339417e7d634c2349485b6c6879fa Mon Sep 17 00:00:00 2001 From: Avril Li Date: Wed, 26 Aug 2020 19:13:36 +0800 Subject: [PATCH 28/65] chore: enable semantic release --- .travis.yml | 20 +++++++++++--------- package.json | 2 -- release.config.js | 12 ++---------- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1ee195..f5161db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,12 +11,14 @@ install: jobs: include: # Define the release stage that runs semantic-release - # - stage: release - # node_js: 10.18 - # # Advanced: optionally overwrite your default `script` step to skip the tests - # # script: skip - # deploy: - # provider: script - # skip_cleanup: true - # script: - # - npm run release + - stage: release + node_js: 10.18 + # Advanced: optionally overwrite your default `script` step to skip the tests + # script: skip + deploy: + provider: script + skip_cleanup: true + on: + branch: master + script: + - npm run release diff --git a/package.json b/package.json index 684e8d6..94b6501 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "@serverless/nuxtjs", - "version": "0.0.13", "main": "src/serverless.js", "publishConfig": { "access": "public" @@ -31,7 +30,6 @@ "**/*.{css,html,js,json,md,yaml,yml}": [ "npm run prettier:fix", "git add ." - ] }, "author": "Tencent Cloud, Inc.", diff --git a/release.config.js b/release.config.js index 53f3398..a13798c 100644 --- a/release.config.js +++ b/release.config.js @@ -1,7 +1,6 @@ module.exports = { verifyConditions: [ '@semantic-release/changelog', - '@semantic-release/npm', '@semantic-release/git', '@semantic-release/github' ], @@ -12,7 +11,8 @@ module.exports = { preset: 'angular', parserOpts: { noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING'] - } + }, + releaseRules: [{ type: 'feat', release: 'patch' }] } ], [ @@ -33,14 +33,6 @@ module.exports = { changelogFile: 'CHANGELOG.md' } ], - [ - '@semantic-release/npm', - { - pkgRoot: '.', - npmPublish: false, - tarballDir: false - } - ], [ '@semantic-release/git', { From c35a48d97b403ebf188901fb7dec8900a926fd79 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 26 Aug 2020 11:19:22 +0000 Subject: [PATCH 29/65] chore(release): version 0.0.13 ## [0.0.13](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.12...v0.0.13) (2020-08-26) ### Bug Fixes * apigw custom domain update bug ([8c1225b](https://github.com/serverless-components/tencent-nuxtjs/commit/8c1225bf6ef306a96abfd05a522eb3e0ea17dcc3)) * cache http server ([8cec64e](https://github.com/serverless-components/tencent-nuxtjs/commit/8cec64eaf62256ad64c48348dd653d54122a85ce)) * optimize static cos output ([5d45e22](https://github.com/serverless-components/tencent-nuxtjs/commit/5d45e22b2290628e2cc085c29a1c225c05ea6f23)) * read bucket and object from srcOriginal ([e5c000d](https://github.com/serverless-components/tencent-nuxtjs/commit/e5c000da0d31c7f9be9038b0605763b26c75fd77)) * static cos bucket has appid suffix ([9ff78a9](https://github.com/serverless-components/tencent-nuxtjs/commit/9ff78a98d87f574e38bcd8d1fa393d83ae7bc0be)) * support eip config ([e5034af](https://github.com/serverless-components/tencent-nuxtjs/commit/e5034af0b6ebb1ce929c7fe6cc37597d7ef8096b)) * traffic zero display bug ([f95f479](https://github.com/serverless-components/tencent-nuxtjs/commit/f95f479662f1ce385832d30031f4b0981e6d4958)) * typo ([584aa55](https://github.com/serverless-components/tencent-nuxtjs/commit/584aa55bff4b929275dff011b921d4c4d7437307)) * update deps ([af359f7](https://github.com/serverless-components/tencent-nuxtjs/commit/af359f735c42d866f96ef13bb6fc68b29f05c021)) * upgrade deps ([b0dd2b3](https://github.com/serverless-components/tencent-nuxtjs/commit/b0dd2b34d56049266fab4862aaee5597a7ef66e6)) * upgrade deps ([f0ebc4d](https://github.com/serverless-components/tencent-nuxtjs/commit/f0ebc4dd747314aaadff0e5c799460e844971b99)) * upgrade deps ([1c4d4f8](https://github.com/serverless-components/tencent-nuxtjs/commit/1c4d4f83d829ff8c031e467dc22a4457c7a4e698)) * upgrade tencent-component-toolkit for deleting compatibility ([e5e3382](https://github.com/serverless-components/tencent-nuxtjs/commit/e5e3382e510999a1832f5888765808f213548c28)) * upgrade tencent-serverless-http ([f06f3ca](https://github.com/serverless-components/tencent-nuxtjs/commit/f06f3caaf62d9d3619676cb4fb99b1050f1df6b1)) ### Features * add layer support ([c2421ec](https://github.com/serverless-components/tencent-nuxtjs/commit/c2421ecb5b0dca91698638a124dd79668c10ecdc)) * init v2 ([a11e63b](https://github.com/serverless-components/tencent-nuxtjs/commit/a11e63b73301175aa9b4c7e36152fbededb8c78b)) * optimize zip flow ([5e3d2e1](https://github.com/serverless-components/tencent-nuxtjs/commit/5e3d2e1cae7525486274a71676f79afbc6cb6267)) * support api gw metrics ([1a14b24](https://github.com/serverless-components/tencent-nuxtjs/commit/1a14b2447b3cbabe57b238bf6adf6b37ca8d0fbf)) * support disable creating apigw ([87e2499](https://github.com/serverless-components/tencent-nuxtjs/commit/87e24998a9e98e1da1419696141344e2b35838f9)) * support metrics ([82e9970](https://github.com/serverless-components/tencent-nuxtjs/commit/82e9970e19658aa637dcf30ad477d3cb1a033a02)) * support scf publish version and traffic setup ([3860cc8](https://github.com/serverless-components/tencent-nuxtjs/commit/3860cc815bb8cd9a609188e524f472d07015f759)) * support static cdn deployment ([d24b941](https://github.com/serverless-components/tencent-nuxtjs/commit/d24b94168f17d39c1281804b18f864bdf9304dff)) * update default entry file sls.js ([b57e734](https://github.com/serverless-components/tencent-nuxtjs/commit/b57e7348d2891f7188489ab97be3c16b7eda5c77)) --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..23b0c04 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +## [0.0.13](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.12...v0.0.13) (2020-08-26) + + +### Bug Fixes + +* apigw custom domain update bug ([8c1225b](https://github.com/serverless-components/tencent-nuxtjs/commit/8c1225bf6ef306a96abfd05a522eb3e0ea17dcc3)) +* cache http server ([8cec64e](https://github.com/serverless-components/tencent-nuxtjs/commit/8cec64eaf62256ad64c48348dd653d54122a85ce)) +* optimize static cos output ([5d45e22](https://github.com/serverless-components/tencent-nuxtjs/commit/5d45e22b2290628e2cc085c29a1c225c05ea6f23)) +* read bucket and object from srcOriginal ([e5c000d](https://github.com/serverless-components/tencent-nuxtjs/commit/e5c000da0d31c7f9be9038b0605763b26c75fd77)) +* static cos bucket has appid suffix ([9ff78a9](https://github.com/serverless-components/tencent-nuxtjs/commit/9ff78a98d87f574e38bcd8d1fa393d83ae7bc0be)) +* support eip config ([e5034af](https://github.com/serverless-components/tencent-nuxtjs/commit/e5034af0b6ebb1ce929c7fe6cc37597d7ef8096b)) +* traffic zero display bug ([f95f479](https://github.com/serverless-components/tencent-nuxtjs/commit/f95f479662f1ce385832d30031f4b0981e6d4958)) +* typo ([584aa55](https://github.com/serverless-components/tencent-nuxtjs/commit/584aa55bff4b929275dff011b921d4c4d7437307)) +* update deps ([af359f7](https://github.com/serverless-components/tencent-nuxtjs/commit/af359f735c42d866f96ef13bb6fc68b29f05c021)) +* upgrade deps ([b0dd2b3](https://github.com/serverless-components/tencent-nuxtjs/commit/b0dd2b34d56049266fab4862aaee5597a7ef66e6)) +* upgrade deps ([f0ebc4d](https://github.com/serverless-components/tencent-nuxtjs/commit/f0ebc4dd747314aaadff0e5c799460e844971b99)) +* upgrade deps ([1c4d4f8](https://github.com/serverless-components/tencent-nuxtjs/commit/1c4d4f83d829ff8c031e467dc22a4457c7a4e698)) +* upgrade tencent-component-toolkit for deleting compatibility ([e5e3382](https://github.com/serverless-components/tencent-nuxtjs/commit/e5e3382e510999a1832f5888765808f213548c28)) +* upgrade tencent-serverless-http ([f06f3ca](https://github.com/serverless-components/tencent-nuxtjs/commit/f06f3caaf62d9d3619676cb4fb99b1050f1df6b1)) + + +### Features + +* add layer support ([c2421ec](https://github.com/serverless-components/tencent-nuxtjs/commit/c2421ecb5b0dca91698638a124dd79668c10ecdc)) +* init v2 ([a11e63b](https://github.com/serverless-components/tencent-nuxtjs/commit/a11e63b73301175aa9b4c7e36152fbededb8c78b)) +* optimize zip flow ([5e3d2e1](https://github.com/serverless-components/tencent-nuxtjs/commit/5e3d2e1cae7525486274a71676f79afbc6cb6267)) +* support api gw metrics ([1a14b24](https://github.com/serverless-components/tencent-nuxtjs/commit/1a14b2447b3cbabe57b238bf6adf6b37ca8d0fbf)) +* support disable creating apigw ([87e2499](https://github.com/serverless-components/tencent-nuxtjs/commit/87e24998a9e98e1da1419696141344e2b35838f9)) +* support metrics ([82e9970](https://github.com/serverless-components/tencent-nuxtjs/commit/82e9970e19658aa637dcf30ad477d3cb1a033a02)) +* support scf publish version and traffic setup ([3860cc8](https://github.com/serverless-components/tencent-nuxtjs/commit/3860cc815bb8cd9a609188e524f472d07015f759)) +* support static cdn deployment ([d24b941](https://github.com/serverless-components/tencent-nuxtjs/commit/d24b94168f17d39c1281804b18f864bdf9304dff)) +* update default entry file sls.js ([b57e734](https://github.com/serverless-components/tencent-nuxtjs/commit/b57e7348d2891f7188489ab97be3c16b7eda5c77)) From 0f2db4a7185138e1188474b096fc2491d9f8ec80 Mon Sep 17 00:00:00 2001 From: Avril Li Date: Wed, 26 Aug 2020 19:22:17 +0800 Subject: [PATCH 30/65] chore: update release rule --- .travis.yml | 1 - release.config.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5161db..d78fad9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - - 8 - 10 install: diff --git a/release.config.js b/release.config.js index a13798c..98b3864 100644 --- a/release.config.js +++ b/release.config.js @@ -11,8 +11,7 @@ module.exports = { preset: 'angular', parserOpts: { noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING'] - }, - releaseRules: [{ type: 'feat', release: 'patch' }] + } } ], [ From 2768d16b50aeea7343dae468421cb7eaf9902f1c Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 27 Aug 2020 15:12:50 +0800 Subject: [PATCH 31/65] fix: optimize deploy flow --- example/package.json | 2 +- serverless.component.yml | 2 +- src/config.js | 1 + src/package.json | 2 +- src/serverless.js | 260 +++++++++++----------- src/utils.js | 462 +++++++++++++++++---------------------- 6 files changed, 341 insertions(+), 388 deletions(-) diff --git a/example/package.json b/example/package.json index 78062d5..d86a931 100644 --- a/example/package.json +++ b/example/package.json @@ -11,7 +11,7 @@ "generate": "nuxt generate" }, "dependencies": { - "nuxt": "^2.0.0" + "nuxt": "^2.14.3" }, "devDependencies": {} } diff --git a/serverless.component.yml b/serverless.component.yml index 123ffc7..b2d1e62 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.13 +version: 0.0.14 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/config.js b/src/config.js index 8ac7bac..7ecf632 100644 --- a/src/config.js +++ b/src/config.js @@ -3,6 +3,7 @@ const CONFIGS = { 'https://serverless-templates-1300862921.cos.ap-beijing.myqcloud.com/nuxtjs-demo.zip', compName: 'nuxtjs', compFullname: 'Nuxt.js', + region: 'ap-guangzhou', handler: 'sl_handler.handler', runtime: 'Nodejs10.15', timeout: 3, diff --git a/src/package.json b/src/package.json index f7e2d70..e24f0ec 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.15.1", + "tencent-component-toolkit": "^1.15.7", "type": "^2.0.0" } } diff --git a/src/serverless.js b/src/serverless.js index f6e5a40..f527a0d 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -1,10 +1,10 @@ const { Component } = require('@serverless/core') -const { MultiApigw, Scf, Apigw, Cns, Cam, Metrics, Cos, Cdn } = require('tencent-component-toolkit') +const { Scf, Apigw, Cns, Cam, Metrics, Cos, Cdn } = require('tencent-component-toolkit') const { TypeError } = require('tencent-component-toolkit/src/utils/error') const { + deepClone, uploadCodeToCos, getDefaultProtocol, - deleteRecord, prepareInputs, prepareStaticCosInputs, prepareStaticCdnInputs @@ -46,134 +46,141 @@ class ServerlessComopnent extends Component { } } - const uploadCodeHandler = [] const outputs = {} const appId = this.getAppId() - regionList.forEach((curRegion) => { - const funcDeployer = async () => { - const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion) - const scf = new Scf(credentials, curRegion) - const tempInputs = { - ...inputs, - code - } - const scfOutput = await scf.deploy(tempInputs) - outputs[curRegion] = { - functionName: scfOutput.FunctionName, - runtime: scfOutput.Runtime, - namespace: scfOutput.Namespace - } - - this.state[curRegion] = { - ...(this.state[curRegion] ? this.state[curRegion] : {}), - ...outputs[curRegion] - } + const funcDeployer = async (curRegion) => { + const code = await uploadCodeToCos(this, appId, credentials, inputs, curRegion) + const scf = new Scf(credentials, curRegion) + const tempInputs = { + ...inputs, + code + } + const scfOutput = await scf.deploy(deepClone(tempInputs)) + outputs[curRegion] = { + functionName: scfOutput.FunctionName, + runtime: scfOutput.Runtime, + namespace: scfOutput.Namespace + } - // default version is $LATEST - outputs[curRegion].lastVersion = scfOutput.LastVersion - ? scfOutput.LastVersion - : this.state.lastVersion || '$LATEST' - - // default traffic is 1.0, it can also be 0, so we should compare to undefined - outputs[curRegion].traffic = - scfOutput.Traffic !== undefined - ? scfOutput.Traffic - : this.state.traffic !== undefined - ? this.state.traffic - : 1 - - if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { - outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion - this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion - } + this.state[curRegion] = { + ...(this.state[curRegion] ? this.state[curRegion] : {}), + ...outputs[curRegion] + } - this.state.lastVersion = outputs[curRegion].lastVersion - this.state.traffic = outputs[curRegion].traffic + // default version is $LATEST + outputs[curRegion].lastVersion = scfOutput.LastVersion + ? scfOutput.LastVersion + : this.state.lastVersion || '$LATEST' + + // default traffic is 1.0, it can also be 0, so we should compare to undefined + outputs[curRegion].traffic = + scfOutput.Traffic !== undefined + ? scfOutput.Traffic + : this.state.traffic !== undefined + ? this.state.traffic + : 1 + + if (outputs[curRegion].traffic !== 1 && scfOutput.ConfigTrafficVersion) { + outputs[curRegion].configTrafficVersion = scfOutput.ConfigTrafficVersion + this.state.configTrafficVersion = scfOutput.ConfigTrafficVersion } - uploadCodeHandler.push(funcDeployer()) - }) - await Promise.all(uploadCodeHandler) + + this.state.lastVersion = outputs[curRegion].lastVersion + this.state.traffic = outputs[curRegion].traffic + } + + for (let i = 0; i < regionList.length; i++) { + const curRegion = regionList[i] + await funcDeployer(curRegion) + } this.save() return outputs } + // try to add dns record + async tryToAddDnsRecord(credentials, customDomains) { + try { + const cns = new Cns(credentials) + for (let i = 0; i < customDomains.length; i++) { + const item = customDomains[i] + if (item.domainPrefix) { + await cns.deploy({ + domain: item.subDomain.replace(`${item.domainPrefix}.`, ''), + records: [ + { + subDomain: item.domainPrefix, + recordType: 'CNAME', + recordLine: '默认', + value: item.cname, + ttl: 600, + mx: 10, + status: 'enable' + } + ] + }) + } + } + } catch (e) { + console.log('METHOD_tryToAddDnsRecord', e.message) + } + } + async deployApigateway(credentials, inputs, regionList) { if (inputs.isDisabled) { return {} } - const apigw = new MultiApigw(credentials, regionList) - const oldState = this.state[regionList[0]] || {} - inputs.oldState = { - apiList: oldState.apiList || [], - customDomains: oldState.customDomains || [] + + const getServiceId = (instance, region) => { + const regionState = instance.state[region] + return inputs.serviceId || (regionState && regionState.serviceId) } - const apigwOutputs = await apigw.deploy(inputs) - const outputs = {} - Object.keys(apigwOutputs).forEach((curRegion) => { - const curOutput = apigwOutputs[curRegion] - outputs[curRegion] = { - serviceId: curOutput.serviceId, - subDomain: curOutput.subDomain, - environment: curOutput.environment, - url: `${getDefaultProtocol(inputs.protocols)}://${curOutput.subDomain}/${ - curOutput.environment - }/` - } - if (curOutput.customDomains) { - outputs[curRegion].customDomains = curOutput.customDomains - } - this.state[curRegion] = { - created: curOutput.created, - ...(this.state[curRegion] ? this.state[curRegion] : {}), - ...outputs[curRegion], - apiList: curOutput.apiList - } - }) - this.save() - return outputs - } - async deployCns(credentials, inputs, regionList, apigwOutputs) { - const cns = new Cns(credentials) - const cnsRegion = {} + const deployTasks = [] + const outputs = {} regionList.forEach((curRegion) => { - const curApigwOutput = apigwOutputs[curRegion] - cnsRegion[curRegion] = curApigwOutput.subDomain - }) + const apigwDeployer = async () => { + const apigw = new Apigw(credentials, curRegion) - const state = [] - const outputs = {} - const tempJson = {} - for (let i = 0; i < inputs.length; i++) { - const curCns = inputs[i] - for (let j = 0; j < curCns.records.length; j++) { - curCns.records[j].value = - cnsRegion[curCns.records[j].value.replace('temp_value_about_', '')] - } - const tencentCnsOutputs = await cns.deploy(curCns) - outputs[curCns.domain] = tencentCnsOutputs.DNS - ? tencentCnsOutputs.DNS - : 'The domain name has already been added.' - tencentCnsOutputs.domain = curCns.domain - state.push(tencentCnsOutputs) - } + const oldState = this.state[curRegion] || {} + const apigwInputs = { + ...inputs, + oldState: { + apiList: oldState.apiList || [], + customDomains: oldState.customDomains || [] + } + } + // different region deployment has different service id + apigwInputs.serviceId = getServiceId(this, curRegion) + const apigwOutput = await apigw.deploy(deepClone(apigwInputs)) + outputs[curRegion] = { + serviceId: apigwOutput.serviceId, + subDomain: apigwOutput.subDomain, + environment: apigwOutput.environment, + url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${ + apigwOutput.environment + }/` + } - // 删除serverless创建的但是不在本次列表中 - try { - for (let i = 0; i < state.length; i++) { - tempJson[state[i].domain] = state[i].records - } - const recordHistory = this.state.cns || [] - for (let i = 0; i < recordHistory.length; i++) { - const delList = deleteRecord(tempJson[recordHistory[i].domain], recordHistory[i].records) - if (delList && delList.length > 0) { - await cns.remove({ deleteList: delList }) + if (apigwOutput.customDomains) { + // TODO: need confirm add cns authentication + if (inputs.autoAddDnsRecord === true) { + // await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains) + } + outputs[curRegion].customDomains = apigwOutput.customDomains + } + this.state[curRegion] = { + created: true, + ...(this.state[curRegion] ? this.state[curRegion] : {}), + ...outputs[curRegion], + apiList: apigwOutput.apiList } } - } catch (e) {} + deployTasks.push(apigwDeployer()) + }) + + await Promise.all(deployTasks) - this.state['cns'] = state this.save() return outputs } @@ -235,7 +242,7 @@ class ServerlessComopnent extends Component { const credentials = this.getCredentials() // 对Inputs内容进行标准化 - const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs( + const { regionList, functionConf, apigatewayConf } = await prepareInputs( this, credentials, inputs @@ -267,17 +274,27 @@ class ServerlessComopnent extends Component { outputs['scf'] = functionOutputs } - // cns depends on apigw, so if disabled apigw, just ignore it. - if (cnsConf.length > 0 && apigatewayConf.isDisabled !== true) { - outputs['cns'] = await this.deployCns(credentials, cnsConf, regionList, apigwOutputs) - } - // start deploy static cdn if (inputs.staticConf) { - const staticDeployRes = await this.deployStatic(credentials, inputs.staticConf, regionList[0]) - if (staticDeployRes) { - this.state.staticConf = staticDeployRes - outputs.staticConf = staticDeployRes + const staticOutputs = {} + const staticTasks = [] + regionList.forEach((region) => { + const staticDeployer = async () => { + const res = await this.deployStatic(credentials, inputs.staticConf, region) + staticOutputs[region] = res + } + staticTasks.push(staticDeployer()) + }) + + await Promise.all(staticTasks) + + if (regionList.length === 1) { + const [oneRegion] = regionList + this.state.staticConf = staticOutputs[oneRegion] + outputs.staticConf = staticOutputs[oneRegion] + } else { + this.state.staticConf = staticOutputs + outputs.staticConf = staticOutputs } } @@ -348,13 +365,6 @@ class ServerlessComopnent extends Component { await Promise.all(removeHandlers) - if (state.cns) { - const cns = new Cns(credentials) - for (let i = 0; i < this.state.cns.length; i++) { - await cns.remove({ deleteList: this.state.cns[i].records }) - } - } - // remove static await this.removeStatic() diff --git a/src/utils.js b/src/utils.js index 5432b03..4aadd29 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,5 @@ const path = require('path') -const { Domain, Cos } = require('tencent-component-toolkit') +const { Cos } = require('tencent-component-toolkit') const { TypeError } = require('tencent-component-toolkit/src/utils/error') const ensureObject = require('type/object/ensure') const ensureIterable = require('type/iterable/ensure') @@ -8,18 +8,50 @@ const download = require('download') const AdmZip = require('adm-zip') const CONFIGS = require('./config') -/* - * Generates a random id - */ const generateId = () => Math.random() .toString(36) .substring(6) +const deepClone = (obj) => { + return JSON.parse(JSON.stringify(obj)) +} + const getType = (obj) => { return Object.prototype.toString.call(obj).slice(8, -1) } +const mergeJson = (sourceJson, targetJson) => { + Object.entries(sourceJson).forEach(([key, val]) => { + targetJson[key] = deepClone(val) + }) + return targetJson +} + +const capitalString = (str) => { + if (str.length < 2) { + return str.toUpperCase() + } + + return `${str[0].toUpperCase()}${str.slice(1)}` +} + +const getDefaultProtocol = (protocols) => { + return String(protocols).includes('https') ? 'https' : 'http' +} + +const getDefaultFunctionName = () => { + return `${CONFIGS.compName}_component_${generateId()}` +} + +const getDefaultServiceName = () => { + return 'serverless' +} + +const getDefaultServiceDescription = () => { + return 'Created by Serverless Component' +} + const removeAppid = (str, appid) => { const suffix = `-${appid}` if (!str || str.indexOf(suffix) === -1) { @@ -70,103 +102,11 @@ const getCodeZipPath = async (instance, inputs) => { return zipPath } -const prepareStaticCosInputs = async (instance, inputs, appId, codeZipPath) => { - const staticCosInputs = [] - const { cosConf } = inputs - const sources = cosConf.sources || CONFIGS.defaultStatics - const { bucket } = cosConf - // remove user append appid - const bucketName = removeAppid(bucket, appId) - const staticPath = `/tmp/${generateId()}` - const codeZip = new AdmZip(codeZipPath) - const entries = codeZip.getEntries() - - // traverse sources, generate static directory and deploy to cos - for (let i = 0; i < sources.length; i++) { - const curSource = sources[i] - const entryName = `${curSource.src}` - let exist = false - entries.forEach((et) => { - if (et.entryName.indexOf(entryName) === 0) { - codeZip.extractEntryTo(et, staticPath, true, true) - exist = true - } - }) - if (exist) { - const cosInputs = { - force: true, - protocol: cosConf.protocol, - bucket: `${bucketName}-${appId}`, - src: `${staticPath}/${entryName}`, - keyPrefix: curSource.targetDir || '/', - acl: { - permissions: 'public-read', - grantRead: '', - grantWrite: '', - grantFullControl: '' - } - } - - if (cosConf.acl) { - cosInputs.acl = { - permissions: cosConf.acl.permissions || 'public-read', - grantRead: cosConf.acl.grantRead || '', - grantWrite: cosConf.acl.grantWrite || '', - grantFullControl: cosConf.acl.grantFullControl || '' - } - } - - staticCosInputs.push(cosInputs) - } - } - return staticCosInputs -} - -const prepareStaticCdnInputs = async (instance, inputs, origin) => { - const { cdnConf } = inputs - const cdnInputs = { - async: true, - area: cdnConf.area || 'mainland', - domain: cdnConf.domain, - serviceType: 'web', - origin: { - origins: [origin], - originType: 'cos', - originPullProtocol: 'https' - }, - autoRefresh: true, - ...cdnConf - } - if (cdnConf.https) { - // using these default configs, for making user's config more simple - cdnInputs.forceRedirect = cdnConf.https.forceRedirect || CONFIGS.defaultCdnConf.forceRedirect - if (!cdnConf.https.certId) { - throw new TypeError( - `PARAMETER_${CONFIGS.compName.toUpperCase()}_HTTPS`, - 'https.certId is required' - ) - } - cdnInputs.https = { - ...CONFIGS.defaultCdnConf.https, - ...{ - http2: cdnConf.https.http2 || 'on', - certInfo: { - certId: cdnConf.https.certId - } - } - } - } - if (cdnInputs.autoRefresh) { - cdnInputs.refreshCdn = { - flushType: cdnConf.refreshType || 'delete', - urls: [`http://${cdnInputs.domain}`, `https://${cdnInputs.domain}`] - } - } - - return cdnInputs -} - -const transformNextConfig = (zipPath) => { +/** + * transform export to module.exports in nuxt.config.js + * @param {string} zipPath code zip path + */ +const transformNuxtConfig = (zipPath) => { const zip = new AdmZip(zipPath) const entries = zip.getEntries() const [entry] = entries.filter((e) => e.name === 'nuxt.config.js') @@ -180,7 +120,7 @@ const transformNextConfig = (zipPath) => { ) ) } else { - console.log('TransformNextConfig: nuxt.config.js not exist.') + console.log('transformNuxtConfig: nuxt.config.js not exist.') } zip.writeZip() } @@ -203,7 +143,7 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) => // transform nuxt.config.js file // export default -> module.exports = - transformNextConfig(zipPath) + transformNuxtConfig(zipPath) // save the zip path to state for lambda to use it instance.state.zipPath = zipPath @@ -235,12 +175,18 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) => object: objectName, method: 'PUT' }) - const slsSDKEntries = instance.getSDKEntries('_shims/handler.handler') + // if shims and sls sdk entries had been injected to zipPath, no need to injected again console.log(`Uploading code to bucket ${bucketName}`) - await instance.uploadSourceZipToCOS(zipPath, uploadUrl, slsSDKEntries, { - _shims: path.join(__dirname, '_shims') - }) + if (instance.codeInjected === true) { + await instance.uploadSourceZipToCOS(zipPath, uploadUrl, {}, {}) + } else { + const slsSDKEntries = instance.getSDKEntries('_shims/handler.handler') + await instance.uploadSourceZipToCOS(zipPath, uploadUrl, slsSDKEntries, { + _shims: path.join(__dirname, '_shims') + }) + instance.codeInjected = true + } console.log(`Upload ${objectName} to bucket ${bucketName} success`) } } @@ -255,67 +201,116 @@ const uploadCodeToCos = async (instance, appId, credentials, inputs, region) => } } -const mergeJson = (sourceJson, targetJson) => { - for (const eveKey in sourceJson) { - if (targetJson.hasOwnProperty(eveKey)) { - if (['protocols', 'endpoints', 'customDomain'].indexOf(eveKey) != -1) { - for (let i = 0; i < sourceJson[eveKey].length; i++) { - const sourceEvents = JSON.stringify(sourceJson[eveKey][i]) - const targetEvents = JSON.stringify(targetJson[eveKey]) - if (targetEvents.indexOf(sourceEvents) == -1) { - targetJson[eveKey].push(sourceJson[eveKey][i]) +const prepareStaticCosInputs = async (instance, inputs, appId, codeZipPath) => { + try { + const staticCosInputs = [] + const { cosConf } = inputs + const sources = cosConf.sources || CONFIGS.defaultStatics + const { bucket } = cosConf + // remove user append appid + const bucketName = removeAppid(bucket, appId) + const staticPath = `/tmp/${generateId()}` + const codeZip = new AdmZip(codeZipPath) + const entries = codeZip.getEntries() + + // traverse sources, generate static directory and deploy to cos + for (let i = 0; i < sources.length; i++) { + const curSource = sources[i] + const entryName = `${curSource.src}` + let exist = false + entries.forEach((et) => { + if (et.entryName.indexOf(entryName) === 0) { + codeZip.extractEntryTo(et, staticPath, true, true) + exist = true + } + }) + if (exist) { + const cosInputs = { + force: true, + protocol: cosConf.protocol, + bucket: `${bucketName}-${appId}`, + src: `${staticPath}/${entryName}`, + keyPrefix: curSource.targetDir || '/', + acl: { + permissions: 'public-read', + grantRead: '', + grantWrite: '', + grantFullControl: '' } } - } else { - if (typeof sourceJson[eveKey] != 'string') { - mergeJson(sourceJson[eveKey], targetJson[eveKey]) - } else { - targetJson[eveKey] = sourceJson[eveKey] + + if (cosConf.acl) { + cosInputs.acl = { + permissions: cosConf.acl.permissions || 'public-read', + grantRead: cosConf.acl.grantRead || '', + grantWrite: cosConf.acl.grantWrite || '', + grantFullControl: cosConf.acl.grantFullControl || '' + } } + + staticCosInputs.push(cosInputs) } - } else { - targetJson[eveKey] = sourceJson[eveKey] } + return staticCosInputs + } catch (e) { + throw new TypeError( + `UTILS_${CONFIGS.compName.toUpperCase()}_prepareStaticCosInputs`, + e.message, + e.stack + ) } - return targetJson -} - -const capitalString = (str) => { - if (str.length < 2) { - return str.toUpperCase() - } - - return `${str[0].toUpperCase()}${str.slice(1)}` -} - -const getDefaultProtocol = (protocols) => { - if (protocols.map((i) => i.toLowerCase()).includes('https')) { - return 'https' - } - return 'http' } -const deleteRecord = (newRecords, historyRcords) => { - const deleteList = [] - for (let i = 0; i < historyRcords.length; i++) { - let temp = false - for (let j = 0; j < newRecords.length; j++) { - if ( - newRecords[j].domain == historyRcords[i].domain && - newRecords[j].subDomain == historyRcords[i].subDomain && - newRecords[j].recordType == historyRcords[i].recordType && - newRecords[j].value == historyRcords[i].value && - newRecords[j].recordLine == historyRcords[i].recordLine - ) { - temp = true - break +const prepareStaticCdnInputs = async (instance, inputs, origin) => { + try { + const { cdnConf } = inputs + const cdnInputs = { + async: true, + area: cdnConf.area || 'mainland', + domain: cdnConf.domain, + serviceType: 'web', + origin: { + origins: [origin], + originType: 'cos', + originPullProtocol: 'https' + }, + autoRefresh: true, + ...cdnConf + } + if (cdnConf.https) { + // using these default configs, for making user's config more simple + cdnInputs.forceRedirect = cdnConf.https.forceRedirect || CONFIGS.defaultCdnConf.forceRedirect + if (!cdnConf.https.certId) { + throw new TypeError( + `PARAMETER_${CONFIGS.compName.toUpperCase()}_HTTPS`, + 'https.certId is required' + ) + } + cdnInputs.https = { + ...CONFIGS.defaultCdnConf.https, + ...{ + http2: cdnConf.https.http2 || 'on', + certInfo: { + certId: cdnConf.https.certId + } + } } } - if (!temp) { - deleteList.push(historyRcords[i]) + if (cdnInputs.autoRefresh) { + cdnInputs.refreshCdn = { + flushType: cdnConf.refreshType || 'delete', + urls: [`http://${cdnInputs.domain}`, `https://${cdnInputs.domain}`] + } } + + return cdnInputs + } catch (e) { + throw new TypeError( + `UTILS_${CONFIGS.compName.toUpperCase()}_prepareStaticCdnInputs`, + e.message, + e.stack + ) } - return deleteList } const prepareInputs = async (instance, credentials, inputs = {}) => { @@ -331,9 +326,6 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // chenck state function name const stateFunctionName = instance.state[regionList[0]] && instance.state[regionList[0]].functionName - // check state service id - const stateServiceId = instance.state[regionList[0]] && instance.state[regionList[0]].serviceId - const functionConf = { code: { src: inputs.src, @@ -343,7 +335,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { name: ensureString(inputs.functionName, { isOptional: true }) || stateFunctionName || - `${CONFIGS.compName}_component_${generateId()}`, + getDefaultFunctionName(), region: regionList, role: ensureString(tempFunctionConf.role ? tempFunctionConf.role : inputs.role, { default: '' @@ -368,11 +360,19 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { layers: ensureIterable(tempFunctionConf.layers ? tempFunctionConf.layers : inputs.layers, { default: [] }), + cfs: ensureIterable(tempFunctionConf.cfs ? tempFunctionConf.cfs : inputs.cfs, { + default: [] + }), publish: inputs.publish, traffic: inputs.traffic, lastVersion: instance.state.lastVersion, eip: tempFunctionConf.eip === true, - l5Enable: tempFunctionConf.l5Enable === true + l5Enable: tempFunctionConf.l5Enable === true, + timeout: tempFunctionConf.timeout ? tempFunctionConf.timeout : CONFIGS.timeout, + memorySize: tempFunctionConf.memorySize ? tempFunctionConf.memorySize : CONFIGS.memorySize, + tags: ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { + default: null + }) } // validate traffic @@ -381,72 +381,55 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { } functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion - functionConf.tags = ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { - default: null - }) - - if (inputs.functionConf) { - functionConf.timeout = inputs.functionConf.timeout - ? inputs.functionConf.timeout - : CONFIGS.timeout - functionConf.memorySize = inputs.functionConf.memorySize - ? inputs.functionConf.memorySize - : CONFIGS.memorySize - if (inputs.functionConf.environment) { - functionConf.environment = inputs.functionConf.environment - } - if (inputs.functionConf.vpcConfig) { - functionConf.vpcConfig = inputs.functionConf.vpcConfig - } + if (tempFunctionConf.environment) { + functionConf.environment = inputs.functionConf.environment + } + if (tempFunctionConf.vpcConfig) { + functionConf.vpcConfig = inputs.functionConf.vpcConfig } // 对apigw inputs进行标准化 - const apigatewayConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} - apigatewayConf.fromClientRemark = fromClientRemark - apigatewayConf.serviceName = inputs.serviceName - apigatewayConf.description = `Serverless Framework Tencent-${capitalString( - CONFIGS.compName - )} Component` - apigatewayConf.serviceId = inputs.serviceId || stateServiceId - apigatewayConf.region = functionConf.region - apigatewayConf.protocols = apigatewayConf.protocols || ['http'] - apigatewayConf.environment = apigatewayConf.environment ? apigatewayConf.environment : 'release' - apigatewayConf.endpoints = [ - { - path: '/', - enableCORS: apigatewayConf.enableCORS, - serviceTimeout: apigatewayConf.serviceTimeout, - method: 'ANY', - function: { - isIntegratedResponse: true, - functionName: functionConf.name, - functionNamespace: functionConf.namespace, - functionQualifier: functionConf.needSetTraffic ? '$DEFAULT' : '$LATEST' + const tempApigwConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} + const apigatewayConf = { + serviceId: inputs.serviceId, + region: regionList, + isDisabled: tempApigwConf.isDisabled === true, + fromClientRemark: fromClientRemark, + serviceName: inputs.serviceName || getDefaultServiceName(instance), + description: getDefaultServiceDescription(instance), + protocols: tempApigwConf.protocols || ['http'], + environment: tempApigwConf.environment ? tempApigwConf.environment : 'release', + endpoints: [ + { + path: '/', + enableCORS: tempApigwConf.enableCORS, + serviceTimeout: tempApigwConf.serviceTimeout, + method: 'ANY', + function: { + isIntegratedResponse: true, + functionName: functionConf.name, + functionNamespace: functionConf.namespace + } } - } - ] - if (apigatewayConf.usagePlan) { + ], + customDomains: tempApigwConf.customDomains || [] + } + if (tempApigwConf.usagePlan) { apigatewayConf.endpoints[0].usagePlan = { - usagePlanId: apigatewayConf.usagePlan.usagePlanId, - usagePlanName: apigatewayConf.usagePlan.usagePlanName, - usagePlanDesc: apigatewayConf.usagePlan.usagePlanDesc, - maxRequestNum: apigatewayConf.usagePlan.maxRequestNum + usagePlanId: tempApigwConf.usagePlan.usagePlanId, + usagePlanName: tempApigwConf.usagePlan.usagePlanName, + usagePlanDesc: tempApigwConf.usagePlan.usagePlanDesc, + maxRequestNum: tempApigwConf.usagePlan.maxRequestNum } } - if (apigatewayConf.auth) { + if (tempApigwConf.auth) { apigatewayConf.endpoints[0].auth = { - secretName: apigatewayConf.auth.secretName, - secretIds: apigatewayConf.auth.secretIds + secretName: tempApigwConf.auth.secretName, + secretIds: tempApigwConf.auth.secretIds } } - // 对cns inputs进行标准化 - const tempCnsConf = {} - const tempCnsBaseConf = inputs.cloudDNSConf ? inputs.cloudDNSConf : {} - - // 分地域处理functionConf/apigatewayConf/cnsConf - for (let i = 0; i < functionConf.region.length; i++) { - const curRegion = functionConf.region[i] + regionList.forEach((curRegion) => { const curRegionConf = inputs[curRegion] if (curRegionConf && curRegionConf.functionConf) { functionConf[curRegion] = curRegionConf.functionConf @@ -454,63 +437,22 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { if (curRegionConf && curRegionConf.apigatewayConf) { apigatewayConf[curRegion] = curRegionConf.apigatewayConf } - - const tempRegionCnsConf = mergeJson( - tempCnsBaseConf, - curRegionConf && curRegionConf.cloudDNSConf ? curRegionConf.cloudDNSConf : {} - ) - - tempCnsConf[functionConf.region[i]] = { - recordType: 'CNAME', - recordLine: tempRegionCnsConf.recordLine ? tempRegionCnsConf.recordLine : undefined, - ttl: tempRegionCnsConf.ttl, - mx: tempRegionCnsConf.mx, - status: tempRegionCnsConf.status ? tempRegionCnsConf.status : 'enable' - } - } - - const cnsConf = [] - // 对cns inputs进行检查和赋值 - if (apigatewayConf.customDomain && apigatewayConf.customDomain.length > 0) { - const domain = new Domain(credentials) - for (let domianNum = 0; domianNum < apigatewayConf.customDomain.length; domianNum++) { - const domainData = await domain.check(apigatewayConf.customDomain[domianNum].domain) - const tempInputs = { - domain: domainData.domain, - records: [] - } - for (let eveRecordNum = 0; eveRecordNum < functionConf.region.length; eveRecordNum++) { - if (tempCnsConf[functionConf.region[eveRecordNum]].recordLine) { - tempInputs.records.push({ - subDomain: domainData.subDomain || '@', - recordType: 'CNAME', - recordLine: tempCnsConf[functionConf.region[eveRecordNum]].recordLine, - value: `temp_value_about_${functionConf.region[eveRecordNum]}`, - ttl: tempCnsConf[functionConf.region[eveRecordNum]].ttl, - mx: tempCnsConf[functionConf.region[eveRecordNum]].mx, - status: tempCnsConf[functionConf.region[eveRecordNum]].status || 'enable' - }) - } - } - cnsConf.push(tempInputs) - } - } + }) return { regionList, functionConf, - apigatewayConf, - cnsConf + apigatewayConf } } module.exports = { + deepClone, generateId, uploadCodeToCos, mergeJson, capitalString, getDefaultProtocol, - deleteRecord, prepareInputs, prepareStaticCosInputs, prepareStaticCdnInputs From ded2d1bec7c34ef812ae260c46791ca7bf5c9bbf Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 27 Aug 2020 07:36:53 +0000 Subject: [PATCH 32/65] chore(release): version 0.0.14 ## [0.0.14](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.13...v0.0.14) (2020-08-27) ### Bug Fixes * optimize deploy flow ([2768d16](https://github.com/serverless-components/tencent-nuxtjs/commit/2768d16b50aeea7343dae468421cb7eaf9902f1c)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23b0c04..ee78b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.0.14](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.13...v0.0.14) (2020-08-27) + + +### Bug Fixes + +* optimize deploy flow ([2768d16](https://github.com/serverless-components/tencent-nuxtjs/commit/2768d16b50aeea7343dae468421cb7eaf9902f1c)) + ## [0.0.13](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.12...v0.0.13) (2020-08-26) From ae8c1db3cb4ce2166de98bc924ec656f72d81473 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 27 Aug 2020 17:10:25 +0800 Subject: [PATCH 33/65] chore: update example --- example/nuxt.config.js | 4 ++-- example/package.json | 2 +- example/pages/index.vue | 28 ++++++---------------------- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/example/nuxt.config.js b/example/nuxt.config.js index cf0d4da..2c220ee 100644 --- a/example/nuxt.config.js +++ b/example/nuxt.config.js @@ -4,14 +4,14 @@ export default { ** Headers of the page */ head: { - title: process.env.npm_package_name || '', + title: 'Serverless Nuxt.js Application', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', - content: process.env.npm_package_description || '' + content: 'Serverless Nuxt.js Application Created By Serverless Framework' } ], link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }] diff --git a/example/package.json b/example/package.json index d86a931..13db53d 100644 --- a/example/package.json +++ b/example/package.json @@ -1,7 +1,7 @@ { "name": "nuxt-demo", "version": "1.0.0", - "description": "My superb Nuxt.js project", + "description": "Serverless Nuxt.js Application Created By Serverless Framework", "author": "Tencent Cloud, Inc.", "private": true, "scripts": { diff --git a/example/pages/index.vue b/example/pages/index.vue index 66b31a4..1dfcad9 100644 --- a/example/pages/index.vue +++ b/example/pages/index.vue @@ -2,27 +2,11 @@
-

- nuxt-demo -

-

- My superb Nuxt.js project -

+

nuxt-demo

+

Serverless Nuxt.js Application Created By Serverless Framework

@@ -49,8 +33,8 @@ export default { } .title { - font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, - 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, sans-serif; display: block; font-weight: 300; font-size: 100px; From b23b26f40355c47990ca4309dcd85dabb29b4e8f Mon Sep 17 00:00:00 2001 From: Avril Li Date: Fri, 28 Aug 2020 12:26:53 +0800 Subject: [PATCH 34/65] test: add integration test --- .prettierignore | 1 + package.json | 6 ++++- tests/integration.test.js | 54 +++++++++++++++++++++++++++++++++++++++ tests/utils.js | 24 +++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/integration.test.js create mode 100644 tests/utils.js diff --git a/.prettierignore b/.prettierignore index 321d972..b63c88e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,4 @@ dist node_modules CHANGELOG.md *.test.js +.nuxt diff --git a/package.json b/package.json index 94b6501..ccdeec5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "access": "public" }, "scripts": { - "test": "npm run lint && npm run prettier", + "int-test": "jest ./tests/integration.test.js --testEnvironment node", + "test": "npm run lint && npm run prettier && npm run int-test", "commitlint": "commitlint -f HEAD@{15}", "lint": "eslint --ext .js,.ts,.tsx .", "lint:fix": "eslint --fix --ext .js,.ts,.tsx .", @@ -43,6 +44,8 @@ "@semantic-release/git": "^9.0.0", "@semantic-release/npm": "^7.0.4", "@semantic-release/release-notes-generator": "^9.0.1", + "@serverless/platform-client-china": "^1.0.35", + "axios": "^0.20.0", "babel-eslint": "^10.1.0", "dotenv": "^8.2.0", "eslint": "^6.8.0", @@ -50,6 +53,7 @@ "eslint-plugin-import": "^2.20.1", "eslint-plugin-prettier": "^3.1.2", "husky": "^4.2.3", + "jest": "^26.4.2", "lint-staged": "^10.0.8", "prettier": "^1.19.1", "semantic-release": "^17.0.4" diff --git a/tests/integration.test.js b/tests/integration.test.js new file mode 100644 index 0000000..993d66f --- /dev/null +++ b/tests/integration.test.js @@ -0,0 +1,54 @@ +const { generateId, getServerlessSdk } = require('./utils') +const execSync = require('child_process').execSync +const path = require('path') +const axios = require('axios') + +// set enough timeout for deployment to finish +jest.setTimeout(600000) + +// the yaml file we're testing against +const instanceYaml = { + org: 'orgDemo', + app: 'appDemo', + component: 'nuxtjs', + name: `nuxtjs-integration-tests-${generateId()}`, + stage: 'dev', + inputs: { + region: 'ap-guangzhou', + runtime: 'Nodejs10.15', + apigatewayConf: { environment: 'test' } + } +} + +// get credentials from process.env but need to init empty credentials object +const credentials = { + tencent: {} +} + +// get serverless construct sdk +const sdk = getServerlessSdk(instanceYaml.org) + +it('should successfully deploy nuxtjs app', async () => { + const instance = await sdk.deploy(instanceYaml, { tencent: {} }) + + expect(instance).toBeDefined() + expect(instance.instanceName).toEqual(instanceYaml.name) + expect(instance.outputs).toBeDefined() + // get src from template by default + expect(instance.outputs.templateUrl).toBeDefined() + expect(instance.outputs.region).toEqual(instanceYaml.inputs.region) + expect(instance.outputs.scf).toBeDefined() + expect(instance.outputs.scf.runtime).toEqual(instanceYaml.inputs.runtime) + expect(instance.outputs.apigw).toBeDefined() + expect(instance.outputs.apigw.environment).toEqual(instanceYaml.inputs.apigatewayConf.environment) + + const response = await axios.get(instance.outputs.apigw.url) + expect(response.data.includes('Nuxt.js')).toBeTruthy() +}) + +it('should successfully remove nuxtjs app', async () => { + await sdk.remove(instanceYaml, credentials) + result = await sdk.getInstance(instanceYaml.org, instanceYaml.stage, instanceYaml.app, instanceYaml.name) + + expect(result.instance.instanceStatus).toEqual('inactive') +}) diff --git a/tests/utils.js b/tests/utils.js new file mode 100644 index 0000000..d047afa --- /dev/null +++ b/tests/utils.js @@ -0,0 +1,24 @@ +const { ServerlessSDK } = require('@serverless/platform-client-china') + +/* + * Generate random id + */ +const generateId = () => + Math.random() + .toString(36) + .substring(6) + +/* + * Initializes and returns an instance of the serverless sdk + * @param ${string} orgName - the serverless org name. + */ +const getServerlessSdk = (orgName) => { + const sdk = new ServerlessSDK({ + context: { + orgName + } + }) + return sdk +} + +module.exports = { generateId, getServerlessSdk } From 5eb78a60ebc51571cbf92a38bc7a2faa4b523bc9 Mon Sep 17 00:00:00 2001 From: Avril Li Date: Tue, 1 Sep 2020 14:58:36 +0800 Subject: [PATCH 35/65] docs: update readme --- README.en.md | 6 +++--- README.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.en.md b/README.en.md index be22ee7..57566e2 100755 --- a/README.en.md +++ b/README.en.md @@ -2,7 +2,7 @@ # Tencent Nuxt.js Serverless Component -[简体中文](https://github.com/serverless-components/tencent-nuxtjs/blob/v2/README.md) | English +[简体中文](./README.md) | English ## Introduction @@ -65,7 +65,7 @@ stage: dev inputs: src: - src: ./src + src: ./ exclude: - .env region: ap-guangzhou @@ -77,7 +77,7 @@ inputs: environment: release ``` -- [More Options](https://github.com/serverless-components/tencent-nuxtjs/blob/v2/docs/configure.md) +- [More Options](/docs/configure.md) ### 4. Deploy diff --git a/README.md b/README.md index 3ffb19d..a38503c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 腾讯云 Nuxt.js Serverless Component -简体中文 | [English](https://github.com/serverless-components/tencent-nuxtjs/blob/v2/README.en.md) +简体中文 | [English](./README.en.md) ## 简介 @@ -84,7 +84,7 @@ inputs: environment: release ``` -- 点此查看[更多配置及说明](https://github.com/serverless-components/tencent-nextjs/tree/v2/docs/configure.md) +- 点此查看[更多配置及说明](/docs/configure.md) ### 3. 部署 From 03f2f5df683257dabfe238b92cbb6f70f8dbbaf7 Mon Sep 17 00:00:00 2001 From: Avril Li Date: Tue, 1 Sep 2020 20:05:40 +0800 Subject: [PATCH 36/65] chore: add ygsec check --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ccdeec5..6047bcb 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ }, "husky": { "hooks": { - "pre-commit": "lint-staged", + "pre-commit": "ygsec && lint-staged", "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", - "pre-push": "npm run lint:fix && npm run prettier:fix" + "pre-push": "ygsec && npm run lint:fix && npm run prettier:fix" } }, "lint-staged": { @@ -45,6 +45,7 @@ "@semantic-release/npm": "^7.0.4", "@semantic-release/release-notes-generator": "^9.0.1", "@serverless/platform-client-china": "^1.0.35", + "@ygkit/secure": "0.0.3", "axios": "^0.20.0", "babel-eslint": "^10.1.0", "dotenv": "^8.2.0", @@ -52,7 +53,7 @@ "eslint-config-prettier": "^6.10.0", "eslint-plugin-import": "^2.20.1", "eslint-plugin-prettier": "^3.1.2", - "husky": "^4.2.3", + "husky": "^4.2.5", "jest": "^26.4.2", "lint-staged": "^10.0.8", "prettier": "^1.19.1", From db0328f498cd45d16e5bc1d6d17cdc5b5c860c42 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 2 Sep 2020 10:19:52 +0800 Subject: [PATCH 37/65] feat: add mark for api request --- README.md | 2 +- example/README.md | 6 +++--- serverless.component.yml | 2 +- src/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a38503c..0672419 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ module.exports = createServer ## 自定义监控 -当在部署 Next.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_next` 和 `/static` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: +当在部署 Nuxt.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_nuxt`、`/static` 和 `/favicon.ico` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: ```js server.get('/no-report', (req, res) => { diff --git a/example/README.md b/example/README.md index 3ffb19d..0672419 100644 --- a/example/README.md +++ b/example/README.md @@ -2,7 +2,7 @@ # 腾讯云 Nuxt.js Serverless Component -简体中文 | [English](https://github.com/serverless-components/tencent-nuxtjs/blob/v2/README.en.md) +简体中文 | [English](./README.en.md) ## 简介 @@ -84,7 +84,7 @@ inputs: environment: release ``` -- 点此查看[更多配置及说明](https://github.com/serverless-components/tencent-nextjs/tree/v2/docs/configure.md) +- 点此查看[更多配置及说明](/docs/configure.md) ### 3. 部署 @@ -214,7 +214,7 @@ module.exports = createServer ## 自定义监控 -当在部署 Next.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_next` 和 `/static` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: +当在部署 Nuxt.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_nuxt`、`/static` 和 `/favicon.ico` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: ```js server.get('/no-report', (req, res) => { diff --git a/serverless.component.yml b/serverless.component.yml index b2d1e62..7e0c3c6 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.0.14 +version: 0.1.0 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/package.json b/src/package.json index e24f0ec..ccfb475 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.15.7", + "tencent-component-toolkit": "^1.16.2", "type": "^2.0.0" } } From c48ef80627cb96204eadbf2546e96c9712d4ced3 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Sep 2020 02:23:30 +0000 Subject: [PATCH 38/65] chore(release): version 0.1.0 # [0.1.0](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.14...v0.1.0) (2020-09-02) ### Features * add mark for api request ([db0328f](https://github.com/serverless-components/tencent-nuxtjs/commit/db0328f498cd45d16e5bc1d6d17cdc5b5c860c42)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee78b61..d29c20b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.1.0](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.14...v0.1.0) (2020-09-02) + + +### Features + +* add mark for api request ([db0328f](https://github.com/serverless-components/tencent-nuxtjs/commit/db0328f498cd45d16e5bc1d6d17cdc5b5c860c42)) + ## [0.0.14](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.13...v0.0.14) (2020-08-27) From 1a883c56fe980aaa7bcc34043cabb99dc0f6cec8 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 3 Sep 2020 19:37:19 +0800 Subject: [PATCH 39/65] fix: update deps --- serverless.component.yml | 2 +- src/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index 7e0c3c6..38720d8 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.1.0 +version: 0.1.1 author: Tencent Cloud, Inc. org: Tencent Cloud, Inc. description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/package.json b/src/package.json index ccfb475..b3f00d8 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.14", "download": "^8.0.0", - "tencent-component-toolkit": "^1.16.2", + "tencent-component-toolkit": "^1.16.4", "type": "^2.0.0" } } From d0b5e39fd17eb6f0db17306d42c85e523dc7ffdb Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 3 Sep 2020 11:43:17 +0000 Subject: [PATCH 40/65] chore(release): version 0.1.1 ## [0.1.1](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.0...v0.1.1) (2020-09-03) ### Bug Fixes * update deps ([1a883c5](https://github.com/serverless-components/tencent-nuxtjs/commit/1a883c56fe980aaa7bcc34043cabb99dc0f6cec8)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d29c20b..1374508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.1](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.0...v0.1.1) (2020-09-03) + + +### Bug Fixes + +* update deps ([1a883c5](https://github.com/serverless-components/tencent-nuxtjs/commit/1a883c56fe980aaa7bcc34043cabb99dc0f6cec8)) + # [0.1.0](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.0.14...v0.1.0) (2020-09-02) From 317500b99c5649ce67492bf164554a4bedcb80b7 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 23 Sep 2020 17:13:53 +0800 Subject: [PATCH 41/65] fix: update deps --- serverless.component.yml | 12 ++++++------ src/package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index 38720d8..3b7913b 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,11 +1,11 @@ name: nuxtjs -version: 0.1.1 -author: Tencent Cloud, Inc. -org: Tencent Cloud, Inc. +version: 0.1.2 +author: 'Tencent Cloud, Inc.' +org: 'Tencent Cloud, Inc.' description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. -keywords: tencent, serverless, nuxt.js -repo: https://github.com/serverless-components/tencent-nuxtjs -readme: https://github.com/serverless-components/tencent-nuxtjs/tree/master/README.md +keywords: 'tencent, serverless, nuxt.js' +repo: 'https://github.com/serverless-components/tencent-nuxtjs' +readme: 'https://github.com/serverless-components/tencent-nuxtjs/tree/master/README.md' license: MIT main: ./src webDeployable: true diff --git a/src/package.json b/src/package.json index b3f00d8..d31074b 100644 --- a/src/package.json +++ b/src/package.json @@ -1,8 +1,8 @@ { "dependencies": { - "adm-zip": "^0.4.14", + "adm-zip": "^0.4.16", "download": "^8.0.0", - "tencent-component-toolkit": "^1.16.4", - "type": "^2.0.0" + "tencent-component-toolkit": "^1.16.7", + "type": "^2.1.0" } } From 70d15643902dcca3111501c12fe7837525a13772 Mon Sep 17 00:00:00 2001 From: Yuga Sun Date: Fri, 25 Sep 2020 11:12:36 +0800 Subject: [PATCH 42/65] fix: support koa server (#9) * fix: support koa server * ci: add github actions * ci: fix integration test * ci: add secret env for test --- .github/workflows/release.yml | 48 ++++++++++ .github/workflows/test.yml | 45 +++++++++ .github/workflows/validate.yml | 45 +++++++++ .travis.yml | 23 ----- README.en.md | 162 --------------------------------- README.md | 46 ++++++++-- example/README.md | 46 ++++++++-- package.json | 3 +- serverless.component.yml | 2 +- src/_shims/handler.js | 6 +- src/_shims/package.json | 2 +- tests/integration.test.js | 10 +- 12 files changed, 232 insertions(+), 206 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/validate.yml delete mode 100644 .travis.yml delete mode 100755 README.en.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4c476a4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Release + +on: + push: + branches: [master] + +jobs: + release: + name: Release + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + persist-credentials: false + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 14.x + registry-url: https://registry.npmjs.org + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v14-${{ runner.os }}-refs/heads/master-${{ hashFiles('package.json') }} + restore-keys: npm-v14-${{ runner.os }}-refs/heads/master- + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + - name: Releasing + run: | + npm run release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + GIT_AUTHOR_NAME: slsplus + GIT_AUTHOR_EMAIL: yuga.sun.bj@gmail.com + GIT_COMMITTER_NAME: slsplus + GIT_COMMITTER_EMAIL: yuga.sun.bj@gmail.com diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e8bdb44 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,45 @@ +name: Test + +on: + pull_request: + branches: [master] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # Ensure connection with 'master' branch + fetch-depth: 2 + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 14.x + registry-url: https://registry.npmjs.org + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }} + restore-keys: | + npm-v14-${{ runner.os }}-${{ github.ref }}- + npm-v14-${{ runner.os }}-refs/heads/master- + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + - name: Running integration tests + run: npm run test + env: + TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }} + TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..3840792 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,45 @@ +name: Validate + +on: + pull_request: + branches: [master] + +jobs: + lintAndFormatting: + name: Lint & Formatting + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # Ensure connection with 'master' branch + fetch-depth: 2 + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 14.x + registry-url: https://registry.npmjs.org + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v14-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }} + restore-keys: | + npm-v14-${{ runner.os }}-${{ github.ref }}- + npm-v14-${{ runner.os }}-refs/heads/master- + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + + - name: Validate Formatting + run: npm run prettier:fix + - name: Validate Lint rules + run: npm run lint:fix diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d78fad9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: node_js - -node_js: - - 10 - -install: - - npm install - -# should change to serverless registry publish -jobs: - include: - # Define the release stage that runs semantic-release - - stage: release - node_js: 10.18 - # Advanced: optionally overwrite your default `script` step to skip the tests - # script: skip - deploy: - provider: script - skip_cleanup: true - on: - branch: master - script: - - npm run release diff --git a/README.en.md b/README.en.md deleted file mode 100755 index 57566e2..0000000 --- a/README.en.md +++ /dev/null @@ -1,162 +0,0 @@ -[![Serverless Nuxtjs Tencent Cloud](https://img.serverlesscloud.cn/2020310/1583829094342-nuxt.js%20_%E9%95%BF.png)](http://serverless.com) - -# Tencent Nuxt.js Serverless Component - -[简体中文](./README.md) | English - -## Introduction - -[Nuxt.js](https://github.com/nuxt/nuxt.js) Serverless Component for Tencent Cloud. - -## Content - -0. [Prepare](#0-prepare) -1. [Install](#1-install) -1. [Create](#2-create) -1. [Configure](#3-configure) -1. [Deploy](#4-deploy) -1. [Remove](#5-Remove) - -### 0. Prepare - -#### Init Nuxt.js Project - -```bash -$ npx create-nuxt-app serverless-nuxtjs -$ cd serverless-nuxtjs -``` - -### 1. Install - -Install the Serverless Framework globally: - -```bash -$ npm install -g serverless -``` - -### 2. Create - -In project root, create the following simple boilerplate: - -```bash -$ touch serverless.yml -$ touch .env # your Tencent api keys -``` - -Add the access keys of a [Tencent CAM Role](https://console.cloud.tencent.com/cam/capi) with `AdministratorAccess` in the `.env` file, using this format: - -``` -# .env -TENCENT_SECRET_ID=XXX -TENCENT_SECRET_KEY=XXX -``` - -- If you don't have a Tencent Cloud account, you could [sign up](https://intl.cloud.tencent.com/register) first. - -### 3. Configure - -```yml -# serverless.yml -component: nuxtjs -name: nuxtjsDemo -org: orgDemo -app: appDemo -stage: dev - -inputs: - src: - src: ./ - exclude: - - .env - region: ap-guangzhou - runtime: Nodejs10.15 - apigatewayConf: - protocols: - - http - - https - environment: release -``` - -- [More Options](/docs/configure.md) - -### 4. Deploy - -#### 4.1 Build static assets - -```bash -$ npm run build -``` - -#### 4.2 Deploy to cloud - -```bash -$ sls deploy -``` - -> Notice: `sls` is short for `serverless` command. - -  - -### 5. Remove - -```bash -$ sls remove -``` - -## More Components - -Checkout the [Serverless Components](https://github.com/serverless/components) repo for more information. - -## Migration for custom express server - -If you had used `express` for you server, you should create entry file `sls.js`, please change depand on your server entry file, below is a template: - -```js -const express = require('express') -const { loadNuxt } = require('nuxt') - -async function createServer() { - // not report route for custom monitor - const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] - - const server = express() - const nuxt = await loadNuxt('start') - - server.all('*', (req, res, next) => { - noReportRoutes.forEach((route) => { - if (req.path.indexOf(route) === 0) { - req.__SLS_NO_REPORT__ = true - } - }) - return nuxt.render(req, res, next) - }) - - // define binary type for response - // if includes, will return base64 encoded, very useful for images - server.binaryTypes = ['*/*'] - - return server -} - -module.exports = createServer -``` - -## Customize Monitor - -When deploying Next.js Application, if net config `role` in `serverless.yml`, it will try to bind `QCS_SCFExcuteRole` role for it, and start customize monitor which will help user to collect monitor data. -For project which have no customize entry file `sls.js`, it will ignore request paths which start with `/_next` and `/static`. If you want to customize `sls.js` file, you can create it by yourself. For no report path, just set `__SLS_NO_REPORT__` to `true` on `req` object, like below: - -```js -server.get('/no-report', (req, res) => { - req.__SLS_NO_REPORT__ = true - return handle(req, res) -}) -``` - -so when user access `GET /no-report` route, it won't report monitor data. - -## License - -MIT License - -Copyright (c) 2020 Tencent Cloud, Inc. diff --git a/README.md b/README.md index 0672419..e8b6a91 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ # 腾讯云 Nuxt.js Serverless Component -简体中文 | [English](./README.en.md) - ## 简介 **腾讯云[Nuxt.js](https://github.com/nuxt/nuxt.js)组件** - 通过使用[**Tencent Serverless Framework**](https://github.com/serverless/components/tree/cloud) , 基于云上 Serverless 服务(如 API 网关、云函数等),实现“0”配置,便捷开发,极速部署采用 Nuxt.js 框架的网页应用,Nuxt.js 组件支持丰富的配置扩展,提供了目前便捷实用,开发成本低的网页应用项目的开发/托管能力。 @@ -84,7 +82,7 @@ inputs: environment: release ``` -- 点此查看[更多配置及说明](/docs/configure.md) +- 点此查看[更多配置及说明](https://github.com/yugasun/tencent-nuxtjs/tree/master/docs/configure.md) ### 3. 部署 @@ -178,7 +176,11 @@ TENCENT_SECRET_KEY=123 可以在 [Serverless Components](https://github.com/serverless/components) repo 中查询更多组件的信息。 -## 项目迁移 - 自定义 express 服务 +## 项目迁移 + +如果项目使用了自定义 Node.js 服务,比如 express 或者 koa,你需要做如下改造工作。 + +### 自定义 express 服务 如果你的 Nuxt.js 项目本身运行就是基于 `express` 自定义服务的,那么你需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: @@ -212,14 +214,46 @@ async function createServer() { module.exports = createServer ``` +### 自定义 koa 服务 + +如果你的项目使用的是 Koa 作为 Node.js 服务,需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: + +```js +const Koa = require('koa') +const { loadNuxt } = require('nuxt') + +async function createServer() { + const server = new Koa() + const nuxt = await loadNuxt('start') + + server.use((ctx) => { + ctx.status = 200 + ctx.respond = false + ctx.req.ctx = ctx + + nuxt.render(ctx.req, ctx.res) + }) + + // define binary type for response + // if includes, will return base64 encoded, very useful for images + server.binaryTypes = ['*/*'] + + return server +} + +module.exports = createServer +``` + ## 自定义监控 +> **目前仅支持自定义 `Express` 服务的项目** + 当在部署 Nuxt.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_nuxt`、`/static` 和 `/favicon.ico` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: ```js -server.get('/no-report', (req, res) => { +server.get('/no-report', (req, res, next) => { req.__SLS_NO_REPORT__ = true - return handle(req, res) + return nuxt.render(req, res, next) }) ``` diff --git a/example/README.md b/example/README.md index 0672419..e8b6a91 100644 --- a/example/README.md +++ b/example/README.md @@ -2,8 +2,6 @@ # 腾讯云 Nuxt.js Serverless Component -简体中文 | [English](./README.en.md) - ## 简介 **腾讯云[Nuxt.js](https://github.com/nuxt/nuxt.js)组件** - 通过使用[**Tencent Serverless Framework**](https://github.com/serverless/components/tree/cloud) , 基于云上 Serverless 服务(如 API 网关、云函数等),实现“0”配置,便捷开发,极速部署采用 Nuxt.js 框架的网页应用,Nuxt.js 组件支持丰富的配置扩展,提供了目前便捷实用,开发成本低的网页应用项目的开发/托管能力。 @@ -84,7 +82,7 @@ inputs: environment: release ``` -- 点此查看[更多配置及说明](/docs/configure.md) +- 点此查看[更多配置及说明](https://github.com/yugasun/tencent-nuxtjs/tree/master/docs/configure.md) ### 3. 部署 @@ -178,7 +176,11 @@ TENCENT_SECRET_KEY=123 可以在 [Serverless Components](https://github.com/serverless/components) repo 中查询更多组件的信息。 -## 项目迁移 - 自定义 express 服务 +## 项目迁移 + +如果项目使用了自定义 Node.js 服务,比如 express 或者 koa,你需要做如下改造工作。 + +### 自定义 express 服务 如果你的 Nuxt.js 项目本身运行就是基于 `express` 自定义服务的,那么你需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: @@ -212,14 +214,46 @@ async function createServer() { module.exports = createServer ``` +### 自定义 koa 服务 + +如果你的项目使用的是 Koa 作为 Node.js 服务,需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: + +```js +const Koa = require('koa') +const { loadNuxt } = require('nuxt') + +async function createServer() { + const server = new Koa() + const nuxt = await loadNuxt('start') + + server.use((ctx) => { + ctx.status = 200 + ctx.respond = false + ctx.req.ctx = ctx + + nuxt.render(ctx.req, ctx.res) + }) + + // define binary type for response + // if includes, will return base64 encoded, very useful for images + server.binaryTypes = ['*/*'] + + return server +} + +module.exports = createServer +``` + ## 自定义监控 +> **目前仅支持自定义 `Express` 服务的项目** + 当在部署 Nuxt.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_nuxt`、`/static` 和 `/favicon.ico` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: ```js -server.get('/no-report', (req, res) => { +server.get('/no-report', (req, res, next) => { req.__SLS_NO_REPORT__ = true - return handle(req, res) + return nuxt.render(req, res, next) }) ``` diff --git a/package.json b/package.json index 6047bcb..1b6d8dd 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,7 @@ "access": "public" }, "scripts": { - "int-test": "jest ./tests/integration.test.js --testEnvironment node", - "test": "npm run lint && npm run prettier && npm run int-test", + "test": "jest ./tests/integration.test.js --testEnvironment node", "commitlint": "commitlint -f HEAD@{15}", "lint": "eslint --ext .js,.ts,.tsx .", "lint:fix": "eslint --fix --ext .js,.ts,.tsx .", diff --git a/serverless.component.yml b/serverless.component.yml index 3b7913b..584a8be 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.1.2 +version: 0.1.3 author: 'Tencent Cloud, Inc.' org: 'Tencent Cloud, Inc.' description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/_shims/handler.js b/src/_shims/handler.js index a6b2146..60c5dc7 100644 --- a/src/_shims/handler.js +++ b/src/_shims/handler.js @@ -21,7 +21,11 @@ module.exports.handler = async (event, context) => { app.request.__SLS_CONTEXT__ = context if (!server) { - server = createServer(app, null, app.binaryTypes || []) + server = createServer( + app.callback && typeof app.callback === 'function' ? app.callback() : app, + null, + app.binaryTypes || [] + ) } context.callbackWaitsForEmptyEventLoop = app.callbackWaitsForEmptyEventLoop === true diff --git a/src/_shims/package.json b/src/_shims/package.json index 6290a44..f17df01 100644 --- a/src/_shims/package.json +++ b/src/_shims/package.json @@ -2,6 +2,6 @@ "dependencies": { "express": "^4.17.1", "tencent-component-monitor": "^1.1.0", - "tencent-serverless-http": "^1.2.0" + "tencent-serverless-http": "^1.3.1" } } diff --git a/tests/integration.test.js b/tests/integration.test.js index 993d66f..e1b541b 100644 --- a/tests/integration.test.js +++ b/tests/integration.test.js @@ -1,6 +1,5 @@ +require('dotenv').config() const { generateId, getServerlessSdk } = require('./utils') -const execSync = require('child_process').execSync -const path = require('path') const axios = require('axios') // set enough timeout for deployment to finish @@ -22,14 +21,17 @@ const instanceYaml = { // get credentials from process.env but need to init empty credentials object const credentials = { - tencent: {} + tencent: { + SecretId: process.env.TENCENT_SECRET_ID, + SecretKey: process.env.TENCENT_SECRET_KEY, + } } // get serverless construct sdk const sdk = getServerlessSdk(instanceYaml.org) it('should successfully deploy nuxtjs app', async () => { - const instance = await sdk.deploy(instanceYaml, { tencent: {} }) + const instance = await sdk.deploy(instanceYaml, credentials) expect(instance).toBeDefined() expect(instance.instanceName).toEqual(instanceYaml.name) From 9ea67e335374f3ec54820de03118cf12b1ec8fa6 Mon Sep 17 00:00:00 2001 From: slsplus Date: Fri, 25 Sep 2020 03:24:35 +0000 Subject: [PATCH 43/65] chore(release): version 0.1.3 ## [0.1.3](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.2...v0.1.3) (2020-09-25) ### Bug Fixes * support koa server ([#9](https://github.com/serverless-components/tencent-nuxtjs/issues/9)) ([70d1564](https://github.com/serverless-components/tencent-nuxtjs/commit/70d15643902dcca3111501c12fe7837525a13772)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1374508..6a5445e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.3](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.2...v0.1.3) (2020-09-25) + + +### Bug Fixes + +* support koa server ([#9](https://github.com/serverless-components/tencent-nuxtjs/issues/9)) ([70d1564](https://github.com/serverless-components/tencent-nuxtjs/commit/70d15643902dcca3111501c12fe7837525a13772)) + ## [0.1.1](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.0...v0.1.1) (2020-09-03) From 50ace57900ebf9b4f405ec38204eb85a78e7aa77 Mon Sep 17 00:00:00 2001 From: yugasun Date: Wed, 30 Sep 2020 14:53:20 +0800 Subject: [PATCH 44/65] fix: support customize entry file --- docs/configure.md | 44 +++++++++++++++++++++------------------- serverless.component.yml | 2 +- src/_shims/handler.js | 4 ++-- src/config.js | 1 + src/utils.js | 24 ++++++++++++++-------- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/docs/configure.md b/docs/configure.md index 21b30e6..553431c 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -17,6 +17,7 @@ inputs: serviceName: mytest # api网关服务名称 runtime: Nodejs10.15 # 运行环境 serviceId: service-np1uloxw # api网关服务ID + entryFile: sls.js # 自定义 server 的入口文件名,默认为 sls.js,如果不想修改文件名为 sls.js 可以自定义 src: ./src # 第一种为string时,会打包src对应目录下的代码上传到默认cos上。 # src: # 第二种,部署src下的文件代码,并打包成zip上传到bucket上 # src: ./src # 本地需要打包的文件目录 @@ -97,29 +98,30 @@ inputs: 主要的参数 -| 参数名称 | 是否必选 | 默认值 | 描述 | -| ------------------------------------ | :------: | :-------------: | :------------------------------------------------------------------ | -| runtime | 否 | Nodejs10.15 | 执行环境, 目前支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 | -| region | 否 | ap-guangzhou | 项目部署所在区域,默认广州区 | -| functionName | 否 | | 云函数名称 | -| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | -| serviceId | 否 | | API 网关服务 ID,如果存在将使用这个 API 网关服务 | -| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | -| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | -| [functionConf](#函数配置) | 否 | | 函数配置 | -| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | -| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | -| [Region special config](#指定区配置) | 否 | | 指定区配置 | -| [staticConf](#静态资源-CDN-配置) | 否 | | 静态资源 CDN 配置 | +| 参数名称 | 必选 | 默认值 | 描述 | +| ------------------------------------ | :--: | :-------------: | :-------------------------------------------------------------- | +| runtime | 否 | `Nodejs10.15` | 执行环境, 支持: Nodejs6.10, Nodejs8.9, Nodejs10.15, Nodejs12.16 | +| region | 否 | `ap-guangzhou` | 项目部署所在区域 | +| functionName | 否 | | 云函数名称 | +| serviceName | 否 | | API 网关服务名称, 默认创建一个新的服务名称 | +| serviceId | 否 | | API 网关服务 ID, 如果存在将使用这个 API 网关服务 | +| entryFile | 否 | `sls.js` | 自定义 server 的入口文件名 | +| src | 否 | `process.cwd()` | 默认为当前目录, 如果是对象, 配置参数参考 [执行目录](#执行目录) | +| layers | 否 | | 云函数绑定的 layer, 配置参数参考 [层配置](#层配置) | +| [functionConf](#函数配置) | 否 | | 函数配置 | +| [apigatewayConf](#API-网关配置) | 否 | | API 网关配置 | +| [cloudDNSConf](#DNS-配置) | 否 | | DNS 配置 | +| [Region special config](#指定区配置) | 否 | | 指定区配置 | +| [staticConf](#静态资源-CDN-配置) | 否 | | 静态资源 CDN 配置 | ## 执行目录 -| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | -| -------- | :------: | :-------------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| src | 否 | String | | 代码路径。与 object 不能同时存在。 | -| exclude | 否 | Array of String | | 不包含的文件或路径, 遵守 [glob 语法](https://github.com/isaacs/node-glob) | -| bucket | 否 | String | | bucket 名称。如果配置了 src,表示部署 src 的代码并压缩成 zip 后上传到 bucket-appid 对应的存储桶中;如果配置了 object,表示获取 bucket-appid 对应存储桶中 object 对应的代码进行部署。 | -| object | 否 | String | | 部署的代码在存储桶中的路径。 | +| 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | +| -------- | :------: | :------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| src | 否 | String | | 代码路径。与 object 不能同时存在。 | +| exclude | 否 | String[] | | 不包含的文件或路径, 遵守 [glob 语法](https://github.com/isaacs/node-glob) | +| bucket | 否 | String | | bucket 名称。如果配置了 src,表示部署 src 的代码并压缩成 zip 后上传到 bucket-appid 对应的存储桶中;如果配置了 object,表示获取 bucket-appid 对应存储桶中 object 对应的代码进行部署。 | +| object | 否 | String | | 部署的代码在存储桶中的路径。 | ## 层配置 @@ -134,7 +136,7 @@ inputs: | 参数名称 | 是否必选 | 类型 | 默认值 | 描述 | | ---------- | :------: | -------- | :----: | :---------------------------------------------- | -| ttl | 否 | Number | 600 | TTL 值,范围 1 - 604800,不同等级域名最小值不同 | +| ttl | 否 | Number | `600` | TTL 值,范围 1 - 604800,不同等级域名最小值不同 | | recordLine | 否 | String[] | | 记录的线路名称 | ### 指定区配置 diff --git a/serverless.component.yml b/serverless.component.yml index 584a8be..f954dea 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.1.3 +version: 0.1.4 author: 'Tencent Cloud, Inc.' org: 'Tencent Cloud, Inc.' description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/_shims/handler.js b/src/_shims/handler.js index 60c5dc7..f51ffad 100644 --- a/src/_shims/handler.js +++ b/src/_shims/handler.js @@ -7,10 +7,10 @@ let app let server module.exports.handler = async (event, context) => { - const userSls = path.join(__dirname, '..', 'sls.js') + const userSls = path.join(__dirname, '..', process.env.SLS_ENTRY_FILE) if (fs.existsSync(userSls)) { // eslint-disable-next-line - console.log('Using user custom sls.js') + console.log(`Using user custom entry file ${process.env.SLS_ENTRY_FILE}`) app = await require(userSls)(true) } else { app = await require('./sls')(false) diff --git a/src/config.js b/src/config.js index 7ecf632..387c385 100644 --- a/src/config.js +++ b/src/config.js @@ -3,6 +3,7 @@ const CONFIGS = { 'https://serverless-templates-1300862921.cos.ap-beijing.myqcloud.com/nuxtjs-demo.zip', compName: 'nuxtjs', compFullname: 'Nuxt.js', + defaultEntryFile: 'sls.js', region: 'ap-guangzhou', handler: 'sl_handler.handler', runtime: 'Nodejs10.15', diff --git a/src/utils.js b/src/utils.js index 4aadd29..116b938 100644 --- a/src/utils.js +++ b/src/utils.js @@ -326,7 +326,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { // chenck state function name const stateFunctionName = instance.state[regionList[0]] && instance.state[regionList[0]].functionName - const functionConf = { + const functionConf = Object.assign(tempFunctionConf, { code: { src: inputs.src, bucket: inputs.srcOriginal && inputs.srcOriginal.bucket, @@ -366,14 +366,12 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { publish: inputs.publish, traffic: inputs.traffic, lastVersion: instance.state.lastVersion, - eip: tempFunctionConf.eip === true, - l5Enable: tempFunctionConf.l5Enable === true, timeout: tempFunctionConf.timeout ? tempFunctionConf.timeout : CONFIGS.timeout, memorySize: tempFunctionConf.memorySize ? tempFunctionConf.memorySize : CONFIGS.memorySize, tags: ensureObject(tempFunctionConf.tags ? tempFunctionConf.tags : inputs.tag, { default: null }) - } + }) // validate traffic if (inputs.traffic !== undefined) { @@ -382,15 +380,25 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { functionConf.needSetTraffic = inputs.traffic !== undefined && functionConf.lastVersion if (tempFunctionConf.environment) { - functionConf.environment = inputs.functionConf.environment + functionConf.environment = tempFunctionConf.environment + functionConf.environment.variables = functionConf.environment.variables || {} + functionConf.environment.variables.SERVERLESS = '1' + functionConf.environment.variables.SLS_ENTRY_FILE = inputs.entryFile || CONFIGS.defaultEntryFile + } else { + functionConf.environment = { + variables: { + SERVERLESS: '1', + SLS_ENTRY_FILE: inputs.entryFile || CONFIGS.defaultEntryFile + } + } } if (tempFunctionConf.vpcConfig) { - functionConf.vpcConfig = inputs.functionConf.vpcConfig + functionConf.vpcConfig = tempFunctionConf.vpcConfig } // 对apigw inputs进行标准化 const tempApigwConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} - const apigatewayConf = { + const apigatewayConf = Object.assign(tempApigwConf, { serviceId: inputs.serviceId, region: regionList, isDisabled: tempApigwConf.isDisabled === true, @@ -413,7 +421,7 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { } ], customDomains: tempApigwConf.customDomains || [] - } + }) if (tempApigwConf.usagePlan) { apigatewayConf.endpoints[0].usagePlan = { usagePlanId: tempApigwConf.usagePlan.usagePlanId, From aaaae9d6f78d1e1c196536441685a006ad675533 Mon Sep 17 00:00:00 2001 From: slsplus Date: Sat, 10 Oct 2020 02:32:35 +0000 Subject: [PATCH 45/65] chore(release): version 0.1.4 ## [0.1.4](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.3...v0.1.4) (2020-10-10) ### Bug Fixes * support customize entry file ([50ace57](https://github.com/serverless-components/tencent-nuxtjs/commit/50ace57900ebf9b4f405ec38204eb85a78e7aa77)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a5445e..d19b823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.4](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.3...v0.1.4) (2020-10-10) + + +### Bug Fixes + +* support customize entry file ([50ace57](https://github.com/serverless-components/tencent-nuxtjs/commit/50ace57900ebf9b4f405ec38204eb85a78e7aa77)) + ## [0.1.3](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.2...v0.1.3) (2020-09-25) From 15805a6b74751407e217ee5bb80dc3e127f92842 Mon Sep 17 00:00:00 2001 From: yugasun Date: Sat, 10 Oct 2020 19:53:30 +0800 Subject: [PATCH 46/65] fix: support all parameters for apigw --- serverless.component.yml | 2 +- src/serverless.js | 2 +- src/utils.js | 37 ++++++++++++++++++++++++------------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/serverless.component.yml b/serverless.component.yml index f954dea..c1569e4 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.1.4 +version: 0.1.5 author: 'Tencent Cloud, Inc.' org: 'Tencent Cloud, Inc.' description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/serverless.js b/src/serverless.js index f527a0d..beb820a 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -159,7 +159,7 @@ class ServerlessComopnent extends Component { environment: apigwOutput.environment, url: `${getDefaultProtocol(inputs.protocols)}://${apigwOutput.subDomain}/${ apigwOutput.environment - }/` + }${apigwInputs.endpoints[0].path}` } if (apigwOutput.customDomains) { diff --git a/src/utils.js b/src/utils.js index 116b938..1f70eca 100644 --- a/src/utils.js +++ b/src/utils.js @@ -314,8 +314,11 @@ const prepareStaticCdnInputs = async (instance, inputs, origin) => { } const prepareInputs = async (instance, credentials, inputs = {}) => { - // 对function inputs进行标准化 - const tempFunctionConf = inputs.functionConf ? inputs.functionConf : {} + const tempFunctionConf = inputs.functionConf + ? inputs.functionConf + : inputs.functionConfig + ? inputs.functionConfig + : {} const fromClientRemark = `tencent-${CONFIGS.compName}` const regionList = inputs.region ? typeof inputs.region == 'string' @@ -396,32 +399,40 @@ const prepareInputs = async (instance, credentials, inputs = {}) => { functionConf.vpcConfig = tempFunctionConf.vpcConfig } - // 对apigw inputs进行标准化 - const tempApigwConf = inputs.apigatewayConf ? inputs.apigatewayConf : {} + const tempApigwConf = inputs.apigatewayConf + ? inputs.apigatewayConf + : inputs.apigwConfig + ? inputs.apigwConfig + : {} const apigatewayConf = Object.assign(tempApigwConf, { - serviceId: inputs.serviceId, + serviceId: inputs.serviceId || tempApigwConf.serviceId, region: regionList, isDisabled: tempApigwConf.isDisabled === true, fromClientRemark: fromClientRemark, - serviceName: inputs.serviceName || getDefaultServiceName(instance), - description: getDefaultServiceDescription(instance), + serviceName: inputs.serviceName || tempApigwConf.serviceName || getDefaultServiceName(instance), + serviceDesc: tempApigwConf.serviceDesc || getDefaultServiceDescription(instance), protocols: tempApigwConf.protocols || ['http'], environment: tempApigwConf.environment ? tempApigwConf.environment : 'release', - endpoints: [ + customDomains: tempApigwConf.customDomains || [] + }) + if (!apigatewayConf.endpoints) { + apigatewayConf.endpoints = [ { - path: '/', + path: tempApigwConf.path || '/', enableCORS: tempApigwConf.enableCORS, serviceTimeout: tempApigwConf.serviceTimeout, method: 'ANY', + apiName: tempApigwConf.apiName || 'index', function: { isIntegratedResponse: true, functionName: functionConf.name, - functionNamespace: functionConf.namespace + functionNamespace: functionConf.namespace, + functionQualifier: + (tempApigwConf.function && tempApigwConf.function.functionQualifier) || '$LATEST' } } - ], - customDomains: tempApigwConf.customDomains || [] - }) + ] + } if (tempApigwConf.usagePlan) { apigatewayConf.endpoints[0].usagePlan = { usagePlanId: tempApigwConf.usagePlan.usagePlanId, From 420da8f9bfe59f44cfcb86d830dea0dd27aa5b85 Mon Sep 17 00:00:00 2001 From: slsplus Date: Mon, 12 Oct 2020 02:19:49 +0000 Subject: [PATCH 47/65] chore(release): version 0.1.5 ## [0.1.5](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.4...v0.1.5) (2020-10-12) ### Bug Fixes * support all parameters for apigw ([15805a6](https://github.com/serverless-components/tencent-nuxtjs/commit/15805a6b74751407e217ee5bb80dc3e127f92842)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d19b823..adcc07b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.5](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.4...v0.1.5) (2020-10-12) + + +### Bug Fixes + +* support all parameters for apigw ([15805a6](https://github.com/serverless-components/tencent-nuxtjs/commit/15805a6b74751407e217ee5bb80dc3e127f92842)) + ## [0.1.4](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.3...v0.1.4) (2020-10-10) From 31b29c7dd0b72e8517736810e775b8ed1847dc90 Mon Sep 17 00:00:00 2001 From: yugasun Date: Tue, 20 Oct 2020 11:00:20 +0800 Subject: [PATCH 48/65] fix: support replace deployment --- .github/workflows/test.yml | 2 + example/README.md | 266 ------------------------------------- example/nuxt.config.js | 26 ++-- example/pages/index.vue | 13 +- example/serverless.yml | 9 ++ package.json | 24 +++- serverless.component.yml | 2 +- src/package.json | 2 +- src/serverless.js | 6 +- src/utils.js | 5 +- 10 files changed, 67 insertions(+), 288 deletions(-) delete mode 100644 example/README.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e8bdb44..26cf7e2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,5 +41,7 @@ jobs: - name: Running integration tests run: npm run test env: + SERVERLESS_PLATFORM_VENDOR: tencent + GLOBAL_ACCELERATOR_NA: true TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }} TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }} diff --git a/example/README.md b/example/README.md deleted file mode 100644 index e8b6a91..0000000 --- a/example/README.md +++ /dev/null @@ -1,266 +0,0 @@ -[![Serverless Nuxtjs Tencent Cloud](https://img.serverlesscloud.cn/2020310/1583829094342-nuxt.js%20_%E9%95%BF.png)](http://serverless.com) - -# 腾讯云 Nuxt.js Serverless Component - -## 简介 - -**腾讯云[Nuxt.js](https://github.com/nuxt/nuxt.js)组件** - 通过使用[**Tencent Serverless Framework**](https://github.com/serverless/components/tree/cloud) , 基于云上 Serverless 服务(如 API 网关、云函数等),实现“0”配置,便捷开发,极速部署采用 Nuxt.js 框架的网页应用,Nuxt.js 组件支持丰富的配置扩展,提供了目前便捷实用,开发成本低的网页应用项目的开发/托管能力。 - -特性介绍: - -- [x] **按需付费** - 按照请求的使用量进行收费,没有请求时无需付费 -- [x] **"0"配置** - 只需要关心项目代码,之后部署即可,Serverless Framework 会搞定所有配置。 -- [x] **极速部署** - 部署速度快,仅需几秒,部署你的整个应用。 -- [x] **实时日志** - 通过实时日志的输出查看业务状态,便于直接在云端开发应用。 -- [x] **云端调试** - 可在云端直接进行项目调试,从而避免本地环境的差异。 -- [x] **便捷协作** - 通过云端控制台的状态信息和部署日志,方便进行多人协作开发。 - -## 快速开始 - -0. [**准备**](#0-准备) -1. [**安装**](#1-安装) -1. [**配置**](#2-配置) -1. [**部署**](#3-部署) -1. [**开发调试**](#4-开发调试) -1. [**查看状态**](#5-查看部署状态) -1. [**移除**](#6-移除) - -更多资源: - -- [**账号配置**](#账号配置) -- [**更多组件**](#更多组件) -- [**FAQ**](#FAQ) - -### 0. 准备 - -#### 初始化 Nuxt.js 项目 - -首先,在本地创建根目录,并初始化一个 Nuxt.js 项目 - -```bash -$ npx create-nuxt-app serverless-nuxtjs -$ cd serverless-nuxtjs -``` - -> 注意:本教程中的 Nuxt 项目使用 JavaScript 与 Npm 安装包进行构建,初始化项目的时候请选择相应的选项 - -### 1. 安装 - -通过 npm 全局安装 [serverless cli](https://github.com/serverless/serverless) - -```bash -$ npm install -g serverless -``` - -### 2. 配置 - -在项目根目录创建 `serverless.yml` 文件,在其中进行如下配置 - -```bash -$ touch serverless.yml -``` - -```yml -# serverless.yml -component: nuxtjs # (必填) 组件名称,此处为nuxtjs -name: nuxtjsDemo # (必填) 实例名称 -org: orgDemo # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid -app: appDemo # (可选) 该 nuxt.js 项目名称 -stage: dev # (可选) 用于区分环境信息,默认值是 dev - -inputs: - src: - src: ./ - exclude: - - .env - region: ap-guangzhou - runtime: Nodejs10.15 - apigatewayConf: - protocols: - - http - - https - environment: release -``` - -- 点此查看[更多配置及说明](https://github.com/yugasun/tencent-nuxtjs/tree/master/docs/configure.md) - -### 3. 部署 - -#### 3.1 构建静态资源 - -进入到 nuxt 项目目录下,构建静态资源 - -```bash -$ cd src && npm run build -``` - -#### 3.2 部署到云端 - -回到在 serverless.yml 文件所在的项目根目录,运行以下指令进行部署: - -```bash -# 进入项目根目录 serverless-nuxtjs -$ sls deploy - -serverless ⚡ framework -Action: "deploy" - Stage: "dev" - App: "appDemo" - Instance: "nuxtjsDemo" - -region: ap-guangzhou -apigw: - serviceId: service-4v2jx72g - subDomain: service-4v2jx72g-1258834142.gz.apigw.tencentcs.com - environment: release - url: https://xxxxxx.gz.apigw.tencentcs.com/release/ -scf: - functionName: nuxtjs_component_mm518kl - runtime: Nodejs10.15 - namespace: default - -139s › nuxtjsDemo › Success -``` - -部署时需要进行身份验证,如您的账号未 [登陆](https://cloud.tencent.com/login) 或 [注册](https://cloud.tencent.com/register) 腾讯云,您可以直接通过 `微信` 扫描命令行中的二维码进行授权登陆和注册。 - -> 注意: 如果希望查看更多部署过程的信息,可以通过`sls deploy --debug` 命令查看部署过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。 -> `sls` 是 `serverless` 命令的简写。 - -### 4. 开发调试 - -部署了 Nuxt.js 应用后,可以通过开发调试能力对该项目进行二次开发,从而开发一个生产应用。在本地修改和更新代码后,不需要每次都运行 `serverless deploy` 命令来反复部署。你可以直接通过 `serverless dev` 命令对本地代码的改动进行检测和自动上传。 - -可以通过在 `serverless.yml`文件所在的目录下运行 `serverless dev` 命令开启开发调试能力。 - -`serverless dev` 同时支持实时输出云端日志,每次部署完毕后,对项目进行访问,即可在命令行中实时输出调用日志,便于查看业务情况和排障。 - -### 5. 查看部署状态 - -在`serverless.yml`文件所在的目录下,通过如下命令查看部署状态: - -``` -$ serverless info -``` - -### 6. 移除 - -在`serverless.yml`文件所在的目录下,通过以下命令移除部署通过以下命令移除部署的 API 网关,移除后该组件会对应删除云上部署时所创建的所有相关资源。 - -```bash -$ sls remove -``` - -和部署类似,支持通过 `sls remove --debug` 命令查看移除过程中的实时日志信息,`sls`是 `serverless` 命令的缩写。 - -### 账号配置 - -当前默认支持 CLI 扫描二维码登录,如您希望配置持久的环境变量/秘钥信息,也可以本地创建 `.env` 文件 - -```bash -$ touch .env # 腾讯云的配置信息 -``` - -在 `.env` 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存 - -如果没有腾讯云账号,可以在此 [注册新账号](https://cloud.tencent.com/register)。 - -如果已有腾讯云账号,可以在 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 中获取 `SecretId` 和`SecretKey`. - -```text -# .env -TENCENT_SECRET_ID=123 -TENCENT_SECRET_KEY=123 -``` - -> 注意:海外 ip 登录时,需要在`.env`文件中添加`SERVERLESS_PLATFORM_VENDOR=tencent` ,使 sls 默认使用 tencent 组件 - -## 更多组件 - -可以在 [Serverless Components](https://github.com/serverless/components) repo 中查询更多组件的信息。 - -## 项目迁移 - -如果项目使用了自定义 Node.js 服务,比如 express 或者 koa,你需要做如下改造工作。 - -### 自定义 express 服务 - -如果你的 Nuxt.js 项目本身运行就是基于 `express` 自定义服务的,那么你需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: - -```js -const express = require('express') -const { loadNuxt } = require('nuxt') - -async function createServer() { - // not report route for custom monitor - const noReportRoutes = ['/_nuxt', '/static', '/favicon.ico'] - - const server = express() - const nuxt = await loadNuxt('start') - - server.all('*', (req, res, next) => { - noReportRoutes.forEach((route) => { - if (req.path.indexOf(route) === 0) { - req.__SLS_NO_REPORT__ = true - } - }) - return nuxt.render(req, res, next) - }) - - // define binary type for response - // if includes, will return base64 encoded, very useful for images - server.binaryTypes = ['*/*'] - - return server -} - -module.exports = createServer -``` - -### 自定义 koa 服务 - -如果你的项目使用的是 Koa 作为 Node.js 服务,需要在项目中自定义入口文件 `sls.js`,需要参考你的服务启动文件进行修改,以下是一个模板文件: - -```js -const Koa = require('koa') -const { loadNuxt } = require('nuxt') - -async function createServer() { - const server = new Koa() - const nuxt = await loadNuxt('start') - - server.use((ctx) => { - ctx.status = 200 - ctx.respond = false - ctx.req.ctx = ctx - - nuxt.render(ctx.req, ctx.res) - }) - - // define binary type for response - // if includes, will return base64 encoded, very useful for images - server.binaryTypes = ['*/*'] - - return server -} - -module.exports = createServer -``` - -## 自定义监控 - -> **目前仅支持自定义 `Express` 服务的项目** - -当在部署 Nuxt.js 应用时,如果 `serverless.yml` 中未指定 `role`,默认会尝试绑定 `QCS_SCFExcuteRole`,并且开启自定义监控,帮助用户收集应用监控指标。对于为自定义入口文件的项目,会默认上报除含有 `/_nuxt`、`/static` 和 `/favicon.ico` 的路由。如果你想自定义上报自己的路由性能,那么可以自定义 `sls.js` 入口文件,对于无需上报的路由,在 express 服务的 `req` 对象上添加 `__SLS_NO_REPORT__` 属性值为 `true` 即可。比如: - -```js -server.get('/no-report', (req, res, next) => { - req.__SLS_NO_REPORT__ = true - return nuxt.render(req, res, next) -}) -``` - -那么用户在访问 `GET /no-report` 路由时,就不会上报自定义监控指标。 - -## License - -MIT License - -Copyright (c) 2020 Tencent Cloud, Inc. diff --git a/example/nuxt.config.js b/example/nuxt.config.js index 2c220ee..282d3d3 100644 --- a/example/nuxt.config.js +++ b/example/nuxt.config.js @@ -1,5 +1,18 @@ export default { mode: 'universal', + env: { + STATIC_URL: process.env.STATIC_URL || '' + }, + /* + ** Build configuration + */ + build: { + extend(config, { isDev, isClient }) { + if (!isDev && process.env.STATIC_URL) { + config.output.publicPath = process.env.STATIC_URL + } + } + }, /* ** Headers of the page */ @@ -14,7 +27,7 @@ export default { content: 'Serverless Nuxt.js Application Created By Serverless Framework' } ], - link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }] + link: [{ rel: 'icon', type: 'image/x-icon', href: `${process.env.STATIC_URL}/favicon.ico` }] }, /* ** Customize the progress-bar color @@ -35,14 +48,5 @@ export default { /* ** Nuxt.js modules */ - modules: [], - /* - ** Build configuration - */ - build: { - /* - ** You can extend webpack config here - */ - extend(config, ctx) {} - } + modules: [] } diff --git a/example/pages/index.vue b/example/pages/index.vue index 1dfcad9..2e8e221 100644 --- a/example/pages/index.vue +++ b/example/pages/index.vue @@ -2,12 +2,13 @@
-

nuxt-demo

-

Serverless Nuxt.js Application Created By Serverless Framework

- +

Welcome to Nuxt.js

+

+ The SSR app is hosted on + + Serverless SSR + +

diff --git a/example/serverless.yml b/example/serverless.yml index a5f2aed..6803e0f 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -17,3 +17,12 @@ inputs: - http - https environment: release + staticConf: + cosConf: + replace: true + bucket: nuxtjs-demo + sources: + - src: .nuxt/dist/client + targetDir: / + - src: static + targetDir: / diff --git a/package.json b/package.json index 1b6d8dd..6192a9e 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "publishConfig": { "access": "public" }, + "description": "Tencent Cloud Nuxt.js Serverless Component", "scripts": { "test": "jest ./tests/integration.test.js --testEnvironment node", "commitlint": "commitlint -f HEAD@{15}", @@ -57,5 +58,26 @@ "lint-staged": "^10.0.8", "prettier": "^1.19.1", "semantic-release": "^17.0.4" - } + }, + "directories": { + "doc": "docs", + "example": "example", + "test": "tests" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/serverless-components/tencent-nuxtjs.git" + }, + "keywords": [ + "serverless-nuxtjs", + "nuxtjs", + "serverless", + "serverless-framework", + "serverless-components", + "tencent-cloud" + ], + "bugs": { + "url": "https://github.com/serverless-components/tencent-nuxtjs/issues" + }, + "homepage": "https://github.com/serverless-components/tencent-nuxtjs#readme" } diff --git a/serverless.component.yml b/serverless.component.yml index c1569e4..247b176 100644 --- a/serverless.component.yml +++ b/serverless.component.yml @@ -1,5 +1,5 @@ name: nuxtjs -version: 0.1.5 +version: 0.1.6 author: 'Tencent Cloud, Inc.' org: 'Tencent Cloud, Inc.' description: Deploy a serverless Nuxt.js application onto Tencent SCF and API Gateway. diff --git a/src/package.json b/src/package.json index d31074b..4b9c0c2 100644 --- a/src/package.json +++ b/src/package.json @@ -2,7 +2,7 @@ "dependencies": { "adm-zip": "^0.4.16", "download": "^8.0.0", - "tencent-component-toolkit": "^1.16.7", + "tencent-component-toolkit": "^1.17.4", "type": "^2.1.0" } } diff --git a/src/serverless.js b/src/serverless.js index beb820a..a33f4b0 100644 --- a/src/serverless.js +++ b/src/serverless.js @@ -194,12 +194,16 @@ class ServerlessComopnent extends Component { if (zipPath) { console.log(`Deploy static for ${CONFIGS.compFullname} application`) // 1. deploy to cos - const staticCosInputs = await prepareStaticCosInputs(this, inputs, appId, zipPath) + const { staticCosInputs, bucket } = await prepareStaticCosInputs(this, inputs, appId, zipPath) const cos = new Cos(credentials, region) const cosOutput = { region } + // flush bucket + if (inputs.cosConf.replace) { + await cos.flushBucketFiles(bucket) + } for (let i = 0; i < staticCosInputs.length; i++) { const curInputs = staticCosInputs[i] console.log(`Starting deploy directory ${curInputs.src} to cos bucket ${curInputs.bucket}`) diff --git a/src/utils.js b/src/utils.js index 1f70eca..965ac90 100644 --- a/src/utils.js +++ b/src/utils.js @@ -251,7 +251,10 @@ const prepareStaticCosInputs = async (instance, inputs, appId, codeZipPath) => { staticCosInputs.push(cosInputs) } } - return staticCosInputs + return { + bucket: `${bucketName}-${appId}`, + staticCosInputs + } } catch (e) { throw new TypeError( `UTILS_${CONFIGS.compName.toUpperCase()}_prepareStaticCosInputs`, From 6a4f958bcd6764972daae11a52af992128cd8833 Mon Sep 17 00:00:00 2001 From: slsplus Date: Tue, 20 Oct 2020 03:12:22 +0000 Subject: [PATCH 49/65] chore(release): version 0.1.6 ## [0.1.6](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.5...v0.1.6) (2020-10-20) ### Bug Fixes * support replace deployment ([31b29c7](https://github.com/serverless-components/tencent-nuxtjs/commit/31b29c7dd0b72e8517736810e775b8ed1847dc90)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index adcc07b..3a592b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.6](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.5...v0.1.6) (2020-10-20) + + +### Bug Fixes + +* support replace deployment ([31b29c7](https://github.com/serverless-components/tencent-nuxtjs/commit/31b29c7dd0b72e8517736810e775b8ed1847dc90)) + ## [0.1.5](https://github.com/serverless-components/tencent-nuxtjs/compare/v0.1.4...v0.1.5) (2020-10-12) From 46a8642f24e1eb720ee1e9f117ced467bacfa9b9 Mon Sep 17 00:00:00 2001 From: yugasun Date: Thu, 22 Oct 2020 14:38:21 +0800 Subject: [PATCH 50/65] chore: update example --- .gitignore | 3 ++- example/.env.example | 5 +++++ example/layouts/default.vue | 13 +++++++++++-- example/nuxt.config.js | 5 +++-- example/serverless.yml | 5 ----- 5 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 example/.env.example diff --git a/.gitignore b/.gitignore index f37c1a6..6ff803f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,8 @@ dist .idea build/ .env* +!.env.example env.js package-lock.json test -yarn.lock \ No newline at end of file +yarn.lock diff --git a/example/.env.example b/example/.env.example new file mode 100644 index 0000000..34f5f65 --- /dev/null +++ b/example/.env.example @@ -0,0 +1,5 @@ +TENCENT_SECRET_ID=123 +TENCENT_SECRET_KEY=123 + +# please change to your real appid +STATIC_URL=https://nuxtjs-demo-.cos.ap-guangzhou.myqcloud.com diff --git a/example/layouts/default.vue b/example/layouts/default.vue index 2f45459..365d2a2 100644 --- a/example/layouts/default.vue +++ b/example/layouts/default.vue @@ -6,8 +6,8 @@