思源笔记 v2.9.7 版本深度解析:云端数据损坏修复、S3/WebDAV 登录策略收紧与 30 余项体验改进
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
v2.9.7 是思源笔记(SiYuan)同步与数据安全路线上的一个关键版本:本版修复了一个可能导致云端数据损坏的问题,官方建议所有用户尽快升级;同时自本版起,接入第三方云端存储 S3/WebDAV 进行数据同步与备份必须先登录思源账号。本文以仓库内官方更新日志 app/changelogs/v2.8.4-v2.12.8/v2.9.7/v2.9.7_zh_CN.md(对应英文版 v2.9.7.md)为主线,结合内核 Go 源码与桌面端实现,逐条拆解本版改动的技术背景、实现原理与升级注意事项,帮助开发者理解“为什么必须升级”以及“登录策略变化背后的校验链路”。
一、版本概览:一次建议“尽快升级”的维护性发布
该变更日志(下文简称“文档”)开头即给出两条高度浓缩的关键信息:
- 本版修复了一个导致云端数据损坏的问题,因此强烈建议尽快升级;
- 自本版起,使用第三方云端存储 S3/WebDAV 需要先登录账号,用户需提前知悉这一行为变化。
其余内容为分类变更清单,共覆盖五大类别:
| 类别 | 条目数 | 说明 |
|---|---|---|
| 改进功能 | 17 项 | 块引浮窗、搜索、移动端输入、文档树提示等 UI 与交互优化 |
| 修复缺陷 | 1 项 | 导出 Word 不渲染行级元素 |
| 开发重构 | 1 项 | 升级 Electron |
| 开发者 | 3 项 | 属性视图日期列、全局鼠标变量、内核 readDir 符号链接支持 |
下面按“为什么重要 → 底层怎么实现 → 用户如何受益”的顺序展开。
二、核心修复:文件被外部占用时不再导致云端数据损坏
文档强调的第一个要点是:此前当本地文件被外部进程占用(例如被同步盘、杀毒软件或手动编辑器锁定时)触发数据同步,有可能导致云端数据损坏;v2.9.7 修复了该问题。
2.1 同步链路上的“占用”风险点
思源的数据同步内核基于 Git 式的仓库(repository)机制,关键代码位于 kernel/model/repository.go 与 kernel/model/sync.go。一次同步会经历:
- 本地索引比对:找出本地与云端的差异文件;
- 打包上传/下载:通过
repository完成数据包传输; - 写入索引:将本次同步结果写入本地/云端索引与同步目录。
其中第 2、3 步涉及对磁盘文件的读写。若某个文档或资源文件恰好被外部进程锁定(例如 Windows 下被其他软件独占打开),读写阶段可能抛出异常或被截断,若异常处理不完善,就可能把不完整或错误的数据状态回写到云端索引,最终表现为云端数据损坏。
从源码结构看,kernel/model/sync.go 中的同步入口会对不同模式(自动/手动/完全手动)、是否启动/退出同步等场景做精细的checkSync判断,本版正是在这类边界条件下补强了异常防护,避免“文件被占用 → 同步继续 → 云端数据被污染”的连锁反应。文档中的原话“文件被外部占用时数据同步不再导致云端数据损坏”即对应这一修复,这也是官方将其列为“建议尽快升级”的首要原因。
2.2 给使用者的操作建议
- 升级前:如果怀疑本地存在被外部程序长期占用的数据文件,可先关闭可能占用工作空间的第三方同步盘或文件监听工具;
- 升级后:首次启动会按新逻辑重建/校验同步状态,建议在网络稳定时手动执行一次完整同步;
- 多设备场景:让所有客户端都升级到 v2.9.7 及以上,避免旧版本客户端仍携带该缺陷参与同步。
三、策略变更:S3/WebDAV 数据同步与备份需要登录
文档第二条关键信息:“从此版本开始,接入第三方云端存储 S3/WebDAV 需要登录后才能使用”。这一改动同时作用于数据同步与数据备份两条功能链路。
3.1 为什么需要登录
思源将第三方对象存储接入视为需要账号体系支撑的高级能力。登录后内核才能校验账号的订阅/一次性付费状态,从而决定是否放行 S3/WebDAV 数据通路。在仓库当前源码中,这一校验链路的证据非常清晰:
- 存储提供者枚举定义在 kernel/conf/sync.go:
const ( ProviderSiYuan = 0 // ProviderSiYuan 为思源官方提供的云端存储服务 ProviderS3 = 2 // ProviderS3 为 S3 协议对象存储提供的云端存储服务 ProviderWebDAV = 3 // ProviderWebDAV 为 WebDAV 协议提供的云端存储服务 ProviderLocal = 4 // ProviderLocal 为本地文件系统提供的存储服务 )- 同步放行判断位于 kernel/model/sync.go 的
checkSync:
switch Conf.Sync.Provider { case conf.ProviderSiYuan: if !IsSubscriber() { ... return false } case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { ... return false } }即:官方云存储要求“订阅用户”,而 S3/WebDAV(以及本地文件系统)要求“已登录且为付费用户”。账号判定函数定义在 kernel/model/conf.go:
IsSubscriber():用户已登录且订阅状态正常(未订阅、封禁或过期均不通过);IsPaidUser():是订阅用户,或完成过一次性的 PRO 功能付费;未登录(GetUser()为 nil)时直接返回 false。
这正是“接入 S3/WebDAV 需要登录”的内核实现:登录是第一步,付费状态是第二步。
3.2 配置模型与 API 鉴权
S3 与 WebDAV 的配置结构同样定义在 kernel/conf/sync.go:
type S3 struct { Endpoint string `json:"endpoint"` // 服务端点 AccessKey string `json:"accessKey"` // Access Key SecretKey string `json:"secretKey"` // Secret Key Bucket string `json:"bucket"` // 存储空间 Region string `json:"region"` // 存储区域 PathStyle bool `json:"pathStyle"` // 是否使用路径风格 SkipTlsVerify bool `json:"skipTlsVerify"` // 是否跳过 TLS 验证 Timeout int `json:"timeout"` // 超时时间,单位:秒 ConcurrentReqs int `json:"concurrentReqs"` // 并发请求数 } type WebDAV struct { Endpoint string `json:"endpoint"` // 服务端点 Username string `json:"username"` // 用户名 Password string `json:"password"` // 密码 SkipTlsVerify bool `json:"skipTlsVerify"` // 是否跳过 TLS 验证 Timeout int `json:"timeout"` // 超时时间,单位:秒 ConcurrentReqs int `json:"concurrentReqs"` // 并发请求数 }在写入配置时,内核会对端点、密钥、桶名等做规范化处理,例如SetSyncProviderS3会对 Endpoint 做NormalizeEndpoint、对超时与并发数做取值范围归一(见 kernel/model/sync.go);SetSyncProviderWebDAV还会显式拒绝坚果云 WebDAV(接口限制所致),返回本地化提示(对应 app/appearance/langs/en.json 的 key 194)。
在内核 API 层,所有同步与备份相关接口都挂载了统一的鉴权中间件。以 kernel/api/router.go 为例:
ginServer.Handle("POST", "/api/sync/setSyncProviderS3", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncProviderS3) ginServer.Handle("POST", "/api/sync/setSyncProviderWebDAV", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncProviderWebDAV) ginServer.Handle("POST", "/api/sync/performSync", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, performSync) ginServer.Handle("POST", "/api/sync/performBootSync", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, performBootSync)任何未登录/非管理员会话请求这些端点都会被CheckAuth拦截,从服务端入口层面保障了“S3/WebDAV 必须登录后才可用”的策略一致性。
3.3 对存量用户的影响与处置
- 已配置 S3/WebDAV 的老用户升级后,若未登录,同步与备份会按新策略停止或提示登录;
- 处置方式:在设置 → 账号中登录思源账号并确认付费状态(一次性 PRO 或订阅均可),再回到同步设置重新触发同步;
- 若收到诸如“requires logging into the account…”之类的本地化提示(见 app/appearance/langs/en.json key 214),说明账号尚未登录或付费状态未生效,可参考提示刷新或重新登录。
四、改进功能逐项解读(17 项)
文档“改进功能”部分罗列了 17 项,覆盖编辑器、搜索、移动端、文档树与视觉细节,可归纳为以下几组。
4.1 块引与引用体验
- 改进块引浮窗位置和大小:优化块引用悬停浮窗的定位算法与尺寸自适应,避免浮窗遮挡正文或在大屏/小屏下显示异常;
- 改进块引计数显示位置:调整块引用计数(引用次数角标)的渲染位置,与行内内容对齐更自然。
4.2 搜索与替换
Alt+↓/↑选择搜索历史关键字:在搜索输入框聚焦时,可通过Alt+↓/↑在历史关键字间循环切换,减少重复输入;- 改进搜索替换:对查找/替换流程的命中定位与高亮联动做了细化;
- 搜索界面添加刷新按钮:在搜索面板增加“刷新”入口,便于重新执行当前关键字搜索;
Ctrl+P搜索不再沿用上一次指定的路径:此前Ctrl+P(全局搜索)会沿用上次手动指定的搜索路径,容易让用户误以为搜索范围不对;本版改为每次按当前上下文/默认范围发起搜索。
4.3 移动端与输入
- 改进移动端字体设置交互:优化移动端设置界面中字体选择/预览的交互路径;
- 改进 Android 端输入法相关问题:针对 Android 软键盘弹起、候选词与编辑器滚动冲突等问题做适配。
4.4 编辑器视觉与文档操作细节
修改图标改为随机图标:将文档/笔记本图标菜单中的操作语义从“修改”改为“随机”,更贴合“从 emoji 中随机挑一个”的实际行为;- 优化文档树提示文案文字间隙大小:修正文档树 tooltip 中文字间距过大导致的排版问题;
- 代码块操作图标不再被遮挡:修复代码块右上角操作按钮(复制、运行等)在部分主题/宽度下被裁切的问题;
- 嵌入块显示提示文案:为嵌入块补充悬停提示,说明其引用来源,便于理解嵌入内容来自何处;
- 优化标签背景和文字颜色:提升标签在深浅主题下的对比度与可读性;
- 删除块后刷新面包屑:删除块后同步更新顶部面包屑导航,避免残留已删除路径;
- 改进菜单滚动条:优化弹出菜单在内容超高时的滚动条样式与交互。
五、缺陷修复:导出 Word 不渲染行级元素
文档“修复缺陷”部分仅一条:修复导出 Word 时不渲染行级元素的问题(Issue #8774)。
行级元素包括加粗、斜体、行内代码、超链接、标记等 Markdown 行内语法。此前从思源导出 Word(docx)时,这些行内样式可能丢失,只保留块级段落结构,导致导出的文档“有段落、无强调”。本版针对导出链路中行级节点的渲染逻辑做了修复,使.docx输出完整保留行内样式。若读者日常使用“导出 Word”功能分发文档,本项修复直接关系到导出质量。
六、开发重构:升级 Electron
文档将“升级 Electron”列为开发重构项(Issue #8797)。桌面端由 app/package.json 声明 Electron 依赖并以 Electron 承载渲染层与主进程,升级主要带来内核安全补丁与系统兼容性改善,同时为后续桌面端新特性奠定基础。值得注意的是,文档把“导出 Word 渲染缺陷”单独列为 bugfix、把“升级 Electron”单独列为 refactor,二者在本版同时合入,升级 Electron 时通常也要求对 app/electron 下的主进程代码做相应回归验证。
七、面向开发者的三项变更
文档“开发者”部分包含三项对二次开发与插件生态有直接价值的改动。
7.1 属性视图添加日期类型列(Issue #8692)
属性视图(数据库)新增date 日期类型。在仓库当前源码中,属性视图的键类型枚举定义于 kernel/av/av.go,KeyTypeDate = "date",并配有日期相关的值结构与时间展示能力。这意味着开发者可以在属性视图中直接为条目建立“日期”维度字段,用于按时间管理文档、任务或条目。
7.2 添加全局鼠标位置变量(PR #8793)
为前端/插件运行环境新增一个全局鼠标位置变量,使开发者无需自行监听mousemove即可在需要的地方读取当前光标坐标。这类能力通常用于浮窗跟随光标、右键菜单定位、拖拽辅助等需要感知指针位置的交互场景。
7.3 内核 API/api/file/readDir支持返回符号链接信息(PR #8805)
文件管理类内核接口/api/file/readDir现在会为每个目录项附带是否为符号链接的标记。在仓库当前实现中,kernel/api/router.go 将该端点注册为:
ginServer.Handle("POST", "/api/file/readDir", model.CheckAuth, model.CheckAdminRole, readDir)而 kernel/api/file.go 中读取目录项时会调用util.IsSymlink(entry),将结果以isSymlink字段随每个文件项返回:
files = append(files, map[string]any{ "name": entry.Name(), "isDir": info.IsDir(), "isSymlink": util.IsSymlink(entry), "updated": info.ModTime().Unix(), })该能力对工作空间外部目录/云盘挂载目录类功能尤其重要:插件或外部集成在枚举目录时,可以识别出符号链接,避免误将链接当作普通文件处理,或据此决定是否跟随链接递归遍历。
八、升级与验证建议
综合以上分析,给出针对本版的落地建议:
- 尽快升级:鉴于“云端数据损坏”修复的优先级,官方建议尽快升级到 v2.9.7 及以上;升级后先在单台设备上验证同步状态再逐步覆盖多端;
- 登录检查:若使用 S3/WebDAV 同步/备份,请确认账号已登录且为订阅或 PRO 付费状态,否则新版会按策略拒绝数据通路;
- 导出验证:常用 Word 导出的用户,升级后可导出一份包含行内样式(加粗、行内代码、超链接)的文档核验修复效果;
- 开发者自测:若你开发属性视图插件或使用
/api/file/readDir,可分别验证 date 列行为与目录项返回结构中的isSymlink字段。
本版所有变更明细均可在仓库 app/changelogs/v2.8.4-v2.12.8/v2.9.7 目录下查阅到中/英/繁三语版本;与同步策略、配置模型、账号判定及文件枚举相关的实现可进一步阅读 kernel/conf/sync.go、kernel/model/sync.go、kernel/model/conf.go 与 kernel/api/file.go 对应源码。
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考