TL;DR
IPFS_PATH="$HOME/Library/Containers/xyz.planetable.Planet/Data/Library/Application Support/ipfs/" $(find /Applications/Planet.app/Contents/Resources/ -name "ipfs-$(uname -m)-*.bin" | head -n 1) config --json DNS.Resolvers '{"eth.": "https://dns.eth.limo/dns-query"}'`
只需要将 Planet 使用的 IPFS 配置文件里的 DNS.Resolvers 设置成 eth.limo 就可以了,具体可以参考 eth.limo 的文档
对普通的 IPFS (比如 IPFS Desktop),直接用上面提到的文档里的命令
ipfs config --json DNS.Resolvers '{"eth.": "https://dns.eth.limo/dns-query"}'
就可以了。如果只安装了 IPFS Desktop,需要替换成对应的 IPFS 命令行。
就像短博文里说的那样,使用 giscus 配合 Planet 批量设置评论区还是有一点麻烦的。
其实主要问题是用户可能通过不同的 IPFS 网关访问,这就导致了不管是 URL 还是 pathname 都不可靠。帖子的标题确实可行,但可能出现没有标题的短博文这种情况。
一开始的选择是如果没有标题那么就用 JS 把 og:title 写入成发帖时间,可以实现功能,但会有两个问题,如果后期加上了标题、或者发帖时间被修改(Planet 似乎有一个 bug 是编辑帖子的时候时间有概率会变成现在),而手动设置时间则无法设置“秒”,出现这两种情况的话,之前的评论就不会显示了。
使用标题还有另一个需要处理的问题就是标题本身发生变化的情况。
所以最终还是决定用 JS 将所有文章的 Open Graph Title 全部动态替换成 Post ID(反正读取这玩意的服务都不会去跑 JS),最终代码如下,似乎只能放在 header 的 Custom Code 里才能生效:
适用于 Plain 模板:
<script>
(function() {
// --- Step 1: Compare og:site_name to the actual page title
const siteNameTag = document.querySelector('meta[property="og:site_name"]');
const siteName = siteNameTag ? siteNameTag.getAttribute('content').trim() : '';
const pageTitle = document.title.trim();
// --- Step 2: If they differ, update og:title with the last part of the pathname
if (siteName !== pageTitle) {
let path = window.location.pathname;
// Remove trailing slash if any
if (path.endsWith('/')) {
path = path.slice(0, -1);
}
// Extract the last segment of the path
const lastPart = path.substring(path.lastIndexOf('/') + 1);
// Update <meta property="og:title" ...>
const ogTitleTag = document.querySelector('meta[property="og:title"]');
if (ogTitleTag) {
ogTitleTag.setAttribute('content', lastPart);
console.log(`Updated og:title to "${lastPart}"`);
}
}
// --- Step 3: Create the wrapper <div> elements
const outerDiv = document.createElement('div');
outerDiv.style.display = 'flex';
outerDiv.style.alignItems = 'center';
outerDiv.style.padding = '0px 20px';
const innerDiv = document.createElement('div');
innerDiv.style.flex = '1';
innerDiv.style.width = '100%';
innerDiv.style.maxWidth = '700px';
innerDiv.style.margin = '20px auto';
// --- Step 4: Load the Giscus script
// (You could also wrap this in a <div> if desired, as in prior examples)
const giscusScript = document.createElement('script');
giscusScript.src = "https://giscus.app/client.js";
giscusScript.setAttribute('data-repo', "extrawdw-code/blog-giscus");
giscusScript.setAttribute('data-repo-id', "R_kgDONkfVTg");
giscusScript.setAttribute('data-category', "General");
giscusScript.setAttribute('data-category-id', "DIC_kwDONkfVTs4ClpWU");
giscusScript.setAttribute('data-mapping', "og:title");
giscusScript.setAttribute('data-strict', "1");
giscusScript.setAttribute('data-reactions-enabled', "1");
giscusScript.setAttribute('data-emit-metadata', "0");
giscusScript.setAttribute('data-input-position', "top");
giscusScript.setAttribute('data-theme', "preferred_color_scheme");
giscusScript.setAttribute('data-lang', "en");
giscusScript.setAttribute('data-loading', "lazy");
giscusScript.setAttribute('crossorigin', "anonymous");
giscusScript.async = true;
// Append script inside the inner div
innerDiv.appendChild(giscusScript);
// Then append the inner div to the outer div
outerDiv.appendChild(innerDiv);
// Insert script into <head> or near end of <body>
document.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(outerDiv);
});
})();
</script>
适用于 Sepia 模板:
<script>
(function() {
// --- Step 1: Compare og:site_name to the actual page title
const siteNameTag = document.querySelector('meta[property="og:site_name"]');
const siteName = siteNameTag ? siteNameTag.getAttribute('content').trim() : '';
const pageTitle = document.title.trim();
// --- Step 2: If they differ, update og:title with the last part of the pathname
if (siteName !== pageTitle) {
let path = window.location.pathname;
// Remove trailing slash if any
if (path.endsWith('/')) {
path = path.slice(0, -1);
}
// Extract the last segment of the path
const lastPart = path.substring(path.lastIndexOf('/') + 1);
// Update <meta property="og:title" ...>
const ogTitleTag = document.querySelector('meta[property="og:title"]');
if (ogTitleTag) {
ogTitleTag.setAttribute('content', lastPart);
console.log(`Updated og:title to "${lastPart}"`);
}
}
// --- Step 3: Create the wrapper <div> elements
const outerDiv = document.createElement('div');
outerDiv.style.display = 'flex';
outerDiv.style.alignItems = 'center';
outerDiv.style.padding = '0px 20px';
const innerDiv = document.createElement('div');
innerDiv.style.flex = '1';
innerDiv.style.width = '100%';
innerDiv.style.maxWidth = '700px';
innerDiv.style.margin = '20px auto';
// --- Step 4: Load the Giscus script
// (You could also wrap this in a <div> if desired, as in prior examples)
const giscusScript = document.createElement('script');
giscusScript.src = "https://giscus.app/client.js";
giscusScript.setAttribute('data-repo', "extrawdw-code/blog-giscus");
giscusScript.setAttribute('data-repo-id', "R_kgDONkfVTg");
giscusScript.setAttribute('data-category', "General");
giscusScript.setAttribute('data-category-id', "DIC_kwDONkfVTs4ClpWU");
giscusScript.setAttribute('data-mapping', "og:title");
giscusScript.setAttribute('data-strict', "1");
giscusScript.setAttribute('data-reactions-enabled', "1");
giscusScript.setAttribute('data-emit-metadata', "0");
giscusScript.setAttribute('data-input-position', "top");
giscusScript.setAttribute('data-theme', "preferred_color_scheme");
giscusScript.setAttribute('data-lang', "en");
giscusScript.setAttribute('data-loading', "lazy");
giscusScript.setAttribute('crossorigin', "anonymous");
giscusScript.async = true;
// Append script inside the inner div
innerDiv.appendChild(giscusScript);
// Then append the inner div to the outer div
outerDiv.appendChild(innerDiv);
// Finally, place this wrapper inside the div with id="main-container"
document.addEventListener('DOMContentLoaded', function() {
const container = document.getElementById("main-container");
if (container) {
container.appendChild(outerDiv);
} else {
console.warn('Could not find a container with id="main-container".');
}
});
})();
</script>
外边套的俩 div 是参考的站长的网站设计。
最终效果如下:
在聚合站点的时候,只要打开“重用原始ID”的选项就可以让评论也“同步”过去。
p.s.:Planet 在聚合站点且保留 ID 的情况下,Bug 太多了……经常出现原帖被当成聚合过来的帖子,然后无法编辑需要重启的情况。
在笔记本上创建了一个 Planet 并且将其聚合到位于 Mac mini 的“主”微博 Planet。
Planet: ipns://k51qzi5uqu5dkd1qdqw2dyr5hry9wfuxhy11xltnif380ismqoyahvj27oyzgt
欢迎访问 Extrawdw 博客的 IPFS 版本。
本网站的 IPNS 地址为:/ipns/k51qzi5uqu5dhk7d5cfgj7d6xkt9u51l87qaysw7jcb5d6wxjub9b23ahu80pg.
一些已废弃/已删除文章的存档可以在 /ipns/k51qzi5uqu5dk04xxfqzcfc2ya5dhoer3xy1i4ppo9u1r4cilcxhih6fpbvfg0 访问。
以上超链接均使用 ipfs.io 网关,你也可以使用其它公共网关或者本地网关进行访问。
First, make sure there is IPNS Key in local IPFS keystore:
ipfs key list
If the key is not present, import with:
ipfs key import <name> <key>
Then, update IPNS record with:
ipfs name publish --key=<key name> /ipfs/...
Extrawdw Blog will also be accessible on extrawdw.bit from now on.
The .bit is another blockchain domain name that can be registered here:
Note: The above is the referral link, if you register through the above link then I will receive a pay back, feel free to delete and replace the referrer. The registration fee will be reduced by 5% if there is a referrer.
One advantage of .bit addresses over .eth is that updating domain records is free: you don't need to pay gas (network fee) for it.
Welcome visitor,
This is Extrawdw Blog (blog.extrawdw.net and i.extrawdw.net) on IPFS.
Archives of obsolete/deleted articles are available at /ipns/k51qzi5uqu5dk04xxfqzcfc2ya5dhoer3xy1i4ppo9u1r4cilcxhih6fpbvfg0/. It might contain posts in Chinese.
Articles will start being posted here soon.
Enjoy!