Astro引入FreshRss订阅的方法
最近,我开始使用Astro,在构建links页面的时候,发现如果每条信息都去添加,实在麻烦,就琢磨怎么才可以省心省力。刚巧,逛1900博客的时候,发现他的liks页面就采用了 关于友情链接和SSG在博客中展示Flux订阅 这种方式来实现。
不过,阅读完文章后,发现他的实现方式比较有难度,而且我对于miniflux、11ty这些都没有上手经验,所以我就想着实现的思路既然一样,我可不可以换一个程序就能够实现呢?而我一直使用的FreshRss,正好可以满足我的需求。那么我的思路也就很明确了,Docker安装Freshrss实现后端服务,Astro进行前端展示,我只需要引入FreshRss的RSS订阅信息,就可以实现我的需求了。
Docker安装Freshrss实现后端服务
我在Typecho就有使用过Freshrss,所以安装起来比较简单,直接使用Docker安装即可。而且现在宝塔面板自带Freshrss,安装起来更加简单。
docker run -d --restart unless-stopped --log-opt max-size=10m \
-p 8080:80 \
-e TZ=Europe/Paris \
-e 'CRON_MIN=1,31' \
-v freshrss_data:/var/www/FreshRSS/data \
-v freshrss_extensions:/var/www/FreshRSS/extensions \
--name freshrss \
freshrss/freshrss
后续就是放行8080端口,然后访问http://你的ip:8080
,进行安装即可。接着在添加反代,将8080端口转发到80端口,这样就可以使用http://你的ip
访问了。具体可以参考呆哥的给博客添加一个输出友链 RSS 的页面,整个部署过程比较简单,这里就不再赘述了。我们只要记住开启api和api密码即可。
Artalk与FreshRss的实时更新机制
Astro的文档中,关于RSS订阅的文档比较少,而且官方的RSS订阅插件,只能订阅RSS源,不能订阅RSS订阅信息。所以,我只能自己动手,丰衣足食了。在这里我其实花了差不多两三天时间,踩了不少坑。因为我的思路是本地部署后生成dist文件,上传到宝塔面板。但是因为要更新Freshrss的订阅信息,所以需要重新生成dist文件,但是dist文件生成后,宝塔面板不会自动更新,所以需要手动更新。这就是个坑。
其实要实现,最简单的办法就是整个文件部署到服务器,给他定时任务,让他去渲染,这样就可以。但是这样太麻烦了,而且我还要在服务器上安装nodejs,所以我就放弃了。另一种方式就是通过Github Action实现,不过我觉得这个思路和我最开始出现了偏差,所以也放弃了。我就只想保持一个思路,我所有文件都在本地构建,但是liks页面却可以单独实现更新。
在这里,我就想到了一个方法,Astro引入Artalk的话,不管你是不是在本地构建的,只要用户评论了,页面不就可以实时显示评论吗?那么我如果我的Freshrss订阅信息,也通过和Artalk的评论功能一样的方式,不就可以实现实时更新了吗?
FreshRSS 与 Artalk 实时更新机制对比分析
特性 | Artalk (评论系统) | FreshRSS (自托管 RSS 阅读器) |
---|---|---|
数据来源 | 用户在你的网站上的交互行为 (发表评论) | 外部源 (订阅的博客/新闻等网站的 RSS Feed) |
数据流向 | 双向 (用户提交 → 你的数据库) | 单向 (外部源 → FreshRSS 数据库) |
触发更新 | 用户驱动 (点击提交按钮) | 定时任务驱动 (后台定期抓取) |
前端角色 | 主动交互 (提交/获取评论) | 主要被动展示 (获取聚合后的文章列表) |
具体分析
也就是说,Freshrss和Artalk一样,都是后端服务内部,不依赖我们的Astro静态站点。那么我们是不是也就可以和artalk一样,通过客户端 JavaScript来按需或者定时获取呢?再简单一点的说法就是,我的所有freshrss订阅信息,都放在一个json文件中,然后通过客户端JavaScript定时去获取,然后渲染到页面上。因为我的这个页面本身已经渲染完毕,只要我把订阅信息包裹成一个组件,组件发生信息变更,这个组件也可以变更,这样也不需要重新渲染整个页面,就可以实现了订阅信息的变更了。
实现这个方法有两个,一个是使用纯js,第二种就是使用 React 组件 (更结构化)。我选用的就是第二种。
实现步骤
- 在 Astro 页面/组件中创建容器:
在你的 Astro 页面 (.astro) 或组件 (.astro / .jsx / .vue / .svelte) 中,创建一个 HTML 元素作为订阅列表的容器。
<Layout>
<!-- 你的原代码 -->
<!-- 组件加载前输出信息 -->
<script>
console.log('即将加载 FreshRSSListReact 组件');
</script>
<FreshRSSListReact lang={lang} client:load />
<!-- 组件加载后输出信息 -->
<script>
document.addEventListener('astro:load', () => {
console.log('FreshRSSListReact 组件加载完成');
});
</script>
</Layout>
- 编写 React 组件:
使用 React 编写一个组件,用于渲染订阅列表。组件接收订阅数据作为输入,并根据数据动态生成订阅列表。
// src/components/FreshRSSList.jsx
import { useState, useEffect } from 'react';
const FRESHRSS_API_URL = 'https://your-freshrss-domain.tld/api/greader.php'; // 或 /api/
const FRESHRSS_USER_ID = 'your_freshrss_username';
const FRESHRSS_API_PASSWORD = 'your_api_password_here'; // 重要:注意安全!
export default function FreshRSSList() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchFreshRSS() {
try {
const authString = `${FRESHRSS_USER_ID}:${FRESHRSS_API_PASSWORD}`;
const encodedAuth = btoa(authString);
const headers = { 'Authorization': `Basic ${encodedAuth}` };
const apiEndpoint = `${FRESHRSS_API_URL}/reader/api/0/stream/contents?output=json&xt=user/-/state/com.google/read&n=20`;
const response = await fetch(apiEndpoint, { headers });
if (!response.ok) throw new Error(`API error: ${response.status}`);
const data = await response.json();
setItems(data.items || []);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchFreshRSS();
}, []); // 空依赖数组表示只在组件挂载时运行一次
if (loading) return <p>Loading FreshRSS subscriptions...</p>;
if (error) return <p className="error">Error: {error}</p>;
if (items.length === 0) return <p>No unread subscriptions found.</p>;
return (
<ul className="freshrss-list">
{items.map((item) => (
<li key={item.id}>
<a
href={item.canonical?.[0]?.href || item.alternate?.[0]?.href || '#'}
target="_blank"
rel="noopener noreferrer"
>
{item.title}
</a>
{item.summary?.content && (
<p className="summary" dangerouslySetInnerHTML={{ __html: item.summary.content }} />
)}
<span className="source">{item.origin?.title || 'Unknown Source'}</span>
</li>
))}
</ul>
);
}
- 引入 React 组件:在 Astro 页面中引入 React 组件。可以使用 React 提供的方法,如 ReactDOM.render 或 ReactDOM.createRoot 来渲染组件。
---
import FreshRSSListReact from '../components/FreshRSSListReact.jsx'; // 引入组件
import Layout from '@/layouts/Layout.astro';
---
<Layout>
<!-- 你的原代码 -->
<!-- 组件加载前输出信息 -->
<script>
console.log('即将加载 FreshRSSListReact 组件');
</script>
<FreshRSSListReact lang={lang} client:load />
<!-- 组件加载后输出信息 -->
<script>
document.addEventListener('astro:load', () => {
console.log('FreshRSSListReact 组件加载完成');
});
</script>
</Layout>
因为Astro部署的方法不一样,还有就是主题各不相同,所以具体的实现方法,还是需要不断进行调整的。通过这样一个思路,后面会慢慢去逐步摸索和实现其它的一些功能,比如 Mastodon 、Artalk 之类的。