Folo 桌面端 v1.5.0 深度解读:双因素认证登录修复、Electron IPC 导出与阅读体验打磨
2026/9/9 20:06:19 网站建设 项目流程

Folo 桌面端 v1.5.0 深度解读:双因素认证登录修复、Electron IPC 导出与阅读体验打磨

【免费下载链接】follow🧡 Folo is the AI RSS Reader项目地址: https://gitcode.com/GitHub_Trending/fol/follow

Folo Desktop v1.5.0 是一次以“稳定性和细节体验”为核心的迭代,官方更新说明(apps/desktop/changelog/1.5.0.md)以经典的 “Shiny new things / Improvements / No longer broken” 三段式记录本版变化。本文将逐条拆解这些更新,并结合 Folo 仓库内 Electron 主进程 IPC、渲染层组件与公共工具代码,还原每一个修复背后的实现原理:从 Electron 下开启双因素认证(2FA)账号的登录链路,到 PDF 导出的 IPC 通道、鉴权 Origin 头、主题偏好持久化、Obsidian 导出选目录,再到 AI 对话输入框与条目列表的细节体验。读完你既能完整了解 v1.5.0 改了什么,也能看清这些改动在代码里“长什么样”。

版本概览:一次面向稳定性与体验的迭代

根据 1.5.0.md 的说明,v1.5.0 没有列出任何“Shiny new things”(全新的亮点功能)条目,重点全部落在 Improvements(改进)与 No longer broken(修复)两大块:

  • 改进方向:打磨认证、钱包、通知与发现页等界面;让 AI 对话输入框在长对话中始终保持在可视区内。
  • 修复方向:Electron 双因素认证登录、PDF 经 IPC 导出、鉴权 Origin 头与渲染层 API 请求、主题偏好持久化、弱网下已悬停未读条目消失、macOS 原生目录选择下的 Obsidian 导出,以及视频播放音调问题。
  • 特别致谢贡献者 @Eumenides-K。

本版本更新说明只覆盖桌面端(apps/desktop),而仓库采用了主进程(layer/main)与渲染进程(layer/renderer)分离的 Electron 分层结构,配合公共包(packages/internal)里的 headers、shared 等模块,因此后续分析会围绕这几处源码展开。

认证:修复 Electron 下启用 2FA 的账号登录

这是 v1.5.0 修复条目中最具技术含量的一条:Electron 内登录已开启双因素认证(2FA)的账号。

问题背景:普通 Web 弹窗与 Electron 会话的差异

Folo 桌面端把认证请求放进主进程处理,而不是让渲染页直接 fetch,原因在于 Electron 的 Cookie 归属在主进程session上,渲染进程的网络请求无法可靠地管理认证 Cookie。因此桌面端将认证逻辑收敛到 IPC 服务 AuthService 中,其组名为auth

修复落点一:TOTP 验证的 IPC 通道

在 auth.ts 中可以看到verifyTotp方法,它向/better-auth/two-factor/verify-totp发起 POST:

  • 请求携带用户输入的 6 位验证码,并可附带trustDevice(信任本设备)字段;
  • 关键在于请求头需要带上 “待验证” 阶段的中间 Cookie。类成员pendingTwoFactorCookieHeader在首次账号密码提交后由requestCredentialAuth写入——当服务端响应里带有twoFactorRedirect: true时,会把响应Set-Cookie中属于受管列表的 Cookie 拼成临时请求头暂存(见 auth.ts),随后verifyTotp用这段中间状态完成第二步认证;
  • 验证成功后清除pendingTwoFactorCookieHeader,并把新的会话 Cookie 持久化进 Electron session。

修复落点二:受管认证 Cookie 的收拢与去重

之所以需要“收拢”,是因为 Better Auth 会下发一系列会话相关 Cookie。主进程维护了一份受管名单,位于 auth-cookies.ts,包含better-auth.session_tokenbetter-auth.session_datatrust_devicetwo_factor等变体。该模块提供了一套完整工具:

  • parseSetCookieHeader手工切分Set-Cookie(规避了 Electron 对多 Set-Cookie 的兼容问题);
  • persistManagedAuthCookiesFromSetCookieHeader把服务端下发的受管 Cookie 写入 Electron session;
  • dedupeManagedAuthCookies对同名的 host-only/domain、secure/非 secure 等 Cookie 去重,保留优先级更高的那份;
  • getPreferredSessionTokenCookie会优先选择__Secure-better-auth.session_token,从而保证安全前缀版本的 Cookie 生效。

这套机制是“Electron 登录支持 2FA”修复的地基:如果没有显式的中间 Cookie 传递与去重管理,两步验证中第一步建立的会话状态很容易丢失。

