fix(backup): WebDAV 恢复后保留铃铛订阅、Watch 来源与已读状态#249
Conversation
…across WebDAV restore WebDAV backup/restore (BackupPanel) hand-assembled its JSON payload and omitted three release-related pieces of frontend-only Zustand state, and handleRestore never read them back — so after a restore the bell-icon subscriptions (releaseSubscriptions), the Watch/custom release source lists (releaseSourceSettings.watchCustomReleaseRepos / customReleaseRepos) and the read markers (readReleases) all reverted to empty. These live only in the frontend (persisted to IndexedDB) with no server-side table, and the bell UI keys off releaseSubscriptions.has(id), so the loss was complete and silent. Mirror the existing local-file export/import behavior in DataManagementPanel: write the three fields into the backup blob (version bumped 1.0 -> 1.1) and restore them via useAppStore.setState / setReleaseSourceSettings. Legacy 1.0 backups without these fields restore to empty via the setter's built-in normalizeReleaseSourceSettings(null) fallback — no error, backward compatible. Co-Authored-By: Claude <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough
ChangesRelease backup data
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request updates the backup and restore functionality in BackupPanel.tsx to include release subscriptions, release source settings, and read releases state, bumping the backup version to 1.1. The review feedback suggests adding defensive filtering to ensure only numbers are added to the releaseSubscriptions and readReleases sets, preventing type pollution from the untyped backup data. Additionally, it is recommended to avoid passing null to setReleaseSourceSettings to adhere to its expected type signature.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (Array.isArray(backupData.releaseSubscriptions)) { | ||
| useAppStore.setState({ releaseSubscriptions: new Set<number>(backupData.releaseSubscriptions) }); | ||
| } |
There was a problem hiding this comment.
为了防止备份文件损坏或被篡改导致 store 中的 Set<number> 混入非数字类型(由于 backupData 是 any 类型,直接传入 new Set 会绕过 TypeScript 的运行时类型检查),建议在恢复时进行防御性过滤,仅保留数字类型的 ID。这与 store 内部从本地存储恢复时的 normalizeNumberSet 行为保持一致。
if (Array.isArray(backupData.releaseSubscriptions)) {
const validIds = backupData.releaseSubscriptions.filter((id): id is number => typeof id === 'number');
useAppStore.setState({ releaseSubscriptions: new Set<number>(validIds) });
}
| if (backupData.releaseSourceSettings !== undefined) { | ||
| setReleaseSourceSettings(backupData.releaseSourceSettings ?? null); | ||
| } |
There was a problem hiding this comment.
Store 中的 setReleaseSourceSettings 动作在类型定义上接收的是非空的 ReleaseSourceSettings。虽然 backupData 作为 any 绕过了编译检查,但传入 null 依然违背了类型契约。建议通过简单的真值检查来避免传递 null,从而保证更好的类型安全。
| if (backupData.releaseSourceSettings !== undefined) { | |
| setReleaseSourceSettings(backupData.releaseSourceSettings ?? null); | |
| } | |
| if (backupData.releaseSourceSettings) { | |
| setReleaseSourceSettings(backupData.releaseSourceSettings); | |
| } |
| if (Array.isArray(backupData.readReleases)) { | ||
| useAppStore.setState({ readReleases: new Set<number>(backupData.readReleases) }); | ||
| } |
There was a problem hiding this comment.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/settings/BackupPanel.tsx`:
- Around line 157-172: Validate backupData.releaseSubscriptions and
backupData.readReleases before constructing their Set instances by filtering
each array to retain only numeric elements, matching the store’s
normalizeNumberSet behavior. Update the corresponding useAppStore.setState calls
in the restore block while preserving the existing handling of
releaseSourceSettings and backward compatibility.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 695624a4-c586-49b0-8871-87ae3847bfff
📒 Files selected for processing (1)
src/components/settings/BackupPanel.tsx
… type Address review findings from gemini-code-assist and coderabbitai on PR #249: - Filter releaseSubscriptions / readReleases to numeric IDs before constructing Set<number>, matching the store's private normalizeNumberSet behavior. Untrusted/corrupted backup JSON could otherwise pollute the sets with non-number types (since backupData is `any` from JSON.parse), silently breaking releaseSubscriptions.has(repoId) lookups. - Pass a truthy (non-null) releaseSourceSettings to setReleaseSourceSettings instead of `?? null`, honoring the setter's non-null ReleaseSourceSettings type contract. Truthy guard keeps 1.0 backward compat (missing field -> skip -> store retains default). Co-Authored-By: Claude <noreply@anthropic.com>
问题
备份到 WebDAV → 恢复备份后,以下 release 相关数据全部丢失:
releaseSubscriptions)releaseSourceSettings.watchCustomReleaseRepos/customReleaseRepos)readReleases)根因
WebDAV 备份/恢复(
src/components/settings/BackupPanel.tsx)自行手工拼装 JSON 负载,遗漏了这三项**仅存于前端 Zustand store(持久化到 IndexedDB)**的状态,且无对应服务端表;handleRestore也从不回填它们。铃铛 UI 凭releaseSubscriptions.has(repoId)判定亮灭(RepositoryCard.tsx),恢复后该集合为new Set()→ 所有铃铛全灭,丢失静默。repositories里的每行subscribed_to_releases布尔列虽存活,但 UI 不读它(resolveReleaseSources用releaseSubscriptions.has(repo.id)过滤),故不解决丢失。平行的本地文件导出/导入
DataManagementPanel.tsx本就处理正确,本次按其行为镜像到 WebDAV 链路。改动(仅
BackupPanel.tsx)backupData写入releaseSubscriptions、releaseSourceSettings、readReleases;version1.0→1.1。setReleases之后用useAppStore.setState回填订阅与已读 Set,用setReleaseSourceSettings(... ?? null)回填来源(setter 内部经normalizeReleaseSourceSettings对null/缺字段安全回退默认空)。向后兼容
旧版
1.0备份无这些字段 ——Array.isArray守卫 + setter 内置normalizeReleaseSourceSettings(null)回退默认空集合,恢复不报错、行为与升级前一致。验证
tsc -p tsconfig.app.json:BackupPanel 零错误;eslint(BackupPanel):通过。vitest:11 文件 / 110 测试全通过。releaseSourceSettings键的旧1.0备份回归向后兼容。🤖 Generated with Claude Code
Summary by CodeRabbit