自建 RSS 服务迁移至 Miniflux

起因

TTRSS 的 web 页面在小屏移动设备根本不可用。 Reeder 对 TTRSS 的 Fever API 的支持同样烂,好半天同步不下来阅读列表。不过可以理解,毕竟 Reeder 早就把 Fever 标记成 Deprecated,给用户打过预防针了。

寻觅许久,终于找到了一款 iOS 上对 Fever 支持良好的阅读器,名叫 Unread。交互极佳,界面好看,不用付费就能使用大部分功能,十分良心。

关于 RSS 的事情本该在此告一段落了,但是我总感觉 TTRSS 太慢了——前端通常要花费数秒钟才能加载到可用程度。另外 Reeder 也始终让我心痒痒:我花钱买的软件就这么搁置了——不行,太浪费,必须找个理由用起来!

Reeder 另外支持的自建协议是 FreshRSS 和 Google Reader API,根据这两种协议找到了另外两款比较主流的能够自建的 RSS 服务:FreshRss 和 Miniflux。

FreshRSS 也是基于 PHP 开发的,提供 Fever 和 Google Reader API,界面比较简单。FreshRSS 可以使用 SQLite 作为数据库,对内存或者磁盘不足的机器十分友好。

Minuflux V1 由 PHP 开发(2013 - 2018),V2 使用 Golang 开发(2018 - ),提供 Fever 和 Google Reader API。

最终选用 Miniflux 的大体原因是因为:

  • Miniflux 的官网和介绍很有趣,能感受到作者对作品的自信、对“精简”这一理念的坚持
  • Miniflux V2 使用 Golang,比起 PHP 我更喜欢 Go
  • FreshRSS 用了一个叫 MinZ 的、连文档都没有的框架,我不喜欢

此外,Miniflux 还有许多吸引人的特性:

  • Golang 开发,性能和部署优势
  • 内存占用比较低(我通过 Portainer 观察是 25MB - 50MB 左右,作为对比 TTRSS 大概是 100MB - 180MB)
  • 提供 OpenAPI、Fever API、Google Reader API
  • 精简、专注核心功能、无插件,JavaScript 使用很克制
  • (不需要插件的)全文拉取功能
  • 服务端渲染不算优势吧
  • 界面简洁,PWA 支持,小屏移动端体验良好
  • 快捷键支持
  • 提供许多 Read It Later 服务的接入功能
  • 开发理念很棒,代码质量把控严格

部署

docker-compose.yml:

version: '3.4'
services:
  miniflux:
    image: miniflux/miniflux:latest
    container_name: miniflux_rss_server
    restart: always
    ports:
      - "183:8080"
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://miniflux:password@db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=password
      - BASE_URL=https://rss2.bolitao.xyz
      - CLEANUP_ARCHIVE_UNREAD_DAYS=-1
      - CLEANUP_ARCHIVE_READ_DAYS=200
      - POLLING_FREQUENCY=20
    healthcheck:
      test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
  db:
    image: postgres:15
    container_name: miniflux_db_postgres
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=password
    volumes:
      - miniflux-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux"]
      interval: 10s
      start_period: 30s
volumes:
  miniflux-db:

之后使用 docker-compose up -d 命令运行,打开 <ip>:<port> 即可访问页面:

添加自定义 CSS,把链接中(github/fengkx/miniflux-theme-pure/master/dist/style.css)的内容复制到 Miniflux 设置中“自定义 CSS”输入框即可。后来我觉得默认样式也不错又把自定义 CSS 去掉了。

完成基础部署后,导入从 TTRSS 导出的 OPML 文件,恢复订阅链接。导入之后有一个问题,就是阅读进度都被重置了,我的未读变成了恐怖的 10k+,不过问题不大,历史文章我几乎都已经阅读过了,所以只需要浏览完近几天更新的 RSS,然后直接把所有文章标记成已读就好。

域名

在域名管理界面配置 DNS。

接着配置 Nginx 反向代理:

vim /etc/nginx/sites-enabled/miniflux.conf

配置内容:

server {
  server_name rss2.bolitao.xyz;

  client_max_body_size        128M;

  location / {
    proxy_pass http://127.0.0.1:183;
    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Ssl     on;
    proxy_set_header  X-Forwarded-Proto   $scheme;
    proxy_set_header  X-Frame-Options     SAMEORIGIN;
    
    client_max_body_size        128M;
    client_body_buffer_size     128k;

    proxy_buffer_size           4k;
    proxy_buffers               4 32k;
    proxy_busy_buffers_size     64k;
    proxy_temp_file_write_size  64k;
  }
}

检查配置:

nginx -t
# output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload nginx:

nginx -s reload

启动 certbot

certbot
# output:
Saving debug log to /var/log/letsencrypt/letsencrypt.log

Which names would you like to activate HTTPS for?
We recommend selecting either all domains, or all domains in a VirtualHost/server block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: docker.bolitao.xyz
2: pw.bolitao.xyz
3: rss.bolitao.xyz
4: rss2.bolitao.xyz
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 4

然后就可以通过域名访问网站了:

在此之后,可以在 Cloudflare 的 DNS 配置中,把代理打开:

备份方案

