收口本地工作区清理与资源投影
清理历史 Electron、Graphify、沙箱和截图等仓库跟踪残留,补充 CodeGraph 与 Convex active deploy source 协作说明。 新增 tree-first 下一阶段设计稿和 2026-05-20 清理总结,记录本地工作区、路径身份和 Zed/Lapce/VSCode 参考收口方向。 扩展 Rust Web 本地文件夹、DocumentBuffer、mindmap 资源、tree runtime 和页面聚合链路,并补充 task455 local-folder mindmap clean smoke。 验证:git diff --check 通过;pnpm store status --store-dir .pnpm-store 通过;npm ls --depth=0 --json 通过;find -L node_modules 未发现断链。cargo test -p mnote-web 当前 418 passed / 35 failed。
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
use crate::app::AppState;
|
||||
use crate::context::RequestContext;
|
||||
use crate::error::WebError;
|
||||
use crate::routes::local_folder_source::{
|
||||
ensure_local_workspace_read_access, load_local_folder_file_tree_snapshot,
|
||||
load_local_folder_page_tree_snapshot,
|
||||
};
|
||||
use crate::routes::query_support::resolve_effective_workspace_id;
|
||||
use crate::routes::snapshot_support::{
|
||||
execute_kernel_query, load_projection_snapshot, load_sidebar_dataset, subtree_query,
|
||||
@@ -22,6 +26,8 @@ pub struct KernelProjectionQuery {
|
||||
pub depth: Option<u32>,
|
||||
pub query: Option<String>,
|
||||
pub max_results: Option<usize>,
|
||||
pub source_kind: Option<String>,
|
||||
pub root_uri: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -65,6 +71,30 @@ async fn project_projection(
|
||||
Query(query): Query<KernelProjectionQuery>,
|
||||
projection: KernelProjectionKind,
|
||||
) -> Result<(StatusCode, Json<Value>), WebError> {
|
||||
let source_kind = query
|
||||
.source_kind
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
if source_kind == Some("local_folder") {
|
||||
let root_uri = query
|
||||
.root_uri
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
WebError::bad_request_code("local_folder_root_required", "缺少本地文件夹 rootUri")
|
||||
})?;
|
||||
ensure_local_workspace_read_access(&context, root_uri)
|
||||
.map_err(|error| error.with_context(&context))?;
|
||||
let snapshot = if projection == KernelProjectionKind::FileTree {
|
||||
load_local_folder_file_tree_snapshot(root_uri)?
|
||||
} else {
|
||||
load_local_folder_page_tree_snapshot(root_uri)?
|
||||
};
|
||||
return Ok(ok_response(&context, snapshot.projection));
|
||||
}
|
||||
|
||||
let effective_workspace_id =
|
||||
resolve_effective_workspace_id(&context, query.workspace_id.as_deref(), true)?
|
||||
.expect("workspace_required 已确保存在");
|
||||
@@ -189,6 +219,7 @@ pub async fn graph(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::{build_app, AppConfig, AppState};
|
||||
use crate::routes::local_folder_source::initialize_local_workspace_for_actor;
|
||||
use axum::body::{to_bytes, Body};
|
||||
use axum::http::{Request, StatusCode};
|
||||
use serde_json::Value;
|
||||
@@ -296,6 +327,50 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tree_projection_routes_short_circuit_local_folder_without_convex_dataset() {
|
||||
let root = std::env::temp_dir().join(format!(
|
||||
"mnote-local-kernel-projection-{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(root.join("本地页面")).expect("create local page bundle");
|
||||
std::fs::write(root.join("本地页面").join("本地页面.md"), "")
|
||||
.expect("write local markdown");
|
||||
let root_uri = format!("file://{}", root.display());
|
||||
initialize_local_workspace_for_actor("dev-user", &root_uri).expect("init local workspace");
|
||||
|
||||
let response = app()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri(format!(
|
||||
"/api/tree/projections/file?workspaceId=local-ws:dev-user:my-space&sourceKind=local_folder&rootUri={root_uri}&rootNodeId=local-md:%E6%9C%AC%E5%9C%B0%E9%A1%B5%E9%9D%A2~2F%E6%9C%AC%E5%9C%B0%E9%A1%B5%E9%9D%A2.md"
|
||||
))
|
||||
.header("x-mnote-actor-id", "dev-user")
|
||||
.header("x-mnote-actor-type", "user")
|
||||
.body(Body::empty())
|
||||
.expect("request"),
|
||||
)
|
||||
.await
|
||||
.expect("response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let body = to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("body");
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["ok"], true);
|
||||
assert_eq!(payload["result"]["projection"], "file_tree");
|
||||
assert_eq!(payload["result"]["sourceKind"], "local_folder");
|
||||
assert!(payload["result"]["items"]
|
||||
.as_array()
|
||||
.expect("items")
|
||||
.iter()
|
||||
.any(|item| item["title"] == "本地页面.md"));
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tree_projection_routes_keep_ok_response_shape() {
|
||||
let response = app()
|
||||
|
||||
Reference in New Issue
Block a user