文件

7.2 KiB

腾讯云部署说明

本文档记录当前后端部署到腾讯云函数 SCF,并连接腾讯云数据库 MySQL 与 COS 的最小流程。

1. 数据库初始化

先确认 server/.env 或部署环境变量中已配置数据库:

DB_HOST=
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_NAME=jw_beauty

本地直连公网数据库时,需要在腾讯云数据库的安全组/白名单中放行本机公网 IP。若本地无法直连,也可以临时通过支持 SOCKS5 的代理建立本地 TCP 隧道,再覆盖 DB_HOSTDB_PORT 执行初始化。

cd server
npm install
npm run db:init

初始化会创建业务表,并写入临时后台账号。生产环境首次登录后应立即更换默认密码。

2. SCF 函数配置

当前函数 jw-beauty-api 是腾讯云 HTTP 函数。HTTP 函数部署包根目录需要包含可执行的 scf_bootstrap,由它启动 HTTP 服务。

本项目的 HTTP 函数启动文件:

scf_bootstrap

scf_bootstrap 会执行:

node scripts/local-server.js

HTTP 服务监听端口使用环境变量 PORT,建议配置为 9000

如果以后改为普通事件函数,入口才使用:

index.main_handler

触发器:

HTTP 触发器或函数 URL

触发器需要透传:

method
path
headers
queryStringParameters
body

3. 环境变量

参考模板:

server/.env.production.example

生产必须配置:

NODE_ENV=production
TENCENT_SECRET_ID=
TENCENT_SECRET_KEY=
TENCENT_REGION=ap-guangzhou
DB_HOST=
DB_PORT=3306
DB_USER=
DB_PASSWORD=
DB_NAME=jw_beauty
COS_BUCKET=jw-bucket-1426323813
COS_REGION=ap-guangzhou
WECHAT_APPID=
WECHAT_SECRET=
JWT_SECRET=
ADMIN_JWT_SECRET=
ADMIN_USERNAME=admin
ADMIN_PASSWORD=

不要提交真实 .env、密钥、数据库密码或微信密钥。

4. 数据库网络

SCF 与腾讯云数据库建议使用同地域、同 VPC 内网连接。

如果使用公网地址,需要确认:

  • 数据库已开启公网访问。
  • 安全组允许 SCF 出口或本机出口访问数据库端口。
  • DB_PORT 与控制台展示端口一致。

5. COS 固件

固件文件先上传到 COS,然后在后台登记对象 key。

相关接口:

GET  /api/v1/admin/firmware
POST /api/v1/admin/firmware
POST /api/v1/admin/firmware/:firmware_id/status
GET  /api/v1/firmware/latest

小程序调用 GET /api/v1/firmware/latest 时,后端会返回短期有效的 COS 签名下载 URL。

6. 前端 API 地址

SCF HTTP 地址确认后,更新:

miniprogram/config/env.js
admin-console/src/config/env.js

testprod 改为腾讯云函数 HTTPS 地址。

微信小程序正式联调还需要在微信公众平台配置 request 合法域名

7. 管理后台 H5 部署到 COS

管理后台构建产物位于:

admin-console/dist/build/h5

先确认 admin-console/src/config/env.js 已指向测试或生产 API,再执行:

cd admin-console
npm install
npm run build:h5

如需直接上传到 COS,可复用 server/.env 中的 COS 凭证:

cd admin-console
npm run deploy:cos

默认上传前缀:

admin/

可通过环境变量覆盖:

ADMIN_COS_PREFIX=admin-test/ npm run deploy:cos

8. 后端 SCF 代码更新

常规后端代码更新流程如下。当前线上函数名为 jw-beauty-api,部署包上传到 COS

deploy/jw-beauty-api/hox-http-function.zip

8.1 拉取代码并检查

git status --short --branch
git pull --ff-only

如果 server/ 有依赖变更,先同步依赖:

cd server
npm install

部署前至少做语法检查:

