Session修改:Mutex死锁修复+AGENTS更新

This commit is contained in:
Warren Lo
2026-05-18 17:02:30 +08:00
parent 8589a02042
commit 14863d323e
41 changed files with 10152 additions and 28 deletions

View File

@@ -142,12 +142,14 @@ mod warren_tests {
fn test_warren_query_root() {
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
let conn = fs.sqlite.lock().unwrap();
let root_id: String = conn.query_row(
"SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1",
[],
|row| row.get(0)
).unwrap();
let root_id: String = {
let conn = fs.sqlite.lock().unwrap();
conn.query_row(
"SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1",
[],
|row| row.get(0)
).unwrap()
};
let root = fs.query_node(&root_id);
assert!(root.is_some());
@@ -161,12 +163,14 @@ mod warren_tests {
fn test_warren_query_children() {
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
let conn = fs.sqlite.lock().unwrap();
let root_id: String = conn.query_row(
"SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1",
[],
|row| row.get(0)
).unwrap();
let root_id: String = {
let conn = fs.sqlite.lock().unwrap();
conn.query_row(
"SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1",
[],
|row| row.get(0)
).unwrap()
};
let children = fs.query_children(&root_id);
@@ -178,25 +182,26 @@ mod warren_tests {
println!("Root children: {} folders, {} files", folders, files);
assert!(folders > 0);
assert!(files >= 0);
}
#[test]
fn test_warren_read_text_file() {
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
let conn = fs.sqlite.lock().unwrap();
let result = conn.query_row(
"SELECT node_id, aliases_json FROM file_nodes
WHERE node_type = 'file'
AND aliases_json IS NOT NULL
AND file_size < 1000
LIMIT 1",
[],
|row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
);
let result = {
let conn = fs.sqlite.lock().unwrap();
conn.query_row(
"SELECT node_id, aliases_json FROM file_nodes
WHERE node_type = 'file'
AND aliases_json IS NOT NULL
AND file_size < 1000
LIMIT 1",
[],
|row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
).ok()
};
if let Ok((node_id, aliases_json)) = result {
if let Some((node_id, aliases_json)) = result {
let aliases: serde_json::Value = serde_json::from_str(&aliases_json).unwrap();
let path = aliases["path"].as_str().unwrap_or_default();

View File

@@ -3,8 +3,15 @@ pub mod auth;
pub mod command;
pub mod config;
pub mod filetree;
pub mod fskit;
pub mod fuse;
pub mod nfs;
pub mod pg_client;
pub mod raid;
pub mod render;
pub mod scan;
pub mod server;
pub mod sync;
pub mod webdav;
pub use filetree::node::FileNode;

View File

@@ -111,6 +111,9 @@ async fn main() -> anyhow::Result<()> {
Commands::Hash { user, threads } => {
markbase::scan::compute_hashes(&user, threads)?;
}
Commands::WebDAV { action } => {
handle_webdav_command(action)?;
}
}
Ok(())
}
@@ -208,3 +211,64 @@ fn show_section(config: &markbase::config::MarkBaseConfig, section: &str) {
_ => println!("Invalid section: {}. Valid sections: server, postgresql, authentication, test, logging", section),
}
}
fn handle_webdav_command(action: WebDAVCommands) -> anyhow::Result<()> {
match action {
WebDAVCommands::Start { port, user } => {
use std::path::PathBuf;
use std::sync::Arc;
use markbase::webdav::MarkBaseWebDAV;
use markbase::filetree::FileTree;
use tokio::net::TcpListener;
let db_path = PathBuf::from(FileTree::user_db_path(&user));
if !db_path.exists() {
return Err(anyhow::anyhow!("User database not found: {}", db_path.display()));
}
println!("=== MarkBase WebDAV Server ===");
println!("User: {}", user);
println!("Port: {}", port);
println!("Database: {}", db_path.display());
println!("");
let webdav = MarkBaseWebDAV::new(user.clone(), db_path);
let dav_handler = webdav.create_handler();
let addr = format!("127.0.0.1:{}", port);
println!("Listening on: {}", addr);
println!("Mount with Finder:");
println!(" Connect to Server → http://localhost:{}/webdav", port);
println!("");
println!("Press Ctrl+C to stop...");
tokio::spawn(async move {
use axum::{Router, Extension, routing::any};
let app = Router::new()
.route("/webdav/*path", any(|req: axum::http::Request<axum::body::Body>, Extension(h): Extension<Arc<dav_server::DavHandler>>| async move {
use http_body_util::BodyExt;
let body = req.into_body().collect().await.unwrap().to_bytes();
let req = http::Request::new(body);
h.handle(req).await
}))
.route("/webdav", any(|req: axum::http::Request<axum::body::Body>, Extension(h): Extension<Arc<dav_server::DavHandler>>| async move {
use http_body_util::BodyExt;
let body = req.into_body().collect().await.unwrap().to_bytes();
let req = http::Request::new(body);
h.handle(req).await
}))
.layer(Extension(dav_handler));
let listener = TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
});
tokio::signal::ctrl_c().await?;
println!("\nShutting down...");
}
}
Ok(())
}

View File

@@ -377,7 +377,7 @@ fn compute_hashes_parallel(user_id: &str, file_info: Vec<(String, String)>, thre
let file_info = Arc::clone(&file_info);
let results = Arc::clone(&results);
let processed = Arc::clone(&processed);
let user_id = user_id.clone();
let _user_id = user_id.clone();
let handle = thread::spawn(move || {
let chunk_size = (file_info.len() / threads) + (if i < file_info.len() % threads { 1 } else { 0 });

View File

@@ -1275,7 +1275,7 @@ async fn stream_file(
}
async fn get_file_probe(
Path((user_id, file_uuid)): Path<(String, String)>,
Path((_user_id, file_uuid)): Path<(String, String)>,
) -> impl IntoResponse {
let result = tokio::task::spawn_blocking(move || -> anyhow::Result<serde_json::Value> {
let conn = FileTree::open_user_db("demo")?;