# 智引未来洞察内容后台

项目采用与官网同端口的 Astro 内容后台：Markdown 文件负责内容存储，Astro 提供管理 API 和 Node SSR 页面。文章列表、分类页、详情页及首页文章区均在请求时读取 CMS 数据并渲染，发布内容后无需静态重建即可生效。

## 启动

```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/data/content/articles/`：本地初始化用工作稿种子
- `src/data/content/published/`：本地初始化用线上稿种子
- `src/data/content/categories.json`：本地初始化用分类种子
- `public/uploads/`：本地开发的上传图片

配置 `CMS_DATA_DIR` 后，上述用户数据改存到该持久化目录。保存草稿不会改动线上稿；点击发布后，编辑稿才覆盖线上稿，SSR 请求会直接读取最新内容。下架会移除线上稿但保留编辑稿；删除同时移除两版。

## 管理 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` | SSR 模式兼容状态接口 |

浏览器后台通过会话 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": "可选的自定义标题"
}
```

## 内容一致性

文章写入、发布、下架、删除和分类修改仍使用共享锁，与代码部署互斥；操作只更新持久化 Markdown 和分类文件，不触发静态页面编译。下一次请求会从 `CMS_DATA_DIR` 读取最新内容。

生产数据必须位于：

```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` 校验语法。
