feat: Enhance WebDAV server and add VFS encoder/decoder tests
- Add user authentication to the WebDAV server using a user database. - Implement encoding and decoding functionality for directories, files, and symlinks in the OurDBFS VFS. - Add comprehensive unit tests for the encoder and decoder functions. - Improve the OurDBFS factory method to handle directory creation more robustly using pathlib. - Add `delete` and `link_delete` methods to the `NestedVFS` and `OurDBVFS` implementations (though currently unimplemented). - Improve WebDAV file handling to correctly determine and set the content type. The previous implementation was incomplete and returned a dummy response. - Update VFS test to actually test functionality. - Remove unnecessary `root_dir` parameter from the WebDAV app.
This commit is contained in:
@@ -18,6 +18,10 @@ high_level_vfs.add_vfs('/config', vfs2) or { panic(err) }
|
||||
high_level_vfs.add_vfs('/data/backup', vfs3) or { panic(err) } // Nested under /data
|
||||
|
||||
// Create WebDAV Server that uses high level VFS
|
||||
webdav_server := webdav.new_app(
|
||||
vfs: high_level_vfs
|
||||
mut webdav_server := webdav.new_app(
|
||||
vfs: high_level_vfs
|
||||
user_db: {
|
||||
'omda': '123'
|
||||
}
|
||||
)!
|
||||
webdav_server.run()
|
||||
|
||||
126
lib/vfs/ourdb_fs/encoder_test.v
Normal file
126
lib/vfs/ourdb_fs/encoder_test.v
Normal file
@@ -0,0 +1,126 @@
|
||||
module ourdb_fs
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
fn test_directory_encoder_decoder() ! {
|
||||
println('Testing encoding/decoding directories...')
|
||||
|
||||
current_time := time.now().unix()
|
||||
dir := Directory{
|
||||
metadata: Metadata{
|
||||
id: u32(current_time)
|
||||
name: 'root'
|
||||
file_type: .directory
|
||||
created_at: current_time
|
||||
modified_at: current_time
|
||||
accessed_at: current_time
|
||||
mode: 0o755
|
||||
owner: 'user'
|
||||
group: 'user'
|
||||
}
|
||||
children: [u32(1), u32(2)]
|
||||
parent_id: 0
|
||||
myvfs: unsafe { nil }
|
||||
}
|
||||
|
||||
encoded := dir.encode()
|
||||
|
||||
mut decoded := decode_directory(encoded) or {
|
||||
return error('Failed to decode directory: ${err}')
|
||||
}
|
||||
|
||||
assert decoded.metadata.id == dir.metadata.id
|
||||
assert decoded.metadata.name == dir.metadata.name
|
||||
assert decoded.metadata.file_type == dir.metadata.file_type
|
||||
assert decoded.metadata.created_at == dir.metadata.created_at
|
||||
assert decoded.metadata.modified_at == dir.metadata.modified_at
|
||||
assert decoded.metadata.accessed_at == dir.metadata.accessed_at
|
||||
assert decoded.metadata.mode == dir.metadata.mode
|
||||
assert decoded.metadata.owner == dir.metadata.owner
|
||||
assert decoded.metadata.group == dir.metadata.group
|
||||
assert decoded.children == dir.children
|
||||
assert decoded.parent_id == dir.parent_id
|
||||
|
||||
println('Test completed successfully!')
|
||||
}
|
||||
|
||||
fn test_file_encoder_decoder() ! {
|
||||
println('Testing encoding/decoding files...')
|
||||
|
||||
current_time := time.now().unix()
|
||||
file := File{
|
||||
metadata: Metadata{
|
||||
id: u32(current_time)
|
||||
name: 'test.txt'
|
||||
file_type: .file
|
||||
created_at: current_time
|
||||
modified_at: current_time
|
||||
accessed_at: current_time
|
||||
mode: 0o644
|
||||
owner: 'user'
|
||||
group: 'user'
|
||||
}
|
||||
data: 'Hello, world!'
|
||||
parent_id: 0
|
||||
myvfs: unsafe { nil }
|
||||
}
|
||||
|
||||
encoded := file.encode()
|
||||
|
||||
mut decoded := decode_file(encoded) or { return error('Failed to decode file: ${err}') }
|
||||
|
||||
assert decoded.metadata.id == file.metadata.id
|
||||
assert decoded.metadata.name == file.metadata.name
|
||||
assert decoded.metadata.file_type == file.metadata.file_type
|
||||
assert decoded.metadata.created_at == file.metadata.created_at
|
||||
assert decoded.metadata.modified_at == file.metadata.modified_at
|
||||
assert decoded.metadata.accessed_at == file.metadata.accessed_at
|
||||
assert decoded.metadata.mode == file.metadata.mode
|
||||
assert decoded.metadata.owner == file.metadata.owner
|
||||
assert decoded.metadata.group == file.metadata.group
|
||||
assert decoded.data == file.data
|
||||
assert decoded.parent_id == file.parent_id
|
||||
|
||||
println('Test completed successfully!')
|
||||
}
|
||||
|
||||
fn test_symlink_encoder_decoder() ! {
|
||||
println('Testing encoding/decoding symlinks...')
|
||||
|
||||
current_time := time.now().unix()
|
||||
symlink := Symlink{
|
||||
metadata: Metadata{
|
||||
id: u32(current_time)
|
||||
name: 'test.txt'
|
||||
file_type: .symlink
|
||||
created_at: current_time
|
||||
modified_at: current_time
|
||||
accessed_at: current_time
|
||||
mode: 0o644
|
||||
owner: 'user'
|
||||
group: 'user'
|
||||
}
|
||||
target: 'test.txt'
|
||||
parent_id: 0
|
||||
myvfs: unsafe { nil }
|
||||
}
|
||||
|
||||
encoded := symlink.encode()
|
||||
|
||||
mut decoded := decode_symlink(encoded) or { return error('Failed to decode symlink: ${err}') }
|
||||
|
||||
assert decoded.metadata.id == symlink.metadata.id
|
||||
assert decoded.metadata.name == symlink.metadata.name
|
||||
assert decoded.metadata.file_type == symlink.metadata.file_type
|
||||
assert decoded.metadata.created_at == symlink.metadata.created_at
|
||||
assert decoded.metadata.modified_at == symlink.metadata.modified_at
|
||||
assert decoded.metadata.accessed_at == symlink.metadata.accessed_at
|
||||
assert decoded.metadata.mode == symlink.metadata.mode
|
||||
assert decoded.metadata.owner == symlink.metadata.owner
|
||||
assert decoded.metadata.group == symlink.metadata.group
|
||||
assert decoded.target == symlink.target
|
||||
assert decoded.parent_id == symlink.parent_id
|
||||
|
||||
println('Test completed successfully!')
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
module ourdb_fs
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.data.ourdb
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
|
||||
// Factory method for creating a new OurDBFS instance
|
||||
@[params]
|
||||
@@ -14,13 +14,11 @@ pub:
|
||||
|
||||
// Factory method for creating a new OurDBFS instance
|
||||
pub fn new(params VFSParams) !&OurDBFS {
|
||||
if !os.exists(params.data_dir) {
|
||||
os.mkdir(params.data_dir) or { return error('Failed to create data directory: ${err}') }
|
||||
pathlib.get_dir(path: params.data_dir, create: true) or {
|
||||
return error('Failed to create data directory: ${err}')
|
||||
}
|
||||
if !os.exists(params.metadata_dir) {
|
||||
os.mkdir(params.metadata_dir) or {
|
||||
return error('Failed to create metadata directory: ${err}')
|
||||
}
|
||||
pathlib.get_dir(path: params.metadata_dir, create: true) or {
|
||||
return error('Failed to create metadata directory: ${err}')
|
||||
}
|
||||
|
||||
mut db_meta := ourdb.new(
|
||||
|
||||
@@ -56,6 +56,16 @@ pub fn (mut self NestedVFS) root_get() !vfscore.FSEntry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) delete(path string) ! {
|
||||
// mut impl, rel_path := self.find_vfs(path)!
|
||||
// return impl.file_read(rel_path)
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) link_delete(path string) ! {
|
||||
// mut impl, rel_path := self.find_vfs(path)!
|
||||
// return impl.file_read(rel_path)
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) file_create(path string) !vfscore.FSEntry {
|
||||
mut impl, rel_path := self.find_vfs(path)!
|
||||
return impl.file_create(rel_path)
|
||||
@@ -112,7 +122,7 @@ pub fn (mut self NestedVFS) dir_delete(path string) ! {
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) exists(path string) bool {
|
||||
mut impl, rel_path := self.find_vfs(path) or {return false}
|
||||
mut impl, rel_path := self.find_vfs(path) or { return false }
|
||||
return impl.exists(rel_path)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,16 @@ pub fn (mut self OurDBVFS) file_write(path string, data []u8) ! {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) delete(path string) ! {
|
||||
// mut impl, rel_path := self.find_vfs(path)!
|
||||
// return impl.file_read(rel_path)
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) link_delete(path string) ! {
|
||||
// mut impl, rel_path := self.find_vfs(path)!
|
||||
// return impl.file_read(rel_path)
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) file_delete(path string) ! {
|
||||
parent_path := os.dir(path)
|
||||
file_name := os.base(path)
|
||||
|
||||
@@ -31,39 +31,39 @@ fn test_vfsourdb() ! {
|
||||
assert test_dir.get_metadata().file_type == .directory
|
||||
|
||||
// Test file creation and writing
|
||||
// mut test_file := vfs.file_create('/test_dir/test.txt')!
|
||||
// assert test_file.get_metadata().name == 'test.txt'
|
||||
// assert test_file.get_metadata().file_type == .file
|
||||
mut test_file := vfs.file_create('/test_dir/test.txt')!
|
||||
assert test_file.get_metadata().name == 'test.txt'
|
||||
assert test_file.get_metadata().file_type == .file
|
||||
|
||||
// test_content := 'Hello, World!'.bytes()
|
||||
// vfs.file_write('/test_dir/test.txt', test_content)!
|
||||
test_content := 'Hello, World!'.bytes()
|
||||
vfs.file_write('/test_dir/test.txt', test_content)!
|
||||
|
||||
// // Test file reading
|
||||
// read_content := vfs.file_read('/test_dir/test.txt')!
|
||||
// assert read_content == test_content
|
||||
// Test file reading
|
||||
read_content := vfs.file_read('/test_dir/test.txt')!
|
||||
assert read_content == test_content
|
||||
|
||||
// // Test directory listing
|
||||
// entries := vfs.dir_list('/test_dir')!
|
||||
// assert entries.len == 1
|
||||
// assert entries[0].get_metadata().name == 'test.txt'
|
||||
// Test directory listing
|
||||
entries := vfs.dir_list('/test_dir')!
|
||||
assert entries.len == 1
|
||||
assert entries[0].get_metadata().name == 'test.txt'
|
||||
|
||||
// // Test exists
|
||||
// assert vfs.exists('/test_dir') == true
|
||||
// assert vfs.exists('/test_dir/test.txt') == true
|
||||
// assert vfs.exists('/nonexistent') == false
|
||||
// Test exists
|
||||
assert vfs.exists('/test_dir') == true
|
||||
assert vfs.exists('/test_dir/test.txt') == true
|
||||
assert vfs.exists('/nonexistent') == false
|
||||
|
||||
// // Test symlink creation and reading
|
||||
// vfs.link_create('/test_dir/test.txt', '/test_dir/test_link')!
|
||||
// link_target := vfs.link_read('/test_dir/test_link')!
|
||||
// assert link_target == '/test_dir/test.txt'
|
||||
// Test symlink creation and reading
|
||||
vfs.link_create('/test_dir/test.txt', '/test_dir/test_link')!
|
||||
link_target := vfs.link_read('/test_dir/test_link')!
|
||||
assert link_target == '/test_dir/test.txt'
|
||||
|
||||
// // Test file deletion
|
||||
// vfs.file_delete('/test_dir/test.txt')!
|
||||
// assert vfs.exists('/test_dir/test.txt') == false
|
||||
// Test file deletion
|
||||
vfs.file_delete('/test_dir/test.txt')!
|
||||
assert vfs.exists('/test_dir/test.txt') == false
|
||||
|
||||
// // Test directory deletion
|
||||
// vfs.dir_delete('/test_dir')!
|
||||
// assert vfs.exists('/test_dir') == false
|
||||
// Test directory deletion
|
||||
vfs.dir_delete('/test_dir')!
|
||||
assert vfs.exists('/test_dir') == false
|
||||
|
||||
println('Test completed successfully!')
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import freeflowuniverse.herolib.vfs.vfscore
|
||||
@[heap]
|
||||
struct App {
|
||||
vweb.Context
|
||||
user_db map[string]string @[required]
|
||||
root_dir pathlib.Path @[vweb_global]
|
||||
user_db map[string]string @[required]
|
||||
// root_dir pathlib.Path @[vweb_global]
|
||||
pub mut:
|
||||
// lock_manager LockManager
|
||||
vfs vfscore.VFSImplementation
|
||||
@@ -21,16 +21,16 @@ pub mut:
|
||||
pub struct AppArgs {
|
||||
pub mut:
|
||||
server_port int = 8080
|
||||
root_dir string @[required]
|
||||
user_db map[string]string @[required]
|
||||
vfs vfscore.VFSImplementation
|
||||
// root_dir string @[required]
|
||||
user_db map[string]string @[required]
|
||||
vfs vfscore.VFSImplementation
|
||||
}
|
||||
|
||||
pub fn new_app(args AppArgs) !&App {
|
||||
root_dir := pathlib.get_dir(path: args.root_dir, create: true)!
|
||||
// root_dir := pathlib.get_dir(path: args.root_dir, create: true)!
|
||||
mut app := &App{
|
||||
user_db: args.user_db.clone()
|
||||
root_dir: root_dir
|
||||
user_db: args.user_db.clone()
|
||||
// root_dir: root_dir
|
||||
server_port: args.server_port
|
||||
vfs: args.vfs
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module webdav
|
||||
|
||||
import vweb
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import encoding.xml
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import encoding.xml
|
||||
import net.urllib
|
||||
import os
|
||||
import vweb
|
||||
|
||||
@['/:path...'; get]
|
||||
fn (mut app App) get_file(path string) vweb.Result {
|
||||
@@ -18,18 +18,16 @@ fn (mut app App) get_file(path string) vweb.Result {
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
println('fs_entry: ${fs_entry}')
|
||||
file_data := app.vfs.file_read(fs_entry.get_path()) or { return app.server_error() }
|
||||
|
||||
// file_data := app.vfs.file_read(fs_entry.path)
|
||||
ext := fs_entry.get_metadata().name.all_after_last('.')
|
||||
content_type := if v := vweb.mime_types[ext] {
|
||||
v
|
||||
} else {
|
||||
'text/plain'
|
||||
}
|
||||
|
||||
// ext := fs_entry.get_metadata().name.all_after_last('.')
|
||||
// content_type := if v := vweb.mime_types[ext] {
|
||||
// v
|
||||
// } else {
|
||||
// 'text/plain'
|
||||
// }
|
||||
|
||||
// app.set_status(200, 'Ok')
|
||||
// app.send_response_to_client(content_type, file_data)
|
||||
app.set_status(200, 'Ok')
|
||||
app.send_response_to_client(content_type, file_data.str())
|
||||
return vweb.not_found() // this is for returning a dummy result
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import time
|
||||
import vweb
|
||||
|
||||
fn (mut app App) generate_response_element(path string, depth int) xml.XMLNode {
|
||||
mut path_ := path.all_after(app.root_dir.path)
|
||||
mut path_ := path
|
||||
if !path_.starts_with('/') {
|
||||
path_ = '/${path_}'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user