# 内容同步与运维说明 本文档补充说明: - 环境变量如何配置 - 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 ```