第七阶段 · 交付上线

16 · 部署与上线交付

为课程终态补上 `api/health` 和上线验收基线,把“本地能跑”收口成“可部署、可排查、可交付”。

课时资源

一、学习目标

本课所在阶段:第七阶段 · 交付上线

学完这一课后,你应该能够:

  • 解释为什么“本地能跑”和“可上线交付”是两种不同的能力
  • 看懂 app/api/health/route.ts 为什么只检查环境变量存在性,而不是替你诊断所有线上问题
  • 明确这节课真正关心的四个关键环境变量分别服务于哪些链路
  • 说清 app/database/supabase.tsapp/contexts/AuthContext.tsxapi/health 之间的关系
  • 给自己建立一条最小上线验收顺序:pnpm buildapi/health、登录、聊天、图片工具、Canvas

二、问题背景

到了第 15 课,这套课程项目已经有了比较完整的产品能力:

  1. 登录和会话边界
  2. Supabase 持久化
  3. 工具调用和图片生成
  4. Canvas 工作区、保存和分享

但这些能力几乎都默认了一个前提:你的本地环境已经配好了。

一旦进入部署语境,问题就会立刻换一个形状:

  1. 现在到底缺的是哪一个环境变量?
  2. 是模型 key 没配,还是 Supabase 地址根本没进运行环境?
  3. 登录失败、聊天失败、图片失败,到底是业务代码问题,还是部署配置问题?

如果没有最后这一课,你很容易把“部署配置没齐”误判成“业务逻辑写坏了”,排查顺序也会越走越乱。

所以第 16 课的重点不是再补一个新产品能力,而是给整套课程一个更像交付版本的收口:

  1. 提供最小可用的健康检查入口
  2. 明确当前终态依赖的关键环境变量
  3. 建立上线后的正确验收顺序

三、核心概念

1. 第 16 课唯一真正新增的源码是 app/api/health/route.ts

如果你把第 15 课和第 16 课的教学源码做目录对比,会发现一个很重要的事实:

  1. 真正新增的文件只有 app/api/health/route.ts
  2. app/agent/chatbot.tsapp/services/chat.service.tsapp/components/canvas/CanvasPanel.tsx 都没有继续升级主链路
  3. 其余改动主要是页面语义、交付态标题和预览样式的收口

这件事很重要,因为它告诉你:部署课解决的不是“再加功能”,而是“给已经完成的功能补一个可交付的入口”。

2. api/health 是部署就绪性检查,不是全链路诊断器

当前实现只做一件事:检查四个关键环境变量有没有值。

它不会替你确认:

  1. 值是不是填对了
  2. Supabase 是否真的可连通
  3. OAuth 回调地址、RLS、Storage 是否都配置正确
  4. Google 或 OpenAI 的权限、配额是否正常

所以这条接口回答的问题很明确:

“至少最关键的配置项有没有缺失?”

3. 服务端环境变量才是部署真相来源

这一课最值得你建立的工程直觉是:

  1. 前端页面里看到的失败现象,通常只是后果,不是根因
  2. 真正可靠的部署入口,应该先看服务端对环境变量的判断

比如当前代码里:

  1. app/database/supabase.ts 缺少 NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY 时会直接抛错
  2. app/contexts/AuthContext.tsx 的 OAuth 登录也依赖同一组 Supabase 公开环境变量
  3. GOOGLE_API_KEYOPENAI_API_KEY 分别影响图片生成与主聊天模型

把这四项集中放进 api/health,本质上是在建立一个最小但统一的服务端排查入口。

4. 部署课的边界是“验收顺序”,不是“一键排障系统”

这节课真正想教你的,不是“所有线上问题都能靠一个接口搞定”,而是正确的排查起点:

  1. 先看 pnpm build 是否通过
  2. 再看环境变量是否齐全
  3. 再验证登录、聊天、图片工具、Canvas 等业务链路

只要排查顺序正确,你就不会把部署问题和业务问题混成一团。

5. 相对第 15 课的真实增量

维度第 15 课第 16 课
核心目标升级 Canvas 工作区建立部署检查和交付验收起点
新增源码artifact 保存、分享、恢复app/api/health/route.ts
页面语义Custom RenderingDeploy & Ship
主页主链路聊天 + Canvas 工作区聊天 + Canvas 工作区保持不变
预览观感开发态工作区预览更接近交付态的深色预览背景

不要低估这种“只加一个健康检查入口”的变化。对教学项目来说,它刚好把“会写功能”和“会交付上线”分成了两个层次。

四、关键文件

app/api/health/route.ts

这是本课唯一真正新增的源码文件。它返回 okcheckstimestamp,是部署排查的第一站。

app/database/supabase.ts

这里会直接读取 NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY。这也是为什么 api/health 必须先检查这两项。

app/contexts/AuthContext.tsx

OAuth 登录链路会在客户端动态创建 Supabase 客户端,同样依赖公开环境变量。

app/page.tsx

这里最值得注意的反而是“没改什么”:聊天主界面和 Canvas 工作区都基本沿用上一课,只是交付态文案和本地缓存 key 切到了 lesson 16。

app/components/ChatHeader.tsx

它把页面头部改成 Lesson 16 / Deploy & Ship,提醒你这一课的关注点已经从功能扩展切换成上线交付。

app/components/canvas/CodePreviewPanel.tsx

虽然它不是健康检查接口,但它把 artifact 预览换成更接近交付态的深色舞台和留白,让成品观感更完整。

