问题概述
lxserver 的 WebDAV 备份同步功能(src/utils/webdavSync.ts、src/index.ts)存在多处逻辑缺陷,导致备份不稳定、配置不生效、首次启动只备份 config.js 等问题。
🔴 缺陷 1:云端为空时只上传 config.js,其他文件不传
文件: src/utils/webdavSync.ts 第 652-662 行
严重程度: 高
在 restoreFromRemote() 中,当云端没有任何散文件和备份时(首次使用 / 清空云端后),代码仅上传 config.js,不调用 syncAllFiles() 上传其余数据文件。
// 第 656-660 行
if (global.lx && global.lx.saveConfig) {
global.lx.saveConfig()
}
await this.uploadFile(config.js) // 只传了 config.js!
return false
影响: 首次启动或清空云端后重启,只有 config.js 被备份到云端,其他文件(users.json、用户歌单快照、自定义源等)永远不会上传,除非用户手动调用 /api/webdav/sync API 或等到 24 小时后的自动备份周期。
修复建议: 此处应调用 await this.syncAllFiles() 替代仅上传 config.js。
🔴 缺陷 2:sync.interval 配置项实际不生效
文件: src/utils/webdavSync.ts 第 43 行
严重程度: 高
this.syncInterval = (config.interval || 60) * 60 * 1000
存在两个问题:
- 单位错误:
config.interval 乘以了 60 * 60 * 1000(即 360 万毫秒),如果用户配置 sync.interval: 60,代码将其理解为 60 分钟而非预期的 60 秒
- 从未使用:
this.syncInterval 赋值后在整个类中没有任何地方被引用。实际的文件变化检测周期是硬编码的 watchInterval = 60000(第 29 行),固定每 60 秒运行一次,与用户配置无关
影响: 用户配置的 sync.interval 完全无效,无论设多少,自动同步始终每 60 秒运行一次。
🔴 缺陷 3:saveConfigToFile() 触发热重载循环
文件: src/index.ts 第 444 行、第 448-475 行
严重程度: 高
启动时序存在竞态条件:
- 第 444 行:
saveConfigToFile() 写入 config.js
- 第 452 行:
fs.watch() 开始监听 config.js 变化
- 第 444 行的写入事件被 watcher 捕获 → 触发
updateConfig()
updateConfig() 调用 stopAutoSync() → startAutoSync()(第 749-753 行)
- 自动同步的 60 秒定时器被重置
如果 restoreFromRemote()(第 381 行,异步启动)在执行过程中也调用了 saveConfig()(第 619/643/657 行),则会再次触发 watcher,形成循环。
影响: 每次容器启动都会触发至少一次不必要的热重载,导致自动同步定时器重置。严重时可能出现"启动→写 config.js→热重载→停同步→重开→再写 config.js→再热重载"的循环。
修复建议:
saveConfigToFile() 应放在第 448 行 fs.watch() 调用之前执行,或
- watcher 应校验文件内容是否真正变化后再触发重载
🟡 缺陷 4:fs.watch 不校验内容是否真实变化
文件: src/index.ts 第 452-474 行
严重程度: 中
fs.watch 仅检测文件写入事件(event === change),不计算文件 hash 或对比内容。而 saveConfigToFile() 每次都是完整序列化写入磁盘,即使配置没有实质变化也会触发写入,进而触发热重载。
🟡 缺陷 5:updateConfig() 不做变更检测,每次无条件重启同步
文件: src/index.ts 第 461-467 行、src/utils/webdavSync.ts 第 742-755 行
严重程度: 中
热重载时无论 WebDAV 的 url/username/password/interval 是否真正变化,都调用 updateConfig(),导致每次都关闭 WebDAV 客户端并重新初始化、重新扫描所有文件、重置 60 秒定时器。
🟡 缺陷 6:syncAllFiles() 上传失败静默吞掉,无控制台输出
文件: src/utils/webdavSync.ts 第 218-233 行、第 445-455 行
严重程度: 中
uploadFile() 内部 catch 了所有错误,仅写入内部 syncLogs 缓冲区(最多 100 条),不打印到 console。用户查看 docker logs 看不到任何上传失败的信息。必须通过 /api/webdav/logs API 才能查看。
🟡 缺陷 7:restoreFromRemote() 恢复后不做数据一致性校验
文件: src/utils/webdavSync.ts 第 581-669 行
严重程度: 中
从云端成功恢复数据到本地后(success=true),只重载了 config.js、users.json 和自定义源,没有对比本地和云端的数据是否完整。如果云端因共享路径或其他原因缺少部分文件,本地的多余文件不会被补传上去。
🟡 缺陷 8:远程路径 /lx-sync/ 和 /lx-sync-backups/ 完全硬编码
文件: src/utils/webdavSync.ts 第 147、175、241、360、469、587 行
严重程度: 中
所有 WebDAV 请求的路径都是硬编码的字符串,无法通过配置修改。当两个 lxserver 实例指向同一个 webdav.url 时,它们会互相覆盖文件。用户只能通过在 webdav.url 中手动拼接路径前缀(如 /dav/instance1/)来规避。
修复建议: 将路径前缀改为可配置项,或从 webdav.url 自动派生唯一路径。
🟢 缺陷 9:备份 zip 先在本地创建再上传
文件: src/utils/webdavSync.ts 第 291-337 行
严重程度: 低
createBackup() 先在本地 /server/data/ 下创建 zip 文件,然后上传,最后删除。若 data 目录磁盘空间不足(虽然实际场景不太可能)或文件被占用,会导致备份失败。可使用流式压缩直接上传避免临时文件。
🟢 缺陷 10:cleanOldBackups() 的备份排序依赖字符串比较
文件: src/utils/webdavSync.ts 第 472 行
严重程度: 低
.sort((a, b) => b.lastmod.localeCompare(a.lastmod))
不同 WebDAV 服务返回的 lastmod 格式可能不同(如 RFC 1123 vs ISO 8601),字符串比较在某些格式下排序不准确,可能导致删除的是错误的备份。
环境信息
- lxserver 版本: latest (Docker 镜像)
- Node.js: v24.16.0
- 测试场景: Docker 容器运行,WebDAV 后端为 TeraCloud(日本节点)
问题概述
lxserver 的 WebDAV 备份同步功能(
src/utils/webdavSync.ts、src/index.ts)存在多处逻辑缺陷,导致备份不稳定、配置不生效、首次启动只备份 config.js 等问题。🔴 缺陷 1:云端为空时只上传 config.js,其他文件不传
文件:
src/utils/webdavSync.ts第 652-662 行严重程度: 高
在
restoreFromRemote()中,当云端没有任何散文件和备份时(首次使用 / 清空云端后),代码仅上传 config.js,不调用syncAllFiles()上传其余数据文件。影响: 首次启动或清空云端后重启,只有 config.js 被备份到云端,其他文件(users.json、用户歌单快照、自定义源等)永远不会上传,除非用户手动调用
/api/webdav/syncAPI 或等到 24 小时后的自动备份周期。修复建议: 此处应调用
await this.syncAllFiles()替代仅上传 config.js。🔴 缺陷 2:
sync.interval配置项实际不生效文件:
src/utils/webdavSync.ts第 43 行严重程度: 高
存在两个问题:
config.interval乘以了60 * 60 * 1000(即 360 万毫秒),如果用户配置sync.interval: 60,代码将其理解为 60 分钟而非预期的 60 秒this.syncInterval赋值后在整个类中没有任何地方被引用。实际的文件变化检测周期是硬编码的watchInterval = 60000(第 29 行),固定每 60 秒运行一次,与用户配置无关影响: 用户配置的
sync.interval完全无效,无论设多少,自动同步始终每 60 秒运行一次。🔴 缺陷 3:
saveConfigToFile()触发热重载循环文件:
src/index.ts第 444 行、第 448-475 行严重程度: 高
启动时序存在竞态条件:
saveConfigToFile()写入 config.jsfs.watch()开始监听 config.js 变化updateConfig()updateConfig()调用stopAutoSync()→startAutoSync()(第 749-753 行)如果
restoreFromRemote()(第 381 行,异步启动)在执行过程中也调用了saveConfig()(第 619/643/657 行),则会再次触发 watcher,形成循环。影响: 每次容器启动都会触发至少一次不必要的热重载,导致自动同步定时器重置。严重时可能出现"启动→写 config.js→热重载→停同步→重开→再写 config.js→再热重载"的循环。
修复建议:
saveConfigToFile()应放在第 448 行fs.watch()调用之前执行,或🟡 缺陷 4:
fs.watch不校验内容是否真实变化文件:
src/index.ts第 452-474 行严重程度: 中
fs.watch仅检测文件写入事件(event === change),不计算文件 hash 或对比内容。而saveConfigToFile()每次都是完整序列化写入磁盘,即使配置没有实质变化也会触发写入,进而触发热重载。🟡 缺陷 5:
updateConfig()不做变更检测,每次无条件重启同步文件:
src/index.ts第 461-467 行、src/utils/webdavSync.ts第 742-755 行严重程度: 中
热重载时无论 WebDAV 的 url/username/password/interval 是否真正变化,都调用
updateConfig(),导致每次都关闭 WebDAV 客户端并重新初始化、重新扫描所有文件、重置 60 秒定时器。🟡 缺陷 6:
syncAllFiles()上传失败静默吞掉,无控制台输出文件:
src/utils/webdavSync.ts第 218-233 行、第 445-455 行严重程度: 中
uploadFile()内部 catch 了所有错误,仅写入内部syncLogs缓冲区(最多 100 条),不打印到 console。用户查看 docker logs 看不到任何上传失败的信息。必须通过/api/webdav/logsAPI 才能查看。🟡 缺陷 7:
restoreFromRemote()恢复后不做数据一致性校验文件:
src/utils/webdavSync.ts第 581-669 行严重程度: 中
从云端成功恢复数据到本地后(
success=true),只重载了 config.js、users.json 和自定义源,没有对比本地和云端的数据是否完整。如果云端因共享路径或其他原因缺少部分文件,本地的多余文件不会被补传上去。🟡 缺陷 8:远程路径
/lx-sync/和/lx-sync-backups/完全硬编码文件:
src/utils/webdavSync.ts第 147、175、241、360、469、587 行严重程度: 中
所有 WebDAV 请求的路径都是硬编码的字符串,无法通过配置修改。当两个 lxserver 实例指向同一个
webdav.url时,它们会互相覆盖文件。用户只能通过在webdav.url中手动拼接路径前缀(如/dav/instance1/)来规避。修复建议: 将路径前缀改为可配置项,或从
webdav.url自动派生唯一路径。🟢 缺陷 9:备份 zip 先在本地创建再上传
文件:
src/utils/webdavSync.ts第 291-337 行严重程度: 低
createBackup()先在本地/server/data/下创建 zip 文件,然后上传,最后删除。若 data 目录磁盘空间不足(虽然实际场景不太可能)或文件被占用,会导致备份失败。可使用流式压缩直接上传避免临时文件。🟢 缺陷 10:
cleanOldBackups()的备份排序依赖字符串比较文件:
src/utils/webdavSync.ts第 472 行严重程度: 低
不同 WebDAV 服务返回的
lastmod格式可能不同(如 RFC 1123 vs ISO 8601),字符串比较在某些格式下排序不准确,可能导致删除的是错误的备份。环境信息