Skip to content

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 部署 (project readvocab-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/ vs app/admin/**),但仍是一次 infra 变更(新增部署 / DNS / 环境变量分配), 属用户的基础设施决策,不擅自执行。


三、与「下载渠道」阻断项的耦合(务必一并决策)

发版就绪过程中发现一个独立于拆分、但必须先解的对外阻断项

  • landing 下载按钮现指向 https://github.com/ttfishnet/reading-browser/releases/latest
  • 但 CI 镜像仓 ttfishnet/reading-browser私有仓(见 memory project_windows_release_pipeline)——外部用户无法访问私有仓 Release 的下载资产
  • 即「发版就绪」的最后一公里卡在:公开分发渠道未定

可选公开分发渠道:

  1. 公开 Release 专用仓(如 ttfishnet/readbrowser-releases),Actions 把产物推到那里;私有主仓不动。
  2. 把安装包上传到对象存储 / CDN(Vercel Blob / R2 / S3),landing 直链。
  3. 主仓/镜像仓转公开(不推荐——会连带暴露源码)。

这项决策与拆分天然配对:若拆出独立 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.tsxNEXT_PUBLIC_DOWNLOAD_URL(fallback 占位)。渠道定后设 env 即可,不改代码。
  • [x] OG/metadataBase 基址已 env 化:app/layout.tsxNEXT_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绝不设信号 ② 的核心——公开部署环境不含此密钥

基础设施侧(需用户账户操作,我无法代执行)

  1. [ ] 公开分发渠道(先解下载阻断项):建公开 Release 专用仓(如 ttfishnet/readbrowser-releases), 改 .github/workflows/release-windows.yml 把 .msi/-setup.exe 也推到该公开仓 (此步改 CI,属 RB 会话/根仓改动,不在 admin/)。私有主仓源码不暴露。
  2. [ ] Vercel 新建 project readbrowser-landing,接同仓、Root Directory=admin
  3. [ ] landing project 按上表配 env(不含 SUPABASE_SERVICE_ROLE_KEY)。
  4. [ ] 为 landing project 配 ignored build step(仅 app/page.tsx / app/layout.tsx / app/globals.css / app/opengraph-image.tsx / components/landing/** / components/ui/** 变更才重建)。
  5. [ ] DNS:正式域名 A/CNAME → landing project;admin 迁到子域(admin.<domain>)。
  6. [ ] 验证:两 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.tsxadmin/app/page.tsxredirect('/admin')admin/app/layout.tsx 元数据回归后台 (加 robots:{index:false})。admin tsc+build 通过。
  • [x] 文档:根 CLAUDE.md §2 结构 + §9 新增「landing 子项目」段;admin/CLAUDE.md 定位/结构/ commit scope 去 landing;本 plan + followups 更新。

剩余基础设施(需用户账户,我代执行不了)

  1. [ ] 公开分发渠道 / 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 完成)。
  2. [x] Vercel project readbrowser-landing 已建 + 部署上线(2026-07-19):Root Directory=landing、 Application Preset=Next.js、env NEXT_PUBLIC_DOWNLOAD_URL=公开仓 releases(未含 service_role)。 线上:https://readbrowser-landing.vercel.app (用户浏览器实测可打开)。
  3. [x] env 已配 NEXT_PUBLIC_DOWNLOAD_URLNEXT_PUBLIC_SITE_URL 待定域名后补(有 fallback)。
  4. [ ] DNS:正式域名 → landing project(顺便补 NEXT_PUBLIC_SITE_URL=该域名修正 OG 基址); admin 保留现 project(根 / 已 redirect 到 /admin),或迁子域 admin.<domain>
  5. [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-browser repo secret;dev.5 用手动 gh release 从产物补发)。公开仓首发时是空仓、需先 README 初始化才能 publish(建 tag 需 commit)。
  6. [x] 验证:landing 独立部署上线;admin project 不受影响(根已 redirect);landing 环境无 service_role。
  7. [ ] DNS/域名:绑正式域名到 landing project + 补 NEXT_PUBLIC_SITE_URL(同 §七.4)。
  8. [x] 下载 UX:Hero 单按钮直接下载(弃列表页两步);用户选方案 A——删 Vercel env NEXT_PUBLIC_DOWNLOAD_URL、直链由 hero.tsxDOWNLOAD_URL fallback 在代码里 own。

🔁 发版流程新增动作(方案 A 的代价,勿忘)

2026-07-19 起加 macOS: 下载直链移到 landing/components/landing/download.tsxTAG 常量 (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 天然只在各自目录变更时构建)。