惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
Recorded Future
Recorded Future
B
Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
GbyAI
GbyAI
IT之家
IT之家
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
罗磊的独立博客
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
腾讯CDC
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
aimingoo的专栏
aimingoo的专栏
博客园 - Franky
Martin Fowler
Martin Fowler
P
Proofpoint News Feed
月光博客
月光博客
博客园_首页
C
Check Point Blog
宝玉的分享
宝玉的分享
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Tenable Blog
Vercel News
Vercel News
SecWiki News
SecWiki News
Security Latest
Security Latest
Microsoft Security Blog
Microsoft Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
阮一峰的网络日志
阮一峰的网络日志
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google Online Security Blog
Google Online Security Blog
N
News and Events Feed by Topic
Y
Y Combinator Blog
U
Unit 42
The Register - Security
The Register - Security
云风的 BLOG
云风的 BLOG
K
Kaspersky official blog
WordPress大学
WordPress大学
Blog — PlanetScale
Blog — PlanetScale
N
Netflix TechBlog - Medium
Jina AI
Jina AI

pseudoyu

周报 #106 - OpenClaw 实践、搬家与生活的能量 周报 #105 - 打破无聊的社会 2025 年末回顾 - Anything different is good 周报 #104 - 被遗忘的生活一隅 周报 #103 - 工作随想与被吞噬的生活 周报 #102 - 我是如何使用 AI 的 周报 #101 - 消失了三个月,我的生活发生了什么变化 周报 #100 - 过去这十年 周报 #99 - 离开 RSS3,迷茫与重启人生 周报 #98 - 生活之书与 Agentic Coding 周报 #97 - 日本旅行小记 28 岁的自白:去找寻自己 周报 #96 - 基于 Roo Code/Cursor 的 iOS 开发工作流 周报 #95 - All AI 与 No AI 周报 #94 - 生活的界限与真实感 周报 #93 - 婚礼纪录片与失落的电影院 周报 #92 - 数码产品 Porn 与断舍离 周报 #91 - 一席、消失的时代与自由意志 周报 #90 - OVH 服务器、Coolify 与对 AI Coding 的思考 周报 #89 - 博客改版与 TheWan App 发布 周报 #88 - 方大同、《回留》与人生清单 周报 #87 - 我们主持了自己的宝可梦婚礼,还为它办了一个展 周报 #86 - 用影像打败时间 周报 #85 - 京华烟云 Moment in Peking 周报 #84 - 想起他们 Echos of Reverie 周报 #83 - 年初收纳(房间、设备、服务、软件) 周报 #82 - Brand New Year 与写日记 2024 年末回顾 - 不完美的日子 周报 #81 - Engaged -> Married 与时间的尺度 周报 #80 - 关于求婚、爱情与婚姻 求婚誓言 | 爱的本质是被看见 周报 #79 - 清迈旅居、曼谷之行与 Follow RSSHub 开发实践 #01:Telegram 频道 RSS 订阅实现与部署方案 Follow —— 信息获取的另一种形态 周报 #78 - NAS、Chromebook 与 Zeabur 折腾小记 周报 #77 - 一间只属于自己的房间(对亲密关系的思考) 周报 #76 - 关于 Adulting 的思考 周报 #75 - 回答 Dayu 关于独立博客的 9 个问题 周报 #74 - 清迈异闻录 周报 #73 - 《社交网络》、Ego 与中秋的现充生活 周报 #71 - Indie App 体验计划 周报 #70 - 消失的附近,Burnout 与 Boreout 周报 #69 - AI Coding 体验与学习的源动力 周报 #68 - 关于写博客这件事与我生活的色彩 使用 GoatCounter 与 Zeabur 搭建网站数据统计系统 周报 #67 - 使用 follow 重塑我的信息输入系统 周报 #66 - 10x 工程师、技术热情与个人工具箱 周报 #65 - Adventure X 体验、Apple Notes 笔记实践与 EpubKit 周报 #64 - 生活在此处(《斯通纳》与《Normal People》) 使用 WebP Cloud 与 Cloudflare WAF 为你的图床添加隐私和版权保护 当云服务器崩溃时,我是如何救援重要数据的 周报 #63 - 不愉快的订花经历、商家和消费者与日渐 AI 化的人 从零开始搭建你的免费图床系统(Cloudflare R2 + WebP Cloud) 2024 年了,我的博客有了什么变化 周报 #62 - 香港之行、5am club 计划与 Rust 学习 周报 #61 - 好朋友的婚礼与对婚姻的思考 27 岁的自白:成长是一件很扫兴的事 周报 #60 - 虫子旁、教育理念与 EpubKit 周报 #59 - 沙漠之行、家庭与家人 周报 #58 - 远程工作这一年 周报 #57 - 生活在别处(巴厘岛之行) 周报 #56 - 面基 Randy 与考科目二 周报 #55 - 油画体验、博客系统升级与对 self-hosting 的思考 周报 #54 - 漂流计划、钱包被盗与 Home Server 周报 #53 - 空白的三个月、生活漂流与春节 周报 #52 - 自谦型人格与感情的另一个视角 周报 #51 - 追星小鹿 Lawrence 与生病 周报 #50 - 天坛漫步、重拾阅读和费曼学习法 周报 #49 - 烧焦的键盘、庞贝神话展、健康调整与神奇的梦 周报 #48 - 内蒙之行、清迈游民、重启的周报与生活 周报 #47 - 采访、拳击与工作状态 周报 #46 - 告别长发、周报初心与合约开发 个人信息获取与知识管理系统(Heptabase + Logseq + Readwise) 周报 #45 - 读书笔记、知识管理与双城生活 周报 #44 - 静寂工人、xLog 与日常 周报 #43 - In the Mood for Love 周报 #42 - 感情观、生活状态与自我 周报 #41 - 生日的仪式感、时间管理与北京之行 26 岁的自白:推石头的人 周报 #40 - 开源之夏、代码之外与流逝的睡眠 周报 #39 - 初患新冠、过去的心绪与失而复得的分享欲 周报 #38 - Foundry、Logseq 与 Surge Ponte 周报 #37 - 浮生半月闲( 武汉 | 香港 | 五月天) 周报 #36 - 肠胃炎、捏捏近况与新的旅途 周报 #35 - 登报、搬家和生病的捏捏 周报 #34 - Don't Stop the Clocks 周报 #33 - 北京 x 故事 x 离别 周报 #32 - 武汉、久别重逢与流动的心绪 周报 #31 - 开源、前端开发与 ChatGPT 实践 周报 #30 - 开源预算、写作初心与对技术的谦卑 Cosmos 区块链架构与 Tendermint 共识机制 周报 #29 - 日语学习经验、Steam Deck 与居酒屋 周报 #28 - Mastodon、故乡与捉迷藏的猫 周报 #27 - 何以为家 周报 #26 - 博客、客制化键盘和新服务器 周报 #25 - 基于 Crossbell 的个人信息输出与同步系统(重构) 摄影集 #02 - Moment In Peking (The Autumn) 摄影集 #01 - Cat In The Box 2022 年末回顾 - 迷茫、低谷与改变 周报 #24 - Love Actually | 真爱至上
从零开始搭建你的免费博客评论系统(Remark42 + fly.io)
pseudoyu · 2024-07-21 · via pseudoyu

