diff --git a/README.md b/README.md new file mode 100644 index 0000000..725ac1e --- /dev/null +++ b/README.md @@ -0,0 +1,128 @@ +# personal-homepage + +一个基于 Astro 的个人主页项目,定位为: + +> **静态站点 + 构建时数据同步层** + +运行时对外提供纯静态页面;构建阶段由脚本从 Gitea / Seafile 拉取数据,生成本地 JSON,再交给 Astro 渲染。 + +## 当前能力 + +- Astro 静态站点 +- Markdown 日志内容 +- 项目 / 分享 / Gitea 活动页 +- 构建时同步 Gitea / Seafile 数据 +- generated JSON schema 校验 +- 统一重建入口 `npm run rebuild` +- 结构化错误码与 `REBUILD_RESULT` 输出,便于 AstrBot / cron 调用 + +## 快速开始 + +### 1. 安装依赖 + +```bash +npm install +``` + +### 2. 配置环境变量 + +复制示例文件: + +```bash +cp .env.example .env +``` + +然后填写: + +- `GITEA_BASE_URL` +- `GITEA_TOKEN` +- `GITEA_USERNAME` +- `SEAFILE_BASE_URL` +- `SEAFILE_TOKEN` + +如果某些远端还没接好,也可以先保留为空;系统会按当前策略回退到 seed data。 + +### 3. 本地开发 + +```bash +npm run dev +``` + +### 4. 手动同步内容 + +```bash +npm run content:sync +``` + +### 5. 完整重建 + +```bash +npm run rebuild +``` + +该命令会执行: + +1. `npm run content:sync` +2. `npm run build` + +## 常用命令 + +| 命令 | 用途 | +|------|------| +| `npm run dev` | 本地开发 | +| `npm run content:sync` | 仅执行内容同步 | +| `npm run build` | 仅执行 Astro 构建 | +| `npm run rebuild` | 同步 + 构建,适合作为 AstrBot / cron 统一入口 | +| `npm run preview` | 本地预览构建结果 | + +## 文档索引 + +- `REQUIREMENTS.md`:需求与范围 +- `TODO.md`:当前推进计划 +- `docs/rebuild-trigger-spec.md`:AstrBot / cron 重建触发与错误码约定 +- `docs/content-sync-guide.md`:环境变量、映射文件、同步脚本职责、故障排查 +- `docs/ui-design-spec.md`:UI 设计规范 + +## 内容入口 + +### 日志 + +- 目录:`src/content/logs/*.md` + +### 项目种子数据 + +- 文件:`src/content/projects/index.json` + +### 分享种子数据 + +- 文件:`src/content/shares/index.json` + +### Seafile 映射 + +- 文件:`src/content/seafile/index.json` + +## 统一重建入口 + +适合 AstrBot / cron 调用的命令: + +```bash +npm run rebuild +``` + +命令结束前会输出一行结构化结果: + +```text +REBUILD_RESULT {...} +``` + +并返回明确退出码,便于通知系统判断成功 / 失败阶段。 + +详细约定见: + +- `docs/rebuild-trigger-spec.md` + +## 当前实现边界 + +- 当前默认策略是失败时回退 seed data,而不是保留上一轮 generated 文件 +- `SEAFILE_MIRROR_DOWNLOADS=true` 目前只保留开关,尚未真正实现文件镜像 +- `npm run rebuild` 当前只做同步和构建,还不包含部署阶段 diff --git a/TODO.md b/TODO.md index 310df9d..28ff97a 100644 --- a/TODO.md +++ b/TODO.md @@ -96,14 +96,14 @@ ### 5. 补运行文档 -- [ ] 在 README 或单独文档里写清: +- [x] 在 README 或单独文档里写清: - [x] AstrBot / cron 如何触发 - [x] AstrBot 调用的统一构建指令与返回约定 - [x] 错误码含义(sync/build/deploy/unknown) - - [ ] 环境变量如何配置 - - [ ] Gitea / Seafile 映射如何填写 - - [ ] 同步脚本职责 - - [ ] 构建失败如何排查 + - [x] 环境变量如何配置 + - [x] Gitea / Seafile 映射如何填写 + - [x] 同步脚本职责 + - [x] 构建失败如何排查 ## P2:后续完善项 diff --git a/docs/content-sync-guide.md b/docs/content-sync-guide.md new file mode 100644 index 0000000..a890686 --- /dev/null +++ b/docs/content-sync-guide.md @@ -0,0 +1,364 @@ +# 内容同步与运维说明 + +本文档补充说明: + +- 环境变量如何配置 +- 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 +```