use crate::webdav::dav_direntry::MarkBaseDavDirEntry; use crate::webdav::dav_file::MarkBaseDavFile; use crate::webdav::dav_metadata::MarkBaseDavMetaData; use dav_server::davpath::DavPath; use dav_server::fs::{ DavDirEntry, DavFile, DavFileSystem, DavMetaData, FsError, FsFuture, FsStream, OpenOptions, }; use futures_util::stream; use rusqlite::Connection; use std::sync::{Arc, Mutex}; pub struct MarkBaseDavFs { inner: Arc, } struct MarkBaseDavFsInner { user_id: String, sqlite: Mutex, } impl MarkBaseDavFs { pub fn new(user_id: &str, db_path: &str) -> Box { let conn = Connection::open(db_path).expect("Failed to open SQLite database"); Box::new(Self { inner: Arc::new(MarkBaseDavFsInner { user_id: user_id.to_string(), sqlite: Mutex::new(conn), }), }) } fn resolve_path(&self, path: &DavPath) -> Option { let path_str = path.as_pathbuf().to_string_lossy().to_string(); let parts: Vec<&str> = path_str.split('/').filter(|s| !s.is_empty()).collect(); if parts.is_empty() { return self.find_root_node(); } let mut current_parent: Option = self.find_root_node(); for part in parts { if let Some(parent_id) = current_parent { current_parent = self.find_child_by_label(&parent_id, part); } else { return None; } } current_parent } fn find_root_node(&self) -> Option { let conn = self.inner.sqlite.lock().unwrap(); conn.query_row( "SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1", [], |row| row.get::<_, String>(0), ) .ok() } fn find_child_by_label(&self, parent_id: &str, label: &str) -> Option { let conn = self.inner.sqlite.lock().unwrap(); conn.query_row( "SELECT node_id FROM file_nodes WHERE parent_id = ? AND label = ?", [parent_id, label], |row| row.get::<_, String>(0), ) .ok() } fn extract_file_path(&self, aliases_json: &Option) -> Option { aliases_json.as_ref().and_then(|json| { if let Ok(aliases) = serde_json::from_str::(json) { aliases .get("path") .and_then(|p| p.as_str()) .map(|s| s.to_string()) } else { None } }) } } impl Clone for MarkBaseDavFs { fn clone(&self) -> Self { Self { inner: Arc::clone(&self.inner), } } } impl DavFileSystem for MarkBaseDavFs { fn open<'a>( &'a self, path: &'a DavPath, _options: OpenOptions, ) -> FsFuture<'a, Box> { let node_id = self.resolve_path(path); match node_id { Some(id) => { let conn = self.inner.sqlite.lock().unwrap(); let result = conn.query_row( "SELECT node_id, label, node_type, file_size, aliases_json FROM file_nodes WHERE node_id = ?", [&id], |row| { Ok(( row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, row.get::<_, Option>(3)?, row.get::<_, Option>(4)?, )) }, ); match result { Ok((_node_id, _label, node_type, _file_size, aliases_json)) => { if node_type == "folder" { Box::pin(std::future::ready(Err(FsError::Forbidden))) } else { let file_path = self.extract_file_path(&aliases_json); match file_path { Some(path) => match std::fs::read(&path) { Ok(data) => Box::pin(std::future::ready(Ok(Box::new( MarkBaseDavFile::new(data), ) as Box))), Err(_) => Box::pin(std::future::ready(Err(FsError::NotFound))), }, None => Box::pin(std::future::ready(Err(FsError::NotFound))), } } } Err(_) => Box::pin(std::future::ready(Err(FsError::NotFound))), } } None => Box::pin(std::future::ready(Err(FsError::NotFound))), } } fn read_dir<'a>( &'a self, path: &'a DavPath, _meta: dav_server::fs::ReadDirMeta, ) -> FsFuture<'a, FsStream>> { let node_id = self.resolve_path(path); match node_id { Some(id) => { let conn = self.inner.sqlite.lock().unwrap(); let mut stmt = conn .prepare( "SELECT node_id, label, node_type, file_size FROM file_nodes WHERE parent_id = ?", ) .unwrap(); let entries = stmt .query_map([&id], |row| { Ok(( row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, row.get::<_, Option>(3)?, )) }) .unwrap(); let mut results: Vec> = Vec::new(); for entry in entries { match entry { Ok((node_id, label, node_type, file_size)) => { results.push(Box::new(MarkBaseDavDirEntry::from_file_node( &node_id, &label, &node_type, file_size, )) as Box); } Err(_) => continue, } } let stream = stream::iter(results.into_iter().map(Ok)); Box::pin(std::future::ready(Ok( Box::pin(stream) as FsStream> ))) } None => Box::pin(std::future::ready(Err(FsError::NotFound))), } } fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box> { let node_id = self.resolve_path(path); match node_id { Some(id) => { let conn = self.inner.sqlite.lock().unwrap(); let result = conn.query_row( "SELECT node_id, label, node_type, file_size, aliases_json FROM file_nodes WHERE node_id = ?", [&id], |row| { Ok(( row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, row.get::<_, Option>(3)?, row.get::<_, Option>(4)?, )) }, ); match result { Ok((_node_id, _label, node_type, file_size, aliases_json)) => { let size = if node_type == "folder" { 0u64 } else { match file_size { Some(s) => s as u64, None => { let file_path = self.extract_file_path(&aliases_json); match file_path { Some(path) => { std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0) } None => 0, } } } }; let metadata = MarkBaseDavMetaData::new(size, node_type == "folder"); Box::pin(std::future::ready(Ok( Box::new(metadata) as Box ))) } Err(_) => Box::pin(std::future::ready(Err(FsError::NotFound))), } } None => Box::pin(std::future::ready(Err(FsError::NotFound))), } } }