Files
linghutianhao 5c4a471280 测试OAuth2
2026-08-14 17:16:25 +08:00

365 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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)
```
### 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 直连 Gitea3011)。
**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 WebhookURL 指向服务器上的小脚本,如 `http://115.190.192.69:8080/hook`),脚本收到推送后执行 `git pull && npm run build`,再重启/重载静态服务
- **方案 B(手动)**:改完内容后 SSH 上服务器 `git pull && npm run build`
- **方案 C(省事)**:服务器定时任务每分钟 `git pull`,有变更才 build
### 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 },
],
},
],
},
});
```