cd server
node -c src/app.js
node -c src/index.js
node -c src/lib/serverless.js
node -c scripts/local-server.js

如果只改了某个路由,也可以额外检查对应文件,例如:

node -c src/routes/subscription.js

8.2 打包后端代码

部署包必须以 server/ 为根目录打包,并包含:

  • index.js
  • package.json
  • package-lock.json
  • scripts/
  • scf_bootstrap
  • src/
  • sql/
  • node_modules/

不要把 server/.env 打进部署包。

实际使用命令:

cd server
rm -f /tmp/hox-http-function.zip
zip -qry /tmp/hox-http-function.zip index.js package.json package-lock.json scripts scf_bootstrap src sql node_modules

8.3 上传部署包到 COS

上传复用 server/.env 中的腾讯云/COS 凭证。命令只读取本地环境变量,不要把真实密钥写入文档或提交到 Git。

cd server
set -a; source ./.env; set +a
node - <<'NODE'
const fs = require('fs')
const COS = require('cos-nodejs-sdk-v5')
const cos = new COS({
  SecretId: process.env.TENCENT_SECRET_ID,
  SecretKey: process.env.TENCENT_SECRET_KEY
})

cos.putObject({
  Bucket: process.env.COS_BUCKET,
  Region: process.env.COS_REGION || process.env.TENCENT_REGION || 'ap-guangzhou',
  Key: 'deploy/jw-beauty-api/hox-http-function.zip',
  Body: fs.createReadStream('/tmp/hox-http-function.zip')
}, err => {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  console.log('uploaded deploy/jw-beauty-api/hox-http-function.zip')
})
NODE

8.4 更新 SCF 函数代码

使用 tccli scf UpdateFunctionCode 指向刚上传的 COS 对象:

cd server
set -a; source ./.env; set +a
/Users/guoguo/.local/bin/tccli scf UpdateFunctionCode \
  --region ap-guangzhou \
  --FunctionName jw-beauty-api \
  --CosBucketName "$COS_BUCKET" \
  --CosObjectName /deploy/jw-beauty-api/hox-http-function.zip \
  --CosBucketRegion "${COS_REGION:-ap-guangzhou}" \
  --secretId "$TENCENT_SECRET_ID" \
  --secretKey "$TENCENT_SECRET_KEY"

如果命令返回 RequestId,表示更新请求已提交成功。函数实例刷新通常需要等待几秒。

8.5 更新后快速验证

node - <<'NODE'
const base = 'https://api.vsai.net.cn'

async function print(label, res) {
  const text = await res.text()
  console.log(label, res.status, text.slice(0, 1000))
  return text
}

;(async () => {
  await print('health', await fetch(base + '/health'))

  const loginText = await print('login', await fetch(base + '/api/v1/admin/login', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ username: 'admin', password: 'admin' })
  }))

  const token = JSON.parse(loginText).data && JSON.parse(loginText).data.token
  if (token) {
    await print('settings', await fetch(base + '/api/v1/admin/settings', {
      headers: { authorization: 'Bearer ' + token }
    }))
    await print('dashboard', await fetch(base + '/api/v1/admin/dashboard', {
      headers: { authorization: 'Bearer ' + token }
    }))
  }
})().catch(err => {
  console.error(err)
  process.exit(1)
})
NODE

常见成功结果:

  • GET /health 返回 code: 0
  • POST /api/v1/admin/login 使用 admin/admin 返回 code: 0
  • GET /api/v1/admin/settings 返回 code: 0
  • GET /api/v1/admin/dashboard 返回 code: 0

9. 部署后验证

建议按顺序验证:

GET  /health
POST /api/v1/admin/login
GET  /api/v1/admin/dashboard
POST /api/v1/admin/devices
GET  /api/v1/admin/devices
GET  /api/v1/admin/firmware

再进入小程序验证:登录、扫码绑定、BLE 连接、绑定确认、普通模式护理、护理记录同步。