This article continues the exploration and builds on the content of the article "[Technical Exploration of xlog] Smoother User Experience 1 Feasibility Study".
In this article, we will attempt to implement the management of xlog articles, allowing us to add, delete, modify, and search our own content based on the official openapi. This article is worth 5 coins as appreciation. Hehe.
Background
To implement the management of xlog articles, we need authorization. How do we prove that you are you and that you can modify someone else's article? In the web2 domain, we usually generate a JWT token using an account password to simulate a user, or obtain an access token through OAuth to authorize specific permissions.
Our technical premise is any runtime environment that supports different runtime environments such as browsers and node.js, so we will not consider authorization methods like the web browser extension Metamask.
Technical Implementation
After some exploration and guidance from the official member diygod, there are currently at least two ways to implement data authorization management: private key and authorization code, with the second method being recommended.
Using Private Key
The first method is to use a private key, which is the wallet's private key. This is a universal solution to prove that I am the owner. The official SDK also supports authorization through private keys.
For example, here is the link: https://crossbell.js.org/#md:connect-with-private-key
Where does the private key come from? Let's take the example of the web browser extension:
In the account details, there is an option to "Show Private Key". By using the official SDK, we can implement authorization.
Please note: the private key is highly confidential and using it is very risky. This is only for technical demonstration purposes.
For users and developers, the following method is recommended: the authorization token method.
Using siwe Authorization
SIWE stands for "sign in with ethereum".
There are several steps to authorize using siwe:
- Enable siwe functionality
- Obtain a token
- Obtain authorization through an API
Enable siwe Functionality
First, visit your xlog dashboard at https://xlog.app/dashboard/ and select "Settings" in the last column of the left menu.
Select "xSettings" and you will see the following display:
I am not very familiar with web3, so let me explain the content in the circular frame in plain language, mainly the Sync Operator section:
- Operation authorization.
- Synchronized operation. By enabling Sync Operator, you can allow content to be synchronized to Crossbell (xlog) through other means.
Once enabled, we have the ability to synchronize content to xlog through code.
Obtain a Token
Currently, the simplest way to obtain a token is to perform the following steps in the console of the xsetting page:
On the xsetting page, right-click and select "Inspect", or enable developer mode, or press F12. It is recommended to use Chrome. A module will pop up, select the "Console" tab, copy the code below into the console, press Enter, and a long string of text will be returned.
JSON.parse(localStorage.getItem('connect-kit:account')).state.wallet.siwe.token
This code is used to obtain the siwe token information. There may be other ways to obtain it, but currently this is the simplest method.
The content returned by this code is the required token information. Please save it yourself.
Please note that there should be no single quotes on both ends, only the content. (Well, I'll make a foolproof design and make the program compatible.)
Please copy the long content returned by the console. This is the authorization token that will be used in the plugin.
For developers, it can be stored in a .env file in a node.js project or in a secret in GitHub to avoid exposing it in plain text.
API Practice
With the token obtained earlier, we can visit the official documentation provided by https://indexer.crossbell.io/docs#/Siwe. Everything we need is here.
Scroll to the top of the page, there is a green "Authorize" button. Clicking it will open a popup window where you can enter the token obtained earlier into the siwe field.
Click "Authorize", and now the header of each request will include Authorization: Bearer ${token}
.
For example, we can use GET /v1/siwe/account
to find out who we are.
The rest is simple. Here are a few APIs you can try on your own:
PUT /v1/siwe/contract/characters/{characterId}/notes
to create a new article for a userPOST /v1/siwe/contract/characters/{characterId}/notes/{noteId}/metadata
to update a specific noteId articleDELETE /v1/siwe/contract/characters/{characterId}/notes/{noteId}
to delete a specific article
Here are a few code snippets that have been tested and are effective.
First, creating an article:
const title = 'Article Title'
const content = 'Article 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", // Custom slug here
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);
});
};
Next, updating an article. Note that the mode
parameter is straightforward, "replace" is simpler to understand than "merge".
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", "new idea"],
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);
});
};
Finally, deleting an article:
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);
});
};
Pitfall Experience
The request body for creating an article is different from the response body for getting the list. The main difference is whether the content is wrapped in an additional layer. It was a bit tricky, but the final parameter for creating an article is valid.
The official API is quite tolerant of input parameters, accepting any parameters. This caused a lot of confusion when the list page couldn't be displayed. It took a long time to troubleshoot, so be extremely careful and double-check. Once you are familiar with the official source code, you can add some restrictions.
Outlook
With the API practices mentioned above, we can achieve the following in the future:
- Write a Node.js script to create and update articles
- For example, in GitHub actions, every time a markdown file is pushed, a function can be called to synchronize it to xlog
- For example, in Obsidian, write a plugin to upload to xlog with a right-click
- Integrate with our own CMS backend for article management
Next, I will modify my Obsidian plugin to upload files to xlog.
Stay tuned.