辛宝的玄酒清谈!
1654 words
8 minutes
技术折腾 xLog 2 深入理解 xlog 的鉴权
2023-10-16

技术折腾 xLog 2 深入理解 xlog 的鉴权

内部链接

  • [[技术折腾 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 - 侧重插件开发]]

本文继续探索,承接 [[技术折腾 xLog 1 可行性探索]] 文章的内容。

本文尝试实现 xlog 文章的管理,实现自己内容的增删改查,基于官方的 openapi。本文价值 5 个币的赞赏,嘻嘻

背景#

如果要实现 xlog 文章的管理操作,这就需要鉴权,如何证明你是你,你能修改某个人的文章?在 web2 领域,我们一般会通过账号密码生成 jwt 模拟用户,或者通过 oauth 获得 access-token 授权操作特定的权限范围。

我们的技术前提是任意运行时环境,同时支持浏览器和 node.js 等不同运行环境,所以不考虑了网页端小狐狸 metamask 方式授权。

技术实现#

经过一番探索,和官方成员的 diygod 的指导,目前至少有两种方式实现数据授权管理,私钥和授权码,推荐第二种。

通过 Private Key#

首先是通过 private key,钱包的私钥。这种是万能的我证明是我的的方案,官方 sdk 也支持通过 private key 实现鉴权。

比如这里 https://crossbell.js.org/#md:connect-with-private-key

private key 从哪里来呢?举例小狐狸:

image.png

在账户详情里,有个 Show Private Key。配合官方 SDK 可以实现鉴权。

请务必注意:private key 非常机密,使用它也非常危险,这里只做技术介绍。

对于用户和开发者,更推荐下面的方案,授权 token 方案。

通过 siwe 授权#

SIWE 是 sign in with ethereum 的缩写。

通过 siwe 授权有以下几个步骤:

  • 开启 siwe 功能
  • 获取 token
  • 通过 api 获得授权

开启 siwe 功能#

首先访问自己的 xlog 控制台 https://xlog.app/dashboard/ 在左侧菜单栏最后一列选择设置。

选择 xSettings ,会有下图的展示:

image.png

我对 web3 不是很熟悉,口语化表达,圆框内的解释可以这样理解,主要是 Sync Operator 部分:

  • 操作授权。
  • 同步操作。通过开启 Sync Operator 可以允许通过其他方式同步内容到 Crossbell 也就是 xlog

一旦开启,我们就拥有了通过代码同步内容到 xlog 的能力。

获取 token#

目前获取 token 的最简单方案是在控制台中这样操作:

在 xsetting 页面,单击右键选择检查,或者开启开发者模式,或者按 F12,推荐使用 Chrome。会弹出一个模块,选择 控制台 Console,复制下面代码到控制台,按回车,会返回一长串文字。

JSON.parse(localStorage.getItem('connect-kit:account')).state.wallet.siwe.token

这段代码的作用是获取 siwe 的 token 信息,应该还有其他方案获取,目前这种方案最简单。

输入这段代码返回的内容就是需要的 token 信息,请自行保存好。

请注意不要两端的单引号,只要内容。(算了,我做个防呆设计,做个程序兼容吧。)

请复制控制台返回这一长串内容,这个叫鉴权 token,插件里会用到。

对开发者来说,在 node.js 项目中通过 .env 存储,在 github 中通过 secret 存储。避免明文暴露。

api 实践#

补充:也推荐使用 crossbell 的 sdk,效果是一致的。

有了刚才获得的 token,我们访问 https://indexer.crossbell.io/docs#/Siwe 官方提供的文档,我们所需要的所有东西都在这里了。

image.png

翻到页面最顶部,有个绿色的 Authorize 按钮,点击弹出一个弹窗,把刚才的 token 填写到 siwe 内容里

image.png

点击授权,这时候我们在每次请求的 header 里会添加 Authorization: Bearer ${token}

比如我们使用 GET /v1/siwe/account 就可以获取当前我们是谁了。

image.png

