Skip to content

回收站管理系统 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构定位
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与资源特性
  8. 故障恢复与数据一致性
  9. 配置选项与生命周期
  10. 排错指南
  11. 结论

引言 ​

本文件面向回收站管理系统的实现文档,聚焦以下目标:

  • 解释文件删除后进入暂存区的移动策略、时间戳管理与元数据保存机制。
  • 说明从回收站恢复原路径时的冲突检测、跨设备回退和元数据清理逻辑。
  • 描述定时清理策略、过期判定算法以及垃圾回收行为。
  • 梳理数据持久化方式、索引文件结构与事务性保证边界。
  • 给出从“文件删除”到“永久清理”的完整生命周期流程。
  • 汇总当前可观测的配置项,并指出尚未暴露为配置的默认值。
  • 提供故障恢复建议和数据完整性保障措施。

该回收站子系统位于本地代理端(Agent),由 HTTP 路由暴露 /api/fs/delete、/api/trash/restore、/api/trash/list 等接口,供扩展端调用。

项目结构定位 ​

回收站功能集中在 Agent 端的 trash.js 模块中,并通过 server.js 的路由层对外暴露 API。测试用例位于 agent/test/unit/trash-cross-device.unit.test.js,用于验证跨设备移动回退逻辑。

graph TB
Client["扩展端或外部客户端"] --> Server["HTTP 服务器<br/>agent/src/server.js"]
Server --> Trash["回收站模块<br/>agent/src/trash.js"]
Trash --> FS["文件系统<br/>fs/promises"]
Trash --> Meta["回收站元数据<br/>metadata.json"]
Server --> Log["日志与安全审计"]

图示来源

章节来源

核心组件 ​

回收站系统由以下关键能力组成:

  • 移动至回收站:对文件或目录执行安全校验后移动到固定回收站目录,生成唯一 ID、记录删除时间戳、计算大小并写入元数据。
  • 恢复文件:根据回收站条目 ID 查找对应实体,检查原路径是否被占用,再移回原路径并清理元数据。
  • 列表查询:列出当前回收站条目,并在查询前触发一次过期清理。
  • 定期清理:启动定时器每 6 小时扫描过期条目,按删除时间超过 7 天进行物理删除。
  • 跨设备移动:优先使用 rename;当发生跨设备错误时回退为复制加删除源,并在复制失败时清理半成品目标。

章节来源

架构总览 ​

回收站在整体 Agent 架构中的位置如下:

sequenceDiagram
participant Ext as "扩展端"
participant Srv as "HTTP 服务器<br/>server.js"
participant Tr as "回收站模块<br/>trash.js"
participant FS as "文件系统"
participant Meta as "metadata.json"
Ext->>Srv : POST /api/fs/delete {path}
Srv->>Tr : moveToTrash(sourcePath, t)
Tr->>FS : stat(sourcePath)
Tr->>FS : movePathCrossDevice(sourcePath, trashPath)
Tr->>Meta : loadMetadata()
Tr->>Meta : saveMetadata(entries + new entry)
Srv-->>Ext : {success, trashId, isDir}
Ext->>Srv : GET /api/trash/list
Srv->>Tr : listTrash()
Tr->>Meta : cleanExpiredTrash()
Tr->>Meta : loadMetadata()
Srv-->>Ext : {success, entries}
Ext->>Srv : POST /api/trash/restore {trashId}
Srv->>Tr : restoreFromTrash(trashId, t)
Tr->>Meta : loadMetadata()
Tr->>FS : movePathCrossDevice(trashPath, originalPath)
Tr->>Meta : saveMetadata(entries without restored)
Srv-->>Ext : {success, restoredPath}

图示来源

详细组件分析 ​

文件暂存机制:移动策略、时间戳与元数据 ​

  • 回收站目录固定为用户主目录下的 .ai-helper-agent/.trash。
  • 每次移动前会确保回收站目录存在;若创建失败则记录错误并抛出异常。
  • 移动逻辑先统计目标大小:文件直接使用文件大小,目录递归计算实际字节数。
  • 回收站条目 ID 由时间戳加随机后缀构成,用于避免同名覆盖。
  • 元数据以 JSON 数组形式保存在 metadata.json 中,每个条目包含:
    • 回收站 ID
    • 原始路径
    • 名称
    • 大小
    • 删除时间戳
    • 是否为目录
  • 移动成功后立即追加新条目并持久化元数据。
flowchart TD
Start(["开始移动"]) --> Ensure["确保回收站目录存在"]
Ensure --> Clean["清理过期条目"]
Clean --> Stat["统计源路径类型与大小"]
Stat --> Move["movePathCrossDevice<br/>rename 优先,EXDEV 回退复制+删除源"]
Move --> LoadMeta["加载现有元数据"]
LoadMeta --> Append["追加新条目"]
Append --> SaveMeta["保存 metadata.json"]
SaveMeta --> Done(["返回成功结果"])

