Files
official-site-base/docs/specs/2026-07-23-official-site-cms-design.md
T

14 KiB
Raw Blame History

官网 + 轻量自助 CMS 白标基座 设计文档

产品线:一人公司开源项目选型清单 §5「建站(官网)」升级方案(产品线 E) 日期2026-07-23 状态:设计已确认,待写实施计划 关联:升级自 一人公司开源项目选型清单 §5 / §9-E


一、背景与目标

1.1 原方案的三个缺口

一人公司选型清单中,官网搭建(产品线 E)原方案是「Astro + Astro Wind」纯 SSG,存在三个缺口:

  1. 基座复用没落地 —— 文档第③条选型原则「一套基座吃多客户」在官网线未体现,每次都 fork 第三方模板,变成一锤子买卖。
  2. 缺「官网 + 自助 CMS」混合方案 —— 纯 SSG 客户改内容只能走你,内容代维护变成长期负担,吃掉利润。
  3. 未覆盖国内部署 —— 文档建议的 Vercel / Cloudflare Pages 在国内访问慢且常被墙,国内客户官网必须备案 + 国内 CDN。

1.2 目标

打造一套可复用的官网白标基座

  • 客户在可视化后台自助改内容(公司 / 产品 / 新闻 / 案例 / 招聘 / 下载 / 留言)
  • 客户侧零服务器 —— 访客访问的是 OSS + CDN 纯静态站
  • 一套基座复制给多客户 —— 换设计 token + 换内容即可交付新客户
  • 国内合规上线 —— ICP 备案 + 阿里云 OSS/CDN

1.3 非目标(YAGNI 边界)

  • 多语言 i18n(除非客户明确付费要求)
  • 电商 / 支付(那是产品线 A,CRMEB)
  • 会员 / 工单 / 审批(那是产品线 B,若依 / 低代码)
  • 富后台 / 多角色 CMS(预留 Payload 适配器接口,遇到高预算客户再加,不在首期实现)

二、已确认的关键决策

# 决策点 结论
1 定位 白标基座 + 首客户驱动(先跑通一个真实客户,再沉淀复用资产)
2 Git 后端 + CI 自建一台 Gitea(含 Gitea Actions),所有客户共用,多租户
3 内容板块 company / products / news / cases / pages + jobs(招聘)/ downloads(下载)/ messages(留言表单)
4 云厂商 阿里云OSS + CDN + 域名 + ICP 备案)

三、技术栈选型(License 全部原文核实)