剩下的事情就简单了,有这样几个接口可以自行实践:

  • PUT /v1/siwe/contract/characters/{characterId}/notes 给用户新建文章
  • POST /v1/siwe/contract/characters/{characterId}/notes/{noteId}/metadata 更新指定 noteId 的文章
  • Delete /v1/siwe/contract/characters/{characterId}/notes/{noteId} 删除指定的文章

提供几个代码片段,经过几次尝试,一下代码片段有效。

首先是创建文章:

const title = '文章标题'
const content = '文章内容'
const base = "https://indexer.crossbell.io";
const ottoCharId = ''
const token = ''

const createPost = async () => {
  const url = `/v1/siwe/contract/characters/${ottoCharId}/notes`;

  const finalUrl = base + url;

  axios
    .request({
      method: "put",
      url: finalUrl,
      headers: {
        Authorization: `Bearer ${token}`,
      },
      data: {
        metadata: {
          tags: ["post"],
          type: "note",
          title: title,
          content: content,
          summary: "",
        sources: ["xlog"],
          attributes: [
            {
              value: "play-xlog-01", // 这里是自定义 slug
              trait_type: "xlog_slug",
            },
          ],
          attachments: [
            {
              name: "cover", // cover
              address: "",
              mime_type: "",
            },
          ],
        },
        locked: false,
        linkItemType: null,
      },
    })
    .then((res) => {
      console.log(2, res.data);
    })
    .catch((err) => {
      console.log(3, err);
    });
};

然后是更新文章,注意这里的 mode 从字面意思看 replace 比较简单

const updatePost = async () => {
  const noteId = 29;
  const mode = "replace"; //'merge'

  const updatePost =
    base +
    `/v1/siwe/contract/characters/${ottoCharId}/notes/${noteId}/metadata`;

  axios
    .request({
      method: "post",
      url: updatePost,
      headers: {
        Authorization: `Bearer ${token}`,
      },
      data: {
        metadata: {
          tags: ["post", "新想法"],
          type: "note",
          title: title,
          content: content,
          sources: ["xlog"],
          // summary: "",
          attributes: [
            {
              value: "play-xlog-01",
              trait_type: "xlog_slug",
            },
          ],
          attachments: [
            {
              name: "cover",
              address: "",
              mime_type: "",
            },
          ],
        },
        mode,
      },
    })
    .then((res) => {
      console.log(JSON.stringify(res.data, null, 2));
    })
    .catch((err) => {
      console.log(3, err);
    });
};

最后是删除文章

const deletePost = async () => {
  const characterId = '';
  const noteId = '';

  const deletePost =
    base + `/v1/siwe/contract/characters/${characterId}/notes/${noteId}`;

  axios
    .request({
      method: "delete",
      url: deletePost,
      headers: {
        Authorization: `Bearer ${token}`,
      },
    })
    .then((res) => {
      console.log(2, res.data);
    })
    .catch((err) => {
      console.log(3, err);
    });
};

踩坑经验#

创建文章的 body,和获取列表页的返回体不一样,主要是 content 是否多包一层,也就是请求参数和入库的数据不一样,稍微有点坑,最终创建文章的参数可用。

官方对入参比较宽容,啥参数都要,笑死,错误参数会导致列表页展示不出来,排查好久眼都花了,还是万分小心,仔细核对,等熟悉了官方源码,得加一加限制。

展望#

有了上述的 api 实践内容,我们未来可以做到以下事情:

  • 编写 node 脚本,实现文章的创建和更新
  • 比如在 github actions 里,每次推送了 markdown 可以调用函数,同步到 xlog
  • 比如在 obsidian 里编写插件,单击右键上传到 xlog
  • 接入自己的 cms 后台,进行文章管理

接下来,我会改造我的 obsidian 插件,实现上传文件到 xlog

image.png

敬请期待。

技术折腾 xLog 2 深入理解 xlog 的鉴权
https://ijust.cc/posts/play-xlog-02/
Author
辛宝 Otto
Published at
2023-10-16