Cloudflare Worker,提供博客管理后台与图片托管服务。 数据存储在 GitHub 仓库中,通过 GraphQL(优先)+ REST(降级)访问。
- 基础 URL(生产):
https://edit.upxuu.com - 基础 URL(本地开发):
http://localhost:8787 - CORS: 全开 (
Access-Control-Allow-Origin: *),支持跨域调用
所有 /api/* 路由都需要在请求头中携带 Bearer Token:
Authorization: Bearer <ADMIN_PASSWORD>
ADMIN_PASSWORD 是 Worker 的 secret(通过 wrangler secret put ADMIN_PASSWORD 设置)。
错误响应:
401 Unauthorizedwrangler.toml [vars] 中定义(也支持在 Cloudflare Dashboard 中覆盖):
| 变量 | 值 | 说明 |
|---|---|---|
| GITHUB_OWNER | ImUpXuu |
博客仓库所有者 |
| GITHUB_REPO | xuhome |
博客仓库名称 |
| GITHUB_BRANCH | main |
博客仓库分支 |
| POSTS_PATH | src/posts |
文章存放目录 |
| TALK_OWNER | ImUpXuu |
说说仓库所有者 |
| TALK_REPO | xuhome |
说说仓库名称 |
| TALK_BRANCH | main |
说说仓库分支 |
| TALK_PATH | src/talk |
说说存放目录 |
| IMAGE_PATH | public/images |
旧版图片目录 |
| BLOG_URL | https://upxuu.com |
博客首页 URL(用于 IndexNow) |
| PHOTO_OWNER | ImUpXuu |
图片仓库所有者(硬编码) |
| PHOTO_REPO | photo |
图片仓库名称(硬编码) |
| PHOTO_BRANCH | main |
图片仓库分支(硬编码) |
| PHOTO_PATH | images |
图片存放目录(硬编码) |
Secret(通过 wrangler secret put 设置):
| Secret | 说明 |
|---|---|
| GITHUB_TOKEN | GitHub 个人访问令牌(需要 repo 权限) |
| ADMIN_PASSWORD | 管理后台 Bearer Token |
以下路由返回 ADMIN_HTML(内嵌的单页应用),前端通过 history.pushState + popstate 做客户端路由。
| 路由 | 视图 |
|---|---|
/ |
编辑器(新建文章) |
/new |
编辑器(同 /) |
/edit/:filename |
编辑器(加载已有文章) |
/list |
文章列表 + 时间筛选 |
/gallery |
图片库 + 时间导航 |
/settings |
博客设置(config.ts + Layout.astro) |
/talk |
说说编辑器 |
/edittalk/:filename |
编辑已有说说 |
通过 Worker 代理图片请求,不暴露 GitHub 真实 URL。
代理博客仓库(GITHUB_REPO / IMAGE_PATH)中的旧版图片。
GET https://edit.upxuu.com/image/2023/hero.jpg
→ https://raw.githubusercontent.com/ImUpXuu/xuhome/main/public/images/2023/hero.jpg
- 缓存:
public, max-age=31536000(1 年) - 不存在时返回
404 Image not found
代理图片仓库(ImUpXuu/photo)中的新版图片。
GET https://edit.upxuu.com/img/2025/6/4/20250604_123_abc.webp
→ https://raw.githubusercontent.com/ImUpXuu/photo/main/images/2025/6/4/20250604_123_abc.webp
- 路径段逐个
encodeURIComponent编码(正确处理年/月/日/层级) - 缓存:
public, max-age=31536000(1 年) - 不存在时返回
404 Image not found
所有路由前缀 /api/,全部需要 Authorization: Bearer <ADMIN_PASSWORD>。
获取文章列表。
- GraphQL 优先:查询
branch:src/posts表达式,内联解析 YAML frontmatter(避免 N+1 查询) - REST 降级:GraphQL 失败时回退到
GET /repos/.../contents/src/posts
响应:
[
{
"name": "2025-01-01-hello.md",
"path": "src/posts/2025-01-01-hello.md",
"sha": "abc123def456",
"title": "Hello World",
"date": "2025-01-01",
"type": "file"
}
]获取单篇文章内容。
GET https://edit.upxuu.com/api/post/2025-01-01-hello.md
响应:
{
"content": "---\ntitle: \"Hello\"\n---\n\n文章正文...",
"sha": "abc123def456"
}content从 GitHub API 的 base64 解码,UTF-8 处理sha用于后续更新
创建或更新文章。
请求:
{
"content": "---\ntitle: \"Hello\"\n---\n\nMarkdown 正文",
"sha": "abc123def456"
}sha对新文件可选;如果省略,Worker 自动获取已存在的 SHA(防止意外覆盖)content经过 UTF-8 编码 → base64 再发送到 GitHub
成功响应:
{
"content": { "sha": "newsha123..." },
"indexNow": { "status": "pending", "url": "https://upxuu.com/posts/2025-01-01-hello" }
}- IndexNow:保存成功后通过
ctx.waitUntil异步将文章 URL 提交到https://api.indexnow.org/indexnow(Bing)。响应立即返回pending,实际提交在后台执行。
删除文章。
DELETE https://edit.upxuu.com/api/post/2025-01-01-hello.md
请求:
{
"sha": "abc123def456"
}sha必填(GitHub API 要求)- 提交信息:
Delete <filename> via Admin
获取图片仓库(ImUpXuu/photo/images/)中的全部图片。
- 递归 GraphQL:遍历整个目录树(年/月/日 层级)
- 返回扁平数组
响应:
[
{
"name": "20250604_123_abc.webp",
"path": "2025/6/4/20250604_123_abc.webp",
"sha": "oid123..."
}
]- 扩展名过滤:
.jpg.jpeg.png.gif.webp.svg
上传图片到图片仓库。
POST https://edit.upxuu.com/api/upload
请求:
{
"filename": "2025/6/4/20250604_123_abc.webp",
"content": "<base64 编码的图片数据>"
}filename使用年/月/日/路径结构(客户端生成)content必须是纯 base64(不是 data URI,Worker 不做二次编码)- 直接上传到 GitHub REST API(绕过
updateGitHubFile的 UTF-8 重新编码,避免二进制文件损坏)
成功响应:
{
"url": "https://edit.upxuu.com/img/2025/6/4/20250604_123_abc.webp"
}- URL 指向 Worker 的
/img/代理,非 GitHub 原始链接
删除图片仓库中的图片。
DELETE https://edit.upxuu.com/api/img/2025/6/4/20250604_123_abc.webp
请求:
{
"sha": "abc123..."
}- 路径为
images/<filename> - 使用硬编码的
PHOTO_OWNER/PHOTO_REPO(非环境变量)
删除博客仓库中的旧版图片。
DELETE https://edit.upxuu.com/api/image/2023/hero.jpg
请求:
{
"sha": "abc123..."
}- 路径为
public/images/<filename>(使用IMAGE_PATH环境变量) - 提交信息:
Delete image <filename> via Admin
获取博客仓库中的 src/config.ts 和 src/layouts/Layout.astro。
GET https://edit.upxuu.com/api/settings
响应:
{
"config": {
"content": "export default {\n title: \"UpXuu\",\n ...\n}",
"sha": "abc123..."
},
"layout": {
"content": "---\n---\n<html>...",
"sha": "def456..."
}
}更新 config 或 layout 文件。
PUT https://edit.upxuu.com/api/settings
请求:
{
"file": "config",
"content": "export default {\n title: \"新标题\",\n ...\n}",
"sha": "abc123..."
}file可选值:"config"或"layout"- 提交信息:
Update <file> via Admin Settings
说说文件存放在 xuhome 仓库的 src/talk/ 下,使用独立的 GitHub 请求函数
(TALK_OWNER / TALK_REPO / TALK_BRANCH)。
获取说说列表。
响应:
[
{
"name": "2025-06-01.mdx",
"path": "src/talk/2025-06-01.mdx",
"sha": "oid123...",
"title": "Some Talk",
"date": "2025-06-01",
"type": "file"
}
]- 解析 YAML frontmatter 中的
title和date字段 - GraphQL 优先,REST 降级
获取单条说说内容。
GET https://edit.upxuu.com/api/talk/2025-06-01.mdx
响应:{ "content": "...", "sha": "..." }
创建或更新说说。
- 自动获取已有 SHA 逻辑同文章
- 使用 Talk 专用的 GitHub helpers(
TALK_OWNER/TALK_REPO/TALK_BRANCH)
删除说说。
请求:{ "sha": "..." }
客户端 → Worker → GitHub GraphQL API(首选)
→ GitHub REST API(降级)
涉及两个仓库:
| 仓库 | 所有者 | 用途 | 凭据方式 |
|---|---|---|---|
xuhome |
ImUpXuu |
文章、设置、说说 | GITHUB_TOKEN + 环境变量 |
photo |
ImUpXuu |
图片 | GITHUB_TOKEN + 硬编码常量 |
图片不直接暴露 GitHub 链接,通过 Worker 代理:
前端 → Worker /img/ → GitHub raw URL
← Worker 缓存 1 年
好处:隐藏真实 GitHub URL、添加 CORS 头、添加长缓存。
PUT /api/post/:filename 成功后:
- 保存文章到 GitHub
- 读取响应数据
- 在响应中添加
indexNow.pending - 调用
ctx.waitUntil(async () => fetch('https://api.indexnow.org/indexnow', {...}))
IndexNow key 和 keyLocation 目前硬编码在代码中(后续应迁移到环境变量)。
xuhome 仓库同时存放 src/posts/(文章)和 src/talk/(说说),
Worker 维护了两套独立的 GitHub 请求函数(通用的 githubRequest 和专用的
talkGithubRequest),区别仅在于读取的仓库变量不同。后续可以参数化
仓库目标来消除重复。
| 状态码 | 返回体 | 说明 |
|---|---|---|
| 401 | Unauthorized |
Bearer Token 缺失或错误 |
| 404 | Not Found |
未知路由 |
| 404 | Image not found |
GitHub 上未找到图片 |
| 4xx | GitHub 返回的 JSON 错误信息 | REST API 错误透传 |
| 500 | Failed to fetch settings files |
无法读取 config.ts 或 Layout.astro |