FreshRSS 0.1—0.7 早期版本演进史:从首个原型到多用户新闻聚合器的技术复盘
【免费下载链接】FreshRSSA free, self-hostable news aggregator…项目地址: https://gitcode.com/gh_mirrors/fr/FreshRSS
导读:本文以仓库内 docs/CHANGELOG-old.md(记录 2013 年 0.1.0 至 2014 年 0.7.1 的法国原版更新日志)为线索,系统复盘 FreshRSS 早期版本的架构演进:从单用户原型到多用户体系、SQL 重构与检索语法、HTML5 内容净化、cookie 与缓存策略,以及至今仍可追溯的代码遗迹。读完本文,你将理解这些"历史版本"如何塑造了今天仓库中的目录结构与核心模块,并掌握
intitle:/inurl:/author:等检索关键字、no-cache.txt调试开关等早期设计在当代源码中的真实落点。
一、版本脉络:FreshRSS 早期发布的四个关键阶段
根据 docs/CHANGELOG-old.md,FreshRSS 从 2013 年 4 月 8 日的 0.1.0(标注为"Première version",即首个版本)到 2014 年 2 月 19 日的 0.7.1,共经历约 8 个里程碑发布,可归纳为四个阶段:
| 阶段 | 版本 | 时间 | 核心主题 |
|---|---|---|---|
| 原型期 | 0.1.0 ~ 0.2.0 | 2013-04 ~ 2013-05 | 首个可运行版本、安装程序、Ajax 刷新 |
| 功能成型期 | 0.3.0 ~ 0.4.0 | 2013-05 ~ 2013-07 | i18n、多主题、lazyload、日志页、阅读/全局视图 |
| 稳定化期 | 0.5.0 ~ 0.6.1 | 2013-10 ~ 2013-11 | 匿名访问控制、token 访问、keep_history、性能修复 |
| 架构重构期 | 0.7 ~ 0.7.1 | 2014-01 ~ 2014-02 | 多用户模式、SQL 重构、新搜索、HTML5 净化、目录重组 |
其中 0.7 与 0.7.1 两个版本贡献了日志约 40% 的条目,是早期发展中最重要的一次架构升级,也是下文分析的重点。
二、v0.7 多用户架构:FreshRSS 从"个人工具"走向"自托管平台"
2.1 三种认证方式的引入
v0.7 的核心变化是引入多用户模式:默认用户(管理员)可以创建和删除其他用户。同时,登录方式从单一方案扩展为三种:
- 表单登录(Form Auth):用户名 + 密码,密码不以明文传输,"相对安全即使没有 HTTPS";要求 JavaScript 与 PHP 5.3+。
- HTTP 认证:例如在 Apache 下创建
./p/i/.htaccess与.htpasswd,此时 HTTP 用户名必须与 FreshRSS 用户名一致。 - Mozilla Persona:通过填写用户的电子邮件地址完成联邦登录(该服务已随 Persona 关停而退出历史舞台)。
该设计在当代源码中仍有明确继承。仓库中 app/Models/Auth.php 依然是认证的核心门面,而 app/Models/FormAuth.php 正是当年"表单登录"路线的直接后代;控制器 app/Controllers/authController.php 负责登录、注册、登出等动作的编排。
2.2 会话 Cookie 策略:目录隔离 + HttpOnly
多用户与匿名可读并存后,v0.7 重写了会话 cookie 策略:
- cookie 名称采用中性的
FreshRSS(避免被部分过滤器误伤); - cookie 作用域被限制在
./FreshRSS/p/i/目录下,从而让图片、CSS、脚本等静态资源"无 cookie 传输",提升 HTTP 性能; - 启用
HttpOnly标志增强安全性。
当代实现中,lib/Minz/Session.php 第 51 行显式设置了$params['httponly'] = true;,并支持通过session_name()配置会话名称,正是这条策略的延续。同时,lib/http-conditional.php 在条件请求指纹中混入session_name() . '=' . session_id(),说明会话上下文仍深度参与 HTTP 缓存判定——这是"按用户区分缓存"的关键实现细节。
2.3 目录重组:一切用户数据归入 ./data/
0.7 对文件布局做了大规模整理,核心原则是"所有用户文件统一放入./data/"。日志中列出的关键迁移包括:
./app/configuration/application.ini→./data/config.php./public/data/Configuration.array.php→./data/*_user.php./public/→./p/,其中./public/index.php→./p/i/index.php(与 cookie 目录策略配套)./actualize_script.php→./app/actualize_script.php(并要求用户同步更新 Cron 配置)
从当前仓库看,这套布局至今基本未变:p/目录存放 Web 入口(p/i/index.php、p/api/等),app/actualize_script.php仍是 CLI 实际化脚本,而data/依然承载users/、favicons/、PubSubHubbub/等运行时数据。0.6 升级到 0.7 时,只需把旧的application.ini与Configuration.array.php放入新目录./data/;之后各版本升级只需保留./data/目录,安装程序即可完成迁移。
三、SQL 重构与搜索系统:v0.7 的"内功"升级
3.1 表前缀与多用户隔离
v0.7 对数据库做了不兼容重构:所有表以用户名作为前缀,以支持多用户模式;同时压缩在 MySQL 侧完成而非 PHP 侧,显著提升性能并"容忍更大的文章数量"。日志明确提示该版本与 0.6 不兼容,需通过安装程序完成升级。
从源码看,这条路线延续至今:数据库 DAO 均按用户上下文操作,例如 app/Models/FeedDAO.php 的 SQL 均拼接用户级前缀表名;app/Models/CategoryDAO.php 第 38 行的注释直接描述_feed表结构为priority:int, pathEntries:string, httpAuth:string, error:int, keep_history:?int, ttl:int, attributes:string,其中keep_history正是 v0.5.0 新增的字段(见下文)。
3.2 搜索关键字:intitle / inurl / author
v0.7 引入"新搜索引擎"并支持intitle:、inurl:、author:三类关键字,且可从移动视图访问。这一语法在现代 FreshRSS 中不仅完整保留,还被大幅扩展:
- app/Models/Search.php 定义了
intitle、intitle_regex、inurl、inurl_regex、author、author_regex、tags、tags_regex等字段,并支持-intitle:、-inurl:等否定形式(见第 426 行附近的序列化逻辑); - app/Controllers/searchController.php 第 85、95 行分别用
intitle:与inurl:构造OR子句,证明这两个关键字在 URL 参数解析层仍然生效; - 布尔搜索的完整语法解析位于 app/Models/BooleanSearch.php,承担
AND/OR/NOT与括号组合的解析工作。
值得注意的另一点:v0.7 起文章排序以"加入 FreshRSS 的时间"而非"声明日期(常不准确)"为准,这使"全部标记为已读"不会误伤阅读期间新到达的文章,也使得分页变得高效。这一"以入库时间为准"的排序策略是新闻聚合器正确性的关键设计。
3.3 其他 SQL 改进
- 显示数据库体积(在 FreshRSS 界面内);
- 修复"将所有收藏标记为已读"的缺陷;
- 表结构按用户前缀重构后,改善了性能并支持远多于 0.6 的文章数量。
四、HTML5 内容净化与媒体处理
RSS 全文抓取面临的最大风险是外部内容注入,v0.7 在 HTML5 层面做了系统性的安全加固:
- 原生 HTML5 媒体支持:
audio、video及关联元素,默认preload="none",并正确重写资源地址(含 HTTPS 场景); - iframe 防护:
sandbox="allow-scripts allow-same-origin"; - object/embed 过滤:直接剔除这两类风险标签;
- 延迟加载:iframe 与 video 使用
postpone=""延迟加载,iframe 内的 JavaScript 同样被推迟。
这套净化思路至今仍体现在内容渲染管线中:文章内容在入库与展示前会经过过滤与属性重写处理,且 HTTPS 重写与延迟加载策略与日志描述一致。对自托管阅读器而言,"抓取不可信源 + 安全渲染"是核心工程命题,v0.7 的做法奠定了后续版本的基调。
五、缓存、条件请求与刷新性能(v0.7.1)
0.7.1 的更新重点在于刷新性能与缓存利用:
- 更快的刷新:充分利用缓存;对不支持条件请求的源,采用"感兴趣内容 MD5 签名"判定是否变化;
- 刷新队列 JSON 化:待刷新 feed 队列的管理更稳健;
- 只刷新近期未刷新的 feed,并允许匿名用户触发刷新;
- SimplePie 升级:修复内存泄漏,增强对无效 feed 的容错。
值得注意的是,条件请求(HTTP Conditional)与缓存键的设计一直延续到今天:lib/http-conditional.php 把会话 ID 纳入缓存指纹,使匿名与登录用户可安全共享 HTTP 缓存。这解释了为何 0.7 会把 cookie 作用域压缩到p/i/——静态资源不带 cookie,才能最大化缓存命中率。
另外,v0.7 引入了开发调试开关:创建./data/no-cache.txt即可禁用 HTTP 缓存。该开关至今有效:app/Controllers/extensionController.php 第 364 行仍检测DATA_PATH . '/no-cache.txt'是否存在,以决定是否跳过条件请求缓存(此处配合httpConditional()使用,缓存时长 604800 秒)。
六、交互与 UI:快捷键、阅读状态与移动端
6.1 快捷键体系(0.7.1)
s在只有一种分享方式时直接分享;- 多种分享方式依次由
1、2、3… 激活; Home跳到第一篇,End跳到最后一篇;- 新增在分类/feed 间移动的快捷键(配合
Shift与Alt修饰键); - 快捷键说明按组展示。
当前仓库中快捷键体系已高度可配置化,用户可在配置页自行绑定,视图模板 app/views/configure/shortcut.phtml 承载了快捷键配置界面,底层由FreshRSS_UserConfiguration持久化。
6.2 阅读状态与自动标记
- 新增"收到即标记已读"选项;
- "全部标记为已读"后可跳过到下一个含未读的分类/feed(0.5.0 起);
- 滚轮滚动时自动标记已读(0.4.0 引入,取代"页面加载时标记"的旧选项);
- 修复阅读导航在末尾处循环的问题,修复点击外部链接图标时误展开文章的问题;
- 新增收藏筛选与已读筛选视图(0.7);
- 允许删除某个 feed 的全部文章(0.7)。
这些交互能力在当代代码中仍可对应:阅读模式(normal/global/reader)由 app/Models/ViewMode.php 与 app/Models/ReadingMode.php 管理,视图模板位于 app/views/index/(normal.phtml、global.phtml、reader.phtml)。
6.3 移动端与可访问性
0.7 强调"更完善的无障碍":无图片(Unicode 替代)与无 CSS 环境下也能使用;0.4 起移动端拥有更大的按钮与导航栏。0.7 同时改进了窄屏长标题的展示,并支持深色主题(0.7 起新增默认深色主题,主题加载机制更稳健)。
七、分享、导入导出与 OPML
- 分享目标扩展:0.2 支持邮件与 Shaarli;0.5 增加 Facebook、Twitter、Google+;0.7 扩展 Shaarli、Poche、Diaspora*、Facebook、Twitter、Google+、邮件,并默认绑定
s快捷键。 - OPML:0.7 实现"即时且更宽容"的 OPML 导入,可导入无分类的 feed;0.4 起导入无效 OPML 会显示错误提示。导入后自动触发 feed 刷新(0.6)。
- 导出:0.2 起支持按 feed 导出 RSS;0.5 起支持为匿名访问生成 token(用于访问 FreshRSS 生成的 RSS 输出)。
在当代仓库中,OPML 导入导出已演进为完整的导入导出服务:app/Services/ImportService.php 与 app/Services/ExportService.php,对应控制器 app/Controllers/importExportController.php,并支持 zip / OPML / SQLite 等多种载体。
八、keep_history:文章保留策略的源头
0.5.0 在feed表新增keep_history字段,用于控制每个 feed 的历史保留策略;0.7 进一步允许"更精细地配置每个 feed 最少保留的文章数"。
从当前源码看,该字段至今仍在活跃使用:
- app/SQL/install.sql.mysql.php 第 39 行附近可看到
INDEX (priority) -- v0.7等带版本注释的建表痕迹; - app/Models/CategoryDAO.php 第 47、50 行在整理/清理分类时读取
$feed['keep_history'],并在第 77 行执行ALTER TABLE _feed DROP COLUMN keep_history的迁移逻辑; - app/Models/Context.php 第 140 行使用
keep_history_default计算每 feed 的最小保留数$keepMin; - app/Models/UserConfiguration.php 声明了
keep_history_default属性(旧版 < 1.15 的兼容处理见 app/Controllers/configureController.php 第 376 行)。
也就是说,"每 feed 保留策略"自 0.5.0 提出、0.7 精细化以来,一直是 FreshRSS 架构的稳定组成部分。
九、早期版本留下的工程遗产清单
将日志与当前仓库对照,可梳理出如下"至今可验证"的遗产:
| 0.1–0.7 引入的特性 | 当代源码落点 |
|---|---|
| 表单认证路线 | app/Models/FormAuth.php、app/Controllers/authController.php |
intitle:/inurl:/author:搜索 | app/Models/Search.php、app/Controllers/searchController.php |
| keep_history 保留策略 | app/Models/CategoryDAO.php、app/Models/Context.php |
./data/no-cache.txt调试开关 | app/Controllers/extensionController.php |
| HttpOnly + 目录限定 cookie | lib/Minz/Session.php |
| 会话参与条件请求指纹 | lib/http-conditional.php |
./p/、./app/actualize_script.php、./data/布局 | 仓库根目录即当前布局 |
| 快捷键与阅读/全局视图 | app/views/configure/shortcut.phtml、app/views/index/ |
| 导入导出服务 | app/Services/ImportService.php、app/Services/ExportService.php |
十、给运维者与开发者的三条实用结论
- 升级历史路径清晰:0.6 及以前版本升级到 0.7 需要迁移配置文件(
application.ini与Configuration.array.php→./data/),0.7 之后只需保留./data/目录交给安装程序处理。若你维护的是当年遗留实例,这条迁移逻辑是理解数据目录结构的关键。 - 缓存与 Cookie 是配套设计:cookie 限定在
p/i/+ 静态资源无 cookie + 条件请求指纹混入会话,三者共同支撑了匿名/登录共存下的缓存效率。现代部署若要调优缓存,应同时审视这三处。 - 调试手段可复用:开发或排障时,创建
data/no-cache.txt可绕过 HTTP 条件缓存;而intitle:、inurl:、author:关键字在 app/Models/Search.php 中已扩展出正则与否定形式,可作为高级检索入口。
延伸阅读:更新版本(0.8 及以后)的英文更新日志见 docs/CHANGELOG-old2.md,当前主分支的完整变更记录见 CHANGELOG.md。本文所有源码引用均来自当前仓库,行号与文件路径可直接在仓库中定位验证。
【免费下载链接】FreshRSSA free, self-hostable news aggregator…项目地址: https://gitcode.com/gh_mirrors/fr/FreshRSS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考