配套
docs/launch-plan.md的 Phase 0 — 上线收口。本文件回答两件事:
- 上线前每个环境变量配什么、从哪拿、配到哪;
- 配完怎么自动 + 人工验证一遍。
清单全部源自代码实际读取点(
grep process.env+@vercel/blob/迁移脚本隐式读取),非凭记忆。
| 命令 | 作用 |
|---|---|
pnpm check:env |
预检当前 shell 环境变量是否齐备(缺必填 → 退出码 1) |
node --env-file=.env.local scripts/check-env.mjs |
预检本地 .env.local |
node --env-file=.env.production.local scripts/check-env.mjs --prod |
预检生产变量(--prod 下禁用旗标变阻断) |
pnpm smoke https://<域名> |
部署后 HTTP 冒烟(探活 + 关键鉴权连线) |
预检本地需 Node ≥ 20.6(原生 --env-file);更低版本用 set -a && source .env.local && set +a && pnpm check:env。
图例:🔴 必填(缺则核心闭环断)· 🟡 建议(缺则该功能不可用)· ⛔ 禁止进生产
| 变量 | 用途 / 代码位置 | 获取 | Vercel 范围 |
|---|---|---|---|
DATABASE_URL |
应用运行时连接(pooled),lib/db.ts |
Neon 的 pooler 串(-pooler 主机) |
Production / Preview |
DATABASE_URL_UNPOOLED |
构建期迁移直连,vercel-build → migrate:up |
Neon 的直连串(无 -pooler) |
Production / Preview |
⚠️ vercel-build=pnpm migrate:up && next build,每次生产构建都会拿DATABASE_URL_UNPOOLED对生产库跑迁移。该变量必须在构建环境可用,否则部署直接失败。
| 变量 | 用途 | 获取 |
|---|---|---|
AUTH_SECRET |
next-auth 会话加密 | openssl rand -base64 32 —— 须重新生成(历史已泄露) |
NEXTAUTH_URL |
邀请邮件链接基址,lib/email.ts |
生产应用地址,如 https://app.urgizat.com |
AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET |
Google OAuth,auth.ts |
Google Cloud Console —— secret 须轮换;回调加 <域名>/api/auth/callback/google |
ADMIN_EMAILS |
逗号分隔,登录即授予 ADMIN,auth.ts:153 |
平台管理员邮箱列表 |
可选 OAuth(启用才配):AUTH_MICROSOFT_ENTRA_ID_ID/SECRET/ISSUER、AUTH_APPLE_ID/SECRET。
| 变量 | 用途 | 获取 |
|---|---|---|
VERCEL_API_TOKEN |
加/验证自有域名,lib/vercel.ts |
vercel.com → Account Settings → Tokens |
VERCEL_PROJECT_ID |
同上 | 项目 Settings → General |
VERCEL_TEAM_ID 🟡 |
同上,仅 Team 账号需要;个人账号可不配(lib/vercel.ts 缺失则省略 teamId 参数) |
团队 Settings |
VERCEL_API_TOKEN/VERCEL_PROJECT_ID缺任意一个 → 用户「发布到自有域名」必失败,整个价值主张断在最后一步。
| 变量 | 用途 | 获取 |
|---|---|---|
PLATFORM_SUBDOMAIN_ROOT |
分配新子域的根域(如 zapbridge.site),app/api/domains/platform-subdomain/route.ts;同时用于 apex 重定向与「阻止用户手动认领」 |
子域池根域,需配通配 DNS 指向平台 |
PLATFORM_SUBDOMAIN_LEGACY_ROOTS |
逗号分隔的遗留根。已停止分配但存量子域仍在服务的旧根,lib/domains/subdomain.ts |
仅在更换子域池根域后才需要 |
未配
PLATFORM_SUBDOMAIN_ROOT→ 平台子域功能整体关闭(用户必须自带域名才能发布)。
⚠️ 遗留根必须继续列在PLATFORM_SUBDOMAIN_LEGACY_ROOTS里:通配 DNS 仍指向平台, 不列的话他人可手动添加competitor.<旧根>且 DNS 验证会真的通过,domains.domain的唯一约束会让存量子域再也无法被重新分配。
| 变量 | 用途 | 获取 |
|---|---|---|
BLOB_READ_WRITE_TOKEN |
编辑器图片上传,app/api/media(@vercel/blob 自动读取) |
在 Vercel 建一个 Blob Store 并 Connect 到项目,token 会自动注入 |
缺则
put()失败,新手做不出带图的页 → 激活率差。
| 变量 | 用途 | 说明 |
|---|---|---|
CRON_SECRET 🔴 |
守护 /api/cron/capi-flush(CAPI 兜底重发) |
随机串;Vercel Cron 命中时自动带 Authorization: Bearer |
CAPI_FAKE ⛔ |
测试不打真实平台 | 禁止进生产 |
vercel.json 已配 cron:0 0 * * *(每天 00:00 UTC)调 /api/cron/capi-flush。
cron 频率与 Vercel 套餐(2026-07-15 记录):该 cron 是失败 CAPI 事件的兜底重发(非核心页面功能,落地页展示/留资不依赖它)。原为
*/10 * * * *(每 10 分钟),但 Vercel Hobby 套餐 cron 只允许每天一次,*/10会导致部署直接失败(部署 check 链接指向 cron usage-and-pricing)。故降频为0 0 * * *:失败事件最长 24h 内补发,平台归因窗口内一般仍有效。若后续投放上量、需要更及时的转化回传,升级 Vercel Pro 后可恢复*/10 * * * *(Pro 无 cron 频率限制)。
通用适配器(lib/ai/client.ts),按下面任一方式配;缺则落地页 AI 生成不可用:
- 通用:
AI_PROVIDER+AI_API_KEY+AI_BASE_URL+AI_MODEL(+AI_JSON_MODE) - 各源 key:
OPENAI_API_KEY(+OPENAI_BASE_URL/OPENAI_MODEL)、DASHSCOPE_API_KEY、GEMINI_API_KEY - ⛔
AI_FAKE:测试桩,禁止进生产
生效收款渠道由超管在 /super-admin/settings 于 Dodo / Creem 间切换(见 lib/billing/provider.ts),实际启用哪个就配哪一组:
| 渠道 | 变量 | Webhook 地址 |
|---|---|---|
| Dodo | DODO_PAYMENTS_API_KEY、DODO_PAYMENTS_WEBHOOK_KEY、DODO_PAYMENTS_ENVIRONMENT、DODO_PRODUCT_STARTER/PRO/AGENCY、DODO_CREDITS_50、DODO_CREDITS_200 |
<域名>/api/webhooks/dodo |
| Creem | CREEM_API_KEY、CREEM_WEBHOOK_SECRET、CREEM_ENVIRONMENT、CREEM_PRODUCT_STARTER/PRO/AGENCY、CREEM_CREDITS_50、CREEM_CREDITS_200 |
<域名>/api/webhooks/creem |
pnpm check:env 会对两组分别做「要么全配、要么全不配」的半配检查。
早期的 LemonSqueezy 接入已废弃,相关
LEMONSQUEEZY_*/LS_*变量可从环境中移除。
| 变量 | 用途 |
|---|---|
RESEND_API_KEY / EMAIL_FROM |
邀请/通知邮件;缺则不发送仅记录错误 |
UNSPLASH_ACCESS_KEY |
编辑器配图搜索 |
NEXT_PUBLIC_APP_URL 🔴 |
客户端可见的应用地址 |
| 变量 | 用途 / 代码位置 | 获取 |
|---|---|---|
NEXT_PUBLIC_SENTRY_DSN |
启用 Sentry 错误上报(客户端 instrumentation-client.ts + 服务端兜底) |
sentry.io 建项目后的 DSN;缺则 Sentry 整体 no-op,不影响其他功能 |
SENTRY_DSN |
服务端专用 DSN(sentry.server/edge.config.ts),未配则回退用 NEXT_PUBLIC_SENTRY_DSN |
通常与上同一个 DSN |
SENTRY_ORG + SENTRY_PROJECT + SENTRY_AUTH_TOKEN |
构建期上传 source map(next.config.ts 三者齐备才套 withSentryConfig) |
sentry.io org/project slug 与 Auth Token |
- 只想要错误捕获:配
NEXT_PUBLIC_SENTRY_DSN即可,运行时立即生效,无需改构建。- 想要可读堆栈(source map):再配
SENTRY_ORG/PROJECT/AUTH_TOKEN,且需允许@sentry/cli的 build 脚本——把pnpm-workspace.yaml里allowBuilds['@sentry/cli']改为true(或pnpm approve-builds)后重装。CI 无这些密钥时走不套包装的干净路径,构建不受影响。- Speed Insights / Web Analytics 无需 env:部署到 Vercel 自动采集(
<SpeedInsights/>+<Analytics/>已挂在app/layout.tsx);三端全部页面覆盖。
AI_FAKE · CAPI_FAKE · DEV_USER_EMAIL(dev 免密登录开关)· DEBUG · HTTP_PROXY/HTTPS_PROXY(本地代理)。
另:
32d26f8 dev 账号固定 pro已进 main——确认其逻辑由NODE_ENV/DEV_USER_EMAIL守卫,生产不会把测试账号当 pro。
# 逐个添加(会交互式询问值与环境)
vercel env add DATABASE_URL production
vercel env add AUTH_SECRET production
# ... 其余同理
# 拉回本地做生产预检
vercel env pull .env.production.local
node --env-file=.env.production.local scripts/check-env.mjs --prod也可在 Dashboard → Project → Settings → Environment Variables 批量录入。Blob/集成类(BLOB_READ_WRITE_TOKEN)优先用 Vercel 集成连接,token 自动注入,不要手抄。
.env.local 注释里历史暴露过 Neon 串 / AUTH_SECRET / Google secret。上线前全部轮换:
- 重新生成
AUTH_SECRET - 轮换
AUTH_GOOGLE_SECRET - 轮换 Neon 数据库密码 → 更新
DATABASE_URL(_UNPOOLED) - 确认
.env.local不在版本库(.gitignore已含)
pnpm smoke <域名> 过了之后,用真实账号 + 真实域名走一遍:
-
curl -sS https://<域名>/api/health→ 返回{"status":"ok","db":"up",...}且 HTTP 200(DB 断则 503,可接 uptime 探针) - 注册 / Google 登录 → 进后台
- 新建落地页 → 编辑 → 上传一张图(验证 Blob)
- 绑定 + 验证一个自有域名(验证
VERCEL_*) - 发布 → 浏览器访问该域名根路径,页面正常
- 提交一次表单/留资 → 后台「线索」可见
- Meta 事件管理工具 / TikTok 后台看到 CAPI 回传事件
- (启用付费时)checkout → webhook → 套餐切换 → 门禁生效
维护:完成项直接勾选;新增/移除 env 时同步更新本表与
scripts/check-env.mjs的清单。