前言

在「2024 年了,我的博客有了什么变化」一文中,我介绍了自己使用 Serverless 平台和一些开源项目搭建的博客系统,也开启了这个系列教程来记录搭建和部署全过程。

本篇是关于评论系统的解决方案。

评论系统迭代

remark42_comments

我常常觉得评论不仅仅是读者与作者之间的沟通互动,其内容本身也是文章的一部分,甚至常常有些评论的思考与观点讨论会比文章本身更有价值,所以对于评论系统一直很重视,并不愿意信任一些第三方托管的服务,不希望有什么审查,也想风格尽可能简约,并与自己的博客风格相符。

在博客发展过程中,评论系统方案也经历过几次迭代,关于评论系统的类型和选择,我很喜欢的开发者 reorx 在「更换博客评论系统」中有详细的介绍了,我不作更多引申了,本文更重个人体验与详细的搭建过程。

我最早使用的博客评论系统是万恶的 Disqus,一个笨重且会收集用户隐私的知名评论系统,因为加载比较慢,且免费版本经常会附带一些广告,实在难以忍受,再加上当时其实也基本上没什么评论,并没有什么迁移负担,用了没多久就直接弃用了。

Utterances

于是换成了另一个基于 GitHub issues 的评论系统 utterances,它会为每篇文章生成一个 issue,用户通过授权 GitHub 登录来对 issue 发表评论。这种方式的好处是只需要授权一个 utterances-bot 来进行管理,无需自己部署服务,维护数据库等。但是用了一段时间后,觉得有几点不足:

  • 基于 GitHub API 进行评论管理,如之后接口变动或对这类利用 issue 进行评论的方式进行限制,会不太稳定
  • 读者必须要授权 GitHub 登录,非技术人员或使用移动端阅读的读者使用起来很不方便
  • 会污染 GitHub 仓库的 Issues 记录,也不方便后续迁移到其他系统

