环境变量
默认模板使用的环境变量、模板默认值和填写要求。
Saavo 使用 .env 保存本地开发变量,使用 .env.production 保存生产环境变量。example.vars 是这两个文件的模板;执行 npm run saavo:init 时,如果 .env 不存在,脚本会根据该模板创建文件,并自动生成缺失的 SAAS_SECRET。
不要提交环境文件
.env 和 .env.production 都可能包含密钥,默认已经加入 .gitignore。不要把这两个文件提交到 Git,也不要在日志、截图或前端代码中输出其中的敏感内容。
VITE_ 变量会公开给浏览器
以 VITE_ 开头的变量会在构建时写入前端文件,任何访客都可以通过浏览器查看。此类变量只能保存公开信息,不能存放 API 密钥、访问令牌、密码或私钥。
环境文件
.env
供本地开发、测试和本地预览使用。npm run saavo:init 会保留已有文件,只补充缺失的 SAAS_SECRET,不会覆盖已经填写的变量。
.env.production
部署脚本使用的生产环境文件。npm run deploy:init 会创建或更新该文件,写入当前 Worker 的 workers.dev 地址,并交互配置所需信息,不能假定已有值全部保持不变。空字符串不会写入远程 Worker,文件中未出现的远程 Secret 也不会被删除。
单独执行本地生产构建时,如果没有 .env.production,项目还支持读取 .env 的本地构建流程。这不代表项目已经具备生产部署配置。
部署生产环境时,公开变量通过 Wrangler --var 写入,其余非空变量通过临时 Secrets 文件写入 Worker Secret。临时文件使用后会立即删除。
| 写入方式 | 变量 |
|---|---|
| 普通变量 | VITE_SITE_URL、VITE_SAAVO_COLLECT_API_HOST、VITE_SAAVO_COLLECT_API_ENDPOINT、CLOUDFLARE_TURNSTILE_SITE_KEY、STRIPE_CONNECTION_ID、PADDLE_CONNECTION_ID、PADDLE_ENVIRONMENT、PADDLE_CLIENT_TOKEN、GITHUB_CLIENT_ID、GOOGLE_CLIENT_ID、R2_ACCOUNT_ID |
| Secret | 其他所有非空变量 |
不要把 Cloudflare 部署凭据写入 .env.production
CLOUDFLARE_API_TOKEN、CLOUDFLARE_API_KEY、CLOUDFLARE_ACCOUNT_ID、CLOUDFLARE_EMAIL、CF_API_TOKEN、CF_ACCOUNT_ID 以及所有以 WRANGLER_ 开头的变量只供部署工具使用。npm run deploy:init 和 npm run deploy:update 会拒绝包含这些变量的 .env.production。
站点和访问统计
VITE_SITE_URL
Prop
Type
example.vars 没有预设该变量。本地开发时会生成 http://127.0.0.1:<端口>;未创建 .env.production 的本地构建使用 http://127.0.0.1:4173;首次部署时,脚本会写入当前 Worker 的 workers.dev 地址。
VITE_SAAVO_COLLECT_API_HOST
Prop
Type
VITE_SAAVO_COLLECT_API_ENDPOINT
Prop
Type
这两个变量只在构建 public/js/analytics.js 时使用。修改后需要重新构建站点。
应用密钥
SAAS_SECRET
Prop
Type
example.vars 中的默认值为空。npm run saavo:init 会在缺少该值时自动生成;也可以执行 npm run secret:generate 生成新值。
SAAS_SECRET 必须长期保持不变
同一项目的 .env 和 .env.production 必须使用完全相同的 SAAS_SECRET。项目产生加密数据后再修改该值,可能导致双重验证密钥、联盟推广收款资料、OAuth 客户端 Secret 等已有数据无法解密。
Cloudflare Turnstile
config/deploy.ts 中的 auth.useTurnstile 默认为 { threshold: 2, interval: '1h' },用于按阈值触发验证。启用该配置时,需要同时填写站点密钥和 Secret Key。设置为 false 后,这两个变量可以留空,不能用 true 代替配置对象。
CLOUDFLARE_TURNSTILE_SITE_KEY
Prop
Type
CLOUDFLARE_TURNSTILE_SECRET_KEY
Prop
Type
example.vars 使用 Cloudflare 官方测试凭据,适合本地开发。部署生产环境前,应为生产域名创建 Turnstile Widget,并替换为真实凭据。
Stripe
config/payment.ts 默认选择 Stripe。Checkout 需要 STRIPE_CONNECTION_ID 和 STRIPE_SECRET_KEY,Webhook 还需要 STRIPE_WEBHOOK_SECRET。缺少前两项时无法创建 Checkout,缺少最后一项时无法接收 Stripe Webhook。
STRIPE_CONNECTION_ID
Prop
Type
同一环境上线后不要随意修改该值,否则已经保存的 Stripe 对象可能会被识别为另一组连接的数据。测试环境和生产环境应使用不同的连接 ID。
STRIPE_SECRET_KEY
Prop
Type
STRIPE_WEBHOOK_SECRET
Prop
Type
Paddle
以下变量已经写入环境模板和类型声明,但项目尚未实现 Paddle Checkout、客户门户和 Webhook。仅填写这些变量不会启用 Paddle 支付。
PADDLE_CONNECTION_ID
Prop
Type
PADDLE_ENVIRONMENT
Prop
Type
PADDLE_API_KEY
Prop
Type
PADDLE_WEBHOOK_SECRET
Prop
Type
PADDLE_CLIENT_TOKEN
Prop
Type
邮件
RESEND_API_KEY
Prop
Type
模板默认使用 Resend,因此生产部署时必须填写该变量。本地缺少该值时,npm run doctor 会给出警告;生产环境缺少该值时,邮件发送不可用,远程检查也无法通过。
如果把 emailProvider.type 改为 cloudflare,则不需要 RESEND_API_KEY,但必须在 wrangler.jsonc 中配置 EMAIL 资源绑定。资源绑定参见Cloudflare 资源绑定。
OAuth 登录
GitHub 和 Google 登录都要求 Client ID 与 Client Secret 成对填写。两个值都为空表示不启用相应登录方式;只填写一个值时,该登录方式仍然不可用,npm run doctor 会给出警告。
GITHUB_CLIENT_ID
Prop
Type
GITHUB_CLIENT_SECRET
Prop
Type
GOOGLE_CLIENT_ID
Prop
Type
GOOGLE_CLIENT_SECRET
Prop
Type
R2 API 凭据
example.vars 将这组变量标记为“签名模板版本下载”所需的 R2 API 凭据。模板保留了变量和类型声明,npm run doctor 也会检查三项是否同时填写;现有业务代码尚未读取它们。这组 API 凭据与 Worker 直接访问 MAIN_R2 的资源绑定无关。
R2_ACCOUNT_ID
Prop
Type
R2_ACCESS_KEY_ID
Prop
Type
R2_SECRET_ACCESS_KEY
Prop
Type
三项都为空表示不配置;只填写其中一部分时,npm run doctor 会给出警告。
外部通知变量
外部通知变量没有固定名称。config/notifications.ts 中的每个 *Binding 字段都填写一个环境变量名,程序再按这个名称读取 Webhook、令牌或签名密钥。变量名可按项目需要自行确定,不必沿用 example.vars 中的示例。
配置文件只保存变量名
config/notifications.ts 只能填写 Binding 名称,不能直接填写 Webhook URL、Bot Token、Chat ID、签名密钥或自定义请求头。
模板默认配置
模板默认没有启用外部通知通道。example.vars 只列出与 notifications.ts 源码注释配套的变量示例,默认值均为空字符串。
| 通知服务 | 示例变量名 | 保存内容 |
|---|---|---|
| Slack | SLACK_OPERATIONS_WEBHOOK_URL | Incoming Webhook URL |
| Discord | DISCORD_SALES_WEBHOOK_URL | Incoming Webhook URL |
| Discord | DISCORD_SALES_THREAD_ID | Thread ID |
| Telegram | TELEGRAM_ALERTS_BOT_TOKEN | Bot Token |
| Telegram | TELEGRAM_ALERTS_CHAT_ID | Chat ID |
| Telegram | TELEGRAM_ALERTS_THREAD_ID | Message Thread ID |
| Microsoft Teams | TEAMS_OPERATIONS_WEBHOOK_URL | Workflow Webhook URL |
| 飞书或 Lark | FEISHU_RELEASE_WEBHOOK_URL | 自定义机器人 Webhook URL |
| 飞书或 Lark | FEISHU_RELEASE_SIGNING_SECRET | 机器人签名密钥 |
| 钉钉 | DINGTALK_RELEASE_WEBHOOK_URL | 自定义机器人 Webhook URL |
| 钉钉 | DINGTALK_RELEASE_SIGNING_SECRET | 机器人加签密钥 |
| 企业微信 | WECOM_RELEASE_WEBHOOK_URL | 群机器人 Webhook URL |
| 通用 Webhook | INTERNAL_AUDIT_WEBHOOK_URL | HTTPS 请求地址 |
| 通用 Webhook | INTERNAL_AUDIT_WEBHOOK_HEADERS | HTTP 请求头组成的 JSON 对象 |
INTERNAL_AUDIT_WEBHOOK_HEADERS 的值是 JSON 对象字符串,例如:
INTERNAL_AUDIT_WEBHOOK_HEADERS='{"Authorization":"Bearer token"}'wrangler.jsonc 中的普通变量
下表默认名称来自模板源码。CLI 创建项目时会先将 saavo-template 前缀替换为项目名。
以下变量不在 example.vars 中,而是直接写在 wrangler.jsonc 的 vars 对象中。npm run deploy:init 会根据 Worker 名称更新这些资源名称,不要在 .env 中重复填写。
ASYNC_POLICY_TASK_QUEUE_NAME
Prop
Type
ASYNC_LOGGER_QUEUE_NAME
Prop
Type
ANALYTICS_QUEUE_NAME
Prop
Type
R2_BUCKET_NAME
Prop
Type
类型声明
worker-configuration.d.ts 由 Wrangler 生成,用于声明环境变量和 Cloudflare 资源绑定的类型。修改 wrangler.jsonc、增删固定变量或调整资源绑定后,应执行:
npm run cf-typegen该文件是生成文件,不要直接编辑。外部通知使用的是用户自定义 Binding 名称,不会作为固定字段逐项写入 CloudflareBindings。