图示来源

章节来源

恢复功能:原路径重建、冲突检测与版本管理 ​

  • 恢复入口通过回收站条目 ID 查找元数据。
  • 如果回收站实体不存在,会从元数据中移除该条目并返回失败。
  • 如果原路径已存在,直接拒绝恢复,提示原路径被占用。
  • 原路径可用时,再次使用跨设备移动逻辑将回收站实体移回原路径。
  • 恢复成功后从元数据中删除该条目并持久化。
  • 当前实现没有多版本恢复逻辑:同一原路径只能恢复一次,重复恢复会因路径冲突失败。
flowchart TD
Start(["开始恢复"]) --> Find["按 trashId 查找条目"]
Find --> Found{"找到条目?"}
Found --> |否| NotFound["返回条目不存在"]
Found --> |是| Exists{"回收站实体存在?"}
Exists --> |否| RemoveMeta["从元数据删除条目"]
RemoveMeta --> FileNotFound["返回文件不存在"]
Exists --> |是| PathOccupied{"原路径是否存在?"}
PathOccupied --> |是| Conflict["返回原路径被占用"]
PathOccupied --> |否| MoveBack["movePathCrossDevice 移回原路径"]
MoveBack --> UpdateMeta["从元数据删除条目"]
UpdateMeta --> Success["返回恢复成功"]

图示来源

章节来源

清理策略:定时清理、空间限制与垃圾回收算法 ​

  • 过期阈值:删除时间超过 7 天的条目被视为过期。
  • 清理触发点:
    • 每次移动或列表查询前都会执行一次过期清理。
    • 服务启动后启动定时器,每 6 小时执行一次清理。
  • 清理算法:
    • 读取所有元数据条目。
    • 按当前时间与 deletedAt 差值判断是否过期。
    • 对过期条目,判断是目录还是文件,分别递归删除或单文件删除。
    • 删除成功后更新元数据,仅保留未过期条目。
  • 空间限制:当前代码未实现基于回收站总大小的空间上限控制;只按时间阈值清理。
flowchart TD
Start(["开始清理"]) --> Load["加载元数据"]
Load --> Now["获取当前时间"]
Now --> Split["分为过期条目与保留条目"]
Split --> ForEach{"遍历过期条目"}
ForEach --> Type{"目录还是文件?"}
Type --> |目录| Recurse["递归删除目录"]
Type --> |文件| Unlink["删除单个文件"]
Recurse --> Next["继续下一个"]
Unlink --> Next
Next --> More{"还有过期条目?"}
More --> |是| ForEach
More --> |否| Save["保存剩余元数据"]
Save --> End(["清理完成"])

图示来源

章节来源

跨设备移动与错误恢复 ​

  • 同文件系统场景:使用 rename,属于元数据操作,瞬时完成。
  • 跨文件系统场景:rename 抛出 EXDEV 时,回退为递归复制后删除源。
  • 复制失败时尽力删除半成品目标,避免残留无主数据。
  • 非 EXDEV 的错误直接向上抛出,不进入回退逻辑。
  • 单元测试覆盖了五种典型路径:同设备成功、EXDEV 回退、非 EXDEV 错误、复制失败清理半成品、无 code 字段错误。
flowchart TD
Start(["开始移动"]) --> TryRename["尝试 rename"]
TryRename --> RenameOK{"rename 成功?"}
RenameOK --> |是| Done["完成"]
RenameOK --> |否| CheckCode{"错误码是否为 EXDEV?"}
CheckCode --> |否| Throw["抛出原始错误"]
CheckCode --> |是| Copy["递归复制源到目标"]
Copy --> CopyOK{"复制成功?"}
CopyOK --> |是| DeleteSrc["删除源路径"]
DeleteSrc --> Done
CopyOK --> |否| Cleanup["删除半成品目标"]
Cleanup --> Throw

图示来源

章节来源

数据持久化机制:索引文件结构、事务保证与一致性 ​

  • 索引文件:~/.ai-helper-agent/.trash/metadata.json。
  • 文件结构:顶层对象包含 entries 数组,每个元素为一个回收站条目。
  • 写入方式:将整个 entries 数组序列化后写回文件。
  • 事务性:当前实现不是原子事务。若写入过程中进程崩溃,可能产生不一致状态。
  • 容错读取:读取元数据时若文件不存在或解析失败,返回空数组,不会中断正常流程。
  • 一致性保障建议:
    • 写入前先写临时文件,再原子替换。
    • 在关键路径增加重试与幂等保护。
    • 对大目录恢复与批量清理增加进度与断点信息。
