2026-08-14 15:28:57 +08:00
|
|
|
|
---
|
|
|
|
|
|
title: cms构建说明
|
|
|
|
|
|
sidebar_position: 10
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## cms
|
|
|
|
|
|
|
|
|
|
|
|
### sveltia-cms
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
1.文件
|
|
|
|
|
|
新增 static/admin/index.html sveltia CMS入口页,CDN方式
|
|
|
|
|
|
新增 static/admin/config.yml sveltia全部配置
|
|
|
|
|
|
|
|
|
|
|
|
2.访问
|
|
|
|
|
|
http://localhost:3000/admin/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### decap-cms(仅本地)
|
|
|
|
|
|
|
|
|
|
|
|
https://decapcms.org/docs/basic-steps/
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
1.文件
|
|
|
|
|
|
新增 static/admin/index.html Decap CMS入口页,CDN方式
|
|
|
|
|
|
新增 static/admin/config.yml Decap全部配置
|
|
|
|
|
|
修改 package.json 增加"cms": "npx decap-server"
|
|
|
|
|
|
|
|
|
|
|
|
2.内容
|
|
|
|
|
|
docs的文件要增加 title
|
|
|
|
|
|
|
|
|
|
|
|
3.启动
|
|
|
|
|
|
npx decap-server 或 npm run cms
|
|
|
|
|
|
|
|
|
|
|
|
4.访问
|
|
|
|
|
|
http://localhost:3000/admin/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 已创建的文件
|
|
|
|
|
|
|
|
|
|
|
|
| 文件 | 说明 |
|
|
|
|
|
|
| ------------------------------------------------------------ | -------------------------------------- |
|
|
|
|
|
|
| [static/admin/index.html](vscode-webview://0ckeaenbrnfu1mc0gmsrksqv66drn38jfo32mrkm9h7l6l40fp43/static/admin/index.html) | Decap CMS 入口,从 CDN 加载 |
|
|
|
|
|
|
| [static/admin/config.yml](vscode-webview://0ckeaenbrnfu1mc0gmsrksqv66drn38jfo32mrkm9h7l6l40fp43/static/admin/config.yml) | 核心配置:backend、集合、媒体文件路径 |
|
|
|
|
|
|
| [server.js](vscode-webview://0ckeaenbrnfu1mc0gmsrksqv66drn38jfo32mrkm9h7l6l40fp43/server.js) | Gitee OAuth 认证代理服务(生产环境用) |
|
|
|
|
|
|
| [.env.example](vscode-webview://0ckeaenbrnfu1mc0gmsrksqv66drn38jfo32mrkm9h7l6l40fp43/.env.example) | 环境变量模板 |
|
|
|
|
|
|
|
|
|
|
|
|
#### Git 仓库
|
|
|
|
|
|
|
|
|
|
|
|
已本地提交并推送到 `git@gitee.com:lhth/testdecap.git`
|
|
|
|
|
|
|
|
|
|
|
|
有一个新的更改(导航栏链接改到 Gitee)在本地但被拦截了,你需要手动推一下:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd "d:/projects/test/my-websitetest"
|
|
|
|
|
|
git push origin master
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 架构说明
|
|
|
|
|
|
|
|
|
|
|
|
**当前配置(开发/测试):proxy backend**
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
浏览器 → Decap CMS → proxy:8081 → 本地 git repo → SSH → Gitee
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
运行方式:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 终端 1:启动 Docusaurus
|
|
|
|
|
|
npm start
|
|
|
|
|
|
|
|
|
|
|
|
# 终端 2:启动 Decap 代理(在项目根目录)
|
|
|
|
|
|
npx decap-server
|
|
|
|
|
|
|
|
|
|
|
|
# 访问 http://localhost:3000/admin/ 即可使用 CMS
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
> **注意**:Docusaurus 开发服务器的 SPA 路由会拦截 `/admin/` 路径。有两个解决办法:
|
|
|
|
|
|
>
|
|
|
|
|
|
> 1. 访问 `http://localhost:3000/admin/index.html` (可能仍会被拦截)
|
|
|
|
|
|
> 2. 用生产模式:`npm run build && npm run serve`,然后访问 `http://localhost:3000/admin/` ✅(已验证可行)
|
|
|
|
|
|
|
|
|
|
|
|
#### 生产环境部署(Gitee OAuth)
|
|
|
|
|
|
|
|
|
|
|
|
当你要部署到远程服务器时,需要:
|
|
|
|
|
|
|
|
|
|
|
|
**1. 在 Gitee 创建 OAuth App**
|
|
|
|
|
|
|
|
|
|
|
|
- 打开 https://gitee.com/oauth/applications
|
|
|
|
|
|
- 应用名称:`My Decap CMS`
|
|
|
|
|
|
- 回调地址:`https://你的域名.com/api/auth`
|
|
|
|
|
|
- 获取 `Client ID` 和 `Client Secret`
|
|
|
|
|
|
|
|
|
|
|
|
**2. 部署认证服务**
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 服务器上运行
|
|
|
|
|
|
GITEE_CLIENT_ID=xxx GITEE_CLIENT_SECRET=xxx node server.js
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
或者部署到 Vercel/Cloudflare Workers 等 Serverless 平台。
|
|
|
|
|
|
|
|
|
|
|
|
**3. 修改 config.yml** 取消注释文末的生产环境配置,注释掉 proxy backend 部分(文件中已有注释说明)。
|
|
|
|
|
|
|
|
|
|
|
|
#### 内容集合
|
|
|
|
|
|
|
|
|
|
|
|
已配置两个集合:
|
|
|
|
|
|
|
|
|
|
|
|
- **博客文章** (`blog/`) — Docusaurus 标准 `YYYY-MM-DD-title.md` 格式
|
|
|
|
|
|
- **文档** (`docs/`) — Docusaurus 文档格式
|
|
|
|
|
|
|
|
|
|
|
|
你可以随时在 [static/admin/config.yml](vscode-webview://0ckeaenbrnfu1mc0gmsrksqv66drn38jfo32mrkm9h7l6l40fp43/static/admin/config.yml) 中增删字段或集合。
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
浏览器 本地机器 Gitee
|
|
|
|
|
|
┌─────────┐ ┌──────────────┐ ┌──────────┐
|
|
|
|
|
|
│ Decap │──proxy──→ │ decap-server │──git──→ │ gitee.com│
|
|
|
|
|
|
│ CMS UI │ HTTP:8081 │ (本地Node进程) │ SSH │ (你的仓库)│
|
|
|
|
|
|
│ admin/ │ │ 读写本地git │ │ │
|
|
|
|
|
|
└────┬────┘ └──────────────┘ └──────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
└── unpkg.com CDN (加载 decap-cms.js)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-08-14 17:16:25 +08:00
|
|
|
|
### sveltia-cms + gitea(当前使用)
|
|
|
|
|
|
|
|
|
|
|
|
Sveltia CMS 直接对接 Gitea:浏览器登录 Gitea(OAuth2 隐式授权),编辑内容保存后直接提交到 Gitea 仓库。
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
浏览器 → Sveltia CMS UI (localhost:3000/admin) → Gitea API (115.190.192.69:3011)
|
|
|
|
|
|
→ OAuth 登录 → 直接 commit/push
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**配置(static/admin/config.yaml)**
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
backend:
|
|
|
|
|
|
name: gitea
|
|
|
|
|
|
repo: admin/docusaurus
|
|
|
|
|
|
branch: main
|
|
|
|
|
|
base_url: http://115.190.192.69:3011
|
|
|
|
|
|
auth_endpoint: /login/oauth/authorize
|
|
|
|
|
|
auth_scope: read_user,write:repository
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Gitea 侧要做两件事(一次性):**
|
|
|
|
|
|
|
|
|
|
|
|
1. 创建 OAuth2 应用
|
|
|
|
|
|
- 登录 Gitea → 头像 → 设置 → 应用 → 管理 OAuth2 应用程序 → 创建新应用
|
|
|
|
|
|
- 应用名称:`Sveltia CMS`
|
|
|
|
|
|
- 重定向 URI:`http://localhost:3000/admin/`(必须与访问 CMS 的地址完全一致,含结尾斜杠;以后换域名/端口要回来改)
|
|
|
|
|
|
- 创建后复制 **Client ID**,填到 `static/admin/config.yaml` 的 `backend.client_id`(去掉注释)(隐式授权不需要 Secret)
|
|
|
|
|
|
|
|
|
|
|
|
2. 开启 CORS(改 Gitea 服务器上的 `app.ini`,然后重启 Gitea)
|
|
|
|
|
|
|
|
|
|
|
|
```ini
|
|
|
|
|
|
[cors]
|
|
|
|
|
|
ENABLED = true
|
|
|
|
|
|
SCHEME = http
|
|
|
|
|
|
ALLOW_DOMAIN = localhost:3000
|
|
|
|
|
|
ALLOW_SUBDOMAIN = false
|
|
|
|
|
|
METHODS = GET,POST,PUT,PATCH,DELETE,OPTIONS
|
|
|
|
|
|
MAX_AGE = 600
|
|
|
|
|
|
ALLOW_CREDENTIALS = false
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
> app.ini 位置:Windows 一般在 `C:\Program Files\Gitea\custom\conf\app.ini`,Linux 在 `/etc/gitea/app.ini`;也可以在 Gitea 后台「站点管理 → 配置」里看到实际路径。
|
|
|
|
|
|
> 若从局域网其他电脑访问,把 `ALLOW_DOMAIN` 改成对应主机名/IP。
|
|
|
|
|
|
|
|
|
|
|
|
**启动与访问**
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm start
|
|
|
|
|
|
# 打开 http://localhost:3000/admin/index.html (若 /admin/ 被 Docusaurus 路由拦截就用这个)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
点 Login with Gitea → 授权后即可编辑,保存即提交到 `admin/docusaurus` 的 `main` 分支。
|
|
|
|
|
|
|
|
|
|
|
|
**排错**
|
|
|
|
|
|
|
|
|
|
|
|
- 登录跳转后报 `invalid client` / 授权页打不开:`client_id` 没填或填错 → 检查 `backend.client_id`
|
|
|
|
|
|
- 登录成功但列表/保存报 CORS 错误(控制台 `Access-Control-Allow-Origin`):Gitea 的 `[cors]` 没开或 `ALLOW_DOMAIN` 没包含 `localhost:3000`,或 Gitea 没重启
|
|
|
|
|
|
- 重定向 URI 不匹配(Gitea 报 `invalid redirect_uri`):Gitea 里 OAuth 应用的重定向 URI 必须与浏览器地址栏实际访问的 CMS 地址逐字符一致
|
|
|
|
|
|
- 分支不对:确认 `branch: main` 与仓库默认分支一致
|
|
|
|
|
|
|
|
|
|
|
|
### 部署到服务器(115.190.192.69,站点 3000 端口 / Gitea 3011 端口)
|
|
|
|
|
|
|
|
|
|
|
|
站点和 Gitea 放同一台服务器,浏览器访问 `http://115.190.192.69:3000/admin/`,CMS 直连 Gitea(3011)。
|
|
|
|
|
|
|
|
|
|
|
|
**1. 构建**
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm run build # 产物在 build/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**2. 把代码弄到服务器(推荐源码方式,便于后续自动重建)**
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 服务器上(需装 node ≥ 20)
|
|
|
|
|
|
git clone http://115.190.192.69:3011/admin/docusaurus.git
|
|
|
|
|
|
cd docusaurus
|
|
|
|
|
|
npm install
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**3. 启动站点(3000 端口)**
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm run build && npm run serve -- --host 0.0.0.0 --port 3000
|
|
|
|
|
|
# 或用 nginx/caddy 把 build/ 目录静态托管在 3000 端口
|
|
|
|
|
|
# 防火墙放行 3000 端口
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**4. Gitea 侧(一次性,改完重启 Gitea)**
|
|
|
|
|
|
|
|
|
|
|
|
- OAuth 应用的重定向 URI 加一行:`http://115.190.192.69:3000/admin/`(本地调试的 `http://localhost:3000/admin/` 可保留,多行添加)
|
|
|
|
|
|
- `app.ini` 的 CORS:
|
|
|
|
|
|
|
|
|
|
|
|
```ini
|
|
|
|
|
|
[cors]
|
|
|
|
|
|
ENABLED = true
|
|
|
|
|
|
SCHEME = http
|
|
|
|
|
|
ALLOW_DOMAIN = 115.190.192.69:3000
|
|
|
|
|
|
ALLOW_SUBDOMAIN = false
|
|
|
|
|
|
METHODS = GET,POST,PUT,PATCH,DELETE,OPTIONS
|
|
|
|
|
|
MAX_AGE = 600
|
|
|
|
|
|
ALLOW_CREDENTIALS = false
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**5. 访问 `http://115.190.192.69:3000/admin/` → Login with Gitea**
|
|
|
|
|
|
|
|
|
|
|
|
**内容更新闭环(关键)**
|
|
|
|
|
|
|
|
|
|
|
|
CMS 里保存 = 直接 commit 到 Gitea 仓库,但静态站点不会自动更新,需要服务器重建:
|
|
|
|
|
|
|
|
|
|
|
|
- **方案 A(推荐)**:Gitea 建 Push Webhook(URL 指向服务器上的小脚本,如 `http://115.190.192.69:8080/hook`),脚本收到推送后执行 `git pull && npm run build`,再重启/重载静态服务
|
|
|
|
|
|
- **方案 B(手动)**:改完内容后 SSH 上服务器 `git pull && npm run build`
|
|
|
|
|
|
- **方案 C(省事)**:服务器定时任务每分钟 `git pull`,有变更才 build
|
|
|
|
|
|
|
2026-08-14 15:28:57 +08:00
|
|
|
|
### tina-cms(卡)
|
|
|
|
|
|
|
|
|
|
|
|
https://tina.io/docs/reference/config
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
1.npm install tinacms @tinacms/cli
|
|
|
|
|
|
2.tina/config.js
|
|
|
|
|
|
3.package.json
|
|
|
|
|
|
// scripts 里加
|
|
|
|
|
|
"tina": "tinacms dev -c \"docusaurus start\"",
|
|
|
|
|
|
4.npm run tina
|
|
|
|
|
|
自动生成tina/__generated__/
|
|
|
|
|
|
├── _graphql.json
|
|
|
|
|
|
├── _lookup.json
|
|
|
|
|
|
├── _schema.json
|
|
|
|
|
|
├── client.ts
|
|
|
|
|
|
├── config.prebuild.jsx
|
|
|
|
|
|
├── frags.gql
|
|
|
|
|
|
├── queries.gql
|
|
|
|
|
|
├── schema.gql
|
|
|
|
|
|
├── static-media.json
|
|
|
|
|
|
├── types.js
|
|
|
|
|
|
└── types.ts
|
|
|
|
|
|
5.npm run tina
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
# 官方提供标准版
|
|
|
|
|
|
const branch =
|
|
|
|
|
|
process.env.NEXT_PUBLIC_TINA_BRANCH ||
|
|
|
|
|
|
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF ||
|
|
|
|
|
|
process.env.HEAD ||
|
|
|
|
|
|
'main';
|
|
|
|
|
|
|
|
|
|
|
|
export default defineConfig({
|
|
|
|
|
|
branch,
|
|
|
|
|
|
// generated by TinaCloud
|
|
|
|
|
|
token: '<Your Read Only Token>',
|
|
|
|
|
|
// generated by TinaCloud
|
|
|
|
|
|
clientId: '<Your Client ID>',
|
|
|
|
|
|
build: {
|
|
|
|
|
|
publicFolder: 'public',
|
|
|
|
|
|
outputFolder: 'admin',
|
|
|
|
|
|
},
|
|
|
|
|
|
schema: {
|
|
|
|
|
|
//Content model definition here...
|
|
|
|
|
|
},
|
|
|
|
|
|
ui: {
|
|
|
|
|
|
previewUrl: (context) => {
|
|
|
|
|
|
// Use Vercel preview deployments based on branch names
|
|
|
|
|
|
return { url: `https://my-app-git-${context.branch}.vercel.app` };
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
media: {
|
|
|
|
|
|
tina: {
|
|
|
|
|
|
publicFolder: 'public',
|
|
|
|
|
|
mediaRoot: 'uploads',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
search: {
|
|
|
|
|
|
tina: {
|
|
|
|
|
|
indexerToken: process.env.TINA_SEARCH_TOKEN,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
import { defineConfig } from "tinacms";
|
|
|
|
|
|
|
|
|
|
|
|
export default defineConfig({
|
|
|
|
|
|
branch: process.env.TINA_BRANCH || "master",
|
|
|
|
|
|
clientId: process.env.TINA_CLIENT_ID || "",
|
|
|
|
|
|
token: process.env.TINA_TOKEN || "",
|
|
|
|
|
|
build: {
|
|
|
|
|
|
publicFolder: "static",
|
|
|
|
|
|
outputFolder: "admin",
|
|
|
|
|
|
},
|
|
|
|
|
|
media: {
|
|
|
|
|
|
tina: {
|
|
|
|
|
|
mediaRoot: "img",
|
|
|
|
|
|
publicFolder: "static",
|
|
|
|
|
|
static: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
schema: {
|
|
|
|
|
|
collections: [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "blog",
|
|
|
|
|
|
label: "博客",
|
|
|
|
|
|
path: "blog",
|
|
|
|
|
|
format: "md",
|
|
|
|
|
|
fields: [
|
|
|
|
|
|
{ type: "string", name: "title", label: "标题", isTitle: true, required: true },
|
|
|
|
|
|
{ type: "datetime", name: "date", label: "发布日期", ui: { dateFormat: "YYYY-MM-DD", timeFormat: false } },
|
|
|
|
|
|
{ type: "string", name: "tags", label: "标签", list: true, ui: { component: "tags" } },
|
|
|
|
|
|
{ type: "string", name: "description", label: "描述/摘要" },
|
|
|
|
|
|
{ type: "rich-text", name: "body", label: "正文", isBody: true },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "docs",
|
|
|
|
|
|
label: "文档",
|
|
|
|
|
|
path: "docs",
|
|
|
|
|
|
format: "md",
|
|
|
|
|
|
fields: [
|
|
|
|
|
|
{ type: "string", name: "title", label: "标题", isTitle: true, required: true },
|
|
|
|
|
|
{ type: "number", name: "sidebar_position", label: "侧边栏位置" },
|
|
|
|
|
|
{ type: "string", name: "tags", label: "标签", list: true, ui: { component: "tags" } },
|
|
|
|
|
|
{ type: "rich-text", name: "body", label: "正文", isBody: true },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|