# 智引未来洞察内容后台

项目采用与官网同端口的 Astro 内容后台：Markdown 文件负责内容存储，Astro 同时提供管理 API 和静态页面生成。文章列表、分类页和详情页均预渲染为 HTML，便于 SEO 与 GEO 发现。

## 启动

```bash
cp .env.example .env
npm install
npm run dev
```

- 开发官网：`http://localhost:4321/`
- 开发后台：`http://localhost:4321/admin/`

生产模式：

```bash
npm run build
npm start
```

- 官网：`http://localhost:8792/`
- 后台：`http://localhost:8792/admin/`
- 洞察中心：`http://localhost:8792/articles/`

## 环境变量

- `CMS_PORT`：官网与后台共用端口，默认 `8792`
- `CMS_PASSWORD`：后台主密码，也用于密码找回
- `CMS_SECRET`：会话签名密钥
- `CMS_API_KEY`：外部管理 API 的 Bearer Token
- `CMS_DATA_DIR`：仓库外持久化内容目录
- `CMS_BUILD_LOCK`：代码部署与内容发布共用的构建锁

生产建议：

```env
CMS_PORT=8792
CMS_DATA_DIR=/root/zhiyinweilai/shared/cms-data
CMS_BUILD_LOCK=/root/zhiyinweilai/shared/site-build.lock
```

## 内容目录与版本

- `src/content/articles/`：工作稿，包括新草稿和已发布文章的未发布修改
- `src/content/published/`：官网构建使用的线上稿
- `src/content/categories.json`：后台维护的分类
- `public/uploads/`：本地开发的上传图片

配置 `CMS_DATA_DIR` 后，上述用户数据改存到该持久化目录。保存草稿不会改动线上稿；点击发布后，编辑稿才覆盖线上稿并触发静态构建。下架会移除线上稿但保留编辑稿；删除同时移除两版，构建失败时自动恢复。

## 管理 API

所有接口使用 `/api/cms` 前缀：

| 分组 | 方法与地址 | 用途 |
| --- | --- | --- |
| 验证 | `GET /api/cms/captcha` | 获取验证码 |
| 会话 | `GET/POST/DELETE /api/cms/session` | 状态、登录、退出 |
| 账号 | `PUT /api/cms/account/password` | 修改密码并注销已有会话 |
| 分类 | `GET/POST/PATCH/DELETE /api/cms/categories` | 分类管理 |
| 文章 | `GET/POST /api/cms/articles` | 列表与新建草稿 |
| 文章 | `GET/PUT/DELETE /api/cms/articles/:slug` | 读取、保存、删除 |
| 草稿 | `DELETE /api/cms/articles/:slug/draft` | 放弃未发布修改 |
| 发布 | `POST /api/cms/articles/:slug/publish` | 发布并构建 |
| 下架 | `POST /api/cms/articles/:slug/unpublish` | 下架并构建 |
| 工具 | `POST /api/cms/preview` | Markdown 预览 |
| 上传 | `POST /api/cms/uploads` | 上传文章图片 |
| 导入 | `POST /api/cms/imports/word` | 导入 `.docx` 草稿与内嵌图片 |
| 构建 | `GET /api/cms/build` | 查询静态构建状态 |

浏览器后台通过会话 Cookie 鉴权；外部程序可使用 `Authorization: Bearer <CMS_API_KEY>`。

## Word 与 Markdown 导入

后台支持 `.md` 和不超过 20MB 的 `.docx`，不支持旧版 `.doc`。第一个一级标题会作为文章标题并从正文移除；Word 内嵌 PNG、JPG、WEBP、GIF 会保存到上传目录并转成 Markdown 图片。导入只创建草稿，不会自动发布；失败时会清理本次已提取图片。

```http
POST /api/cms/imports/word
Authorization: Bearer <CMS_API_KEY>
Content-Type: application/json
```

```json
{
  "fileName": "GEO洞察文章.docx",
  "dataUrl": "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,...",
  "category": "GEO洞察",
  "title": "可选的自定义标题"
}
```

## 构建一致性

发布、下架、删除文章或修改已使用分类时，后台先获取共享构建锁。若代码部署或其他构建已持锁，本次操作返回冲突，不进入等待队列。后台在临时目录执行 Astro 构建，成功后原子替换 `dist/`；失败则恢复内容并保留旧站点。

生产数据必须位于：

```text
/root/zhiyinweilai/
├── current/     # 当前代码和构建产物，部署时覆盖
└── shared/      # .env、CMS 数据、上传文件和锁
```

## GitLab、PM2 与 Nginx

`.gitlab-ci.yml` 使用单目录覆盖部署：停止指定的 `zhiyinweilai` PM2 进程，清空 `current/`，导出当前提交，关联 `shared/.env`，安装依赖并构建，重新启动并进行本机健康检查，最后安装和验证 Nginx 配置。

默认参数：

- 部署根目录：`/root/zhiyinweilai`
- PM2 应用名：`zhiyinweilai`
- 服务端口：`8792`
- Nginx 配置：`/etc/nginx/conf.d/zhiyinweilai.conf`
- 公网域名：`www.zhiyintec.com`

Runner 需要被授权执行固定的配置安装、`nginx -t` 和 reload 命令。建议建立 `/etc/sudoers.d/zhiyinweilai-runner`：

```sudoers
Cmnd_Alias ZHIYIN_NGINX = \
  /usr/bin/install -o root -g root -m 0644 /root/zhiyinweilai/current/deploy/nginx/zhiyinweilai.conf /etc/nginx/conf.d/zhiyinweilai.conf, \
  /usr/sbin/nginx -t, \
  /usr/bin/systemctl reload nginx

gitlab-runner ALL=(root) NOPASSWD: ZHIYIN_NGINX
```

完成后使用 `sudo visudo -cf /etc/sudoers.d/zhiyinweilai-runner` 校验语法。
