Add CMS configuration schema, docs, and validation (#551)
This commit is contained in:
parent
d91f2b928f
commit
302794d2a6
29
.github/workflows/cms-config.yml
vendored
Normal file
29
.github/workflows/cms-config.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Validate CMS configuration
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'config/cms.json'
|
||||
- 'config/cms.schema.json'
|
||||
- 'scripts/validate_cms_config.py'
|
||||
- '.github/workflows/cms-config.yml'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'config/cms.json'
|
||||
- 'config/cms.schema.json'
|
||||
- 'scripts/validate_cms_config.py'
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install dependencies
|
||||
run: python -m pip install --upgrade pip jsonschema
|
||||
- name: Validate cms.json
|
||||
run: python scripts/validate_cms_config.py
|
||||
25
Makefile
25
Makefile
@ -80,7 +80,7 @@ endif
|
||||
# Database initialization
|
||||
# -----------------------------------------------------------------------------
|
||||
init-db:
|
||||
@psql $(PG_DSN) -f rag-server/sql/schema.sql
|
||||
@psql $(PG_DSN) -f rag-server/sql/schema.sql
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build targets
|
||||
@ -89,16 +89,16 @@ init-db:
|
||||
build: update-dashboard-manifests build-cli build-server build-dashboard
|
||||
|
||||
build-cli:
|
||||
$(MAKE) -C rag-server/cmd/rag-server-cli build
|
||||
$(MAKE) -C rag-server/cmd/rag-server-cli build
|
||||
|
||||
build-server:
|
||||
$(MAKE) -C rag-server build
|
||||
$(MAKE) -C rag-server build
|
||||
|
||||
build-dashboard:
|
||||
$(MAKE) -C dashboard build SKIP_SYNC=1
|
||||
$(MAKE) -C dashboard build SKIP_SYNC=1
|
||||
|
||||
update-dashboard-manifests:
|
||||
$(MAKE) -C dashboard sync-dl-index
|
||||
$(MAKE) -C dashboard sync-dl-index
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Run targets
|
||||
@ -107,19 +107,19 @@ $(MAKE) -C dashboard sync-dl-index
|
||||
start: start-openresty start-server start-dashboard start-dl start-docs
|
||||
|
||||
start-server:
|
||||
$(MAKE) -C rag-server start
|
||||
$(MAKE) -C rag-server start
|
||||
|
||||
start-dashboard:
|
||||
$(MAKE) -C dashboard start
|
||||
$(MAKE) -C dashboard start
|
||||
|
||||
|
||||
stop: stop-server stop-dashboard stop-openresty
|
||||
|
||||
stop-server:
|
||||
$(MAKE) -C rag-server stop
|
||||
$(MAKE) -C rag-server stop
|
||||
|
||||
stop-dashboard:
|
||||
$(MAKE) -C dashboard stop
|
||||
$(MAKE) -C dashboard stop
|
||||
|
||||
start-openresty:
|
||||
ifeq ($(OS),Darwin)
|
||||
@ -152,3 +152,10 @@ else
|
||||
endif
|
||||
|
||||
restart: stop start
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# CMS configuration validation
|
||||
# -----------------------------------------------------------------------------
|
||||
.PHONY: lint-cms
|
||||
lint-cms:
|
||||
python3 scripts/validate_cms_config.py
|
||||
|
||||
@ -37,6 +37,13 @@ XControl 通过 LangChainGo 统一接入多种大模型,并为 AskAI、CLI 与
|
||||
- **Memory 与历史追踪**:支持 Conversation Buffer 等对话记忆机制,增强交互体验。
|
||||
|
||||
|
||||
## CMS configuration
|
||||
|
||||
A unified CMS setup is defined in [`config/cms.json`](config/cms.json). The schema at [`config/cms.schema.json`](config/cms.schema.json) ensures templates, themes, extensions and content sources stay in sync across deployments.
|
||||
|
||||
- Refer to [`docs/cms/README.md`](docs/cms/README.md) for usage instructions, extension development notes and theme customization guidelines.
|
||||
- Follow the migration playbook in [`docs/cms/migration-guide.md`](docs/cms/migration-guide.md) when switching existing sites to the CMS architecture.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Tested on **Ubuntu 22.04 x64** and **macOS 26 arm64**.
|
||||
|
||||
69
config/cms.json
Normal file
69
config/cms.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"$schema": "./cms.schema.json",
|
||||
"templates": [
|
||||
{
|
||||
"name": "marketing-landing",
|
||||
"entry": "templates/marketing/index.tsx",
|
||||
"description": "Default landing page for campaign microsites.",
|
||||
"previewPath": "previews/marketing-landing.png"
|
||||
},
|
||||
{
|
||||
"name": "docs-home",
|
||||
"entry": "templates/docs/home.tsx",
|
||||
"description": "Documentation homepage wiring search, changelog and highlights."
|
||||
}
|
||||
],
|
||||
"theme": {
|
||||
"name": "xcontrol-galaxy",
|
||||
"version": "1.0.0",
|
||||
"author": "XControl Design Systems",
|
||||
"variables": {
|
||||
"primaryColor": "#4055ff",
|
||||
"accentColor": "#39c2f0",
|
||||
"fontFamily": "Inter, system-ui, sans-serif"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
{
|
||||
"name": "search",
|
||||
"package": "@xcontrol/cms-extension-search",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"provider": "algolia",
|
||||
"indexName": "xcontrol_docs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ab-testing",
|
||||
"package": "@xcontrol/cms-extension-experiments",
|
||||
"enabled": false,
|
||||
"config": {
|
||||
"allocation": "5%"
|
||||
}
|
||||
}
|
||||
],
|
||||
"contentSources": [
|
||||
{
|
||||
"type": "git",
|
||||
"name": "marketing-site",
|
||||
"readOnly": false,
|
||||
"options": {
|
||||
"remote": "git@github.com:xcontrol/marketing-site.git",
|
||||
"branch": "main",
|
||||
"contentPath": "content/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "filesystem",
|
||||
"name": "product-docs",
|
||||
"readOnly": true,
|
||||
"options": {
|
||||
"path": "../docs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"deployment": {
|
||||
"preview": true,
|
||||
"defaultLocale": "en-US"
|
||||
}
|
||||
}
|
||||
158
config/cms.schema.json
Normal file
158
config/cms.schema.json
Normal file
@ -0,0 +1,158 @@
|
||||
{
|
||||
"$id": "https://xcontrol.dev/schemas/cms.schema.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "XControl CMS Configuration",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"templates",
|
||||
"theme",
|
||||
"extensions",
|
||||
"contentSources"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"templates": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"entry"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"entry": {
|
||||
"description": "The relative path to the template entry point.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewPath": {
|
||||
"description": "Optional static preview asset path.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"version"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"variables": {
|
||||
"description": "Theme tokens exposed to templates.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"package"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"package": {
|
||||
"description": "Resolvable package name or path.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"config": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentSources": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"name",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"git",
|
||||
"filesystem",
|
||||
"api",
|
||||
"database"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"options": {
|
||||
"description": "Source specific configuration payload.",
|
||||
"type": "object"
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deployment": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"preview": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"defaultLocale": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "Optional JSON Schema declaration for tooling support."
|
||||
}
|
||||
}
|
||||
}
|
||||
50
docs/cms/README.md
Normal file
50
docs/cms/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# XControl CMS 平台指南
|
||||
|
||||
本文档介绍如何使用全新的 CMS 配置、扩展生态以及主题定制流程,帮助团队快速落地内容管理方案。
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **配置文件位置**:所有实例统一使用 [`config/cms.json`](../../config/cms.json) 描述模板、主题、扩展及内容源。提交前可运行 `make lint-cms` 或 GitHub Actions 自动校验以确保配置符合 JSON Schema。
|
||||
2. **模板管理**:
|
||||
- `templates` 数组声明可用模板的 `name` 与入口文件 `entry`;
|
||||
- 可添加 `description` 与 `previewPath` 提供运营预览信息;
|
||||
- 模板内可读取主题变量 `theme.variables`,应通过公共的 `@xcontrol/cms-theme` SDK 获取。
|
||||
3. **主题切换**:
|
||||
- `theme` 字段记录版本、作者与可覆写的变量;
|
||||
- 在 CI/CD 中读取 `theme.version` 控制静态资源缓存;
|
||||
- 自定义变量建议遵循 `kebabCase` 或 `camelCase`,并在设计系统中记录来源。
|
||||
4. **扩展启用**:
|
||||
- `extensions` 数组列出扩展包;`enabled=false` 的扩展会被打包但默认不挂载;
|
||||
- `config` 字段由扩展自行解析,应在扩展仓库提供 JSON Schema 或 TypeScript 类型定义;
|
||||
- 推荐将第三方密钥通过环境变量传入扩展,避免写入配置文件。
|
||||
5. **内容源配置**:
|
||||
- `contentSources` 支持 `git`、`filesystem`、`api` 和 `database` 四种类型;
|
||||
- 每个内容源都需要一个唯一的 `name` 与自定义 `options`;
|
||||
- 若设定 `readOnly: true`,发布面板会禁用写入操作。
|
||||
|
||||
## 扩展开发手册
|
||||
|
||||
1. **开发准备**:
|
||||
- 使用 `pnpm create @xcontrol/cms-extension <name>` 脚手架初始化项目;
|
||||
- 在 `package.json` 中声明入口 `main` 与 `module`,同时导出扩展元数据 `getExtensionMeta()`。
|
||||
2. **生命周期**:
|
||||
- 扩展需实现 `register(app, context)` 方法,在其中挂载路由、配置表单或任务;
|
||||
- 扩展可通过 `context.cmsConfig` 读取解析后的 `cms.json`;
|
||||
- 对长耗时任务使用 `context.queue.enqueue`,避免阻塞渲染线程。
|
||||
3. **测试与发布**:
|
||||
- 使用 `pnpm test` 运行扩展单测;
|
||||
- 通过 `pnpm build` 生成产物,并在 `dist/manifest.json` 中输出能力声明;
|
||||
- 发布到内网 NPM 仓库后,可在主项目 `config/cms.json` 中引用。
|
||||
|
||||
## 主题定制指南
|
||||
|
||||
1. **继承基础主题**:复制官方主题仓库 `@xcontrol/cms-theme-galaxy`,在 `tokens/` 中调整颜色、排版、阴影等设计变量。
|
||||
2. **变量发布**:
|
||||
- 执行 `pnpm build` 生成 `theme.json`;
|
||||
- 在仓库中创建 `releases/<version>` 标签,与 `cms.json` 的 `theme.version` 对齐;
|
||||
- 将 `theme.json` 发布到静态资源 CDN,配置 `THEME_REGISTRY_URL` 指向该地址。
|
||||
3. **模板调试**:
|
||||
- 本地运行 `pnpm dev --template <template-name>`,自动读取主题变量热更新;
|
||||
- 使用 `previewPath` 提供的静态图检视最终效果;
|
||||
- 主题变量新增时记得更新 `theme.variables` 以同步到配置文件。
|
||||
|
||||
41
docs/cms/migration-guide.md
Normal file
41
docs/cms/migration-guide.md
Normal file
@ -0,0 +1,41 @@
|
||||
# 迁移至 XControl CMS 的实施指南
|
||||
|
||||
本文梳理了从旧版静态页面体系迁移到全新 CMS 架构的步骤,并提供回滚方案以保障发布安全。
|
||||
|
||||
## 迁移准备
|
||||
|
||||
1. **梳理现有页面**:整理旧系统的路由、模板和数据源,并标注关键依赖(例如第三方 API、Webhook)。
|
||||
2. **建立对照表**:在项目 wiki 中维护“旧页面 → 新模板”的映射,明确所需内容源与扩展。
|
||||
3. **同步配置**:将上述映射写入 [`config/cms.json`](../../config/cms.json),并针对每个模板准备默认内容样例。
|
||||
|
||||
## 实施步骤
|
||||
|
||||
1. **搭建预览环境**:
|
||||
- 创建 `feature/cms-migration` 分支;
|
||||
- 部署独立的 CMS 预览环境,指向 `cms.json` 中定义的内容源;
|
||||
- 确保团队成员可登录并在预览中校对页面。
|
||||
2. **内容迁移**:
|
||||
- 使用 `scripts/content-migrator`(TODO)或手动脚本,将旧 Markdown/HTML 转换为 CMS 支持的内容块;
|
||||
- 对照对照表验证每个页面的链接、组件与扩展生效情况;
|
||||
- 在预览环境执行冒烟测试,覆盖核心业务流(注册、下单、反馈表单等)。
|
||||
3. **灰度发布**:
|
||||
- 在 CDN/网关上新增 `cms-beta` 域名,对应新的渲染服务;
|
||||
- 内部用户和部分真实流量切换到新域,收集性能及错误日志;
|
||||
- 若未出现阻塞问题,逐步放大流量并更新主域名解析。
|
||||
|
||||
## 回滚方案
|
||||
|
||||
1. **保留旧环境**:迁移期间保留旧渲染服务与静态资源,避免同名覆盖;
|
||||
2. **可控切换**:所有流量切换通过网关开关或流量调度平台完成,开关命名为 `enable_new_cms`;
|
||||
3. **回滚流程**:
|
||||
- 若监控中断或错误率超出阈值,立即关闭 `enable_new_cms` 将流量打回旧系统;
|
||||
- 执行 `make revert-cms-release`(脚本需在部署仓库中实现)撤销最新配置发布;
|
||||
- 将问题归档在 incident 工单,收集 `cms.json` 与扩展日志,确认修复后重新灰度。
|
||||
|
||||
## 里程碑检查
|
||||
|
||||
- ✅ 完成 `cms.json` 配置与 Schema 校验;
|
||||
- ✅ 预览环境可完整渲染所有页面;
|
||||
- ✅ 灰度发布通过性能、监控、无障碍检查;
|
||||
- ✅ 回滚演练至少一次,确保运维手册可执行。
|
||||
|
||||
48
scripts/validate_cms_config.py
Executable file
48
scripts/validate_cms_config.py
Executable file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate the CMS configuration using the JSON schema."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
try:
|
||||
import jsonschema
|
||||
except ImportError as exc: # pragma: no cover - handled in CI setup
|
||||
raise SystemExit(
|
||||
"jsonschema package is required to validate cms.json; install with 'pip install jsonschema'."
|
||||
) from exc
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
CONFIG_PATH = ROOT / "config" / "cms.json"
|
||||
SCHEMA_PATH = ROOT / "config" / "cms.schema.json"
|
||||
|
||||
|
||||
def load_json(path: pathlib.Path) -> dict:
|
||||
try:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
return json.load(handle)
|
||||
except FileNotFoundError as exc:
|
||||
raise SystemExit(f"Missing required file: {path}") from exc
|
||||
except json.JSONDecodeError as exc:
|
||||
raise SystemExit(f"Failed to parse {path}: {exc}") from exc
|
||||
|
||||
|
||||
def main() -> int:
|
||||
schema = load_json(SCHEMA_PATH)
|
||||
config = load_json(CONFIG_PATH)
|
||||
|
||||
try:
|
||||
jsonschema.validate(instance=config, schema=schema)
|
||||
except jsonschema.ValidationError as exc:
|
||||
location = " > ".join(str(item) for item in exc.absolute_path)
|
||||
message = f"cms.json validation error at {location or '<root>'}: {exc.message}"
|
||||
raise SystemExit(message) from exc
|
||||
|
||||
print("cms.json matches cms.schema.json")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Reference in New Issue
Block a user