feat(tree): checkpoint resource lifecycle work
提交当前顶层 mnote Git 工作区,范围集中在 04-tree-domain 的 resource/trash/filetree 生命周期、mnote-web resource_trash 路由、Convex/Next 兼容接口、sidebar/file-tree 客户端适配、smoke 脚本与对应设计/bug 记录。 不包含被 ignore 的 design/05-editor-mainline/reference-code/leptos-tiptap 嵌套仓库改动。新增 smoke 的测试密码改为运行时读取 MNOTE_E2E_PASSWORD,避免提交明文 credential assignment。
This commit is contained in:
@@ -391,6 +391,15 @@ fn convex_command_args_for_plan(plan: &RuntimeCommandExecutionPlan) -> Value {
|
||||
) {
|
||||
strip_tree_artifact_fields(&mut args);
|
||||
}
|
||||
if matches!(
|
||||
plan.command_name.as_str(),
|
||||
"tree.resource.archive"
|
||||
| "tree.resource.restore"
|
||||
| "tree.resource.rename"
|
||||
| "tree.resource.purge"
|
||||
) {
|
||||
args = convex_resource_lifecycle_args_for_plan(plan, &args);
|
||||
}
|
||||
if matches!(plan.command_name.as_str(), "mindmaps.put")
|
||||
|| matches!(plan.function_name.as_str(), "mindmaps:put")
|
||||
{
|
||||
@@ -433,6 +442,69 @@ fn convex_command_args_for_plan(plan: &RuntimeCommandExecutionPlan) -> Value {
|
||||
args
|
||||
}
|
||||
|
||||
fn convex_resource_lifecycle_args_for_plan(
|
||||
plan: &RuntimeCommandExecutionPlan,
|
||||
args: &Value,
|
||||
) -> Value {
|
||||
let lifecycle = args.get("resourceLifecyclePlan").and_then(Value::as_object);
|
||||
let action = lifecycle
|
||||
.and_then(|value| value.get("action"))
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("");
|
||||
let resource_kind = lifecycle
|
||||
.and_then(|value| value.get("resourceKind"))
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("file");
|
||||
if resource_kind != "file" {
|
||||
return args.clone();
|
||||
}
|
||||
let id = args
|
||||
.get("id")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let user_id = args
|
||||
.get("userId")
|
||||
.and_then(Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or(plan.actor_id.as_str())
|
||||
.to_string();
|
||||
match action {
|
||||
"archive" => json!({
|
||||
"userId": user_id,
|
||||
"id": id,
|
||||
"patch": {
|
||||
"deleted_at": now_iso_like(),
|
||||
"deleted_by": user_id,
|
||||
"purged_at": null,
|
||||
},
|
||||
}),
|
||||
"restore" => json!({
|
||||
"userId": user_id,
|
||||
"id": id,
|
||||
"patch": {
|
||||
"deleted_at": null,
|
||||
"deleted_by": null,
|
||||
"purged_at": null,
|
||||
},
|
||||
}),
|
||||
"rename" => json!({
|
||||
"userId": user_id,
|
||||
"id": id,
|
||||
"patch": {
|
||||
"file_name": args.get("newName").and_then(Value::as_str).unwrap_or_default(),
|
||||
},
|
||||
}),
|
||||
"purge" => json!({
|
||||
"userId": user_id,
|
||||
"id": id,
|
||||
"expiredDeletedAt": "2126-01-01T00:00:00Z",
|
||||
}),
|
||||
_ => args.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_tree_artifact_fields(args: &mut Value) {
|
||||
if let Value::Object(map) = args {
|
||||
map.remove("streamDeltaHint");
|
||||
@@ -996,6 +1068,42 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convex_resource_lifecycle_args_keep_effective_user_id() {
|
||||
let plan = RuntimeCommandExecutionPlan {
|
||||
command_name: "tree.resource.archive".into(),
|
||||
command_id: "cmd_resource_archive_1".into(),
|
||||
function_name: "mediaAssets:patchById".into(),
|
||||
workspace_id: Some("ws_1".into()),
|
||||
request_id: "req_1".into(),
|
||||
trace_id: "trace_1".into(),
|
||||
actor_id: "anonymous".into(),
|
||||
idempotency_key: None,
|
||||
source: json!({}),
|
||||
payload_json: "{}".into(),
|
||||
args_json: json!({
|
||||
"id": "asset_1",
|
||||
"userId": "convex_user_1",
|
||||
"resourceKind": "file",
|
||||
"resourceLifecyclePlan": {
|
||||
"resourceKind": "file",
|
||||
"action": "archive",
|
||||
"assetId": "asset_1"
|
||||
},
|
||||
"streamDeltaHint": {"family": "tree", "kind": "remove_asset"},
|
||||
"domainEventHint": {"eventType": "tree.resource.archived"},
|
||||
"domainEventPlan": {"eventType": "tree.resource.archived"},
|
||||
}),
|
||||
};
|
||||
|
||||
let args = convex_command_args_for_plan(&plan);
|
||||
|
||||
assert_eq!(args["userId"], "convex_user_1");
|
||||
assert_eq!(args["id"], "asset_1");
|
||||
assert!(args.get("resourceLifecyclePlan").is_none());
|
||||
assert!(args.get("streamDeltaHint").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convex_command_args_adapts_mindmap_command_apply_for_legacy_mutation() {
|
||||
let plan = RuntimeCommandExecutionPlan {
|
||||
|
||||
Reference in New Issue
Block a user