Cusdis + Supabase + Vercel

Cusdis 是 Randy 做的一个注重数据隐私的开源的评论系统,十分轻量,经过 gzipped 后大约只有 5kb,从名字来看也知道是难以忍受 Disqus,自己做了一个替代版,因此它也是支持 Disqus 历史数据导入的,很贴心。

从 2021 年中就开始使用了,到现在整整三年了,除了最开始的时候因为 Heroku、Railway 相继收费而折腾了一下部署平台外,一直都稳稳地运行着,不过我在使用中也有遇到一些问题:

  • 大概是由于微信内置浏览器做了一些魔改,在博客从微信聊天/对话打开是看不到评论组件的
  • 尽管可以输入邮箱,但并不支持订阅评论回复
  • 需要管理员手动审核评论,但评论提醒的 TG Bot 时常失效而错过评论

不过整体来说时至今日依然是十分值得推荐的方案,轻量,方便自部署,风格也简约好看,搭建教程参看「轻量级开源免费博客评论系统解决方案 (Cusdis + Railway)」。

鉴于 Railway 从去年 8 月起已经取消了 Free Plan,如果依然想完全免费使用,可以使用 Vercel/Netlify/Zeabur 免费部署主项目,并在 Supabase 上部署一个免费的 PostgreSQL 数据库实例,把链接作为环境变量传入 Cusdis 服务中即可,其他流程大同小异。

另外因为其核心功能已经许久没有什么更新,比起其他较为成熟的评论系统也显得有些简陋,不过由于我也秉持着够用即可的原则,一直没动迁移/更新的念头,只有在其中一阵子在学前端时还参与了一些 Cusdis V2 版本的开发,不过也没做多久。

由于四月时 Vercel 部署升级的时候一直失败,导致接近几周的时间没收到评论,再加上确实有了一些功能需求,所以下定决心进行迁移,探究起了新的方案。

调研了一圈后选择了 reorx 在「更换博客评论系统」一文中最后选定的 Remark42

单纯就配置选项来说比起 Cusdis 还是丰富了不少,目前配置了常用的几种社交账号登录(GitHub、Twitter、Telegram、邮箱)、可以匿名评论、支持邮件订阅回复提醒并且也设置了 TG bot 提醒,并且部署在 fly.io,go 单二进制 + 数据库单文件,很舒服的解决方案,更详细的 Remark42 的介绍和优势可以参看上面那篇文章。