app/api/artifacts/route.ts

这个文件的主链路没有重写,只是把 RLS 报错提示改成指向 lesson 16 的 supabase-schema.sql,说明本课是在前一课的产品能力上做收口。

package.json

这里保留了标准的 dev / build / start / lint 脚本,表明课程终态就是一套标准 Next.js 项目部署路径。

五、整体流程

这张图最关键的信息是:上线验收应该从服务端入口 api/health 开始,而不是先盲目点页面功能。

六、运行过程

1. 先用 pnpm build 确认项目本身可以被构建

这一课没有引入额外部署脚本,package.json 依然只有标准的:

  1. pnpm dev
  2. pnpm build
  3. pnpm start
  4. pnpm lint

这意味着你的第一道门槛非常清楚:先确认项目能被标准 Next.js 构建流程接受。

2. 把四个关键环境变量映射到部署环境

app/api/health/route.ts 当前关心的四项分别是:

  1. NEXT_PUBLIC_SUPABASE_URL
  2. NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY
  3. GOOGLE_API_KEY
  4. OPENAI_API_KEY

这四项刚好覆盖了当前课程终态最核心的几条主线:

  1. Supabase 地址和公开 key
  2. 主聊天模型
  3. 图片生成工具

3. 部署后先请求 api/health

这一步的价值不在于“它能查出所有问题”,而在于它能让你立刻知道:

  1. 哪些关键变量已经存在
  2. 当前环境是否至少具备最小运行条件

如果这一步就失败,就没有必要先去怀疑聊天页或工作区本身。

4. 再回头验证真正的业务链路

api/health 返回 ok: true 后,再按顺序验证:

  1. 登录和会话恢复
  2. 普通聊天
  3. 工具调用
  4. 图片生成
  5. Canvas 工作区和分享页

这个顺序的意义在于:先排配置,再排业务,避免排查路径倒着走。

5. app/page.tsx 基本沿用上一课,说明部署课没有再改主产品心智

第 16 课最值得你记住的一点,不是“改了什么”,而是“没改什么”:

  1. 主页仍然保留聊天 + Canvas 工作区
  2. 仍然会按 threadId 拉取 artifact
  3. 仍然通过 ResizablePanel 管理右侧工作区

这说明部署课不会继续给你增加新的产品复杂度,它做的是收口,而不是加戏。

6. 交付态视觉收口只是辅助,不是本课主线

app/components/ChatHeader.tsxapp/components/canvas/CodePreviewPanel.tsx 的修改,主要是把成品观感往“Ready to Ship”方向推了一步。

这类改动有意义,但你要分清主次:

  1. 真正新增的交付能力是 api/health
  2. 视觉收口只是让这套课程终态看起来更像最终产品

七、关键代码解析

关键代码 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(),
  });
}

代码解析:

  1. 这段代码的目标非常克制,只检查“有没有值”,不检查“值是不是对”。
  2. 返回结构也很简单,只有 okcheckstimestamp,非常适合作为第一站排查入口。
  3. 如果没有这条接口,部署问题往往会在登录失败或聊天失败时才暴露,排查起点会更混乱。

关键代码 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。'
  );
}

代码解析:

  1. 这段代码说明:Supabase 相关环境变量不是“可选优化项”,而是启动条件。
  2. 一旦缺失,数据库客户端初始化阶段就会直接失败,后面的认证、持久化和 artifact 能力都会跟着断。
  3. 也正因为如此,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',
  },
});

代码解析:

  1. GitHub OAuth 登录会在客户端动态创建 Supabase 客户端,这一步同样需要公开环境变量。
  2. 这就是为什么某些部署问题看起来像“登录按钮坏了”,其实根因仍然是环境变量缺失。
  3. 把这条链路和 api/health 放在一起理解,你才会真正知道部署排查应该从哪里开始。

八、常见问题

1. 为什么 api/health 返回 ok: true,页面还是可能出错?

因为它只检查“变量是否存在”,不会检查值是否正确,也不会替你验证 OAuth 回调、RLS、第三方权限和网络连通性。

2. 为什么要在服务端检查 NEXT_PUBLIC_* 变量?

因为这些值虽然会暴露给客户端,但服务端模块同样直接依赖它们。部署时最可靠的真相来源仍然是服务端运行环境。

3. 这一课是不是应该新增部署脚本或监控面板?

当前课程没有走到那一步。本课只建立最小交付基线,重点是把验收顺序和排查入口先搭起来。

4. 为什么第 16 课几乎不碰聊天主链路?

因为前 15 课已经把产品能力搭完了。部署课的职责是收口,不是再加功能。

九、练习题

  1. api/health 的返回值扩展成 missing 字段,直接列出缺失的环境变量名称。
  2. api/health 增加一个只在开发或管理员环境可见的保护条件,思考生产环境是否应该公开这条接口。
  3. 写一个简单的 smoke check 脚本,先请求 api/health,再按顺序验证登录、聊天、图片和 Canvas。

十、总结

第 16 课真正补上的不是一个新产品功能,而是一套交付时必须有的排查起点。

你现在应该已经形成这样的上线思路:

  1. 先确认项目能构建
  2. 再确认关键环境变量齐全
  3. 再按顺序验证登录、聊天、工具、图片和 Canvas

只要这条顺序建立起来,整套课程就从“本地做得出来”真正迈到了“可以部署、可以排查、可以交付”。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page