diff --git a/Cargo.lock b/Cargo.lock index daef3cf..e515ee2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3048,6 +3048,7 @@ dependencies = [ "log", "lz4_flex 0.11.6", "md5 0.8.0", + "nfsserve", "nix 0.29.0", "once_cell", "poly1305 0.8.0", diff --git a/data/auth.sqlite b/data/auth.sqlite index 80243da..93a57fc 100644 Binary files a/data/auth.sqlite and b/data/auth.sqlite differ diff --git a/markbase-core/Cargo.toml b/markbase-core/Cargo.toml index f8ab23b..e128562 100644 --- a/markbase-core/Cargo.toml +++ b/markbase-core/Cargo.toml @@ -89,12 +89,16 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } # === LDAP Authentication (Phase 2) === ldap3 = { version = "0.11", optional = true } # Async LDAP client (compatible with AD + OpenLDAP) +# === NFS Server (Phase 11) === +nfsserve = { version = "0.11", optional = true } # NFSv3/NFSv4 server implementation + [features] default = [] # 默认不启用可选格式 optional-formats = ["unrar", "xz2", "sevenz-rust"] # 争议格式可选启用 smb-server = ["dep:smb-server"] # SMB server feature flag async-vfs = ["dep:reqwest"] # Async VfsBackend trait + native async S3 ldap = ["dep:ldap3"] # LDAP authentication provider +nfs = ["dep:nfsserve"] # NFSv3/NFSv4 server feature flag [dev-dependencies] # tempfile moved to dependencies (needed for archive extraction) diff --git a/markbase-core/src/cli/tools/mod.rs b/markbase-core/src/cli/tools/mod.rs index 4c458a9..fb1a64e 100644 --- a/markbase-core/src/cli/tools/mod.rs +++ b/markbase-core/src/cli/tools/mod.rs @@ -1,6 +1,8 @@ pub mod render; pub mod smb_server; pub mod test; +#[cfg(feature = "nfs")] +pub mod nfs_server; use clap::Subcommand; @@ -12,6 +14,8 @@ pub enum ToolsCommands { Test(test::TestCommand), #[command(flatten)] SmbServer(smb_server::SmbServerCommand), + #[cfg(feature = "nfs")] + Nfs(nfs_server::NfsServerCommand), } pub async fn handle_tools_command(cmd: ToolsCommands) -> anyhow::Result<()> { @@ -19,6 +23,8 @@ pub async fn handle_tools_command(cmd: ToolsCommands) -> anyhow::Result<()> { ToolsCommands::Render(c) => render::handle_render_command(c)?, ToolsCommands::Test(c) => test::handle_test_command(c)?, ToolsCommands::SmbServer(c) => smb_server::handle_smb_server_command(c).await?, + #[cfg(feature = "nfs")] + ToolsCommands::Nfs(c) => nfs_server::run_nfs_server(c).await?, } Ok(()) } diff --git a/markbase-core/src/cli/tools/nfs_server.rs b/markbase-core/src/cli/tools/nfs_server.rs new file mode 100644 index 0000000..9270e5b --- /dev/null +++ b/markbase-core/src/cli/tools/nfs_server.rs @@ -0,0 +1,41 @@ +use clap::Args; +use std::path::PathBuf; +use std::sync::Arc; + +use crate::vfs::{local_fs::LocalFs, nfs_server::{NfsVfsServer, NfsConfig}}; + +#[derive(Debug, Args)] +pub struct NfsServerCommand { + /// Port to listen on (default: 2049) + #[arg(short, long, default_value = "2049")] + port: u16, + + /// Root directory to export + #[arg(short, long, default_value = "/tmp/nfs_export")] + root: PathBuf, + + /// Share name (export name) + #[arg(short, long, default_value = "export")] + share_name: String, +} + +pub async fn run_nfs_server(cmd: NfsServerCommand) -> anyhow::Result<()> { + println!("Starting NFS server on port {}", cmd.port); + println!("Export directory: {}", cmd.root.display()); + println!("Share name: {}", cmd.share_name); + + if !cmd.root.exists() { + std::fs::create_dir_all(&cmd.root)?; + println!("Created export directory: {}", cmd.root.display()); + } + + let vfs = Arc::new(LocalFs::new()); + let server = NfsVfsServer::new(vfs, cmd.root.clone()).with_port(cmd.port); + + println!("NFS server starting..."); + server.start(cmd.port).await?; + + println!("NFS server stopped"); + + Ok(()) +} \ No newline at end of file diff --git a/markbase-core/src/vfs/mod.rs b/markbase-core/src/vfs/mod.rs index aba732f..7badc28 100644 --- a/markbase-core/src/vfs/mod.rs +++ b/markbase-core/src/vfs/mod.rs @@ -24,6 +24,8 @@ pub mod async_fs; pub mod async_s3_fs; #[cfg(feature = "async-vfs")] pub mod async_smb_fs; +#[cfg(feature = "nfs")] +pub mod nfs_server; use std::path::{Path, PathBuf}; use std::time::SystemTime; diff --git a/markbase-core/src/vfs/nfs_server.rs b/markbase-core/src/vfs/nfs_server.rs new file mode 100644 index 0000000..39a8ea3 --- /dev/null +++ b/markbase-core/src/vfs/nfs_server.rs @@ -0,0 +1,63 @@ +use crate::vfs::{VfsBackend, VfsError}; +use std::path::PathBuf; +use std::sync::Arc; + +pub struct NfsVfsServer { + vfs: Arc, + root: PathBuf, + port: u16, +} + +impl NfsVfsServer { + pub fn new(vfs: Arc, root: PathBuf) -> Self { + Self { + vfs, + root, + port: 2049, + } + } + + pub fn with_port(self, port: u16) -> Self { + Self { port, ..self } + } + + pub async fn start(&self, port: u16) -> Result<(), VfsError> { + #[cfg(feature = "nfs")] + { + println!("NFS server starting on port {}", port); + println!("Export directory: {}", self.root.display()); + + // TODO: Implement actual NFS server using nfsserve crate + // Current implementation is a placeholder + + Err(VfsError::Unsupported("NFS server implementation pending (requires nfsserve crate API study)".to_string())) + } + + #[cfg(not(feature = "nfs"))] + { + Err(VfsError::Unsupported("NFS server requires 'nfs' feature".to_string())) + } + } +} + +pub struct NfsConfig { + pub port: u16, + pub root: PathBuf, + pub vfs: Arc, +} + +impl Default for NfsConfig { + fn default() -> Self { + Self { + port: 2049, + root: PathBuf::from("/"), + vfs: Arc::new(crate::vfs::local_fs::LocalFs::new()), + } + } +} + +impl NfsConfig { + pub fn build(&self) -> NfsVfsServer { + NfsVfsServer::new(self.vfs.clone(), self.root.clone()).with_port(self.port) + } +} \ No newline at end of file