主题
Landing Page 拆分计划(发版驱动评估)
物理位置:
~/reading-browser/docs/plans/landing-split-plan.md创建:2026-07-19 · 承接:admin 会话 P3(landing 发版就绪 + 拆分评估) 上游:admin-integration-followups.md§三 Q1 · 前置:landing 发版就绪已完成(本会话) 状态:代码侧已执行完毕(2026-07-19)。 用户选方案 C(彻底拆成独立子项目,一劳永逸), 已落地:landing/独立子项目建成、admin 侧 landing 移除、两项目各自 build 通过。 剩基础设施步骤(新建独立 Vercel project + DNS + 公开 Release 仓/CI)待用户账户操作,见 §七。 下载渠道:公开 Release 专用仓(用户已建空仓readbrowser-releases;CI 步骤草稿见 §七.CI)。
一、为什么现在评估
ReadBrowser 近期有对外发版计划(Windows 内测版,走 GitHub Actions 构建 → GitHub Release,见 RB memory project_windows_release_pipeline)。发版后:
- landing
/从「占位介绍页」变成真实外部用户访问的公开产品页。 - 该页当前与
/admin/*(需登录、持有service_role)同一个 Vercel 部署 (projectreadvocab-admin)。
这直接触碰 §三 Q1 预设的「该拆信号」,需重新判定。
二、信号复评(发版语境)
| 信号 | 原描述 | 发版后判定 |
|---|---|---|
| ① 独立域名 + 独立发布节奏 | landing 要自己的域名与发布节奏 | 部分触发。发版需要一个可对外挂的产品域名;landing 内容节奏(文案/下载/SEO)已开始独立于 admin 数据层演进。 |
② 公开站可用性不被 admin 部署耦合(防御性,不与带 service_role 的 deployment 同体) | — | 实质触发(最强)。① 可用性耦合:admin 侧一次坏部署 = 公开产品页一起挂(对外事故)。② 爆炸半径:公开营销站与 service_role 密钥同处一个运行时/环境,最小化原则要求分离。 |
| ③ landing 长成真营销站 | landing 从 stub 长成正式营销站 | 正在触发。本会话已加下载区 / OG 图 / SEO meta;后续大概率还要 changelog / 截图 / FAQ 等。 |
结论建议:倾向拆分。 ② 是主驱动——公开营销站不应与持有 service_role 的后台部署同体。但当前是内测版、受众有限,事故严重度尚不高,故: 计划就绪 + 由用户决定「立即执行」还是「随受众扩大 / 域名落定时执行」。
注意:拆分「很便宜」(路由组已隔离
app/page.tsx+components/landing/vsapp/admin/**),但仍是一次 infra 变更(新增部署 / DNS / 环境变量分配), 属用户的基础设施决策,不擅自执行。
三、与「下载渠道」阻断项的耦合(务必一并决策)
发版就绪过程中发现一个独立于拆分、但必须先解的对外阻断项:
- landing 下载按钮现指向
https://github.com/ttfishnet/reading-browser/releases/latest。 - 但 CI 镜像仓
ttfishnet/reading-browser是私有仓(见 memoryproject_windows_release_pipeline)——外部用户无法访问私有仓 Release 的下载资产。 - 即「发版就绪」的最后一公里卡在:公开分发渠道未定。
可选公开分发渠道:
- 建公开 Release 专用仓(如
ttfishnet/readbrowser-releases),Actions 把产物推到那里;私有主仓不动。 - 把安装包上传到对象存储 / CDN(Vercel Blob / R2 / S3),landing 直链。
- 主仓/镜像仓转公开(不推荐——会连带暴露源码)。
这项决策与拆分天然配对:若拆出独立 landing 部署 + 独立域名,公开分发渠道 (下载直链)也在同一批基础设施里一起落地最省事。
四、拆分方案对比
方案 A:同一 monorepo,新增独立 Vercel Project(推荐)
- 新建 Vercel project
readbrowser-landing,指向同一个 git 仓,但 Root Directory 仍admin、并通过 build filter / 独立 project 的 ignored build step 只在 landing 相关文件变更时重建。- ⚠️ 局限:同一
admin/代码库同时被两个 Vercel project 部署时,landing project 仍会打包整个 Next app(含 admin 路由)。要真正剥掉 admin bundle,需把 landing 抽成独立 Next app(见方案 C)或用 route-level 排除。
- ⚠️ 局限:同一
- service_role 边界:landing project 的环境变量不注入
SUPABASE_SERVICE_ROLE_KEY。 landing 不读任何 Supabase(纯静态),天然不需要。→ 公开部署环境里根本没有该密钥。 - 域名:landing project 绑正式域名(如
readbrowser.com),admin project 绑admin.readbrowser.com或保留*.vercel.app。 - 成本:低。风险:两 project 共享一份代码,admin 代码变更仍可能触发 landing 重建(可用 ignored build step 缓解)。
方案 B:同一 Vercel Project,多域名(最省事,隔离最弱)
- 不拆部署,只给现有 project 加一个营销域名指向
/。 - 不满足信号 ②:可用性与密钥仍同体。仅解决「域名」诉求。
- 仅当判定「暂不真拆、先上域名过渡」时选。
方案 C:landing 抽成独立 Next app(隔离最彻底,成本最高)
- 把
app/page.tsx+components/landing/+ landing 用到的components/ui/*抽到独立目录(如landing/)或独立仓,独立package.json、独立 Vercel project。 - service_role 边界:landing 部署产物完全不含 admin 代码/依赖,爆炸半径归零。
- 成本:需拆共享 UI 组件(shadcn
button/card/separator等)、独立依赖树、 两套 CI。solo dev 维护两套项目成本上升。 - 适合 landing 真长成大型营销站后再上。
推荐路径:现在选 方案 A(独立 project + 不注入 service_role + 正式域名), 满足信号 ②/① 的 80%,成本可控;待 landing 显著变大再演进到方案 C。
五、执行清单(方案 A)
代码侧(本会话已完成 ✅ —— 拆分执行时无需再改代码)
- [x] landing 下载 URL 改由环境变量注入:
components/landing/download.tsx读NEXT_PUBLIC_DOWNLOAD_URL(fallback 占位)。渠道定后设 env 即可,不改代码。 - [x] OG/metadataBase 基址已 env 化:
app/layout.tsx读NEXT_PUBLIC_SITE_URL(fallback 占位)。 - [x] landing 纯静态、不读 Supabase:landing project 天然无需
service_role,把该密钥 从 landing project env 剔除即满足信号 ②(代码层已确认无supabase-server依赖)。
landing project 所需环境变量(Vercel Dashboard 配,均无敏感项):
| 变量 | 值 | 说明 |
|---|---|---|
NEXT_PUBLIC_SITE_URL | 正式域名(如 https://readbrowser.com) | 修正 OG/metadataBase 基址 |
NEXT_PUBLIC_DOWNLOAD_URL | 公开分发渠道 Release 地址 | 下载按钮目标 |
SUPABASE_SERVICE_ROLE_KEY | 绝不设 | 信号 ② 的核心——公开部署环境不含此密钥 |
基础设施侧(需用户账户操作,我无法代执行)
- [ ] 公开分发渠道(先解下载阻断项):建公开 Release 专用仓(如
ttfishnet/readbrowser-releases), 改.github/workflows/release-windows.yml把 .msi/-setup.exe 也推到该公开仓 (此步改 CI,属 RB 会话/根仓改动,不在 admin/)。私有主仓源码不暴露。 - [ ] Vercel 新建 project
readbrowser-landing,接同仓、Root Directory=admin。 - [ ] landing project 按上表配 env(不含
SUPABASE_SERVICE_ROLE_KEY)。 - [ ] 为 landing project 配 ignored build step(仅
app/page.tsx/app/layout.tsx/app/globals.css/app/opengraph-image.tsx/components/landing/**/components/ui/**变更才重建)。 - [ ] DNS:正式域名 A/CNAME → landing project;admin 迁到子域(
admin.<domain>)。 - [ ] 验证:两 project 独立部署、admin 坏部署不影响 landing、landing 运行时环境无 service_role。
六、暂不执行的判据(历史——已被方案 C 覆盖)
用户 2026-07-19 选「一劳永逸」直接执行方案 C,本节判据作废,保留作决策留痕。
- 内测受众极小(个位数 tester)、事故半径可接受时,可延后。
- 触发点:受众显著扩大 / 正式域名落定 / landing 加入更多营销页面之一发生时执行本计划。
七、方案 C 执行记录(2026-07-19,代码侧完成)
用户选**方案 C(彻底独立子项目)**而非 A —— 理由:一劳永逸,避免以后在「A 半拆状态」反复纠结。
已完成(代码,两项目各自 build 通过)✅
- [x] 新建
~/reading-browser/landing/独立 pnpm 子项目(裁剪依赖:@base-ui/react/cva/clsx+tailwind-merge/lucide-react/ Tailwind v4;无 supabase-js / recharts)。 - [x] 迁入:
app/{layout,page,globals.css,opengraph-image,favicon}+components/landing/*+components/ui/{button,card,separator}+lib/utils.ts+ 全套 config(tsconfig/next/postcss/ components.json/eslint/.gitignore)。landing/CLAUDE.md已建。 - [x]
pnpm --dir landing install+tsc --noEmit+build通过;dev 实测/title 干净、 OG 图 200 image/png、#download+ Windows CTA 就位。 - [x] admin 侧移除:删
admin/components/landing/*+admin/app/opengraph-image.tsx;admin/app/page.tsx改redirect('/admin');admin/app/layout.tsx元数据回归后台 (加robots:{index:false})。admintsc+build通过。 - [x] 文档:根
CLAUDE.md§2 结构 + §9 新增「landing 子项目」段;admin/CLAUDE.md定位/结构/ commit scope 去 landing;本 plan + followups 更新。
剩余基础设施(需用户账户,我代执行不了)
- [ ] 公开分发渠道 / CI(下载阻断项):用户已建空公开仓
ttfishnet/readbrowser-releases。 CI 草稿(.github/workflows/release-windows.yml加 "Publish to public releases repo" 步, 用softprops/action-gh-release+repository:覆盖 +secrets.PUBLIC_RELEASE_TOKEN) 已写、待 commit。PAT secret 用户已配(步骤 2 完成)。 - [x] Vercel project
readbrowser-landing已建 + 部署上线(2026-07-19):Root Directory=landing、 Application Preset=Next.js、envNEXT_PUBLIC_DOWNLOAD_URL=公开仓 releases(未含 service_role)。 线上:https://readbrowser-landing.vercel.app (用户浏览器实测可打开)。 - [x] env 已配
NEXT_PUBLIC_DOWNLOAD_URL;NEXT_PUBLIC_SITE_URL待定域名后补(有 fallback)。 - [ ] DNS:正式域名 → landing project(顺便补
NEXT_PUBLIC_SITE_URL=该域名修正 OG 基址); admin 保留现 project(根/已 redirect 到/admin),或迁子域admin.<domain>。 - [x] 下载真链:首个 tag
v0.1.0-dev.5已发布到公开仓(含 .msi/-setup.exe,2026-07-19)。 CI 的 "Publish to public releases repo" 步首次失败(PUBLIC_RELEASE_TOKEN未落在 Actions 仓 → 已修正到ttfishnet/reading-browserrepo secret;dev.5 用手动gh release从产物补发)。公开仓首发时是空仓、需先 README 初始化才能 publish(建 tag 需 commit)。 - [x] 验证:landing 独立部署上线;admin project 不受影响(根已 redirect);landing 环境无 service_role。
- [ ] DNS/域名:绑正式域名到 landing project + 补
NEXT_PUBLIC_SITE_URL(同 §七.4)。 - [x] 下载 UX:Hero 单按钮直接下载(弃列表页两步);用户选方案 A——删 Vercel env
NEXT_PUBLIC_DOWNLOAD_URL、直链由hero.tsx的DOWNLOAD_URLfallback 在代码里 own。
🔁 发版流程新增动作(方案 A 的代价,勿忘)
2026-07-19 起加 macOS: 下载直链移到
landing/components/landing/download.tsx的TAG常量 (3 条:Win-setup.exe/ mac_aarch64.dmg/ mac_x64.dmg)。 每次发新内测版,除了走/release,还要把 download.tsx 顶部的TAG = "v0.1.0-dev.N"改成新版本号并 push (landing 自动重部署;只改一个 TAG 常量即三条直链齐更)。资产名随 app version(tauri.conf 的0.1.0) 变化时也要同步。转正式版(非 prerelease)后改用.../releases/latest/download/<asset>永久直链,此动作退役。
方案 C 后 §四/§五/§六「方案 A(旧)」段仅存档;实际以本节为准。ignored build step 不再需要 (两项目物理分目录,Vercel 各自 Root Directory 天然只在各自目录变更时构建)。