选型 License 核实来源 选型理由
官网框架 AstroSSG MIT withastro/astro LICENSE 零运行时、SEO 满分、Content Collections 类型安全
官网主题 Astro Wind 起步,逐步沉淀自有主题 MIT onwidget/astrowind LICENSE 半天换皮;基座化后替换为自有 token 主题
自助 CMS Sveltia CMS MIT sveltia/sveltia-cms LICENSE.txt + 官方 FAQ Decap 的现代替代、原生支持 Gitea、活跃;Keystatic 锁 GitHub 不适合国内
Git 后端 + CI 自建 Gitea+ Gitea Actions MIT go-gitea/gitea LICENSE 单二进制极轻、被 Sveltia 原生支持、一台服务多客户、完全自控
表单后端 自研轻量微服务 自研(MIT 复用 Gitea 那台轻量云,零额外服务器
对象存储 + CDN 阿里云 OSS + CDN 商业 国内访问快、抗刷、备案友好
域名 / 备案 阿里云域名 + ICP 备案 商业 国内合规必须

技术验证结论(已查实)Sveltia CMS / Decap CMS 都不原生支持 Gitee(要走自建 git-gateway + OAuth 代理,复杂且脆);GitHub 国内 OAuth 不稳。Gitea 是两大 CMS 都原生支持的国内可用后端,且自带 Actions CI,故选自建 Gitea。

全部为 🟢 MIT / 商业服务,无 AGPL / GPL / BSL 传染风险,白标转售安全。


四、整体架构

  ① 客户编辑                ② 构建发布                 ③ 访客访问
 ┌────────────┐  提交    ┌─────────────────┐  同步   ┌──────────────┐
 │  Sveltia   │────────▶│  自建 Gitea     │────────▶│  阿里云      │
 │  /admin    │         │  + Gitea Actions │        │  OSS + CDN   │
 │  (可视化)  │◀────────│   (构建 Astro)   │        │  (纯静态)    │
 └────────────┘  OAuth  └─────────────────┘         └──────────────┘
        │                       │
        │              ┌────────┴────────┐
        └──────────────┤  一台轻量云     │
            表单提交   │  ① Gitea + CI   │
                       │  ② 表单微服务   │  ← 服务【所有客户】
                       │  ③ SQLite 留底  │     多租户共用
                       └─────────────────┘

三层职责

  • 编辑层:客户在 https://<客户域名>/adminSveltia CMS 单页应用)可视化编辑,OAuth 登录自建 Gitea。
  • 构建 / 存储层:自建 Gitea(一台轻量云)。客户提交 → Gitea Actions 自动构建 Astro → ossutil 同步到该客户的 OSS bucket → 刷新 CDN。表单微服务也跑在这台机上。
  • 访客层:阿里云 OSS 托管静态站 + CDN 加速,访客全程不碰服务器,快、稳、抗刷。

五、内容模型(Astro Content Collections

每个集合配 Zod schema 校验,客户在 Sveltia 填错会自动报错。

集合 类型 字段 说明
company 单例 name / intro / logo / phone / email / address / icp 全站公共信息(页头页脚)
products 集合 title / cover / summary / body / category / sort 产品或服务
news 集合 title / date / category / cover / body 新闻动态(带分页 + RSS
cases 集合 client / industry / cover / result / body 客户案例
pages 集合 slug / title / body / showInNav 关于我们、联系等静态页
jobs 集合 title / location / department / requirement / body 招聘岗位
downloads 集合 name / description / fileUrl / category / size 下载资源(文件存 OSS,见 §七)
messages 表单 name / phone / email / message / createdAt 留言(见 §六,由表单微服务写入)

messages 不由客户手动编辑,而是由访客提交的留言表单经微服务写入(通过 Gitea API 落成 Markdown 文件到该集合),客户在 /admin 查看,同时收到邮件通知。


六、留言表单方案(messages

纯静态站(OSS + CDN)无法处理后端逻辑,故复用 Gitea 那台轻量云,跑一个自研的极轻表单微服务。

6.1 技术栈

  • 运行时Node.js + Hono(或 Express
  • 写内容:通过 Gitea API 把留言写成 Markdown 提交到 messages 集合(Git 即留底,无需额外数据库)
  • 邮件nodemailer + 阿里云邮件推送(DirectMailSMTP,或腾讯企业邮 SMTP
  • 失败兜底:Gitea 写失败时记本地日志文件,保证不丢留言
  • 部署systemd 或 docker,跑在 Gitea 同机
  • 凭据:微服务持有一个专用 Gitea token,仅对该客户仓库有写权限(最小权限)

6.2 处理流程

访客填表 → POST https://forms.<你的服务域名>/api/submit
         → ① 蜜罐字段检查(机器人会填隐藏字段 → 丢弃)
         → ② IP 限流(每 IP 每分钟 ≤5 次)
         → ③ 字段校验(Zod)
         → ④ 经 Gitea API 写入 `messages` 集合(Markdown,客户在 /admin 查看)
         → ⑤ SMTP 发邮件通知客户(含留言内容)
         → 返回成功页

6.3 多租户

一个微服务服务所有客户:配置文件映射 form_id → { 客户邮箱, 跳转页 }。新客户加一行配置即可。前端表单的 action 带上该客户的 form_id

6.4 防垃圾

  • 蜜罐字段(必填陷阱)
  • IP 限流
  • 可选:阿里云验证码 / hCaptcha(垃圾严重时再加,YAGNI

七、下载文件方案(downloads

下载文件(PDF 手册、驱动、视频)不进 Git 仓库(大文件不友好),统一存 OSS

  • 文件手动或脚本上传到该客户 OSS bucket 的 /downloads/ 目录
  • downloads 内容集合只记录元信息:name / description / fileUrlOSS URL/ category / size
  • 客户在 Sveltia 编辑下载项的描述,文件本身由你或客户用 OSS 工具上传(Sveltia 的媒体库只给图片用,不混用)

小文件(<2MB 的 PDF)可直接 Sveltia 上传到 Gitea 仓库;大文件走 OSS。以 OSS 为主,避免仓库膨胀。


八、白标复用(新客户上线流程)

基座 = 一个 Astro 主题仓库(含组件区段 + Content Collections schema + Sveltia 配置模板 + Gitea Action 工作流)。

新客户交付 = 7 步:

  1. fork 基座 → 在自建 Gitea 建新仓库(独立组织/权限隔离)
  2. 改设计 token → 改一个 CSS 变量文件(主色 / 字体 / 圆角),整站换皮
  3. 配 Sveltia config.ts → 勾选该客户需要的内容板块(如纯展示型客户去掉 jobs/downloads
  4. 客户填内容 → 在 /admin 用 Sveltica 录入
  5. 域名 + 备案 → 阿里云买域名 + ICP 备案(约 7-20 工作日)
  6. 建 OSS bucket + CDN → 配静态站托管 + 绑定自定义域名
  7. 配 Gitea Action → 构建后 ossutil 上传 + 刷新 CDN → 上线,转月度维护续费

每个客户 = 一个 Gitea 仓库 + 一个 OSS bucket + 一个域名 + 一行表单配置;基座代码复用、配置隔离


九、国内部署(阿里云)

  • ICP 备案:客户域名必须备案,否则 CDN / 解析受阻(备案约 7-20 工作日,提前启动)
  • OSS 静态站托管:开 bucket 的静态网站托管 + 绑定自定义域名(HTTPS)
  • CDN 加速:回源 OSS,缓存策略 —— HTML 短缓存(1-5 分钟,便于内容更新生效)、JS/CSS/图片长缓存 + 文件名 hash
  • CI 发布脚本Gitea Action 构建后用 ossutil cp -rf dist/ oss://<bucket>/ 上传 + 调 OpenAPI 刷新 CDN
  • HTTPS:阿里云免费 SSL 证书(或 Let's Encrypt),绑 OSS / CDN
  • ⚠️ 不要用 Vercel / Cloudflare Pages(国内访问慢 / 常被墙)

十、成本测算

金额 说明
固定:轻量云(Gitea + CI + 表单微服务) ≈ 60-100 元/月 所有客户共用2H2G 推荐
每客户:OSS + CDN ≈ 5-15 元/月 按量,企业官网流量小
每客户:域名 ≈ 55 元/年
每客户:备案 0 阿里云免费备案
每客户:邮件推送 极低 DirectMail 按发信量,几毛/月
客单价(参考文档 E 线) 5k-2 万 + 月度维护续费 半天换皮、长期续费

结论:新增一个客户的边际成本 ≈ 60-100 元/年(域名 + OSS/CDN),客单价 5k 起,毛利极高。


十一、风险与先验证点(Phase 0 必须跑通)

# 风险 / 验证点 应对
1 Gitea Actions 能否跑 Astro 构建 兼容 GitHub Actions 语法,Phase 0 用 node runner 镜像验证
2 Sveltia + Gitea OAuth 登录 / 提交全链路 Phase 0 配 Gitea OAuth App 跑通
3 CDN 刷新延迟 秒-分钟级,HTML 配短缓存可接受
4 多租户 Gitea 权限隔离 每客户独立组织 / 仓库权限
5 表单微服务防垃圾 / 可用性 蜜罐 + 限流先上,验证码按需
6 备案周期长阻塞交付 合同里写明备案周期,提前启动或先用临时域名

十二、实施阶段

阶段 目标 产出
Phase 0 技术验证 最小链路跑通:Astro + Sveltia + 自建 Gitea → 构建 → OSS → 访问;表单微服务跑通 一个可编辑、可留言的 demo 站
Phase 1 基座化 沉淀自有主题 + Content Collections7 个集合)+ Sveltia 配置模板 + 白标脚本(换 token / 建 bucket 可复用基座仓库
Phase 2 首客户上线 用真实客户驱动,验证商业闭环 + 续费 第一个交付 + 月度维护合同
Phase 3(可选) 加 Payload 适配器,接富后台 / 多角色高预算客户 双档产品线(轻量 / 富后台)

十三、CMS 适配器(预留,兼容未来 Payload)

把内容获取抽象成统一接口,保护 Astro 层不随 CMS 变化:

// 概念接口
interface ContentAdapter {
  getCollection<T>(name: string): Promise<T[]>;
  getSingleton<T>(name: string): Promise<T>;
}
  • 默认实现MarkdownAdapter —— 读本地 MarkdownGitea + Sveltia 路线,首期实现)
  • 预留实现PayloadAdapter —— 调 Payload REST/GraphQL APIPhase 3,遇到富后台客户再加)

切换 CMS 只换适配器,Astro 页面层与 Content Collections schema 不变。


十四、交付前检查清单(沿用选型清单 §十)

每个客户上线前:

  • 该客户仓库的 LICENSE 文件原文我读了吗?(基座 + 依赖)
  • 有没有子目录独立 LICENSEee/ / server/ / enterprise/)?
  • 我的交付方式(私有部署 / SaaS / 转售)会触发哪个条款?
  • Sveltia / Gitea / Astro 的 License 变更记录复查了吗?

把 LICENSE 原文 + 近 3 年 license 变更记录存档到客户合同附件。


参考资料