虽然 Remark42 提供了一些迁移方案,但本身并不支持我使用的 Cusdis,但好在它是用 Golang 写的,我自己添加了迁移逻辑,将这些年沉淀下来的 438 条评论数据都无缝迁移过来了。

Remark42 + fly.io 的方案仅牵扯到单个服务,数据库使用的是 boltdb 挂载于 volume 中,但所有操作都在 fly.io 的 Free Plan 中。

下面将从零开始介绍如何搭建这个免费评论系统。

Remark42 本身代码开源 —— 「GitHub - umputun/remark42」,并提供了官方维护的镜像,文档清晰易读,可以根据自己的实际需求进行配置。

安装 flyctl 命令行工具

fly.io 与我之前使用的 Railway、Zeabur 等很大的一个不同点是它大部分操作基于命令行与配置文件,而不是在网页端管理后台进行操作,所以首先需要根据文档安装 flyctl 命令行工具。

以 macOS 为例,我使用 brew 进行安装:

brew install flyctl

授权登录

打开终端工具,使用以下命令进行授权登录:

flyctl auth login

fly_auth_login

fly_auth_web

在 Web 端进行账户登录或新建账号,完成后点击 Continue as xxx 即完成 flyctl 命令行的授权登录。

创建应用目录

create_fly_config

由于我通常会手动进行进行配置管理,而不是用它官方的模板,所以我会新建一个类似 remark42-on-fly 的目录,并将所有的配置文件、环境变量等放在这个路径下。

并使用 VS Code 进行编辑(也可以使用 vim 或者其他编辑器/IDE)。

配置文件

fly.io 主要是使用 .toml 格式的配置文件进行服务管理,以下是我部署的服务对应的配置文件:

app = 'yu-remark42-01'
primary_region = 'hkg'

[build]
image = 'umputun/remark42:latest'

[[mounts]]
source = 'remark42_data_01'
destination = '/srv/var'

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 1
processes = [ 'app' ]

[env]
REMARK_URL = 'https://yu-remark42-01.fly.dev/'
SECRET = 'remark42-secret'
SITE = 'remark42-demo'
ADMIN_SHARED_ID = ''

[[vm]]
cpu_kind = 'shared'
cpus = 1
memory_mb = 256

这是详细的配置说明:

  • app:应用名称,这里我使用了 yu-remark42-01,可以根据自己的实际情况进行修改
  • primary_region:部署区域,可以从这个列表中选择自己想部署的区域,我选择了香港
  • [Build],这个部分主要是服务镜像相关的配置
    • image:服务镜像,使用了官方提供的 umputun/remark42:latest,如有需要可以指定 tag 版本
  • [[mounts]],这个部分主要是挂载数据卷的配置,由于 Remark42 使用 boltdb 数据库,需要持久化存储
    • source:数据卷名称,这里我使用了 remark42_data_01
    • destination:挂载目录,这里我挂载到了 /srv/var,这个目录是 Remark42 默认的数据存储目录
  • [http_service],这个部分主要是服务相关的配置
    • internal_port:服务内部端口,使用 8080
    • force_https:强制使用 HTTPS
    • auto_stop_machines:设置为 false
    • auto_start_machines:设置为 true,即自动启动
    • min_machines_running:最小运行机器数,设置为 1
    • processes:服务进程,设置为 app
  • [env],配置环境变量
    • REMARK_URL:Remark42 服务的 URL,这里我使用了 https://yu-remark42-demo.fly.dev/,这是 fly.io 自动生成的,后续如果有了自定义域名则需要更改
    • SITE:站点名称,这里我使用了 remark42-demo
    • SECRET:自定义的 JWT Token,这里我使用了 remark42-secret
    • ADMIN_SHARED_ID:管理员 ID,这里我使用了空字符串,即没有管理员,后续可以补充
  • [[vm]],这个部分主要是机器相关的配置
    • cpu_kind:CPU 类型,设置为 shared
    • cpus:CPU 数量,设置为 1
    • memory_mb:内存,设置为 256MB