渲染层的 TOTP 交互

在渲染进程侧,2FA 的设置与验证界面在 two-factor.tsx 中实现:

  • TwoFactorForm先要求输入当前密码(PasswordForm),无密码用户会看到“请先设置密码”的提示;
  • 开启后展示 TOTP URI 二维码(QRCode),随后要求用TOTPForm输入 6 位数字验证码完成确认,校验 schema 为z.string().length(6).regex(/^\d+$/)
  • 验证码错误时(错误文案匹配 “invalid two factor authentication” 或错误码4007)会清空输入、给出表单级错误并触发一次 shake 抖动动画;
  • 成功后调用userActions.updateWhoami({ twoFactorEnabled: true })更新当前用户状态。

v1.5.0 的 “Polished authentication” 改进也与上述流程有关:启用/关闭 2FA、TOTP 初始化等交互都经过打磨,配合 Electron 会话 Cookie 修复,让 2FA 从“设置”到“登录”整条链路可用。

鉴权 Origin 头:修复认证头与渲染层 API 请求

v1.5.0 的另一条认证修复是“Fixed auth origin headers and restored renderer API requests”。这背后是桌面端两个请求头工具函数,定义在公共包 packages/internal/utils/src/headers.ts:

  • createAuthRequestOriginHeaders(webUrl)(headers.ts):解析 Web 端 URL 的origin,生成{ Origin, Referer }。Better Auth 服务端会校验请求的Origin/Referer是否为可信来源,Electron 渲染页默认 Origin 是app://folo.is这类自定义协议,若直接以该 Origin 发认证请求会被服务端拒绝。此函数把请求“伪装”成来自 Web 站点,从而通过服务端校验;
  • createDesktopAPIHeaders({ version })(headers.ts):根据运行平台(macOS DMG/MAS、Windows EXE/MS、Linux 等)生成X-App-PlatformX-App-Name: Folo WebX-App-Version,供服务端识别客户端形态。

主进程在拼装认证请求头时正是{ ...createDesktopAPIHeaders(...), ...createAuthRequestOriginHeaders(...) }(见 auth.ts)。此外,AuthService 还提供了fetchWithAuth(auth.ts),它只允许代理到与VITE_API_URL同源的请求,并自动补充受管 Cookie——这就是渲染层“恢复 API 请求”的通道:渲染进程通过该 IPC 发请求,主进程统一补上 Origin/Referer/平台头与会话 Cookie,避免了过去认证头缺失导致 API 请求失败的问题。

PDF 导出:修复 Electron IPC 通道下的页面另存为 PDF

“Fixed PDF export through Electron IPC” 对应主进程 app.ts 的exportCurrentPageAsPdf方法:

  1. 通过 Electrondialog.showSaveDialog弹出保存对话框,默认文件名Untitled.pdf,文件过滤器仅限 PDF,并开启createDirectoryshowOverwriteConfirmation
  2. 调用sender.printToPDF(来自当前 WebContents/IPC 调用方)生成 PDF,关键选项为printBackground: true(保留背景)与preferCSSPageSize: true(优先使用页面 CSS 指定的尺寸);
  3. ensurePdfExtension保证路径以.pdf结尾,随后fsp.writeFile写入用户选择的文件并返回完整路径。

由于该方法走的是 IPC(@IpcMethod),渲染层(如阅读视图里的“导出当前页为 PDF”命令)必须经主进程执行才能真正拿到 Chromium 的printToPDF能力。此前这类导出若绕道渲染层或通道不完整会导致失败或空白 PDF,v1.5.0 修复后整条链路(IPC → SaveDialog → printToPDF → 写盘)均可正常工作。

主题偏好持久化:让深色/浅色选择在重启后依然生效

“Fixed theme preference persistence” 修复了 Electron 下主题偏好无法稳定持久化的问题。渲染层相关 Hook 是 useSyncTheme.ts:

  • 在 Electron 环境,useSyncThemeElectron通过 IPC 读取ipcServices.setting.getAppearance(),把结果应用到internal_useSetTheme(),并同步写入document.documentElement.dataset.theme;当取值为system时跟随系统深色模式(appIsDark)换算成具体主题;
  • 写入侧useSetThemeIN_ELECTRON为真时额外调用ipcServices.setting.setAppearance(colorMode)(useSyncTheme.ts),即主题选择不写在渲染层 localStorage,而是持久化到主进程设置存储中。

若渲染层与主进程间的读写时机、取值(light/dark/system)传递不一致,就会出现“看起来切了主题、重启后又回退”的持久化问题。该修复统一了 Electron 下主题的读写路径,确保偏好被正确保存并在应用重启后被主进程回放。

AI 对话输入框:长对话中始终停留在可视区

Improvements 中有一条体验向改动:在较长的 AI 对话过程中,把输入框保持在视口内。渲染层 AI 聊天布局(ai-chat)中,消息区与输入区采用分离布局,输入区组件为 ChatInput.tsx,它基于 Lexical 富文本编辑器 +ScrollArea实现多行编辑与自动增高;消息区(ChatMessageContainer.tsx)则配合useAutoScroll做底部滚动吸附。

当流式输出把消息区撑得很高时,如果输入框布局用的是普通文档流底部定位,长对话会把输入框挤出可视区。v1.5.0 的改进即调整此类场景下的容器与滚动行为,使输入框在长对话过程中始终可见、可输入,避免用户被迫在回答过程中反复滚动寻找输入位置。

Obsidian 集成:macOS 原生选目录与导出可靠性

“Fixed Obsidian vault selection and export reliability on macOS with native folder picking” 与主进程两处 IPC 有关:

  1. 目录选择: app.ts 的selectDirectory使用dialog.showOpenDialog({ properties: ["openDirectory"] })弹出原生目录选择框(macOS 上即是系统级的文件夹选择面板),返回选中的 vault 路径;配套checkPathExists(app.ts)用于校验路径仍存在。这解决的是此前使用非原生选择器在 macOS 上无法可靠读取/写入所选 vault 的问题。
  2. 导出写文件: integration.ts 的saveToObsidian接收标题、正文、作者、发布时间与vaultPath,把标题清洗后(截断到 80 字符)拼接为.md文件路径,生成 YAML frontmatter(调用createObsidianFrontmatter,包含urlauthorpublishedAttags: ["folo"]feedTitlefeedUrl),最终写入# 标题 + 正文的 Markdown 文件;已存在同名文件会返回 “File already exists”。

仓库中还保留了配套测试 integration.test.ts,它会创建临时目录模拟 vault,验证导出文件确实被写入且包含预期内容,同时覆盖了obsidian://URL scheme 的合法性校验(主进程只允许把obsidianbeardraftsthingsnotion等白名单协议交给shell.openExternal,见 integration.ts)。

其余修复与改进:弱网悬停、视频播放音调与界面打磨

v1.5.0 中还有几条以 changelog 形式记录、无法完全从当前主分支源码还原定位的修复,按官方说明口径如下:

  • 弱网下悬停的未读条目消失:渲染层条目列表有hoverMarkUnread(悬停标记未读)等交互设置(见 EntryItemWrapper.tsx),其背后涉及悬停时对条目的状态变更请求。在弱网下若该请求与列表渲染竞态,就会出现悬停后未读条目短暂消失的表现,此修复旨在保证网络抖动时悬停操作的状态一致性;
  • Android 视频播放改变原始音调:Folo 支持以不同倍速播放媒体(播放速率状态定义在 atoms/player.ts,playbackRate会直接作用于audio.playbackRate)。若按倍速处理视频却不做音调补偿,就会出现“变速后音调被改变”的现象。按 changelog 描述,v1.5.0 修复了该场景,保证变速播放时保持原始音频音调;
  • 钱包、通知与发现页打磨:配合认证体验的改进,v1.5.0 同时打磨了钱包、通知与 Discover 等表面的视觉与交互细节(渲染层对应 modules 下的discoverwalletnotification相关模块)。

注:上述条目以官方 1.5.0.md 为准;其中未在主分支源码中找到明确修复 commit 的条目,本文只按 changelog 口径转述,不做额外推断。

小结

Folo Desktop v1.5.0 是一个没有新功能、但把“地基”打扎实的版本:Electron 的认证链路(2FA 中间 Cookie、Origin/Referer 头、受管 Cookie 去重)被系统性修复,PDF 导出回归原生 IPC 通道,主题偏好持久化有了统一读写路径,Obsidian 导出在 macOS 上改为原生选目录并加固了写盘逻辑,AI 对话输入框与弱网悬停等体验细节也得到打磨。

这些改动大多发生在 主进程 IPC 服务 与 渲染层 modules 中,对于想理解“Electron 应用如何可靠地做服务端登录、Cookie 管理与文件导出”的开发者而言,是非常好的研读样本。下一版本可关注仓库中 next.md 模板的动态,了解社区贡献者(如 @Eumenides-K)与维护团队后续的演进方向。

【免费下载链接】follow🧡 Folo is the AI RSS Reader项目地址: https://gitcode.com/GitHub_Trending/fol/follow

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询