Skip to content

feat: implement Cloudreve to AsterDrive migration#1

Open
cg8-5712 wants to merge 5 commits into
AsterCommunity:masterfrom
cg8-5712:master
Open

feat: implement Cloudreve to AsterDrive migration#1
cg8-5712 wants to merge 5 commits into
AsterCommunity:masterfrom
cg8-5712:master

Conversation

@cg8-5712

@cg8-5712 cg8-5712 commented Jul 13, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • 新功能
    • 新增 Cloudreve → AsterDrive 迁移命令行工具:check 输出兼容性预检报告;migrate 分阶段迁移,并支持 --report-path--run-id--resume、可选包含已删除数据、非空目标策略与跳过不支持的存储策略。
    • 支持直链 v2 重签名、标签迁移、后台任务归档;blobs 支持分批提交与更精细的断点续跑。
  • 文档
    • 更新迁移指南:字段映射、有限断点续传语义、JSON 报告结构与发布前校验指引。
  • 变更
    • 优化迁移所依赖的数据表映射与目标记录处理,提高兼容性。

Copilot AI review requested due to automatic review settings July 13, 2026 09:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@socket-security

socket-security Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedmime_guess@​2.0.510010090100100
Addedurlencoding@​2.1.310010093100100
Addedhmac@​0.12.110010093100100

View full report

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

新增 Cloudreve 到 AsterDrive 的数据库迁移引擎、CLI 命令、分阶段迁移、检查点恢复、结果验证、SQLite 集成测试及使用文档,并调整实体映射与依赖声明。

Changes

数据库迁移工具