erDiagram
TRASH_METADATA {
array entries
}
ENTRY {
string id
string originalPath
string name
number size
number deletedAt
boolean isDir
}
TRASH_METADATA ||--o{ ENTRY : "包含"

图示来源

章节来源

回收站生命周期:从删除到永久清理 ​

stateDiagram-v2
[*] --> 待删除
待删除 --> 已移动到回收站 : "moveToTrash"
已移动到回收站 --> 已恢复 : "restoreFromTrash"
已恢复到原路径 : "恢复成功"
已移动到回收站 --> 已过期 : "deletedAt 超过 7 天"
已过期 --> 已永久删除 : "cleanExpiredTrash"
已恢复到原路径 --> [*]
已永久删除 --> [*]

图示来源

章节来源

依赖关系分析 ​

回收站模块依赖以下外部能力:

  • Node.js 文件系统 API:mkdir、rename、cp、rm、readFile、writeFile、readdir、stat、unlink。
  • 路径与操作系统工具:join、basename、homedir。
  • 国际化文案:通过 i18n.js 的翻译函数输出中文或英文日志。
  • 服务器路由:server.js 负责认证、参数校验、日志记录,并调用回收站模块。
graph LR
Server["server.js"] --> Trash["trash.js"]
Trash --> FS["fs/promises"]
Trash --> I18n["i18n.js"]
Trash --> OS["os/path"]
Server --> Security["security.js"]
Server --> Logger["logger.js"]

图示来源

章节来源

性能与资源特性 ​

  • 同设备移动使用 rename,属于元数据操作,性能开销极低。
  • 跨设备移动需要完整复制目录内容,时间复杂度与目录大小成正比。
  • 目录大小计算采用递归遍历,时间复杂度与目录节点数量成正比。
  • 定期清理每 6 小时执行一次,避免频繁 IO。
  • 列表查询会先清理过期条目,因此在大回收站场景下首次查询可能较慢。
  • 当前未实现回收站容量上限控制,大量大目录堆积可能导致磁盘空间压力。

[本节为通用性能讨论,不直接分析具体代码行]

故障恢复与数据完整性 ​

  • 元数据读取失败时返回空数组,不会导致服务不可用。
  • 回收站实体缺失时,恢复逻辑会主动清理对应元数据条目,避免“幽灵条目”。
  • 跨设备复制失败时会尽力删除半成品目标,减少脏数据。
  • 定时清理失败会记录错误日志,但不会中断服务。
  • 由于元数据写入非原子,建议在后续版本引入临时文件 + 原子替换,提升崩溃恢复能力。
  • 对于大目录恢复,建议增加分片恢复、进度回调与失败重试。

章节来源

配置选项与生命周期 ​

当前回收站相关配置情况如下:

配置项当前行为是否可配置说明
回收站目录~/.ai-helper-agent/.trash否固定路径,便于集中管理
保留期限7 天否基于 deletedAt 与当前时间比较
清理间隔6 小时否使用定时器周期性清理
空间上限未实现否仅按时间清理,不按大小限制
语言跟随请求或服务器语言是通过 setTrashLang 设置
定时清理开关服务启动时自动启动否可通过停止清理函数关闭

API 生命周期要点:

  • 删除文件:POST /api/fs/delete,内部调用 moveToTrash。
  • 恢复文件:POST /api/trash/restore,内部调用 restoreFromTrash。
  • 列出回收站:GET /api/trash/list,内部调用 listTrash。
  • 服务启动:startPeriodicCleanup() 启动定时清理。
  • 服务关闭:stopPeriodicCleanup() 停止定时清理。

章节来源

排错指南 ​

常见问题与排查建议:

现象可能原因排查建议
删除失败路径不存在、权限不足、跨设备复制失败检查 /api/fs/delete 返回错误,查看回收站日志
恢复失败且提示原路径被占用原路径已被其他程序创建或恢复未完成确认原路径是否存在,必要时手动处理冲突
恢复失败且提示文件不存在回收站实体已被手动删除或已过期清理检查 metadata.json 与回收站目录
回收站列表为空但仍有文件元数据损坏或未正确写入检查 metadata.json 是否可读、是否包含 entries
回收站持续占用磁盘大目录长期未恢复且未过期手动恢复或删除,或调整保留策略
定时清理未生效服务未启动清理定时器或清理异常检查服务启动日志与清理错误日志

相关日志键参考英文文案:

  • 无法创建回收站目录
  • 清理过期条目数量
  • 移动到回收站日志
  • 恢复日志
  • 定期清理启动与错误

章节来源

结论 ​

回收站管理系统在当前实现中提供了可靠的文件删除保护机制:通过固定目录暂存、时间戳标记、元数据索引与定时清理,实现了从“软删除”到“硬删除”的完整生命周期。其优势包括跨设备移动回退、恢复冲突检测、定期清理与服务集成。主要改进方向包括:

  • 将保留期限、清理间隔、空间上限等参数暴露为可配置项。
  • 引入原子写入与事务式元数据更新,提升崩溃恢复能力。
  • 为大目录恢复与批量清理增加进度、重试与断点续传。
  • 增加回收站容量监控与告警,防止磁盘空间耗尽。
  • 在多版本恢复场景中支持历史版本管理,避免原路径冲突导致的恢复失败。