16 · 部署与上线交付
为课程终态补上 `api/health` 和上线验收基线,把“本地能跑”收口成“可部署、可排查、可交付”。
课时资源
- 查看本课源码
- 建议先读
app/api/health/route.ts - 建议先读
app/database/supabase.ts - 建议先读
app/contexts/AuthContext.tsx - 建议先读
app/page.tsx
一、学习目标
本课所在阶段:第七阶段 · 交付上线。
学完这一课后,你应该能够:
- 解释为什么“本地能跑”和“可上线交付”是两种不同的能力
- 看懂
app/api/health/route.ts为什么只检查环境变量存在性,而不是替你诊断所有线上问题 - 明确这节课真正关心的四个关键环境变量分别服务于哪些链路
- 说清
app/database/supabase.ts、app/contexts/AuthContext.tsx与api/health之间的关系 - 给自己建立一条最小上线验收顺序:
pnpm build、api/health、登录、聊天、图片工具、Canvas
二、问题背景
到了第 15 课,这套课程项目已经有了比较完整的产品能力:
- 登录和会话边界
- Supabase 持久化
- 工具调用和图片生成
- Canvas 工作区、保存和分享
但这些能力几乎都默认了一个前提:你的本地环境已经配好了。
一旦进入部署语境,问题就会立刻换一个形状:
- 现在到底缺的是哪一个环境变量?
- 是模型 key 没配,还是 Supabase 地址根本没进运行环境?
- 登录失败、聊天失败、图片失败,到底是业务代码问题,还是部署配置问题?
如果没有最后这一课,你很容易把“部署配置没齐”误判成“业务逻辑写坏了”,排查顺序也会越走越乱。
所以第 16 课的重点不是再补一个新产品能力,而是给整套课程一个更像交付版本的收口:
- 提供最小可用的健康检查入口
- 明确当前终态依赖的关键环境变量
- 建立上线后的正确验收顺序
三、核心概念
1. 第 16 课唯一真正新增的源码是 app/api/health/route.ts
如果你把第 15 课和第 16 课的教学源码做目录对比,会发现一个很重要的事实:
- 真正新增的文件只有
app/api/health/route.ts app/agent/chatbot.ts、app/services/chat.service.ts、app/components/canvas/CanvasPanel.tsx都没有继续升级主链路- 其余改动主要是页面语义、交付态标题和预览样式的收口
这件事很重要,因为它告诉你:部署课解决的不是“再加功能”,而是“给已经完成的功能补一个可交付的入口”。
2. api/health 是部署就绪性检查,不是全链路诊断器
当前实现只做一件事:检查四个关键环境变量有没有值。
它不会替你确认:
- 值是不是填对了
- Supabase 是否真的可连通
- OAuth 回调地址、RLS、Storage 是否都配置正确
- Google 或 OpenAI 的权限、配额是否正常
所以这条接口回答的问题很明确:
“至少最关键的配置项有没有缺失?”
3. 服务端环境变量才是部署真相来源
这一课最值得你建立的工程直觉是:
- 前端页面里看到的失败现象,通常只是后果,不是根因
- 真正可靠的部署入口,应该先看服务端对环境变量的判断
比如当前代码里:
app/database/supabase.ts缺少NEXT_PUBLIC_SUPABASE_URL或NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY时会直接抛错app/contexts/AuthContext.tsx的 OAuth 登录也依赖同一组 Supabase 公开环境变量GOOGLE_API_KEY和OPENAI_API_KEY分别影响图片生成与主聊天模型
把这四项集中放进 api/health,本质上是在建立一个最小但统一的服务端排查入口。
4. 部署课的边界是“验收顺序”,不是“一键排障系统”
这节课真正想教你的,不是“所有线上问题都能靠一个接口搞定”,而是正确的排查起点:
- 先看
pnpm build是否通过 - 再看环境变量是否齐全
- 再验证登录、聊天、图片工具、Canvas 等业务链路
只要排查顺序正确,你就不会把部署问题和业务问题混成一团。
5. 相对第 15 课的真实增量
| 维度 | 第 15 课 | 第 16 课 |
|---|---|---|
| 核心目标 | 升级 Canvas 工作区 | 建立部署检查和交付验收起点 |
| 新增源码 | artifact 保存、分享、恢复 | app/api/health/route.ts |
| 页面语义 | Custom Rendering | Deploy & Ship |
| 主页主链路 | 聊天 + Canvas 工作区 | 聊天 + Canvas 工作区保持不变 |
| 预览观感 | 开发态工作区预览 | 更接近交付态的深色预览背景 |
不要低估这种“只加一个健康检查入口”的变化。对教学项目来说,它刚好把“会写功能”和“会交付上线”分成了两个层次。
四、关键文件
这是本课唯一真正新增的源码文件。它返回 ok、checks 和 timestamp,是部署排查的第一站。
这里会直接读取 NEXT_PUBLIC_SUPABASE_URL 和 NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY。这也是为什么 api/health 必须先检查这两项。
OAuth 登录链路会在客户端动态创建 Supabase 客户端,同样依赖公开环境变量。
这里最值得注意的反而是“没改什么”:聊天主界面和 Canvas 工作区都基本沿用上一课,只是交付态文案和本地缓存 key 切到了 lesson 16。
它把页面头部改成 Lesson 16 / Deploy & Ship,提醒你这一课的关注点已经从功能扩展切换成上线交付。
app/components/canvas/CodePreviewPanel.tsx
虽然它不是健康检查接口,但它把 artifact 预览换成更接近交付态的深色舞台和留白,让成品观感更完整。
这个文件的主链路没有重写,只是把 RLS 报错提示改成指向 lesson 16 的 supabase-schema.sql,说明本课是在前一课的产品能力上做收口。
这里保留了标准的 dev / build / start / lint 脚本,表明课程终态就是一套标准 Next.js 项目部署路径。
五、整体流程
这张图最关键的信息是:上线验收应该从服务端入口 api/health 开始,而不是先盲目点页面功能。
六、运行过程
1. 先用 pnpm build 确认项目本身可以被构建
这一课没有引入额外部署脚本,package.json 依然只有标准的:
pnpm devpnpm buildpnpm startpnpm lint
这意味着你的第一道门槛非常清楚:先确认项目能被标准 Next.js 构建流程接受。
2. 把四个关键环境变量映射到部署环境
app/api/health/route.ts 当前关心的四项分别是:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEYGOOGLE_API_KEYOPENAI_API_KEY
这四项刚好覆盖了当前课程终态最核心的几条主线:
- Supabase 地址和公开 key
- 主聊天模型
- 图片生成工具
3. 部署后先请求 api/health
这一步的价值不在于“它能查出所有问题”,而在于它能让你立刻知道:
- 哪些关键变量已经存在
- 当前环境是否至少具备最小运行条件
如果这一步就失败,就没有必要先去怀疑聊天页或工作区本身。
4. 再回头验证真正的业务链路
当 api/health 返回 ok: true 后,再按顺序验证:
- 登录和会话恢复
- 普通聊天
- 工具调用
- 图片生成
- Canvas 工作区和分享页
这个顺序的意义在于:先排配置,再排业务,避免排查路径倒着走。
5. app/page.tsx 基本沿用上一课,说明部署课没有再改主产品心智
第 16 课最值得你记住的一点,不是“改了什么”,而是“没改什么”:
- 主页仍然保留聊天 + Canvas 工作区
- 仍然会按
threadId拉取 artifact - 仍然通过
ResizablePanel管理右侧工作区
这说明部署课不会继续给你增加新的产品复杂度,它做的是收口,而不是加戏。
6. 交付态视觉收口只是辅助,不是本课主线
app/components/ChatHeader.tsx 和 app/components/canvas/CodePreviewPanel.tsx 的修改,主要是把成品观感往“Ready to Ship”方向推了一步。
这类改动有意义,但你要分清主次:
- 真正新增的交付能力是
api/health - 视觉收口只是让这套课程终态看起来更像最终产品
七、关键代码解析
关键代码 1:app/api/health/route.ts 给部署建立最小排查入口
function hasEnv(name: string) {
return typeof process.env[name] === 'string' && process.env[name]!.length > 0;
}
export async function GET() {
const checks = {
supabaseUrl: hasEnv('NEXT_PUBLIC_SUPABASE_URL'),
supabaseAnonKey: hasEnv('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY'),
googleApiKey: hasEnv('GOOGLE_API_KEY'),
openaiApiKey: hasEnv('OPENAI_API_KEY'),
};
const ready = Object.values(checks).every(Boolean);
return NextResponse.json({
ok: ready,
checks,
timestamp: new Date().toISOString(),
});
}代码解析:
- 这段代码的目标非常克制,只检查“有没有值”,不检查“值是不是对”。
- 返回结构也很简单,只有
ok、checks和timestamp,非常适合作为第一站排查入口。 - 如果没有这条接口,部署问题往往会在登录失败或聊天失败时才暴露,排查起点会更混乱。
关键代码 2:app/database/supabase.ts 说明为什么 Supabase 环境变量必须先检查
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY;
if (!supabaseUrl || !supabaseAnonKey) {
throw new Error(
'缺少 Supabase 环境变量,请在 .env 中设置 NEXT_PUBLIC_SUPABASE_URL 和 NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY。'
);
}代码解析:
- 这段代码说明:Supabase 相关环境变量不是“可选优化项”,而是启动条件。
- 一旦缺失,数据库客户端初始化阶段就会直接失败,后面的认证、持久化和 artifact 能力都会跟着断。
- 也正因为如此,
api/health必须优先把它们单独列出来。
关键代码 3:app/contexts/AuthContext.tsx 说明 OAuth 链路为什么同样依赖公开环境变量
const { createClient } = await import('@supabase/supabase-js');
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY!;
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
flowType: 'implicit',
},
});代码解析:
- GitHub OAuth 登录会在客户端动态创建 Supabase 客户端,这一步同样需要公开环境变量。
- 这就是为什么某些部署问题看起来像“登录按钮坏了”,其实根因仍然是环境变量缺失。
- 把这条链路和
api/health放在一起理解,你才会真正知道部署排查应该从哪里开始。
八、常见问题
1. 为什么 api/health 返回 ok: true,页面还是可能出错?
因为它只检查“变量是否存在”,不会检查值是否正确,也不会替你验证 OAuth 回调、RLS、第三方权限和网络连通性。
2. 为什么要在服务端检查 NEXT_PUBLIC_* 变量?
因为这些值虽然会暴露给客户端,但服务端模块同样直接依赖它们。部署时最可靠的真相来源仍然是服务端运行环境。
3. 这一课是不是应该新增部署脚本或监控面板?
当前课程没有走到那一步。本课只建立最小交付基线,重点是把验收顺序和排查入口先搭起来。
4. 为什么第 16 课几乎不碰聊天主链路?
因为前 15 课已经把产品能力搭完了。部署课的职责是收口,不是再加功能。
九、练习题
- 把
api/health的返回值扩展成missing字段,直接列出缺失的环境变量名称。 - 给
api/health增加一个只在开发或管理员环境可见的保护条件,思考生产环境是否应该公开这条接口。 - 写一个简单的 smoke check 脚本,先请求
api/health,再按顺序验证登录、聊天、图片和 Canvas。
十、总结
第 16 课真正补上的不是一个新产品功能,而是一套交付时必须有的排查起点。
你现在应该已经形成这样的上线思路:
- 先确认项目能构建
- 再确认关键环境变量齐全
- 再按顺序验证登录、聊天、工具、图片和 Canvas
只要这条顺序建立起来,整套课程就从“本地做得出来”真正迈到了“可以部署、可以排查、可以交付”。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。