主题
Lampio Landing — 项目指南
嵌套 CLAUDE.md,工作在
landing/下时自动加载。 2026-07-19 从admin/拆出为独立子项目(landing-split-plan.md方案 C)。
1. 定位
Lampio 的公开产品落地页(/)——对外发版后真实外部用户访问的介绍 + 下载页。
- 物理位置:
landing/,与admin/并列的独立 pnpm 项目(自己的 package.json / lockfile / node_modules,非 workspace 成员)。 - 纯静态:无 Supabase、无 Auth、无
service_role、无任何数据库调用。这是拆分的核心目的 ——公开站与持密钥的 admin 部署解耦(可用性 + 密钥爆炸半径)。
2. 技术栈
Next.js 16.2.3(App Router)· TypeScript strict · Tailwind CSS 4 + shadcn/ui(base-nova)· lucide-react · 动态 OG 图用 next/og · @vercel/analytics(访问统计,见 §3.2)。与 admin 同栈, 但依赖树是裁剪过的子集(无 supabase-js / recharts)。
3. 结构
landing/
├── proxy.ts ← i18n 路由代理(Next 16 取代 middleware.ts;无前缀请求按 Accept-Language 重定向 /zh|/en,默认 zh,手写解析零依赖)
├── app/
│ ├── [lang]/ ← 无库 i18n 段(lang ∈ {zh,en});[lang]/layout.tsx = 根 layout
│ │ ├── layout.tsx ← <html lang> 动态 + generateStaticParams + generateMetadata(per-locale title/OG)
│ │ ├── page.tsx ← 落地页;getDictionary(lang) 后传 dict slice 给各 section
│ │ └── opengraph-image.tsx ← 动态社交预览图(next/og,1200×630,per-locale 文案)
│ ├── robots.ts ← /robots.txt(force-static,全站可抓 + 指向 sitemap)
│ ├── sitemap.ts ← /sitemap.xml(localizedPaths × locales,每条自带全组 hreflang + x-default)
│ ├── globals.css ← Tailwind v4 + shadcn 主题
│ └── favicon.ico
├── components/
│ ├── landing/{hero,features,download,footer,lang-switcher}.tsx ← 组件收 dict slice;lang-switcher 中英切换(hero 右上)
│ └── ui/{button,card,separator}.tsx ← 从 admin 复制的副本(独立演进)
└── lib/
├── dictionaries.ts ← 无库 i18n 字典(zh/en 对象 + getDictionary;zh 为类型基准 Dictionary=typeof zh)+ `bcp47`(<html lang> 与 hreflang 共用)
├── legal.ts ← 隐私政策 / 服务条款中英长文 + `LAST_UPDATED`(也当法务页的 sitemap lastmod)
├── site.ts ← 站点级 SEO 单一真相源:siteUrl / localizedPaths / alternatesFor()
└── utils.ts ← cn()(copy)3.1 SEO(2026-08-13)
- 加页面 = 往
lib/site.ts的localizedPaths加一行,sitemap 自动铺满两种 locale; 再在该页generateMetadata里alternates: alternatesFor(lang, path)。 - 🔒 canonical/hreflang 只放页面级,禁止上提到
[lang]/layout.tsx——layout 的 metadata 会被 privacy/terms 原样继承,canonical 全指向/zh首页(比没有 canonical 更糟)。 x-default指向不带 locale 前缀的 URL(/、/privacy),由proxy.ts按 Accept-Language 跳转——正是 Google 说的「自动跳转的通用页」;这类重定向 URL 不进 sitemap 条目本身。- hreflang 码取
dictionaries.ts::bcp47,与<html lang>同一份:两处不一致时 Google 会整组忽略 hreflang。 lastModified:首页读site.ts::CONTENT_LAST_MODIFIED(改dictionaries.ts营销文案时手动更新), 法务页读legal.ts::LAST_UPDATED。刻意不用构建时间——每次部署都变会让爬虫直接无视该字段。
3.2 访问统计(2026-08-13)
@vercel/analytics 的 <Analytics /> 挂在 [lang]/layout.tsx 的 <body> 末尾。选它而非 Plausible/Cloudflare 的理由:脚本从本站域名下发(/_vercel/insights/*)→ 无 cookie、无跨站标识、 大陆可达性与站点本身同命;且 Vercel 本就是托管方 = 隐私政策不新增第三方点名对象。
- 🔒 它不读任何密钥,与「landing 无 Supabase / 无
service_role」隔离无关,别把它当破口。 - 与客户端 opt-in 遥测(
usage_events,Supabase)不同源、不合流——那是应用内学习行为,这里只有网页流量。 - 数据要真正开始进:需在 Vercel 项目 → Analytics 面板点一次 Enable(代码侧已就绪,未 enable 时脚本 no-op)。
- 隐私政策已如实披露(
lib/legal.ts中英各三处:适用范围 / 收集清单 / 第三方处理方 + 摘要); 再动统计口径就要同步改那几段并 bumpLAST_UPDATED。
i18n(2026-07-24):中英双语走 Next.js 官方「无库 i18n」范式——
app/[lang]/段 +lib/dictionaries.ts字典 +proxy.tsAccept-Language 重定向 +lang-switcher。/zh/en真实 URL(利于 SEO/分享/per-locale OG)。零新依赖; proxy 只做 locale 重定向、不触任何密钥,不破坏「无 Supabase/service_role」隔离("纯静态"约束是安全面,非字面零 edge)。 加语言 = 在locales加码 + 字典补一份;改文案 = 只动lib/dictionaries.ts(组件不含硬编码文案)。
components/ui/*+lib/utils.ts是 admin 的副本(全独立子项目的代价)。landing 极少改这些,与 admin 漂移可接受;真要改共享 UI 时两处各改一次。
4. 环境变量(Vercel)
| 变量 | 说明 |
|---|---|
NEXT_PUBLIC_SITE_URL | 正式域名,修正 OG/metadataBase 基址(域名 = lampio.app,已注册 2026-07-28;代码 fallback 同为 https://lampio.app) |
NEXT_PUBLIC_DOWNLOAD_URL | 下载按钮目标(公开 Release 渠道,fallback 占位) |
🔒 绝不设 SUPABASE_SERVICE_ROLE_KEY(landing 不需要,设了就毁了拆分的意义)。
5. 开发 / 验证
- 本地:
pnpm dev(默认 3000,可-p换端口避让 admin);/会按浏览器语言重定向到/zh或/en - 验证:
pnpm exec tsc --noEmit+pnpm build(无 RB dev-flow skill,非 Tauri)。build 后确认路由树含/zh/en两份 +/zh|en/opengraph-image+Proxy (Middleware) - ⚠️ 改路由/i18n 后若 tsc 报
.next/types/validator.ts引用旧文件,先rm -rf .next && pnpm build再生成类型再 tsc(Next 类型是 build 产物)
6. 发布
- Vercel 独立 project,Root Directory=
landing。 - 下载渠道 + 拆分收尾详见
docs/plans/archive/landing-split-plan.md。