feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+51
View File
@@ -0,0 +1,51 @@
# 腾讯云验证码短信接入
本项目的 SaaS 家庭端、管理端、设备绑定入口和小程序原生登录共用一套服务端短信发送器。前端只提交手机号、图片验证码和短信验证码,不持有腾讯云密钥。服务端沿用现有 6 位随机码、5 分钟有效、一次性消费、最多 5 次验证、60 秒重发间隔、每手机号每小时最多 5 次,以及会话和 IP 限流。
## 腾讯云侧准备
在 [短信控制台](https://console.cloud.tencent.com/smsv2) 开通国内短信,并创建短信应用、实名认证资质、已审核通过的签名和“验证码短信”正文模板。模板支持验证码变量,以及可选的有效分钟数变量;参数顺序由下方 `template_params` 配置。取得 SDK AppID、签名**名称**、各用途的模板 ID。在 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 创建仅供服务端使用的密钥,并给该身份授予发送短信所需的最小权限。
## 服务端单文件配置(推荐)
填写 `deploy/server/tencent-sms-template/tencent-sms.json.example`,保存为 `tencent-sms.json`,上传到服务器 `/etc/data-terminal-cloud/voice-runtime/tencent-sms.json`。其中包含 SecretId、SecretKey、短信 SDK AppID、签名名称和四个模板 ID:`login`、`register`、`change_password`、`change_phone`。AppKey 不使用。
默认模板参数只有验证码。若正文还包含有效分钟数,在同一个 JSON 文件中设置 `"template_params": {"register": ["code", "expires_minutes"]}`;按数组顺序传入验证码和实际有效期 `5`,不填写真实验证码。当前腾讯云账号的注册模板已确认是两个变量,另外三个模板均为一个变量。
现有服务器 Compose 已只读挂载该目录。API 和 worker 默认自动读取容器内 `/run/secrets/voice-runtime/tencent-sms.json`;不存在时沿用原有环境变量配置。也可用 `TENCENT_SMS_CONFIG_FILE` 指定其他容器内绝对路径,显式指定的文件缺失会拒绝启动。单文件存在时,其 `enabled` 和短信配置优先于旧版环境变量;callbacks 等其他服务不读取此文件。支持 UTF-8 BOM,格式错误的报错不会包含密钥内容。
部署本次服务端代码及 `000070_sms_purpose` 迁移后,重启 API 和 worker 加载该文件。不会因为仅上传文件就自动启用或热加载。
家庭端/小程序的注册与登录共用入口,服务端按手机号凭据是否存在选择注册或登录模板。管理端登录始终用登录模板。SaaS 的“忘记密码”发送 `purpose=CHANGE_PASSWORD`,使用修改密码模板;验证后沿用已有的登录会话与密码设置流程。请求号同时绑定手机号和请求用途,不能跨用途重放发送。
`change_phone` 已支持独立配置和在发送器中按用途选模板;当前没有更换手机号的业务接口和页面,登录接口不接受 `CHANGE_PHONE`。新增换号功能时必须单独验证当前账号、旧/新手机号并绑定验证用途,不能把换号验证码当登录凭据。
## 兼容旧版环境变量与分文件配置
API 与 worker 使用同一组配置。生产环境把 `SecretId` 和 `SecretKey` 分别放在只读密钥文件中,不放进仓库、前端、小程序、公开日志或 Compose 明文环境变量:
```dotenv
ONBOARDING_SMS_LOGIN_ENABLED=true
ONBOARDING_SMS_PROVIDER=tencent
TENCENT_SMS_SECRET_ID_FILE=/run/secrets/voice-runtime/tencent-sms-secret-id
TENCENT_SMS_SECRET_KEY_FILE=/run/secrets/voice-runtime/tencent-sms-secret-key
TENCENT_SMS_SDK_APP_ID=<短信应用 SDK AppID>
TENCENT_SMS_SIGN_NAME=<审核通过的签名名称>
TENCENT_SMS_TEMPLATE_ID=<审核通过的验证码模板 ID>
```
仍须保留原有 `ONBOARDING_MASTER_KEY`、`ONBOARDING_ENTRY_URL` 和可信代理配置。`ONBOARDING_SMS_PROVIDER=gateway` 保留原自建 HTTPS 网关模式。未配置完整时不要开启 `ONBOARDING_SMS_LOGIN_ENABLED`;启动校验会拒绝缺少必需参数的配置。当前腾讯云适配器只向 `+86` 中国大陆手机发送,避免把国内模板误用到国际号码。
腾讯云 SDK 使用官方 API v2021-01-11 的 `SendSms`,每次传一个手机号,并按对应用途配置传入验证码及可选的有效分钟数参数;检查单号码 `SendStatusSet.Code == "Ok"` 后才将平台短信挑战标记为已发送。`SessionContext` 放入挑战 UUID 供发送记录关联,但腾讯云并不以它作为幂等键。平台对于超时等不确定结果**不会自动重试**,避免重复发送和计费;用户需等待重发间隔后重新申请。
## 接口
- SaaS 家庭端:现有 `GET /api/v1/web/login/session`、`POST /api/v1/web/login/sms/send`、`POST /api/v1/web/login/sms/verify`,网页界面无需改动。
- SaaS 管理端:现有 `/api/v1/web/operator/login/...` 对应接口,只有已预先绑定手机号的管理员能登录,不会通过短信自动创建管理员。
- 小程序:先 `POST /api/v1/mini/login/session` 取得 `customer.` 或 `operator.` 登录令牌;后续请求带 `Authorization: MiniLogin <login_token>` 与 `X-Request-ID: <UUID v4>`。调用 `POST /api/v1/mini/login/captcha` 获取图片验证码,再调用 `POST /api/v1/mini/login/sms/send`(请求体与网页 `SMSSendInput` 相同,现有登录页发送 `purpose=LOGIN`,服务端自动选择注册或登录模板),最后调用 `POST /api/v1/mini/login/sms/verify`,成功后获得 `Mini <access_token>`。无需浏览器 Cookie。响应中的 `sms_enabled` 可用于隐藏不可用的短信入口。
微信小程序页面源码位于独立的 `voicelink` 项目,登录页已接入手机号、图片验证码、短信验证码及 60 秒重发交互。已有微信授权/密码登录保持不变。小程序与本服务端需要分别构建和发布。
## 上线前验证
配置审核通过的签名/模板及密钥后,在测试手机号上实际发送一条,确认腾讯云控制台的发送记录、手机收到的内容、网页和小程序验证成功、错误验证码与重复验证失败,再逐步开启正式流量。不要用固定验证码或把验证码记录到日志。线上部署前应单独检查 Compose 最终环境变量,并避免把密钥内容打印到终端。