personal-homepage/docs/content-sync-guide.md

365 lines
8.0 KiB
Markdown
Raw 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.

# 内容同步与运维说明
本文档补充说明:
- 环境变量如何配置
- Gitea / Seafile 映射如何填写
- 同步脚本职责
- 构建失败如何排查
---
## 1. 同步链路概览
当前重建链路是:
```text
npm run rebuild
├─ npm run content:sync
│ ├─ 读取 seed data
│ ├─ 拉取 Gitea 数据
│ ├─ 读取 / 拉取 Seafile 数据
│ ├─ 生成 generated JSON
│ └─ 执行 schema 校验
└─ npm run build
└─ Astro 渲染静态页面
```
生成结果默认写入:
- `src/data/generated/projects.json`
- `src/data/generated/shares.json`
- `src/data/generated/gitea-activity.json`
页面层优先读取 generated data如果 generated 文件缺失或校验失败,会按当前策略回退到 seed data / placeholder data。
---
## 2. 环境变量说明
复制:
```bash
cp .env.example .env
```
### 2.1 Gitea
| 变量 | 必填 | 作用 |
|------|------|------|
| `GITEA_BASE_URL` | 建议 | Gitea 根地址 |
| `GITEA_TOKEN` | 建议 | 构建阶段访问 Gitea API 的 token |
| `GITEA_USERNAME` | 建议 | 要同步活动流的用户名 |
如果三者都配置完整,则会执行真实 Gitea 同步;否则:
- 项目数据回退到 seed data
- activity 回退到 placeholder
### 2.2 Seafile
| 变量 | 必填 | 作用 |
|------|------|------|
| `SEAFILE_BASE_URL` | 可选 | Seafile 根地址 |
| `SEAFILE_TOKEN` | 可选 | Seafile API token |
当前 Seafile 支持两种资源方式:
1. 映射文件里直接填写 `url`
2. 填写 `repo_id + path`,由脚本请求 Seafile API 生成下载地址
如果没有完整 Seafile 配置,但映射文件已经直接提供 `url`,页面仍可正常展示这些链接。
### 2.3 输出与行为
| 变量 | 默认值 | 作用 |
|------|--------|------|
| `SYNC_OUTPUT_DIR` | `src/data/generated` | generated JSON 输出目录 |
| `STRICT_SYNC` | `false` | 为 `true` 时,同步失败直接中断,不再回退 |
| `SEAFILE_MIRROR_DOWNLOADS` | `false` | 预留开关,当前未实现文件镜像 |
| `DOWNLOADS_OUTPUT_DIR` | `public/downloads` | 未来镜像文件的目标目录 |
### 2.4 Gitea 调优参数
| 变量 | 默认值 | 作用 |
|------|--------|------|
| `GITEA_ACTIVITY_DAYS` | `70` | 热力图回溯天数 |
| `GITEA_ACTIVITY_PER_DAY_LIMIT` | `50` | 每天请求的 activity 条数上限 |
| `GITEA_RECENT_ITEM_LIMIT` | `8` | recent activity 保留条数 |
| `GITEA_REQUEST_TIMEOUT_MS` | `15000` | Gitea 请求超时 |
| `GITEA_REQUEST_CONCURRENCY` | `5` | 活动流按天拉取时的并发数 |
### 2.5 Seafile 调优参数
| 变量 | 默认值 | 作用 |
|------|--------|------|
| `SEAFILE_REQUEST_TIMEOUT_MS` | `15000` | Seafile 请求超时 |
---
## 3. 映射文件如何填写
### 3.1 项目种子数据:`src/content/projects/index.json`
每个项目至少包含:
```json
{
"name": "personal-homepage",
"description": "项目描述",
"gitea_repo": "basil/personal-homepage",
"cover_image": "/images/projects/personal-homepage.svg",
"demo_video": "",
"download_link": "",
"tags": ["astro", "static-site"],
"featured": true
}
```
字段说明:
- `gitea_repo`:项目与 Gitea 仓库关联键,格式必须是 `owner/repo`
- `cover_image`:封面图静态路径
- `download_link`:可选的兜底下载地址
- `tags`:标签数组
- `featured`:是否出现在首页精选区
### 3.2 分享种子数据:`src/content/shares/index.json`
适合放不依赖 Seafile API 的静态分享项:
```json
{
"name": "个人主页需求文档",
"description": "站点范围说明",
"url": "#",
"time": "2026-05-03"
}
```
### 3.3 Seafile 映射:`src/content/seafile/index.json`
结构分两块:
- `projects[]`:挂到项目详情 / 下载区
- `shares[]`:挂到全站分享列表
#### 项目资源示例
```json
{
"projects": [
{
"project_repo": "basil/personal-homepage",
"downloads": [
{
"name": "Windows 构建包",
"description": "项目打包文件",
"url": "https://example.com/file.zip",
"type": "build",
"platform": "windows"
}
]
}
]
}
```
#### 通过 Seafile API 解析资源示例
```json
{
"name": "Windows 构建包",
"repo_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"path": "/builds/win/demo.zip",
"type": "build",
"platform": "windows"
}
```
可选字段:
- `description`
- `url`
- `repo_id`
- `path`
- `type``build | demo | document | asset`
- `platform`
- `size`
- `updated_at`
- `time`
规则:
- 如果 `url` 已提供,脚本直接使用它,不再请求 Seafile API
- 如果只有 `repo_id + path`,且 Seafile 配置完整,脚本会请求 API 补全下载地址和元数据
- 如果 API 请求失败:
- `STRICT_SYNC=false`:回退到映射文件已有字段
- `STRICT_SYNC=true`:同步中断
---
## 4. 脚本职责
### `scripts/fetch-gitea.ts`
负责:
- 根据 `src/content/projects/index.json` 中的 `gitea_repo` 拉取仓库信息
- 根据 `GITEA_USERNAME` 拉取 `/users/{username}/activities/feeds`
- 聚合生成:
- `projects` 补充字段
- `gitea-activity.json`
### `scripts/fetch-seafile.ts`
负责:
- 读取 `src/content/seafile/index.json`
- 解析项目下载资源和全站分享资源
- 必要时调用 Seafile API 生成下载链接和元数据
- 产出:
- `projects[].downloads[]`
- `shares.json`
### `scripts/sync-content.ts`
负责:
- 读取 seed data
- 调用 Gitea / Seafile 同步逻辑
- 对 generated JSON 执行 schema 校验
- 写出 generated 文件
- 对外返回同步阶段错误码
### `scripts/rebuild.ts`
负责:
- 作为 AstrBot / cron 的统一入口
- 顺序执行:
- `npm run content:sync`
- `npm run build`
- 输出 `REBUILD_RESULT`
- 返回结构化退出码
---
## 5. 失败排查
### 5.1 先看统一入口结果
执行:
```bash
npm run rebuild
```
重点关注:
- shell 退出码
- `REBUILD_RESULT {...}`
- `symbol`
- `stage`
- `logTail`
### 5.2 常见错误分类
#### `GITEA_SYNC_FAILED` / `11`
说明 Gitea 同步失败,常见原因:
- `GITEA_BASE_URL` 错误
- `GITEA_TOKEN` 权限不足或失效
- `GITEA_USERNAME` 错误
- `src/content/projects/index.json` 中某个 `gitea_repo` 不存在
- 远端 API 404 / 401 / 500
检查项:
1. 打开 Gitea 仓库地址确认仓库存在
2. 检查 token 是否有效
3. 检查 `gitea_repo` 是否是正确的 `owner/repo`
#### `SEAFILE_SYNC_FAILED` / `12`
说明 Seafile 资源解析失败,常见原因:
- `SEAFILE_BASE_URL` 错误
- `SEAFILE_TOKEN` 无效
- `repo_id` 错误
- `path` 不存在
检查项:
1. 确认映射文件中的 `repo_id``path`
2. 确认 token 是否有读取权限
3. 如果只是临时不稳定,可先直接填写 `url` 兜底
#### `SCHEMA_VALIDATION_FAILED` / `13`
说明 seed data 或 generated data 不符合 schema。
常见原因:
- `gitea_repo` 不是 `owner/repo`
- `tags` 不是字符串数组
- 日期字段不是可解析时间
- 下载资源缺少 `name`
检查项:
1. 检查 `src/content/projects/index.json`
2. 检查 `src/content/shares/index.json`
3. 检查 `src/content/seafile/index.json`
4. 根据报错里的路径定位字段
#### `BUILD_FAILED` / `20`
说明 Astro 构建阶段失败。
常见原因:
- 页面组件使用了不兼容字段
- 内容文件存在格式问题
- 某个页面渲染依赖的数据格式异常
建议:
1. 单独运行 `npm run build`
2. 查看 Astro 输出的具体页面报错
#### `CONFIG_INVALID` / `40`
说明运行环境本身不完整。
常见原因:
- Node / npm 不存在
- 命令执行环境有问题
### 5.3 `STRICT_SYNC` 的使用建议
- 开发期:建议 `STRICT_SYNC=false`
- 更方便先看页面效果
- 正式定时任务:视你对错误容忍度决定
- 想保守兜底:`false`
- 想强制发现问题:`true`
---
## 6. 推荐运维方式
当前更推荐:
1. 仓库负责统一入口:`npm run rebuild`
2. AstrBot 或 cron 负责定时触发
3. AstrBot 读取退出码和 `REBUILD_RESULT` 发送通知
如果只是人工排查,最小路径是:
```bash
npm run content:sync
npm run build
```