去年还是前年就在思考如何在 Ghost 中集成 Activitypub 的互动,还找了一个国内博客大佬的实现 ActivityPub 协议的简单实现 - Lawrence Li ,不过这位大佬的方案是完全自己实现协议部分,对我来说有点太复杂了。
最近用 Cloudflare Worker 完成了很多有意思的小东西,对 Worker 能实现什么又有了新一层的了解。 所以今天忽然灵光一闪,Ghost 有 webhook,我就不能利用它和 Worker 实现文章 key 和嘟文 key 的匹配,然后在页面直接加载嘟文互动数据吗?
所以通过和 AI 的几轮互动,我得到了下面这个有意思的东西,应该是能完整实现的。
以下是完整的端到端实现方案,涵盖博客发布同步到 GoToSocial、Cloudflare Worker/KV 存储管理、以及互动数据 API 提供的全流程:
系统架构图
[博客系统] --Webhook--> [Cloudflare Worker] --API--> [GoToSocial]
↑ ↓
|__[KV存储文章↔Toot映射]__|
一、前置准备
-
所需资源:
- Cloudflare 账户(启用 Workers 和 KV)
- GoToSocial 实例(获取 API 访问令牌)
- 支持 Webhook 的博客系统(如 Ghost/Hugo 等)
-
配置 KV 命名空间:
wrangler kv:namespace create BLOG_TOOT_MAPPING
在
wrangler.toml
中添加绑定:[[kv_namespaces]] binding = "BLOG_TOOT_MAPPING" id = "创建的KV命名空间ID"
二、核心实现代码(Cloudflare Worker)
完整 worker.js
代码:
// 配置常量
const GTS_INSTANCE = "https://您的GoToSocial实例域名";
const GTS_TOKEN = "您的GoToSocial访问令牌";
const CACHE_TTL = 600; // 互动数据缓存时间(秒)
export default {
async fetch(request, env) {
const url = new URL(request.url);
const path = url.pathname;
// 处理博客Webhook(新文章发布)
if (path === '/webhook/new-post' && request.method === 'POST') {
return handleNewPost(request, env);
}
// 提供互动数据API
if (path === '/api/interactions' && request.method === 'GET') {
return getInteractions(url.searchParams, env);
}
return new Response('Not Found', { status: 404 });
}
};
// 处理新文章Webhook
async function handleNewPost(request, env) {
try {
const { post_id, title, url, excerpt } = await request.json();
// 发布到GoToSocial
const tootResp = await fetch(`${GTS_INSTANCE}/api/v1/statuses`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${GTS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: `新文章发布:${title}\n${url}\n\n${excerpt}`,
visibility: "public"
})
});
const tootData = await tootResp.json();
// 存储映射关系到KV
await env.BLOG_TOOT_MAPPING.put(
`post:${post_id}`,
JSON.stringify({
toot_id: tootData.id,
toot_uri: tootData.uri,
created_at: Date.now()
})
);
return new Response('Sync success', { status: 200 });
} catch (err) {
return new Response(err.message, { status: 500 });
}
}
// 获取互动数据
async function getInteractions(params, env) {
const postId = params.get('post_id');
if (!postId) return new Response('Missing post_id', { status: 400 });
// 从KV获取Toot信息
const tootData = await env.BLOG_TOOT_MAPPING.get(`post:${postId}`);
if (!tootData) return new Response('Mapping not found', { status: 404 });
const { toot_id } = JSON.parse(tootData);
// 从GoToSocial获取互动数据(带缓存)
const gtsResponse = await fetch(
`${GTS_INSTANCE}/api/v1/statuses/${toot_id}/context`,
{
headers: {
'Authorization': `Bearer ${GTS_TOKEN}`,
'CF-Cache-Tag': `interactions_${toot_id}`
},
cf: { cacheTtl: CACHE_TTL }
}
);
if (!gtsResponse.ok) {
return new Response('Failed to fetch interactions', { status: 502 });
}
// 格式化响应数据
const interactions = await gtsResponse.json();
const formatted = {
post_id: postId,
toot_id: toot_id,
replies: interactions.descendants.map(item => ({
id: item.id,
author: {
name: item.account.display_name,
avatar: item.account.avatar
},
content: item.content,
created_at: item.created_at
})),
stats: {
replies_count: interactions.descendants.length,
// 其他统计信息...
}
};
return new Response(JSON.stringify(formatted), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' // 按需修改
}
});
}
三、部署与配置
1. 部署 Worker
# 安装依赖
npm install -g wrangler
# 登录Cloudflare
wrangler login
# 发布Worker
wrangler publish
2. 配置博客 Webhook
在博客后台设置 Webhook:
-
URL:
https://您的worker域名/webhook/new-post
-
触发事件: ” 文章发布/更新 ”
-
请求体示例(根据实际博客调整):
{ "post_id": "文章唯一ID", "title": "文章标题", "url": "文章完整URL", "excerpt": "文章摘要" }
四、使用流程
1. 文章发布同步流程
- 作者在博客发布文章
- 博客系统发送 Webhook 到 Worker
- Worker 自动:
- 在 GoToSocial 发布 Toot
- 存储文章 ID 与 Toot ID 的映射到 KV
2. 前端获取互动数据
在博客文章页添加:
<script>
async function loadInteractions(postId) {
const resp = await fetch(
`https://您的worker域名/api/interactions?post_id=${postId}`
);
const data = await resp.json();
// 示例:渲染回复列表
data.replies.forEach(reply => {
const div = document.createElement('div');
div.innerHTML = `
<img src="${reply.author.avatar}" width="30">
<strong>${reply.author.name}</strong>
<p>${reply.content}</p>
`;
document.getElementById('comments').appendChild(div);
});
}
// 调用示例(从页面元素获取postId)
loadInteractions('当前文章ID');
</script>
<div id="comments"><!-- 互动内容将在这里渲染 --></div>
五、增强功能建议
-
数据更新机制:
- 添加
/webhook/post-update
端点处理文章更新 - 使用 KV 的
metadata
记录最后同步时间
- 添加
-
安全增强:
// 在handleNewPost开头添加验证 const auth = request.headers.get('Authorization'); if (auth !== `Bearer ${WEBHOOK_SECRET}`) { return new Response('Unauthorized', { status: 401 }); }
-
监控与日志:
-
在 Worker 中添加日志输出:
console.log(`Synced post ${post_id} to toot ${tootData.id}`);
-
在 Cloudflare Dashboard 查看实时日志
-
-
错误恢复:
- 添加定期同步任务(通过 Cron Trigger)
- 实现 KV 数据校验接口
该方案完整实现了:
- 博客→GoToSocial 的自动同步
- 使用 Cloudflare KV 持久化映射关系
- 提供标准化 API 获取互动数据
- 前端友好 JSON 格式响应