Miniflux 本身不在机器产生什么数据,只需要备份数据库就好。所以参考原来的 TTRSS 备份脚本,简单改改就能用了。

vim /root/scripts/miniflux_backup.sh

脚本内容:

#!/bin/bash

SCRIPT_DIR="/root"
NOW=$(date +"%Y%m%d")
TMP_PATH='/tmp'
DOCKER_NAME='miniflux_db_postgres'
MINIFLUX_DB="$TMP_PATH/miniflux_db.sql"
BAK_FILE_NAME="miniflux_db-$NOW.tar.gz"
BAK_FILE="$TMP_PATH/$BAK_FILE_NAME"
DROPBOX_DIR="/root/dropbox"

docker exec "$DOCKER_NAME" pg_dumpall -c -U miniflux > "$MINIFLUX_DB"
echo "数据库 SQL 导出完毕,正在打包压缩..."
tar cfzP "$BAK_FILE" "$MINIFLUX_DB"
echo "打包完成,准备上传..."
"$SCRIPT_DIR"/dropbox_uploader.sh upload "$BAK_FILE" "$DROPBOX_DIR/$BAK_FILE_NAME"
if [ $? -eq 0 ];then
     echo "上传完成"
else
     echo "上传失败"
fi
rm -f "$MINIFLUX_DB" "$BAK_FILE"

随后赋权,试运行一遍:

chmod +x /root/scripts/miniflux_backup.sh && bash /root/scripts/miniflux_backup.sh

输出:

数据库 SQL 导出完毕,正在打包压缩...
打包完成,准备上传...
 > Uploading "/tmp/miniflux_db-20230111.tar.gz" to "/root/dropbox/miniflux_db-20230111.tar.gz"... DONE
上传完成

然后配置定时任务:

crontab -e

打开的编辑器中添加以下行:

0 3 * * 0 /bin/bash /root/scripts/miniflux_backup.sh > /dev/null

完毕。

TTRSS 星标数据处理

把数据迁得差不多之后,为了节省一些 VPS 的资源,我准备关闭原来的 TTRSS 实例、进行一些清理工作。但我发现待办中还有最后一项让我头疼的事,那就是我的 TTRSS 有许多收藏的文章,我得想办法把它们导出。

我首先想到的是把这些东西导出到 Miniflux,但是有点困难,退而求其次准备把他们导出到 wallabag。

插件方式

首先,需要在 TTRSS 部署 ttrss-to-wallabag-v2 插件。我使用的是 Awesome TTRSS 的镜像,默认已经自带 ttrss-to-wallabag-v2 插件,只需要将其开启即可。

编辑 TTRSS 的 docker-compose.yml 文件,修改环境变量,在 ENABLE_PLUGINS 后添加 wallabag_v2

添加完成后重启 TTRSS。

之后去到 wallabag 页面,右上角点击头像 - API 客户端管理,创建一个新的客户端:

将该值填入 TTRSS 的设置 - Wallbag V2 设置:

然后就可以在 TTRSS 通过快捷键 a w,把文章加入 wallabag 了。

但是我有数百篇 Star,一篇篇添加太费劲。我想过用 TTRSS 的 Filter 功能把星标文章导出到 wallabag,但是 Filter 没有针对 Star 文章或 labels 进行操作的功能。于是,这个方案被我废弃掉。

通过 API 自行导出

我看了下 TTRSS Fever APIwallabag API 的文档,发现其调用不算困难,于是在 ttrss-to-wallabag-v2 这个插件的提示下,准备自己写一个小玩具进行导出。

开发过程中使用 Unirest 库调用 Wallabag 的 /api/entries.json API 时一直出现 HTTP 500,Wallabag 的文档和日志太烂,根本没地方查错,差点想放弃。最后干脆摆烂拷贝 Apifox 生成的调用示例代码竟然调通了...原来是 Header 需要多加个 Content-Type: application/json

玩具的功能很简单,调用 Fever API,获取 TTRSS 加星标的文章,然后通过 wallabag 的 API 导入,没有日志,没有异常保护,没有多线程。我自己跑 716 篇文章迁移耗时大概十五分钟,检查后没有出现数据丢失。如果真丢了,也定位不到哪篇丢了,因为我没加日志框架。

image-20230112001025834

代码在这里,需要 JDK 8:

bolitao/ttrss-starred-to-wallabag: Tiny-Tiny-RSS starred articles to wallabag

体验

完爆 TTRSS,Miniflux 的全文拉取很好用,页面体验非常舒适,还支持 PWA!

有一个小瑕疵——iOS 字体显示有问题,我在 Miniflux 设置的字体是 Serif,但是 PWA 应用依旧是显示非衬线字体。猜测是 iOS 的通病,因为即使我用 Reeder 阅读器设置衬线字体,也依旧会使用系统默认的非衬线字体显示中文。

最后,诚心推荐 RSSHubRSSHub Radar,RSSHub 可以将许多网站内容转为 RSS 订阅链接,Radar 可以检查网站的可订阅内容、一键订阅内容到 TTRSS / Miniflux。我主要用它订阅一些视频和社交博主的更新以及订阅一些瑟瑟内容。服务可以免费部署在 Vercel 上,记得在环境变量设置一下访问密钥以避免 Vercel 用量超量。

Ref.

加载评论