fix: open local uploaded resources
This commit is contained in:
@@ -42,6 +42,7 @@ struct LocalFolderEntry {
|
||||
struct LocalFolderMetadata {
|
||||
page_options: BTreeMap<String, Value>,
|
||||
trash_entries: BTreeMap<String, LocalTrashEntry>,
|
||||
uploaded_assets: BTreeMap<String, LocalUploadedAssetEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -156,6 +157,19 @@ pub(crate) struct LocalTrashEntry {
|
||||
pub(crate) purged_at: Option<u128>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct LocalUploadedAssetEntry {
|
||||
#[serde(default, alias = "documentId")]
|
||||
document_id: String,
|
||||
#[serde(default, alias = "relativePath")]
|
||||
relative_path: String,
|
||||
#[serde(default, alias = "fileName")]
|
||||
file_name: String,
|
||||
#[serde(default, alias = "createdAtMs")]
|
||||
created_at_ms: u128,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LocalFolderRow {
|
||||
node_id: String,
|
||||
@@ -2523,6 +2537,21 @@ pub(crate) fn write_local_markdown_asset(
|
||||
let root_relative_path = normalize_relative_path(&canonical_root, &target)?;
|
||||
let markdown_relative_path = normalize_markdown_relative_asset_path(markdown_dir, &target)?;
|
||||
let asset_type = local_upload_asset_type(kind, &file.content_type);
|
||||
let mut uploaded_assets = metadata.uploaded_assets;
|
||||
uploaded_assets.insert(
|
||||
root_relative_path.clone(),
|
||||
LocalUploadedAssetEntry {
|
||||
document_id: document_id.to_string(),
|
||||
relative_path: root_relative_path.clone(),
|
||||
file_name: target
|
||||
.file_name()
|
||||
.and_then(|value| value.to_str())
|
||||
.unwrap_or(&sanitized_name)
|
||||
.to_string(),
|
||||
created_at_ms: now_ms(),
|
||||
},
|
||||
);
|
||||
write_uploaded_asset_index_metadata(&canonical_root, &uploaded_assets)?;
|
||||
Ok(json!({
|
||||
"id": format!("local:asset:{root_relative_path}"),
|
||||
"asset_type": asset_type,
|
||||
@@ -4506,6 +4535,9 @@ fn load_local_folder_metadata(root: &Path) -> Result<LocalFolderMetadata, WebErr
|
||||
Ok(LocalFolderMetadata {
|
||||
page_options: load_metadata_value_map(&root.join(".mnote").join("page-options.json"))?,
|
||||
trash_entries: load_trash_index_map(&root.join(".mnote").join("trash-index.json"))?,
|
||||
uploaded_assets: load_uploaded_asset_index_map(
|
||||
&root.join(".mnote").join("uploaded-assets.json"),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4568,6 +4600,19 @@ fn load_trash_index_map(path: &Path) -> Result<BTreeMap<String, LocalTrashEntry>
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
fn load_uploaded_asset_index_map(
|
||||
path: &Path,
|
||||
) -> Result<BTreeMap<String, LocalUploadedAssetEntry>, WebError> {
|
||||
if !path.exists() {
|
||||
return Ok(BTreeMap::new());
|
||||
}
|
||||
let value = read_metadata_json(path)?;
|
||||
let source = value.get("entries").unwrap_or(&value);
|
||||
let entries: BTreeMap<String, LocalUploadedAssetEntry> = serde_json::from_value(source.clone())
|
||||
.map_err(|error| metadata_invalid(path, format!("上传资源索引损坏: {error}")))?;
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
fn metadata_invalid(path: &Path, message: impl Into<String>) -> WebError {
|
||||
WebError::bad_request_code(
|
||||
"local_metadata_invalid",
|
||||
@@ -4641,6 +4686,25 @@ fn write_page_options_metadata(
|
||||
write_json_atomic(&path, &value)
|
||||
}
|
||||
|
||||
fn write_uploaded_asset_index_metadata(
|
||||
root: &Path,
|
||||
entries: &BTreeMap<String, LocalUploadedAssetEntry>,
|
||||
) -> Result<(), WebError> {
|
||||
let mnote_dir = root.join(".mnote");
|
||||
fs::create_dir_all(&mnote_dir).map_err(|error| {
|
||||
WebError::bad_request_code(
|
||||
"local_metadata_write_failed",
|
||||
format!("无法创建本地元数据目录 {}: {error}", mnote_dir.display()),
|
||||
)
|
||||
})?;
|
||||
let path = mnote_dir.join("uploaded-assets.json");
|
||||
let value = json!({
|
||||
"version": 1,
|
||||
"entries": entries,
|
||||
});
|
||||
write_json_atomic(&path, &value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn write_json_atomic(path: &Path, value: &Value) -> Result<(), WebError> {
|
||||
let tmp_path = path.with_extension("json.tmp");
|
||||
@@ -4684,8 +4748,11 @@ fn scan_directory(
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let uploaded_asset = metadata.uploaded_assets.get(&entry.relative_path);
|
||||
let row_kind = if entry.is_dir {
|
||||
"folder".to_string()
|
||||
} else if uploaded_asset.is_some() {
|
||||
"asset".to_string()
|
||||
} else if is_markdown_file(&entry.file_name) {
|
||||
"markdown".to_string()
|
||||
} else {
|
||||
@@ -4706,7 +4773,9 @@ fn scan_directory(
|
||||
child_count,
|
||||
expandable: entry.is_dir && child_count > 0,
|
||||
expanded_by_default: depth < 1 && entry.is_dir && entry_count <= 80,
|
||||
document_id: if is_markdown_file(&entry.file_name) {
|
||||
document_id: if let Some(asset) = uploaded_asset {
|
||||
Some(asset.document_id.clone())
|
||||
} else if is_markdown_file(&entry.file_name) {
|
||||
Some(local_markdown_path_page_id(&entry.relative_path))
|
||||
} else {
|
||||
None
|
||||
@@ -4968,6 +5037,29 @@ fn scan_markdown_page_tree(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let uploaded_asset = metadata.uploaded_assets.get(&entry.relative_path);
|
||||
if let Some(asset) = uploaded_asset {
|
||||
directory_rows.push(LocalFolderRow {
|
||||
node_id: local_node_id(&entry.relative_path),
|
||||
row_id: format!("local:asset:{}", entry.relative_path),
|
||||
parent_node_id: parent_node_id.clone(),
|
||||
title: entry.file_name.clone(),
|
||||
depth,
|
||||
position: position as u32,
|
||||
row_kind: "asset".to_string(),
|
||||
icon_hint: icon_hint_for_entry(&entry),
|
||||
relative_path: entry.relative_path.clone(),
|
||||
source_uri: file_uri_for_path(&entry.path),
|
||||
child_count: 0,
|
||||
expandable: false,
|
||||
expanded_by_default: false,
|
||||
document_id: Some(asset.document_id.clone()),
|
||||
capabilities: local_entry_capabilities(&entry),
|
||||
workspace_id: workspace_id.to_string(),
|
||||
root_source_uri: root_source_uri.to_string(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if is_markdown_file(&entry.file_name) {
|
||||
let markdown = fs::read_to_string(&entry.path).unwrap_or_default();
|
||||
let parsed = parse_markdown_page(&markdown, &entry.file_name);
|
||||
@@ -8284,6 +8376,49 @@ fn main() {}
|
||||
.expect("read copied asset"),
|
||||
b"new-image"
|
||||
);
|
||||
let markdown_asset = write_local_markdown_asset(
|
||||
&root_uri,
|
||||
"local-md:docs~2FREADME~2FREADME.md",
|
||||
"attachment",
|
||||
LocalUploadFile {
|
||||
name: "notes.md".to_string(),
|
||||
content_type: "text/markdown".to_string(),
|
||||
bytes: b"# Uploaded notes\n".to_vec(),
|
||||
},
|
||||
)
|
||||
.expect("upload markdown asset");
|
||||
assert_eq!(markdown_asset["sourcePath"], "notes.md");
|
||||
let uploaded_asset_index =
|
||||
std::fs::read_to_string(root.join(".mnote").join("uploaded-assets.json"))
|
||||
.expect("uploaded asset index");
|
||||
assert!(uploaded_asset_index.contains("docs/README/notes.md"));
|
||||
|
||||
let snapshot = load_local_folder_file_tree_snapshot(&root_uri).expect("file tree");
|
||||
let items = snapshot.projection["items"].as_array().expect("items");
|
||||
let notes_row = items
|
||||
.iter()
|
||||
.find(|item| item["title"].as_str() == Some("notes.md"))
|
||||
.expect("uploaded markdown asset row");
|
||||
assert_eq!(notes_row["rowKind"].as_str(), Some("asset"));
|
||||
assert_eq!(notes_row["iconHint"].as_str(), Some("markdown"));
|
||||
assert_eq!(
|
||||
notes_row["resourceMeta"]["assetId"].as_str(),
|
||||
Some("local-file:docs/README/notes.md")
|
||||
);
|
||||
let page_tree = load_local_folder_page_tree_snapshot(&root_uri).expect("page tree");
|
||||
let page_items = page_tree.projection["items"]
|
||||
.as_array()
|
||||
.expect("page items");
|
||||
let page_notes_row = page_items
|
||||
.iter()
|
||||
.find(|item| item["title"].as_str() == Some("notes.md"))
|
||||
.expect("uploaded markdown asset page row");
|
||||
assert_eq!(page_notes_row["rowKind"].as_str(), Some("asset"));
|
||||
assert_eq!(page_notes_row["iconHint"].as_str(), Some("markdown"));
|
||||
assert_eq!(
|
||||
page_notes_row["resourceMeta"]["assetId"].as_str(),
|
||||
Some("local-file:docs/README/notes.md")
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user