Layer / File(s) Summary
依赖与实体映射基础
Cargo.toml, crates/cloudreve-entities/src/*, src/lib.rs
补充迁移依赖,导出迁移模块,移除实体的显式 public schema 映射,并删除 files.deleted_at 字段。
迁移引擎与安全校验
src/migration/mod.rs
新增迁移选项、结构化报告、源数据加载、兼容性检查、目标安全校验、阶段编排与迁移结果验证。
检查点与批量恢复
src/migration/checkpoint.rs, src/migration/mod.rs
新增阶段光标、对象映射和运行检查点持久化,支持 blobs 分批提交、resume 恢复及失败状态记录。
分阶段数据迁移
src/migration/phases.rs
按阶段迁移策略、用户、文件夹、Blob、文件版本、元数据、分享、直链与任务,并维护源目标 ID 映射。
CLI、测试与文档
src/main.rs, README.md, docs/*, src/migration/mod.rs
新增 checkmigrate 子命令、报告输出、SQLite 集成测试,并补充字段映射、架构、恢复语义及发布前校验。

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant MigrationEngine
  participant CloudreveDB
  participant AsterDriveDB
  CLI->>MigrationEngine: 执行 check 或 migrate
  MigrationEngine->>CloudreveDB: 加载源数据并检查兼容性
  MigrationEngine->>AsterDriveDB: 校验 schema 与目标状态
  MigrationEngine->>AsterDriveDB: 分阶段写入并保存 checkpoint
  MigrationEngine-->>CLI: 输出 MigrationReport
Loading

Poem

源库捎来旧时光,
目标表接住新行囊。
策略、用户、Blob 排队走,
直链换上新月光。
检查点守住半途灯,
报告在终点发亮。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了本次提交的核心变更:实现 Cloudreve 到 AsterDrive 的迁移功能。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/migration/mod.rs (1)

254-263: 🚀 Performance & Scalability | 🔵 Trivial

全量入内存 + 单事务写入,大库要留个心眼。

load 对每张源表都 .all(db) 一次性拉进内存,随后所有阶段在同一个目标事务里写入。对小型实例没问题,但面对大规模 Cloudreve 部署,内存占用与目标库的长事务/锁持有时间都会成为瓶颈。若目标用户群里存在这种规模,建议按批分页读取与提交,或至少在文档里标注内存需求。

🤖 Prompt for 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.

In `@src/migration/mod.rs` around lines 254 - 263, Update SourceData::load and the
migration flow that consumes it to avoid loading every source table with
unbounded .all(db) calls and keeping all writes in one long transaction. Read
and migrate records in bounded batches with periodic commits, preserving
migration ordering and include_deleted behavior; alternatively, document the
required memory and transaction-size limitations if batching cannot be
introduced.
🤖 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 `@crates/cloudreve-entities/src/entities.rs`:
- Line 7: 为 entities 实体补充 PostgreSQL schema 绑定,更新 `cr::entities::Entity` 上的
SeaORM 表属性以显式指定 Cloudreve 使用的 schema,避免依赖连接默认 schema;同时保持 `src/migration/mod.rs`
中通过 `Database::connect` 查询该实体时能正确解析非 public schema,并补充对应的 PostgreSQL 集成测试。

In `@crates/cloudreve-entities/src/files.rs`:
- Around line 7-8: 保留 files::Model 中的 deleted_at 字段,并在文件迁移查询流程中对 files 应用
filter_deleted,与其他实体保持一致;确保 include_deleted 为 false 时排除已删除文件,避免其迁移到目标库。

---

Nitpick comments:
In `@src/migration/mod.rs`:
- Around line 254-263: Update SourceData::load and the migration flow that
consumes it to avoid loading every source table with unbounded .all(db) calls
and keeping all writes in one long transaction. Read and migrate records in
bounded batches with periodic commits, preserving migration ordering and
include_deleted behavior; alternatively, document the required memory and
transaction-size limitations if batching cannot be introduced.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e369702-3c4b-4b9f-a6fa-4469913354bc

📥 Commits

Reviewing files that changed from the base of the PR and between 0f71f8e and 967c39e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • Cargo.toml
  • README.md
  • crates/cloudreve-entities/src/dav_accounts.rs
  • crates/cloudreve-entities/src/direct_links.rs
  • crates/cloudreve-entities/src/entities.rs
  • crates/cloudreve-entities/src/file_entities.rs
  • crates/cloudreve-entities/src/files.rs
  • crates/cloudreve-entities/src/fs_events.rs
  • crates/cloudreve-entities/src/groups.rs
  • crates/cloudreve-entities/src/metadata.rs
  • crates/cloudreve-entities/src/nodes.rs
  • crates/cloudreve-entities/src/oauth_clients.rs
  • crates/cloudreve-entities/src/oauth_grants.rs
  • crates/cloudreve-entities/src/passkeys.rs
  • crates/cloudreve-entities/src/settings.rs
  • crates/cloudreve-entities/src/shares.rs
  • crates/cloudreve-entities/src/storage_policies.rs
  • crates/cloudreve-entities/src/tasks.rs
  • crates/cloudreve-entities/src/users.rs
  • src/lib.rs
  • src/main.rs
  • src/migration/mod.rs
  • src/migration/phases.rs

#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(schema_name = "public", table_name = "entities")]
#[sea_orm(table_name = "entities")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'search_path|schema_name|CLOUDREVE_DATABASE_URL' src crates Cargo.toml

Repository: AsterCommunity/AsterDriveMigration

Length of output: 236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== entities.rs ==\n'
cat -n crates/cloudreve-entities/src/entities.rs

printf '\n== migration/mod.rs ==\n'
cat -n src/migration/mod.rs

printf '\n== sea_orm / schema / search_path / database url references ==\n'
rg -n 'search_path|schema_name|Schema::|Database::connect|CLOUDREVE_DATABASE_URL|set_schema|with_schema|schema' src crates Cargo.toml

Repository: AsterCommunity/AsterDriveMigration

Length of output: 35564


🌐 Web query:

SeaORM #[sea_orm(schema_name = "...")] PostgreSQL default schema behavior table_name search_path

💡 Result:

In SeaORM, the #[sea_orm(schema_name = "...", table_name = "...")] attribute explicitly binds an entity to a specific PostgreSQL schema [1][2]. When schema_name is provided, SeaORM generates SQL queries that qualify the table name with the specified schema (e.g., "schema_name"."table_name") [3][4]. The interaction between schema_name and the PostgreSQL search_path is as follows: 1. Explicit Schema Binding: By using the schema_name attribute, you are telling SeaORM to always use that specific schema for the entity, regardless of the active search_path [3][4]. 2. PostgreSQL search_path: The search_path (configured via ConnectOptions::set_schema_search_path [5] or the database connection string [6]) is a PostgreSQL-level mechanism that tells the database which schemas to look into when an identifier is unqualified [6][7]. 3. Behavior: If you define an entity with a schema_name, SeaORM treats it as a fully qualified identifier. Consequently, the search_path has no effect on these specific entities because the query already includes the schema prefix [3]. If you do not specify a schema_name, SeaORM will not prefix the table, and PostgreSQL will rely on the search_path to resolve the table location [6]. Note that there are known edge cases, particularly with schema synchronization tools, where some generated SQL (such as for indexes or constraints) may not always consistently use the qualified schema name unless explicitly handled or configured [3]. For managing PostgreSQL schema configuration in SeaORM, you can use: - ConnectOptions: opt.set_schema_search_path("my_schema".into()) [5] - Connection String: postgres://user:pass@host/db?options[search_path]=my_schema [6]

Citations:


🌐 Web query:

SeaORM schema_name PostgreSQL default search_path public unqualified table name entity model

💡 Result:

In SeaORM, managing PostgreSQL schemas and unqualified table names is handled primarily through the schema_name attribute in the entity model and connection configuration [1][2][3]. To map an entity to a specific PostgreSQL schema, you use the schema_name attribute within the #[sea_orm(...)] derive macro [1][2]. This ensures the generated SQL appropriately qualifies the table name with the schema (e.g., "schema_name"."table_name") [4][2]. For connection-level handling, specifically when you want to avoid manual schema qualification or rely on the PostgreSQL search_path, you have two main options: 1. Connection Options: You can set the default PostgreSQL schema search path programmatically when establishing the database connection [5][6]. let mut opt = ConnectOptions::new("postgres://user:pass@localhost/db".to_owned); opt.set_schema_search_path("my_schema".into); let db = Database::connect(opt).await?; 2. Connection String: You can append the options[search_path] parameter to your connection string, which allows SQLx and SeaORM to honor the search path without requiring explicit schema qualification in your code [6]. postgres://user:pass@localhost/db?options[search_path]=my_schema Note that while schema_name explicitly defines the schema for an entity, relying on search_path can sometimes lead to issues with certain automated tools or generated queries (such as schema-sync creating indexes with unqualified table names if not handled correctly) [7][4]. If you explicitly define schema_name in your entity model, SeaORM typically generates schema-qualified identifiers [4][2]. If you require specific migration behaviors for non-public schemas, you can pass the -s <schema_name> flag to sea-orm-cli or set the DATABASE_SCHEMA environment variable [5][8].

Citations:


补上 PostgreSQL schema 绑定
crates/cloudreve-entities/src/entities.rs:7 现在只有 table_name = "entities"src/migration/mod.rs 里又是直接 Database::connect(url) 后跑 cr::entities::Entity::find().all(db),没有任何 schema_name / search_path 配置。这样源表解析全靠连接默认 schema,非 public 部署会读错表或直接失败。要么显式固定 Cloudreve schema,要么把 search_path 配进连接串,再补 PostgreSQL 集成测试。

🤖 Prompt for 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.

In `@crates/cloudreve-entities/src/entities.rs` at line 7, 为 entities 实体补充
PostgreSQL schema 绑定,更新 `cr::entities::Entity` 上的 SeaORM 表属性以显式指定 Cloudreve 使用的
schema,避免依赖连接默认 schema;同时保持 `src/migration/mod.rs` 中通过 `Database::connect`
查询该实体时能正确解析非 public schema,并补充对应的 PostgreSQL 集成测试。

Comment on lines +7 to 8
#[sea_orm(table_name = "files")]
pub struct Model {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

不要移除 files.deleted_at,否则删除文件也会被迁移。

src/migration/mod.rs:235-285 对其他实体调用了 filter_deleted,但 files 直接使用查询结果;移除该字段后,include_deleted = false 无法过滤已删除文件,目标库会出现本应排除的记录。请保留字段,并对 files 同样应用 filter_deleted

🤖 Prompt for 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.

In `@crates/cloudreve-entities/src/files.rs` around lines 7 - 8, 保留 files::Model
中的 deleted_at 字段,并在文件迁移查询流程中对 files 应用 filter_deleted,与其他实体保持一致;确保
include_deleted 为 false 时排除已删除文件,避免其迁移到目标库。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/migration/phases.rs`:
- Around line 455-491: 在迁移流程中更新标签关联逻辑:除现有的 tags 去重外,围绕
entity_properties::ActiveModel 插入增加按 (entity_type, entity_id, tag_id)
去重,确保同一实体复用同一 tag_id 时不会重复插入 system.tags 关联。仅首次出现该组合时插入并递增 migrated_properties 和
migrated_tag_assignments,重复关联应直接跳过。
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d9f558bf-e792-4885-a64d-5de52a8434af

📥 Commits

Reviewing files that changed from the base of the PR and between 967c39e and 8110984.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • Cargo.toml
  • README.md
  • docs/cloudreve-to-asterdrive-field-mapping.md
  • docs/migration-architecture.md
  • src/main.rs
  • src/migration/mod.rs
  • src/migration/phases.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main.rs
  • Cargo.toml

Comment thread src/migration/phases.rs
Comment on lines +455 to +491
let tag_id = match tags.get(&(owner_user_id, normalized_name.clone())) {
Some(tag_id) => *tag_id,
None => {
let tag = ad::tags::ActiveModel {
scope_type: Set("personal".to_string()),
owner_user_id: Set(Some(owner_user_id)),
team_id: Set(None),
name: Set(name),
normalized_name: Set(normalized_name.clone()),
color: Set(target_tag_color(&metadata.value)),
sort_order: Set(0),
created_at: Set(metadata.created_at),
updated_at: Set(metadata.updated_at),
..Default::default()
}
.insert(transaction)
.await
.wrap_err_with(|| format!("migrate tag metadata {}", metadata.id))?;
tags.insert((owner_user_id, normalized_name), tag.id);
report.migrated_tags += 1;
tag.id
}
};
ad::entity_properties::ActiveModel {
entity_type: Set(entity_type.to_string()),
entity_id: Set(entity_id),
namespace: Set("system.tags".to_string()),
name: Set(tag_id.to_string()),
value: Set(None),
..Default::default()
}
.insert(transaction)
.await
.wrap_err_with(|| format!("attach migrated tag for metadata {}", metadata.id))?;
report.migrated_properties += 1;
report.migrated_tag_assignments += 1;
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 确认 AD entity_properties 是否对 (entity_type, entity_id, namespace, name) 建唯一约束/索引
fd -t f . crates/asterdrive-entities/src | xargs rg -nP 'entity_properties|unique|index' -l
rg -nP -C3 'unique|index_name|primary_key' $(fd 'entity_propert' crates/asterdrive-entities/src)

Repository: AsterCommunity/AsterDriveMigration

Length of output: 2670


标签关联也要去重,不然大小写变体会把迁移撞回去

tags 只去重了标签行,system.tags 这条 entity_properties 还是每条 metadata 都在插。entity_properties(entity_type, entity_id, namespace, name) 有唯一键,同一文件上的 tag:Importanttag:important 会复用同一个 tag_id,第二次写入就直接撞唯一约束,整笔迁移回滚。这里得再按 (entity_type, entity_id, tag_id) 去重。

🤖 Prompt for 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.

In `@src/migration/phases.rs` around lines 455 - 491, 在迁移流程中更新标签关联逻辑:除现有的 tags
去重外,围绕 entity_properties::ActiveModel 插入增加按 (entity_type, entity_id, tag_id)
去重,确保同一实体复用同一 tag_id 时不会重复插入 system.tags 关联。仅首次出现该组合时插入并递增 migrated_properties 和
migrated_tag_assignments,重复关联应直接跳过。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main.rs (1)

24-26: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

source_url / target_url / default_password 加上 hide_env_values = true
src/main.rs:24-26,38-39 这三个 env 参数会把已设置的值暴露到 --helpdirect_link_secret 已经隐藏了,这里也该统一处理,避免泄露数据库连接串和默认密码。

🤖 Prompt for 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.

In `@src/main.rs` around lines 24 - 26, Update the argument definitions for
source_url, target_url, and default_password in the CLI arguments struct to
include hide_env_values = true alongside their existing env settings. Match the
existing direct_link_secret configuration and leave unrelated arguments
unchanged.
🧹 Nitpick comments (2)
README.md (1)

59-59: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

补清 --allow-non-empty-target 的边界说明。 README 现在只说它会跳过空库保护,但没说清楚它不是合并模式:正常迁移还是一堆 insert,已有记录不会被覆盖,撞上主键/唯一约束就会失败。--resume 也只是同一 run_id 的中断续跑,不是“对非空目标重复执行”的通用语义。

🤖 Prompt for 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.

In `@README.md` at line 59, 完善 README 中 --allow-non-empty-target
的说明,明确它仅跳过目标核心表必须为空的保护,不是合并或覆盖模式;迁移仍执行插入,已有记录不会被覆盖,遇到主键或唯一约束冲突可能失败。同时说明 --resume
仅用于续跑同一 run_id 的中断迁移,不支持对非空目标重复执行。
src/main.rs (1)

86-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

emit_report 建议先打印再写文件,并修正 bail 消息。

当前顺序是先写文件再打印,若 write_json_report 失败,用户在 stdout 上什么都看不到。建议调换顺序,让用户始终能看到报告内容。此外,行 92 的 bail 消息说 "inspect the JSON report",但如果用户未提供 --report-path,根本没有 JSON 文件可查,消息具有误导性。

♻️ 建议修复:调换顺序并修正消息
 fn emit_report(report_path: Option<&Path>, report: &MigrationReport) -> Result<()> {
-    if let Some(path) = report_path {
-        write_json_report(path, report)?;
-    }
     println!("{report}");
+    if let Some(path) = report_path {
+        write_json_report(path, report)?;
+    }
     if report.validation.performed && !report.validation.passed {
-        bail!("migration committed but post-migration validation failed; inspect the JSON report");
+        bail!("migration committed but post-migration validation failed; review the report above{}",
+              report_path.map(|_| "").unwrap_or(" (re-run with --report-path to save a JSON report)"));
     }
     Ok(())
 }
🤖 Prompt for 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.

In `@src/main.rs` around lines 86 - 94, Update emit_report to print the
MigrationReport before calling write_json_report, ensuring report output appears
even when file writing fails. Also revise the validation-failure bail message so
it does not imply a JSON report exists when no report_path was provided, while
retaining the existing failure behavior.
🤖 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 `@docs/cloudreve-to-asterdrive-field-mapping.md`:
- Around line 520-538: 更新“JSON 报告字段”表,补充 MigrationReport 中缺失的
schema_version、generated_at、dry_run、warnings 和 skipped 字段说明;保留现有 skipped_by_type
与 skipped_objects 行,并确保表格完整覆盖报告结构体中的字段。

---

Outside diff comments:
In `@src/main.rs`:
- Around line 24-26: Update the argument definitions for source_url, target_url,
and default_password in the CLI arguments struct to include hide_env_values =
true alongside their existing env settings. Match the existing
direct_link_secret configuration and leave unrelated arguments unchanged.

---

Nitpick comments:
In `@README.md`:
- Line 59: 完善 README 中 --allow-non-empty-target
的说明,明确它仅跳过目标核心表必须为空的保护,不是合并或覆盖模式;迁移仍执行插入,已有记录不会被覆盖,遇到主键或唯一约束冲突可能失败。同时说明 --resume
仅用于续跑同一 run_id 的中断迁移,不支持对非空目标重复执行。

In `@src/main.rs`:
- Around line 86-94: Update emit_report to print the MigrationReport before
calling write_json_report, ensuring report output appears even when file writing
fails. Also revise the validation-failure bail message so it does not imply a
JSON report exists when no report_path was provided, while retaining the
existing failure behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3dfb2848-af0d-46c8-860a-eb1d697fc4f2

📥 Commits

Reviewing files that changed from the base of the PR and between 8110984 and d7623ea.

📒 Files selected for processing (7)
  • README.md
  • docs/cloudreve-to-asterdrive-field-mapping.md
  • docs/migration-architecture.md
  • src/main.rs
  • src/migration/checkpoint.rs
  • src/migration/mod.rs
  • src/migration/phases.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/migration-architecture.md
  • src/migration/mod.rs
  • src/migration/phases.rs

Comment on lines +520 to +538
## 16. JSON 报告字段

通过 `--report-path <path>` 可为 `check` 或 `migrate` 输出 `schema_version=1` 的 JSON 报告。

| 报告字段 | 内容 |
|---|---|
| `source_*` | Cloudreve 各类源对象数量 |
| `migrated_*` | 本次写入 AD 的各类对象数量 |
| `skipped_by_type` | 按 `file`、`blob`、`share`、`direct_link` 等类型聚合的跳过数量 |
| `skipped_objects` | 每条跳过记录的对象类型、Cloudreve source ID 和明确原因 |
| `mappings` | policy、policy group、user、folder、blob、file、share、task 的排序 source ID -> target ID |
| `tag_assignments` | Cloudreve metadata ID、源 file/folder ID、AD entity ID、AD tag ID 和标签名 |
| `direct_links` | Cloudreve direct-link/file ID、AD file ID、新 URL、原名称、下载次数和限速 |
| `validation` | 是否执行/通过,以及每项检查的 expected、actual 和失败信息 |
| `run_id` | checkpoint run ID;未指定时由迁移工具生成 UUID |
| `resumed` | 本次执行是否从已有 checkpoint 恢复 |
| `completed_stages` | 已原子提交完成的迁移阶段列表 |

当前提交后校验覆盖核心表增量数量、导入任务是否全为终态且无 lease、`system.tags` 关联是否存在、`cloudreve.direct_links` 属性中的 URL 是否与报告一致。报告不会保存数据库密码、存储密钥或 Cloudreve task private state,但会包含新 direct-link URL,因此必须限制报告文件访问权限。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

JSON 报告字段表漏了五个字段。

对照 MigrationReport 结构体,schema_version(正文提到但未列入表格)、generated_atdry_runwarningsskipped(总跳过数,表里只列了 skipped_by_typeskipped_objects)都没出现在表格中。用户照着文档解析报告会漏读这些字段。

📝 建议补充的表格行
 | 报告字段 | 内容 |
 |---|---|
+| `schema_version` | 报告 schema 版本,当前为 `1` |
+| `generated_at` | 报告生成时间(UTC) |
 | `source_*` | Cloudreve 各类源对象数量 |
 | `migrated_*` | 本次写入 AD 的各类对象数量 |
+| `skipped` | 跳过对象总数 |
+| `dry_run` | 是否为 dry-run 模式 |
+| `warnings` | 迁移过程中产生的警告信息列表 |
 | `skipped_by_type` | 按 `file`、`blob`、`share`、`direct_link` 等类型聚合的跳过数量 |
 | `skipped_objects` | 每条跳过记录的对象类型、Cloudreve source ID 和明确原因 |
 | `mappings` | policy、policy group、user、folder、blob、file、share、task 的排序 source ID -> target ID |
 | `tag_assignments` | Cloudreve metadata ID、源 file/folder ID、AD entity ID、AD tag ID 和标签名 |
 | `direct_links` | Cloudreve direct-link/file ID、AD file ID、新 URL、原名称、下载次数和限速 |
 | `validation` | 是否执行/通过,以及每项检查的 expected、actual 和失败信息 |
 | `run_id` | checkpoint run ID;未指定时由迁移工具生成 UUID |
 | `resumed` | 本次执行是否从已有 checkpoint 恢复 |
 | `completed_stages` | 已原子提交完成的迁移阶段列表 |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## 16. JSON 报告字段
通过 `--report-path <path>` 可为 `check``migrate` 输出 `schema_version=1` 的 JSON 报告。
| 报告字段 | 内容 |
|---|---|
| `source_*` | Cloudreve 各类源对象数量 |
| `migrated_*` | 本次写入 AD 的各类对象数量 |
| `skipped_by_type` |`file``blob``share``direct_link` 等类型聚合的跳过数量 |
| `skipped_objects` | 每条跳过记录的对象类型、Cloudreve source ID 和明确原因 |
| `mappings` | policy、policy group、user、folder、blob、file、share、task 的排序 source ID -> target ID |
| `tag_assignments` | Cloudreve metadata ID、源 file/folder ID、AD entity ID、AD tag ID 和标签名 |
| `direct_links` | Cloudreve direct-link/file ID、AD file ID、新 URL、原名称、下载次数和限速 |
| `validation` | 是否执行/通过,以及每项检查的 expected、actual 和失败信息 |
| `run_id` | checkpoint run ID;未指定时由迁移工具生成 UUID |
| `resumed` | 本次执行是否从已有 checkpoint 恢复 |
| `completed_stages` | 已原子提交完成的迁移阶段列表 |
当前提交后校验覆盖核心表增量数量、导入任务是否全为终态且无 lease、`system.tags` 关联是否存在、`cloudreve.direct_links` 属性中的 URL 是否与报告一致。报告不会保存数据库密码、存储密钥或 Cloudreve task private state,但会包含新 direct-link URL,因此必须限制报告文件访问权限。
## 16. JSON 报告字段
通过 `--report-path <path>` 可为 `check``migrate` 输出 `schema_version=1` 的 JSON 报告。
| 报告字段 | 内容 |
|---|---|
| `schema_version` | 报告 schema 版本,当前为 `1` |
| `generated_at` | 报告生成时间(UTC) |
| `source_*` | Cloudreve 各类源对象数量 |
| `migrated_*` | 本次写入 AD 的各类对象数量 |
| `skipped` | 跳过对象总数 |
| `dry_run` | 是否为 dry-run 模式 |
| `warnings` | 迁移过程中产生的警告信息列表 |
| `skipped_by_type` |`file``blob``share``direct_link` 等类型聚合的跳过数量 |
| `skipped_objects` | 每条跳过记录的对象类型、Cloudreve source ID 和明确原因 |
| `mappings` | policy、policy group、user、folder、blob、file、share、task 的排序 source ID -> target ID |
| `tag_assignments` | Cloudreve metadata ID、源 file/folder ID、AD entity ID、AD tag ID 和标签名 |
| `direct_links` | Cloudreve direct-link/file ID、AD file ID、新 URL、原名称、下载次数和限速 |
| `validation` | 是否执行/通过,以及每项检查的 expected、actual 和失败信息 |
| `run_id` | checkpoint run ID;未指定时由迁移工具生成 UUID |
| `resumed` | 本次执行是否从已有 checkpoint 恢复 |
| `completed_stages` | 已原子提交完成的迁移阶段列表 |
当前提交后校验覆盖核心表增量数量、导入任务是否全为终态且无 lease、`system.tags` 关联是否存在、`cloudreve.direct_links` 属性中的 URL 是否与报告一致。报告不会保存数据库密码、存储密钥或 Cloudreve task private state,但会包含新 direct-link URL,因此必须限制报告文件访问权限。
🤖 Prompt for 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.

In `@docs/cloudreve-to-asterdrive-field-mapping.md` around lines 520 - 538,
更新“JSON 报告字段”表,补充 MigrationReport 中缺失的
schema_version、generated_at、dry_run、warnings 和 skipped 字段说明;保留现有 skipped_by_type
与 skipped_objects 行,并确保表格完整覆盖报告结构体中的字段。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/migration/phases.rs (1)

300-314: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

文件迁移阶段全量加载 entities 和 file_entities 到内存

migrate_files 一次性把源库的 entitiesfile_entities 全部拉进内存。blob 阶段已经做了分批处理,文件阶段却没有——大型 Cloudreve 实例(百万级文件 + 千万级 entities)跑下来直接 OOM 不是开玩笑的。

建议参照 migrate_blobs_batched 的分批策略,按 file ID 区间分批加载关联数据,避免单次查询撑爆内存。

🤖 Prompt for 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.

In `@src/migration/phases.rs` around lines 300 - 314, Update migrate_files to
avoid loading all entities and file_entities at once: mirror
migrate_blobs_batched’s batching strategy and query source records in file-ID
ranges, processing and releasing each batch before fetching the next. Preserve
the include_deleted filtering and existing migration/report behavior while
ensuring memory usage remains bounded for large databases.
🤖 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/migration/phases.rs`:
- Around line 352-355: 在计算 primary_entity_id 的回退逻辑中,保留 file.primary_entity 存在但不在
blob_mappings 时回退到 version_entities.last() 的行为,并在该回退发生时记录一条 warning,明确说明原
primary entity 无对应 blob 且当前版本已被替换。修改 primary_entity_id 附近的处理,避免无效或不存在的
primary_entity 触发无关警告。

---

Nitpick comments:
In `@src/migration/phases.rs`:
- Around line 300-314: Update migrate_files to avoid loading all entities and
file_entities at once: mirror migrate_blobs_batched’s batching strategy and
query source records in file-ID ranges, processing and releasing each batch
before fetching the next. Preserve the include_deleted filtering and existing
migration/report behavior while ensuring memory usage remains bounded for large
databases.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 92195ceb-a99d-4b32-88b6-ae1c23925b36

📥 Commits

Reviewing files that changed from the base of the PR and between d7623ea and 8116384.

📒 Files selected for processing (7)
  • README.md
  • docs/cloudreve-to-asterdrive-field-mapping.md
  • docs/migration-architecture.md
  • src/main.rs
  • src/migration/checkpoint.rs
  • src/migration/mod.rs
  • src/migration/phases.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main.rs
  • docs/cloudreve-to-asterdrive-field-mapping.md
  • docs/migration-architecture.md
  • README.md
  • src/migration/mod.rs

Comment thread src/migration/phases.rs
Comment on lines +352 to +355
let primary_entity_id = file
.primary_entity
.filter(|id| blob_mappings.contains_key(id))
.or_else(|| version_entities.last().map(|entity| entity.id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

primary entity 回退时静默吞掉了信息

file.primary_entity 存在但不在 blob_mappings 中时,代码直接回退到 version_entities.last(),没记录任何 warning。用户根本不知道这个文件的"当前版本"被换了——版本语义可能就此错乱。

🔧 建议在回退时补一条 warning
-        let primary_entity_id = file
-            .primary_entity
-            .filter(|id| blob_mappings.contains_key(id))
-            .or_else(|| version_entities.last().map(|entity| entity.id));
+        let primary_entity_id = match file.primary_entity {
+            Some(id) if blob_mappings.contains_key(&id) => Some(id),
+            Some(id) => {
+                report.warnings.push(format!(
+                    "file {} primary entity {} was not migrated, falling back to latest version",
+                    file.id, id
+                ));
+                version_entities.last().map(|entity| entity.id)
+            }
+            None => version_entities.last().map(|entity| entity.id),
+        };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let primary_entity_id = file
.primary_entity
.filter(|id| blob_mappings.contains_key(id))
.or_else(|| version_entities.last().map(|entity| entity.id));
let primary_entity_id = match file.primary_entity {
Some(id) if blob_mappings.contains_key(&id) => Some(id),
Some(id) => {
report.warnings.push(format!(
"file {} primary entity {} was not migrated, falling back to latest version",
file.id, id
));
version_entities.last().map(|entity| entity.id)
}
None => version_entities.last().map(|entity| entity.id),
};
🤖 Prompt for 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.

In `@src/migration/phases.rs` around lines 352 - 355, 在计算 primary_entity_id
的回退逻辑中,保留 file.primary_entity 存在但不在 blob_mappings 时回退到 version_entities.last()
的行为,并在该回退发生时记录一条 warning,明确说明原 primary entity 无对应 blob 且当前版本已被替换。修改
primary_entity_id 附近的处理,避免无效或不存在的 primary_entity 触发无关警告。

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.

2 participants