Skip to content

feat: safer project trash flow and faster project refresh#68

Merged
Zhang-Henry merged 4 commits intomainfrom
feat/rash-projectt-management
Mar 22, 2026
Merged

feat: safer project trash flow and faster project refresh#68
Zhang-Henry merged 4 commits intomainfrom
feat/rash-projectt-management

Conversation

@davidliuk
Copy link
Copy Markdown
Collaborator

@davidliuk davidliuk commented Mar 21, 2026

Summary

  • add a safer trash-based project deletion flow where moving to trash is logical only and physical files stay in place until explicit permanent delete
  • keep project refresh database-backed in normal operation and avoid broad filesystem scans after a one-time bootstrap migration
  • harden trash lifecycle behavior around restore, logical delete, physical delete safety, and config consistency
  • improve trash page UX so it refreshes immediately, loads lazily, and does not block normal project refresh

Reviewer Fixes

  • bootstrap the projects table from existing Claude/config project sources on first run so upgrades do not show an empty dashboard
  • refuse physical delete if the current directory at the original path no longer matches the trashed project instance identity
  • store logical deletions in _deletedProjects metadata instead of leaking dead deleted markers
  • scope trash visibility by owner, validate restore paths, and serialize project-config writes to reduce race conditions
  • chunk session metadata lookups to stay under SQLite variable limits
  • stop fetching trash on every normal project refresh, add a trash loading state, and remove trashed projects from the Settings project picker

Testing

  • npm run typecheck

Context

@davidliuk davidliuk requested a review from bbsngg March 21, 2026 20:36
@Zhang-Henry
Copy link
Copy Markdown
Collaborator

Code Review

严重 Bug 1 — 升级后所有项目消失

server/projects.jsgetProjects() 现在完全依赖 DB 的 projects 表,不再扫描文件系统。首次升级时 DB 为空,projectDb.getAllProjects(userId) 返回 [],用户看到 0 个项目。

现有的 migrateLegacyProjects() 只处理 vibelab → dr-claw 工作区重命名,不处理从 ~/.claude/projects/ 到 DB 的通用迁移。

需要:添加首次运行时从文件系统扫描并填充 projects 表的迁移逻辑。

严重 Bug 2 — 永久删除可能误删其他项目

deleteTrashedProject()physical 分支:

await fs.rm(trashMeta.originalPath, { recursive: true, force: true });

如果用户 trash 了项目 A(路径 /home/user/my-project),然后在相同路径创建了项目 B,再回到垃圾箱永久删除 A,会 rm -rf 掉项目 B 的文件。没有验证该路径是否仍属于被删项目。

建议:删除前检查目录内容是否与原项目匹配(如比较某个标识文件),或至少二次确认。

严重 Bug 3 — logical delete 标记无限增长

deleteTrashedProject()logical 分支在 project-config.json 写入 { deleted: { deletedAt } } 后直接 return true,后面的 delete config[projectName] + saveProjectConfig() 永远不会执行。这些标记会无限积累。

中等问题

  • 多用户信息泄露getTrashedProjects() 中 config-only 的 trash 项目(dbEntry 为 null 或 user_id 为 null)对所有用户可见
  • SQLite 变量数限制getSessionsByProjects()IN (...) 子句无上限,超过 SQLite 默认 999 变量限制会报错
  • restoreProject 不验证路径:restore 在 metadata 层面成功,但 originalPath 可能已被删除或移动
  • Config 文件写竞争deleteProjectrestoreProjectdeleteTrashedProject 都做 loadProjectConfig()saveProjectConfig(),无锁。并发操作可能互相覆盖
  • TypeScript 类型错误SidebarModals.tsx[...projects, ...trashProjects].map(normalizeProjectForSettings)TrashProject[] 缺少 Project 的必要字段(sessionssessionMetataskmaster 等),应该编译失败(但 CI 通过了,需确认)

小问题

  • 3个死函数(sanitizeTrashNamegetProjectsTrashRootmovePathSafely)和1个未使用常量 PROJECTS_TRASH_DIR_NAME,看起来是废弃设计的残留
  • TrashDashboard 无 loading 状态,初始会闪现 "Trash is empty"
  • currentTime 只在渲染时计算一次,"Deleted X ago" 时间戳不会更新
  • 每次项目刷新都额外请求 /api/projects/trash,大多数用户不需要
  • 错误提示用 window.alert() 而非应用内 toast/notification
  • encodeProjectPathserver/utils/sessionIndex.js 中重复定义
  • Trash 项目出现在 Settings 的项目下拉列表中,可能造成用户困惑

@davidliuk
Copy link
Copy Markdown
Collaborator Author

Thanks @Zhang-Henry for your comprehensive review! I already fixed them with a new commit.

@Zhang-Henry Zhang-Henry merged commit bcd2d77 into main Mar 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Project deletion from dashboard deletes entire directory without adequate warning - dangerous UX

2 participants