创建服务

完成并检查配置后,运行以下命令进行服务创建:

flyctl launch

fly_launch_remark42

环境变量配置

目前只是部署了服务,并没有设置环境变量,因此服务启动会有问题,接下来我们设置环境变量,放在prod.env 文件中:

AUTH_GITHUB_CID=<your_github_cid>
AUTH_GITHUB_CSEC=<your_github_csec>
AUTH_TWITTER_CID=<your_twitter_cid>
AUTH_TWITTER_CSEC=<your_twitter_csec>
AUTH_ANON=true
AUTH_TELEGRAM=true
TELEGRAM_TOKEN=<your_telegram_token>
NOTIFY_ADMINS=telegram
NOTIFY_TELEGRAM_CHAN=<your_telegram_group>
NOTIFY_USERS=email
AUTH_EMAIL_ENABLE=true
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
SMTP_TLS=true
[email protected]
SMTP_PASSWORD=<your_password>
[email protected]
[email protected]

环境变量的部分相对比较复杂,具体参数参看文档

登录/授权配置

我配置了匿名评论、GitHub、Twitter 与 Telegram 几种方式,可以根据自己的情况配置其他登录方式。

  • 匿名登录
    • AUTH_ANON:是否允许匿名评论,我选择了允许,即用户可以不登录评论
  • GitHub 登录
    • AUTH_GITHUB_CIDAUTH_GITHUB_CSEC:GitHub OAuth App 的 Client ID 与 Client Secret
  • Twitter 登录
    • AUTH_TWITTER_CIDAUTH_TWITTER_CSEC:Twitter OAuth App 的 Client ID 与 Client Secret
  • Telegram 登录
    • AUTH_TELEGRAM:是否允许 Telegram 登录
    • TELEGRAM_TOKEN:Telegram Bot Token,通过 botfather 创建
  • 邮箱登录
    • AUTH_EMAIL_ENABLE:是否允许邮箱登录
    • AUTH_EMAIL_FROM:邮箱登录的发送邮箱

通知配置

  • Telegram 通知管理员,参看文档这部分进行 Telegram Bot 的创建和配置
    • NOTIFY_ADMINS:通知管理员的方式,选择 telegram
    • NOTIFY_TELEGRAM_CHAN:如启用 telegram 通知管理员,需要配置对应 Channel id,只需要填写 t.me/xxx 后面的 id 部分即可,如 pseudoyuchat
  • Email 通知用户,参看文档这部分进行邮箱 SMTP 等配置
    • NOTIFY_USERS:通知用户的方式,我选择了了 email, 即邮件通知,则需要配置下文的 SMTP
    • NOTIFY_EMAIL_FROM:邮箱通知的发送地址

邮件 SMTP 配置

上文的邮箱登录与邮箱通知都需要配置 SMTP 服务器,这部分也可以根据自己的邮箱服务商参照文档进行配置。

  • SMTP_HOST:SMTP 服务器地址
  • SMTP_PORT:SMTP 服务器端口
  • SMTP_TLS:是否启用 TLS
  • SMTP_USERNAME:SMTP 用户名
  • SMTP_PASSWORD:SMTP 密码

导入环境变量到服务

根据以上说明完成环境变量配置后,在配置文件和环境变量文件所在目录运行以下命令导入环境变量:

fly secrets import < prod.env

fly_secret_import

deploy_status_remark42

执行完成后到 fly.io 控制台查看服务状态即可,如为 Deployed 状态即表示部署成功。

配置自定义域名(可选)

如果你不想使用 fly.io 提供的默认域名,可以配置自定义域名。

custom_domain_flyio

进入 fly.io 控制台,选择刚部署的 yu-remark42-01 服务,点击左侧的 Certificates 选项,然后点击右上角 Add a Certificate,按照提示添加自定义域名即可。

