内部链接
- [[技术折腾 xLog 1 可行性探索]]
- [[技术折腾 xLog 2 深入理解 xlog 的鉴权]]
- [[技术折腾 xLog 3 实现一个 obsidian 插件]]
- [[技术折腾 xLog 4 用 unStorage 封装 xLogDriver]]
- [[速通 - xLog 背后的 CrossBell SDK]]
- [[从官方 XLOG Obsidian 插件中能学到什么]]
- [[开发 Obsidian Sync To Xlog 插件之 处理 obsidian 的图片]]
- [[速通-CrossBell 的开源作品]]
- [[速通 Obsidian Docs - 侧重插件开发]]
想写个系列文章,从技术角度折腾地球上最好的 blog 平台 xlog.app, 争取做到以下事情(又开始夸海口了,新朋友,这是我的一种表达风格哈):
- 坚守心爱的文本编辑器,不离开 obsidian,一样发布 xlog 博客
- 不开网页,依然可以访问 xlog ,阅读其他人的博客和互动
- 同步 xlog 博客,用自己的域名和服务器展示自己的内容
- github + xlog = 多租户 cms
- 成为 xlog 成员
口嗨完毕,开始尝试技术的可行性探索。本次内容我们来谈,如何通过官方提供的 openapi 访问 xlog 内容。
需求背景
xlog 作为地球上最好的 blog 平台,有两个小问题,国内大陆访问可能会稍慢;全量迁移博客到 xlog 之后,自己的服务器就闲置了。
或者干脆一点,能不能拿在 xlog 上发布文章,自己部署一份儿?展望后面,谁说 xlog.app 才是访问 xlog 的唯一方式,我们是不是限制了 xlog 成为地球上最好的 headlessCMS 的潜力?
那么开搞。
技术探索
从目前公开的信息来看,xlog 官方提供了多种方式获取 xlog 内容,以访问自己的数据为例:
- 可以通过
crossbell
SDK 访问数据 - 可以通过 http restful API 访问数据
接下来我们通过两种方式分别访问自己的数据,当然是已发布的公开文章。
前置准备
看下面的内容的大前提是,你需要是 xlog 的用户,没有的快去注册吧。
获取公开的地址 Address,访问 https://xlog.app/dashboard ,按照图片圈选位置,点击,会提示 【已复制】,可以把这个视为当前用户在 xlog 上的唯一 ID。如果你有多个角色,ID 是不同的。
我们找个小本本把他记录上,以我的为例,后续使用它来访问: 0xA0fb033D4849b13A16690EEbdd575Dd90bF29711
接下来我们使用不同的方式获取数据。
通过 SDK 访问数据
这里需要自备 Node.js 环境,如果你是非 Node 技术栈,直接看下面 通过官方 API 访问章节
官方 SDK 的 Github 地址 https://github.com/Crossbell-Box/crossbell.js
吐槽:官方的文档不是很好,回头给他贡献修改下。
我们先使用只读用户访问数据。这里对应官方文档的 Class Indexer
首先启动项目
mkdir xlog-api-demo
cd xlog-api-demo && pnpm init
pnpm i crossbell
# 准备 ts 环境
pnpm i typescript tsx -D
touch index.ts
我们在 index.ts
中组织下面代码
import { createIndexer } from "crossbell";
const indexer = createIndexer();
const Address = process.env.USER_ADDRESS as `0x${string}`;
const getUserByAddress = async () => {
const res = await indexer.character.getMany(Address);
console.log(res);
};
getUserByAddress();
具体执行,看对应的仓库,主要是根据 address 获取角色信息。可以获取 metadata 和 charactorId
// const characterId = 53710;
const getNotesByCharacterId = async (characterId: number) => {
const res = await indexer.note.getMany({
characterId,
includeNestedNotes: false,
limit: 10,
});
console.log(res);
};
这个就可以读取到用户的数据了。翻页可以通过 total + cursor + limit 实现, 游标 cursor 在每次请求结果中体现。
查询具体详情,可以通过这个实现
const noteId = 16;
const characterId = 53710;
const getNoteById = async (noteId: number) => {
const res = await indexer.note.get(characterId, noteId);
// console.log(res);
console.log(res?.metadata?.content);
};
getNoteById(noteId);
类推其他方法,这里不一一列举,可以实现各类数据的展示。
通过以上两个方法,我们可以实现个人博客的搭建了,首页网站基础信息、列表页更新列表、详情页信息展示。
这里有两个细节,提一下:
- 自定义的 embed,比如音视频,需要自己兼容,这个有机会留到后面处理
- 图片和链接全程用的 ipfs,这里展开说下
ipfs 协议
ipfs 协议包罗万物,简单说就是一串 hash,或者 cid 对应一个网络资源,细节不展开,对 web 来说 ipfs 并不是 http 协议,要展示图片,需要有一层协议转换的过程,这里面就涉及到 api 网关的解析。
这一块,一开始我是翻源码自己找到了 gateway ,后来返现好像不用特别 geek,访问 https://crossbell-ipfs-utils.vercel.app/img/bafkreiarfgti3xpv2oznl7rzanfbzm7gvklvcwn5poqb53wlhi3n4cwp2a 这个网页,这个网页从不同的 gateway 加载图片,你选择一个顺眼的就可以
注意,为了更快的加载速度,如果你考虑子托管,显然自己处理资源是最好的。
另外,xlog 有专门的工具库处理这个问题,可以作为编程方案使用,做个备忘
import { ipfsFetch } from "@crossbell/ipfs-fetch";
ipfsFetch(ipfsUrl)
.then((res) => {
console.log(1, res);
})
.catch((err) => {
console.log(3, err);
});
细节就谈这么多,更多的交互翻一翻 api 就好了。
通过官方 API 访问
刚才的技术方案依赖 Node.js,如果你擅长其他语言,或者想足够简单,官方还提供了 api,通过 restful api 进行交互,侵入性更低。
直接访问
https://indexer.crossbell.io/docs
按照 SDK 里提到的 address 作为入口
- 访问 Character 第一个 Get characters of an address 获取 characterId
/v1/addresses/{address}/characters
- 访问 Get a character by characterId 获取用户基础信息,给首页使用
/v1/characters/{characterId}
- 访问
/v1/characters/{characterId}/notes
得到对应人的文章 - 访问
/v1/notes/{characterId}/{noteId}
得到对应的文章详情页
这里后续会实际使用,细节先略过
通过以上访问,依然可以实现和 sdk 一样的效果。
接下来,我们会继续探索,如果通过 sdk/api 实现后台管理,除了读取内容,我们还希望可以对文章进行增删改查,实现另一个 xlog。
补充 ipfs 文件上传
实际体验看,在 xlog 上传的图片、音频、视频文件会自动上传到 ipfs ,并替换图片链接为 ipfs 链接。
目前有两种方案,使用 xlog 的 ipfs 上传协议、使用 http 自己的方案。
这里简单给 ipfs 上传指个路,正好熟悉下 xlog 源码。
思路如下:
- 因为图片上传是触发在拖拽和粘贴,所以源码中可以搜索 drag/patse 定位到
src/components/ui/ImageUploader.tsx#handleDrop
- 进一步定位到
handleFile
-uploadFile
-useUploadFile
-UploadFile
- 可以看到是通过 POST 协议提交到指定的 接口上
截图先忽略,我发现没有鉴权…
结论
以上,是可行性探索,至少可以通过接口实现数据的获取,在下一篇文章中,我们会探索,通过鉴权实现文章的发布等管理操作,在那个时候 xlog 就具有了 headless cms 的能力。