Skip to content

Repository files navigation

Local Folo

保留 Folo 完整 Web 阅读体验,在本地离线运行 RSS 订阅与阅读。

Local Folo 是从 Folo 单体仓库中裁剪出的 本地 RSS 阅读器。它沿用官方桌面端 Web 渲染层(apps/desktop/layer/renderer)作为 UI 基线,并内置与 api.folo.is 兼容的 Local Folo API,使订阅、时间线、列表、设置等核心能力在无需账号与云端的情况下即可运行。

维度 取值
应用版本 1.7.0(见 apps/desktop/package.json
包管理器 pnpm 10.17.0
运行时 Node.js ≥ 20(推荐与 pnpm 版本匹配)
UI 框架 React 19 + Vite 7 + Tailwind CSS 3
上游项目 RSSNext/Folo

1. 适用场景

  • 本地 RSS 阅读:订阅 HTTP(S) / Atom / RSS 源,在 Folo 原生时间线 UI 中阅读,数据保存在本机。
  • RSSHub 路由消费:通过 rsshub:// 协议订阅 RSSHub 路由,需自行部署或指向可用的 RSSHub 实例(默认 http://localhost:1200)。
  • 对外暴露 Entries API:通过 folo-entries-gateway8787 端口提供与 https://api.folo.is/entries 兼容的 HTTP 接口,供外部客户端拉取条目。
  • 定时刷新:内置 cron 调度,按 15 / 30 / 60 / 120 / 360 分钟间隔自动刷新全部订阅。

2. 架构总览

flowchart TB
    subgraph Browser[浏览器 / PWA]
        UI[Folo Web Renderer<br/>React 19 + Jotai/Zustand]
    end

    subgraph Desktop[apps/desktop]
        Vite[Vite Dev Server<br/>开发模式]
        Prod[production.ts<br/>生产静态服务]
        API[local-folo-api<br/>Local Folo API Handler]
    end

    subgraph Gateway[folo-entries-gateway :8787]
        Proxy[proxy 模式 → 转发主服务]
        Synthetic[synthetic 模式 → 内置样例]
    end

    subgraph External[外部依赖]
        RSSHub[RSSHub 实例<br/>rsshub:// 路由解析]
        Curl[curl<br/>Feed 抓取]
    end

    subgraph Storage[本地持久化]
        Data[".local-rss/folo.db<br/>订阅 · 条目 · 设置"]
    end

    UI -->|同源 API| Vite
    UI -->|同源 API| Prod
    Vite --> API
    Prod --> API
    API --> Data
    API --> Curl
    API --> RSSHub
    Gateway -->|FOLO_UPSTREAM_URL| Prod
    Client[外部 HTTP 客户端] --> Gateway
Loading

请求路径说明

模式 UI 服务 API 挂载点
开发 (pnpm dev:web) Vite :2240 Vite 插件 localFoloApiPlugin 中间件
生产 (pnpm start) Node HTTP :2240 server/production.ts 内嵌同一 Handler

开发与生产共用 createLocalFoloRequestHandler,避免 build:web 后仍依赖 Vite configureServer


3. 保留与裁剪范围

保留

路径 说明
apps/desktop/layer/renderer Folo 桌面 Web UI(时间线、订阅、设置、阅读器等)
apps/desktop/plugins/vite/local-folo-api.ts Local Folo API 核心实现
apps/desktop/server/production.ts 生产环境静态资源 + API 服务
packages/internal/* 共享包:components、store、models、database 等
packages/configs 跨包 TypeScript / ESLint 配置
packages/internal/folo-entries-gateway Entries HTTP 网关
locales/ 多语言资源(en、zh-CN、zh-TW、ja、fr-FR 等)
apps/desktop/changelog/ 升级日志(含 next.md,Vite 构建时注入 CHANGELOG_CONTENT
patches/ pnpm 依赖补丁

有意裁剪

以下模块未包含在本仓库,以减小体积并聚焦本地 Web 场景:

  • 移动端(mobile)、SSR、Landing 页
  • CLI、OTA 更新、Electron 打包与发布流水线
  • E2E 测试与 CI 发布脚本

4. 技术栈

层级 技术
构建 Vite 7、TypeScript 5.9、pnpm workspace
前端 React 19、Tailwind CSS、Jotai、Zustand、React Query
本地 API Node.js 原生 HTTP、node-croncurl 抓取 Feed
数据 PostgreSQL(DATABASE_URL)或 SQLite 回退(.local-rss/folo.db
网关 @follow/folo-entries-gateway(独立 HTTP 服务)
PWA vite-plugin-pwa(Service Worker 离线缓存)

5. 环境要求

  • Node.jspnpm@10.17.0(见根目录 package.jsonpackageManager 字段)
  • curl 必须在 PATH 中;Feed 抓取与 RSSHub 连通性测试均通过 curl 完成
  • (可选)RSSHub 实例:解析 rsshub:// 路由时需要,默认基址为 http://localhost:1200

启用 corepack(推荐):

corepack enable
corepack prepare pnpm@10.17.0 --activate

6. 快速开始

6.1 安装依赖

pnpm install

6.2 开发模式

pnpm dev:web

浏览器访问 **http://localhost:2240**。Local RSS 模式默认开启(apps/desktop/.envVITE_LOCAL_RSS_MODE=1),API 与 UI 同源,无需额外代理。

6.3 生产构建与运行

pnpm build:web   # 输出到 apps/desktop/out/web
pnpm start       # 启动 production.ts,默认 :2240

或一步完成:

pnpm prod        # build:web && start:all

6.4 同时启动主服务 + Entries 网关

当外部客户端需要通过 8787 端口访问 /entries 时:

# 开发
pnpm dev:all

# 生产
pnpm start:all
服务 默认端口 说明
Folo 主服务 2240 UI + Local Folo API
Entries Gateway 8787 兼容 api.folo.is/entries

6.5 Docker 一键部署(推荐生产)

自带 PostgreSQL 16 + LocalFolo,数据库 schema 在应用首次启动时自动创建。

前置:Docker Engine + Compose 插件。

cp .env.example .env          # 按需改 PUBLIC_ORIGIN、POSTGRES_PASSWORD
./deploy/docker-deploy.sh     # 或 pnpm run deploy:docker

默认访问 **http://localhost:2240**。常用子命令:

命令 说明
./deploy/docker-deploy.sh --no-build 跳过镜像构建
./deploy/docker-deploy.sh --logs 查看日志
./deploy/docker-deploy.sh --down 停止(保留 pgdata)
./deploy/docker-deploy.sh --reset-db 清空 Postgres 卷

Docker 相关环境变量见根目录 .env.examplePUBLIC_ORIGINHOST_PUBLISHPOSTGRES_*)。容器内 RSSHub 默认 http://host.docker.internal:1200


7. 环境变量

主配置文件:

7.1 前端 / 主服务

变量 默认值 说明
VITE_DEV_PORT 2240 开发服务器端口
VITE_WEB_URL http://localhost:2240 Web 应用基址
VITE_API_URL http://localhost:2240 API 基址;Local RSS 模式须指向 localhost
VITE_LOCAL_RSS_MODE 1 启用本地 RSS 模式(关闭云端请求)
VITE_LOCAL_RSSHUB_BASE_URL http://localhost:1200 rsshub:// 路由解析的默认 RSSHub 基址
VITE_BUILD_TYPE production 构建类型标识
VITE_INBOXES_EMAIL @local.folo 本地模式占位邮箱
PORT VITE_DEV_PORT 生产服务器端口覆盖

Local RSS 模式下,VITE_API_URL 的 hostname 必须为 localhost127.0.0.1,否则应用启动时会抛出校验错误(见 apps/desktop/layer/renderer/src/lib/local-mode.ts)。

7.2 Entries Gateway

变量 默认值 说明
PORT 8787 网关监听端口
FOLO_UPSTREAM_URL http://127.0.0.1:2240 proxy 模式下转发目标
FOLO_ENTRIES_GATEWAY_MODE proxy proxy 转发主服务;synthetic 使用内置样例数据

7.3 RSSHub 与定时刷新

RSSHub 基址与 Cron 间隔可在 设置 → 集成 中图形化配置,运行时写入 .local-rss/folo.dbmeta 表,优先级高于 .env 中的 VITE_LOCAL_RSSHUB_BASE_URL


8. Local Folo API

核心实现:apps/desktop/plugins/vite/local-folo-api.ts

8.1 已实现的核心路由

路径 方法 功能
/subscriptions GET / POST / PATCH / DELETE 订阅管理
/feeds GET Feed 元数据
/feeds/refresh POST 刷新指定 Feed
/entries GET / POST 条目查询(与官方 API 兼容)
/entries/preview POST 订阅前预览
/entries/readability POST 正文提取
/lists GET / POST / PATCH / DELETE 本地列表管理
/categories GET / PATCH / DELETE 分类管理
/settings GET / PATCH 设置读写
/discover GET / POST 发现(本地模式返回空结果)
/better-auth/* * 认证占位(本地单用户)

未实现或无需云端的能力(通知、收件箱、钱包、AI、计费等)返回 安全空响应createSafeFallback),避免 UI 报错。

8.2 本地管理端点

路径 方法 功能
/__local/config GET / POST 读取/保存 RSSHub 基址、Cron 配置
/__local/refresh-all GET / POST 手动刷新全部订阅
/__local/test-rsshub POST 测试 RSSHub 连通性(curl 探针)
/__local/rss GET 导出本地 RSS(调试)

对应 UI 组件:apps/desktop/layer/renderer/src/modules/settings/LocalRsshubInstancePanel.tsxLocalCronRefreshPanel.tsx


9. 本地数据持久化

Local Folo 服务端数据默认写入 PostgreSQL(推荐与 LinkLoom 共用实例,独立 folo 库)。未配置 DATABASE_URL 时回退到 SQLite 文件。

9.0 PostgreSQL(推荐)

apps/desktop/.env 中配置:

DATABASE_URL=postgres://linkloom:linkloom@localhost:5432/folo

首次使用前在 Postgres 实例上创建库(LinkLoom Docker 示例):

docker exec linkloom-postgres-1 psql -U linkloom -d linkloom \
  -c "CREATE DATABASE folo OWNER linkloom;"

应用启动时 local-folo-api 会自动执行 schema 迁移(schema_migrations + 全部表)。

从 SQLite 迁移数据(若已有 .local-rss/folo.db):

DATABASE_URL=postgres://linkloom:linkloom@localhost:5432/folo \
  node apps/desktop/scripts/migrate-folo-sqlite-to-postgres.mjs

备份pg_dump "postgres://linkloom:linkloom@localhost:5432/folo" > folo-backup.sql

架构说明:PostgreSQL 是唯一持久化源(RSS、已读、收藏、未读、设置)。浏览器在 VITE_LOCAL_RSS_MODE=1 下仅保留 Zustand 内存态,不再向 IndexedDB 写入上述数据。

9.0.1 SQLite 回退(可选)

未设置 DATABASE_URL / POSTGRES_* 时,数据写入:

.local-rss/folo.db          # SQLite 主库(WAL 模式)
.local-rss/folo.db-wal       # SQLite WAL 日志(运行期)
.local-rss/folo.db-shm       # SQLite 共享内存(运行期)

该目录已在 .gitignore 中排除。备份或迁移时请整体复制 .local-rss/

9.1 数据模型概览

主键 说明
feeds id Feed 元数据;url 上有 UNIQUE 索引,等价于旧版 feedIdByUrl
entries idUNIQUE(feed_id, guid) 条目;按 (feed_id, published_at_ms) 建索引,无条数上限
subscriptions id 用户订阅;按 feed_id / view / category 建索引
lists id 用户创建的列表(feed_ids 存 JSON 数组)
settings tab 每个设置 Tab 一行,payload 为 JSON
meta key 扁平 KV 配置:localRsshubBaseUrllocalCronRefreshMinutes
collections entry_id 收藏条目
unread subscription_id 各订阅未读计数

DDL 与 Drizzle schema 见:

9.2 抓取策略(增量上窜,永不删除历史)

  • 抓取入口 ensureFeedByUrl 执行 INSERT INTO entries ... ON CONFLICT(feed_id, guid) DO UPDATE:内容会被新数据覆盖,但 inserted_atread 状态保留。
  • 抓回 0 条不会删除任何已存条目(解决 Twitter 等源间歇空 RSS 覆盖历史的旧 bug)。
  • 时间线 /entries 直接走 SQL 索引分页,不再触发同步抓取;只在打开"第一页"且某个 feed 的 updated_at 距今超过 15 分钟时启动一次后台异步刷新(不阻塞响应)。
  • 主动刷新仍由内置 cron(/__local/config 中配置)与手动触发的 /feeds/refresh/__local/refresh-all 负责。

9.3 备份 / 重置

  • PostgreSQL 备份pg_dump "$DATABASE_URL" > folo-backup.sql
  • SQLite 备份:复制整个 .local-rss/ 目录
  • 完全重置(PG)dropdb / createdb 后重启应用自动建表
  • 完全重置(SQLite):删除 .local-rss/folo.db*(含 -wal / -shm

10. 本地 RSS 模式与安全

启用 VITE_LOCAL_RSS_MODE=1 后,前端会安装 网络守卫apps/desktop/layer/renderer/src/lib/local-mode.ts):

  • 拦截对 api.folo.isapp.folo.is、PostHog、Sentry、Firebase 等远程域名的 fetch 请求
  • 允许 localhost127.0.0.1 及当前页面同源请求
  • 确保阅读数据不意外发往云端

本地用户使用固定 ID(createStableId("local-folo-user")),无需注册或登录。


11. 项目结构

.
├── apps/desktop/                    # 桌面 Web 运行时
│   ├── layer/renderer/              # Folo Web UI(页面、模块、组件)
│   ├── plugins/vite/
│   │   └── local-folo-api.ts        # Local Folo API 实现
│   ├── server/production.ts         # 生产 HTTP 服务
│   ├── changelog/                   # 版本更新日志
│   ├── .env / .env.example          # 环境配置
│   └── vite.config.ts
├── packages/
│   ├── configs/                     # 共享构建配置
│   └── internal/
│       ├── atoms/                   # Jotai 原子状态
│       ├── components/              # 共享 UI 组件
│       ├── constants/               # 应用常量
│       ├── database/                # Drizzle ORM
│       ├── hooks/                   # 共享 React Hooks
│       ├── models/                  # 数据模型
│       ├── shared/                  # 跨平台工具与环境变量
│       ├── store/                   # Zustand 状态
│       ├── utils/                   # 工具函数
│       └── folo-entries-gateway/    # Entries HTTP 网关
├── locales/                         # i18n 翻译文件
├── patches/                         # pnpm 依赖补丁
├── .local-rss/folo.db               # 本地 RSS 数据(运行时生成)
├── package.json                     # Workspace 根脚本
├── pnpm-workspace.yaml
└── turbo.json

内部包开发约定见 packages/internal/AGENTS.md


12. 脚本参考

根目录 package.json

命令 说明
pnpm dev:web 启动 Vite 开发服务器(:2240)
pnpm dev:all 并行启动 dev:web + Entries Gateway
pnpm build:web 构建 Web 产物到 apps/desktop/out/web
pnpm start 启动生产服务器
pnpm start:all 并行启动 start + Entries Gateway
pnpm prod build:web 后执行 start:all
pnpm entries:gateway 单独启动 Entries Gateway
pnpm typecheck TypeScript 类型检查

13. 常见问题

Feed 刷新失败

  1. 确认 curl 已安装且在 PATH 中
  2. 检查 Feed URL 是否可访问;rsshub:// 路由需 RSSHub 实例在线
  3. 设置 → 集成 中执行 RSSHub 连通性测试
  4. 查看终端 [local-folo-api] 日志

Entries Gateway 返回 503

网关处于 proxy 模式但主服务未启动。先运行 pnpm dev:webpnpm start,确认 2240 端口可达。

构建后页面空白

确认已执行 pnpm build:web,且 apps/desktop/out/web/index.html 存在。pnpm start 会在缺少构建产物时返回 404 提示。

定时刷新不生效

  1. 设置 → 集成 → 定时刷新 中选择非零间隔
  2. 确认 localCronPublicBaseUrl 可从运行 cron 的机器访问(默认使用浏览器 origin)
  3. 仅开发/生产 Node 进程运行时 cron 有效;纯静态托管无法调度

14. 上游与致谢

  • UI 与交互设计源自 RSSNext/Folo(Folo Team)
  • 本仓库为官方 Folo 的本地裁剪版本,保留完整 Web UI,移除 Electron 打包与云端依赖,专注于离线 Web 阅读场景

如需完整 Electron 客户端、移动端与云端同步,请访问官方 FoloGitHub 仓库

About

本地部署版Folo

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages