Actual 26.5.1/26.5.2 补丁发布解析:认证限流、自签名证书与 UUID 生成兼容性修复

发布时间:2026/9/11 16:04:59
Actual 26.5.1/26.5.2 补丁发布解析:认证限流、自签名证书与 UUID 生成兼容性修复 Actual 26.5.1/26.5.2 补丁发布解析认证限流、自签名证书与 UUID 生成兼容性修复【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual本篇基于 Actual 仓库官方发布公告 2026-05-08-release-26-5-1.md 编写。v26.5.1 是一个聚焦的补丁版本针对自托管场景下的三个真实痛点——认证接口限流误伤、桌面端连接自签名证书服务器失败、非 HTTPS 环境下 UUID 生成报错——给出了修复方案。阅读本文后你将理解这三次修复的底层原因与源码实现位置并掌握对应的升级方式与配置要点。一、版本概览26.5.1 与 26.5.2 功能完全相同发布公告明确说明v26.5.1 与 v26.5.2 在功能上是完全一致的functionally identical。额外发布 26.5.2 的唯一目的是解决 Windows Store 应用商店发布环节的问题不包含任何代码层面的差异。Docker Tag26.5.1/26.5.2发布时间2026-05-08类型补丁版本patch release仅包含 bugfix不引入新功能本版本共合入三个修复项全部围绕自托管self-hosted部署体验展开PR修复内容主要贡献者#7707认证限流只统计失败的登录尝试danielhopkins#7713修复桌面端自签名证书功能MikesGlitch#7734UUID 生成回退使用uuid库而非crypto.randomUUID()MatissJanis完整的版本历史与变更记录维护在 packages/docs/docs/releases.md 中可对照查阅该版本前后的功能演进。二、修复一认证限流只统计失败的登录尝试#77072.1 问题背景Actual 的同步服务器sync-server对认证接口做了速率限制rate limiting用于抵御暴力破解。此前的实现会对窗口期内的所有登录请求计数包括密码正确、登录成功的请求。这带来的副作用是在正常的多次登录、或客户端重试场景下合法用户也可能被限流器拦截返回 429造成被锁死的体验。#7707 的修复思路很直接只把失败的登录尝试计入限流计数成功的登录不再消耗配额。2.2 源码实现限流器定义在 packages/sync-server/src/app-account.js基于express-rate-limit中间件const authRateLimiter rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 attempts per window legacyHeaders: false, standardHeaders: true, skipSuccessfulRequests: true, message: { status: error, reason: too-many-requests }, });关键参数说明skipSuccessfulRequests: true本次修复的核心。开启后响应状态码为 2xx 的请求即登录成功的请求不会计入限流计数只有失败请求如密码错误返回 4xx才会累积windowMs: 15 * 60 * 1000时间窗口为 15 分钟max: 5每个窗口内允许的失败尝试上限为 5 次standardHeaders: true/legacyHeaders: false通过标准化的RateLimit-*响应头而非废弃的X-RateLimit-*把限流状态暴露给客户端message触发限流时返回的 JSON 响应体reason固定为too-many-requests。该限流器被挂载到两个未认证端点见 app-account.jsapp.post(/bootstrap, authRateLimiter, async (req, res) { ... }); app.post(/login, authRateLimiter, async (req, res) { ... });/bootstrap用于首次初始化实例仅可调用一次/login用于密码登录。两者共享同一个限流器实例因此限流计数在/login与/bootstrap之间是联动的。2.3 测试用例验证对应的行为测试位于 packages/sync-server/src/app-account.test.js共覆盖三个场景连续失败触发 429连续 5 次用错误密码调用/login后第 6 次请求返回429响应体为{ status: error, reason: too-many-requests }跨端点共享限流连续 5 次失败的/login之后再调用/bootstrap同样被限流器拦截返回 429验证了同一限流器应用于多个端点的设计非认证端点不受影响即使/login已触发限流GET /needs-bootstrap这类非认证端点仍返回 200。此外测试的beforeEach中通过authRateLimiter.resetKey(127.0.0.1)重置限流计数保证测试之间相互独立。2.4 对使用者的影响该修复对 API 行为的影响是透明的限流窗口15 分钟、失败次数上限5 次、429 响应格式均未改变行为变化体现在成功登录不再消耗配额多设备、多客户端频繁登录时更不容易被误锁触发限流后客户端应依据RateLimit-*响应头中的Retry-After类信息等待窗口重置而不是立即重试。三、修复二桌面端自签名证书支持#77133.1 问题背景Actual 桌面应用Electron在连接使用**自签名证书self-signed certificate**的自托管服务器时其底层fetch调用会因无法信任该证书而校验失败导致同步、登录等请求被拒。此前该功能存在回归本版本将其修复。3.2 源码实现修复逻辑位于 packages/desktop-electron/index.ts 的createBackgroundProcess函数中。桌面端启动后台服务器进程时会先从global-store.json读取全局偏好async function loadGlobalPrefs() { let state: GlobalPrefsJson {}; try { state JSON.parse( fs.readFileSync( path.join(process.env.ACTUAL_DATA_DIR!, global-store.json), utf8, ), ); } catch { logMessage(info, Could not load global state - using defaults); state {}; } return state; }随后如果全局偏好中存在server-self-signed-cert则将其注入后台进程的环境变量if (globalPrefs[server-self-signed-cert]) { envVariables { ...envVariables, NODE_EXTRA_CA_CERTS: globalPrefs[server-self-signed-cert], // add self signed cert to env - fetch can pick it up }; }机制说明server-self-signed-cert偏好值指向自签名证书的路径通过设置NODE_EXTRA_CA_CERTS环境变量Node.js 的fetch/https层会将该证书追加到系统 CA 信任链中从而能够正常验证自签名证书该环境变量在 fork 后台 server 进程utilityProcess.fork(__dirname /server.js, ...)时一并传入因此只影响桌面端启动的后台服务器进程不影响系统其他进程注释明确说明了这一设计意图add self signed cert to env - fetch can pick it up。3.3 使用与验证方式在桌面端设置或直接编辑global-store.json中配置server-self-signed-cert指向你的自签名证书文件路径重启桌面应用使createBackgroundProcess重新读取全局偏好并重建后台进程环境连接使用该证书的自托管服务器登录与同步请求即可正常完成。从代码结构看该修复同时保证了重启后依然生效createBackgroundProcess每次都会重新调用loadGlobalPrefs()确保最新配置被加载。四、修复三UUID 生成回退到uuid库#77344.1 问题背景在更早的版本中项目部分位置改用 Web 平台原生的crypto.randomUUID()生成 UUID。该 API 的可用性依赖安全上下文secure context——即仅在 HTTPS 或 localhost 环境下可用。对于通过纯 HTTP非 HTTPS地址访问的自部署实例调用crypto.randomUUID()会直接抛出异常导致会话标识等关键数据无法生成功能不可用。#7734 的处理方式是回退将相关调用重新改为使用成熟的uuid库uuidv4()它不依赖安全上下文在任何环境下都能稳定生成 UUID v4。4.2 源码实现以 packages/loot-core/src/platform/client/connection/index.ts 为例import { v4 as uuidv4 } from uuid;会话请求的标识符生成index.ts使用const id uuidv4();uuid库在整个仓库中被广泛使用包括平台层连接模块 packages/loot-core/src/platform/client/connection/index.electron.ts、packages/loot-core/src/platform/client/undo/index.ts、packages/loot-core/src/platform/server/sqlite/index.electron.ts服务端账户与同步逻辑如 packages/loot-core/src/server/accounts/app.ts、packages/loot-core/src/server/accounts/sync.ts、packages/loot-core/src/server/cloud-storage.ts数据库迁移脚本如 packages/loot-core/migrations/1722804019000_create_dashboard_table.js、packages/loot-core/migrations/1765518577215_multiple_dashboards.js测试与 mock 数据如 packages/loot-core/src/mocks/budget.ts、packages/loot-core/src/mocks/index.ts。由此可见项目整体统一采用uuid库生成标识符这既保证了跨平台浏览器/Electron/Node的一致性也规避了安全上下文差异带来的兼容性问题。4.3 对使用者的影响修复后通过 HTTP 访问的自部署实例未启用 HTTPS 反代的环境不会再因为crypto.randomUUID不可用而出现会话/请求标识生成失败生成结果仍是标准 UUID v4对外接口与数据格式无任何变化无需迁移既有数据。五、升级指引5.1 Docker 部署自托管用户可直接拉取新标签进行升级docker pull actualbudget/actual-server:26.5.1 # 或等价标签 docker pull actualbudget/actual-server:26.5.2由于两个标签功能一致选用任意一个即可升级后建议通过浏览器访问管理页验证登录与同步正常。5.2 版本兼容性说明本次补丁不包含数据库迁移migrations因此升级过程不涉及数据结构变更回退到 26.5.0 也是安全的前提是期间没有写入依赖新格式的数据限流行为15 分钟窗口 / 5 次失败上限保持不变仅计数口径发生变化无需调整客户端重试逻辑若你此前因自签名证书问题在桌面端使用过server-self-signed-cert偏好升级后该偏好会继续按预期生效。六、小结v26.5.1/26.5.2 虽然是一个小补丁版本但三个修复都精准地指向了自托管部署链路中的真实痛点认证限流app-account.js通过skipSuccessfulRequests让成功登录不再消耗限流配额配合 app-account.test.js 的三组用例验证了限流边界自签名证书desktop-electron/index.ts通过将证书路径注入NODE_EXTRA_CA_CERTS环境变量恢复了桌面端连接私有服务器的能力UUID 生成connection/index.ts回退到uuid库彻底消除了 HTTP 非安全上下文下的运行时异常。对于运行自托管 Actual 的用户这是值得及时跟进的一个版本对于二次开发或审计需求的读者上述源码路径可以作为理解这三块逻辑的起点。【免费下载链接】actualA local-first personal finance app项目地址: https://gitcode.com/GitHub_Trending/ac/actual创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考