custom_domain_dns_in_fly

点击 Create Certificate 后,会有一个页面显示你所需要添加的 DNS 记录,按照提示添加即可。

cloudflare_dns_remark42

flyio_certificate_success

例如我的域名托管在 Cloudflare,我按照提示添加了两条 DNS 记录,返回页面后点击 Check again 或等待一段时间后刷新查看,都显示绿色即为配置成功。

change_remark_url

此时,我们可以在 fly.toml 中修改 REMARK_URL 为自定义域名,然后执行以下命令重新部署服务即可,之后对配置文件进行任何改动都可以使用该命令进行更新:

fly deploy

上文我们完成的 Remark42 服务的部署,现在则需要在我们的博文中加入 Remark42 评论组件,以我使用的 Hugo 博客为例。

我在 Hugo 博客的 layouts/partials 目录下新建了一个 comments.html 文件,用于定义 Remark42 评论组件:

<div class="comments">
  <div class="title">
    <span>Comments</span>
    <span class="counter"><span class="remark42__counter" data-url="{{ .Permalink }}"></span></span>
  </div>
  <div id="remark42"></div>
</div>

<script>
  var remark_config = {
    host: 'https://comments.pseudoyu.com',
    site_id: 'pseudoyu.com',
    components: ['embed', 'counter'],
    max_shown_comments: 20,
    simple_view: true,
    theme: 'light',
  }
</script>

<script>
  ;(function () {
    // init or reset remark42
    const remark42 = window.REMARK42
    if (remark42) {
      remark42.destroy()
      remark42.createInstance(remark_config)
    } else {
      for (const component of remark_config.components) {
        var d = document,
          s = d.createElement('script')
        s.src = `${remark_config.host}/web/${component}.mjs`
        s.type = 'module'
        s.defer = true
        // prevent the <script> from loading mutiple times by InstantClick
        s.setAttribute('data-no-instant', '')
        d.head.appendChild(s)
      }
    }
  })()
</script>

remark_config 中的 hostsite_id 需要根据自己的实际配置进行修改,其他部分配置可以保持不变,或根据文档进行调整。

配置好 commnets 组件后,在 layouts/posts/single.html 中文章底部引入:

{{ partial "comments.html" . }}

add_comments_code_in_hugo

大体位置如图所示,如使用的是其他主题或博客系统,则需要找到自己文章对应的模板文件进行修改。

本地预览/部署网站

test_remark42_embedded

此时可以在本地预览或部署网站以查看评论系统是否正常显示,至此我们的服务部署完成。

获取 User ID 并配置 Admin

get_user_id_remark42

登录授权完成后并测试评论后,可在 Remark42 中点击头像打开管理页面,双击后 CMD/Ctrl+C 可以获取以 github_ 或其他平台开头的 User ID,可以将其配置到 ADMIN_SHARED_ID 中(更改 fly.toml 配置文件并运行 fly deploy 重新部署,即可成为管理员,管理员有权限对其他用户的评论进行删除等管理操作。

其他

我把之前 Cusdis 中的评论数据按照一定条件导出 json 格式的数据,并通过 go 程序进行格式转换与迁移,因此保留了之前所有的评论。

因为 Cusdis 本身不提供导出功能且迁移的需求太过小众,我并没有直接向上游贡献代码,也没有写成完善的脚本,有类似需求的朋友可以参考这个 PR 进行处理 —— 「feat: add cusdis to remark42 migrator support by pseudoyu · Pull Request #1 · pseudoyu/remark42」。

总结

以上就是我的博客评论系统的搭建过程,评论系统的搭建与配置相对繁复,且本文的配置方式或许会随时时间而过时,遇到问题可多参照官方文档

这是我的博客搭建部署系列教程之一,如对数据统计系统、博客内搜索等搭建感兴趣,请持续关注,希望能对大家有所参考。