feat: Add move operation and file type checks
- Add `move` operation to the VFS interface and implementations. This allows for moving files and directories within the VFS. - Add `is_dir`, `is_file`, and `is_symlink` methods to the `FSEntry` interface and implementations. This allows for robust file type checking before performing operations.
This commit is contained in:
@@ -23,6 +23,9 @@ pub mut:
|
||||
pub interface FSEntry {
|
||||
get_metadata() Metadata
|
||||
get_path() string
|
||||
is_dir() bool
|
||||
is_file() bool
|
||||
is_symlink() bool
|
||||
}
|
||||
|
||||
// VFSImplementation defines the interface that all vfscore implementations must follow
|
||||
@@ -47,6 +50,7 @@ mut:
|
||||
get(path string) !FSEntry
|
||||
rename(old_path string, new_path string) !
|
||||
copy(src_path string, dst_path string) !
|
||||
move(src_path string, dst_path string) !
|
||||
delete(path string) !
|
||||
|
||||
// Symlink operations
|
||||
|
||||
@@ -9,6 +9,21 @@ mut:
|
||||
metadata Metadata
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &LocalFSEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &LocalFSEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &LocalFSEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
fn (e LocalFSEntry) get_metadata() Metadata {
|
||||
return e.metadata
|
||||
}
|
||||
@@ -242,6 +257,20 @@ pub fn (myvfs LocalVFS) copy(src_path string, dst_path string) ! {
|
||||
os.cp(abs_src, abs_dst) or { return error('Failed to copy ${src_path} to ${dst_path}: ${err}') }
|
||||
}
|
||||
|
||||
pub fn (myvfs LocalVFS) move(src_path string, dst_path string) ! {
|
||||
abs_src := myvfs.abs_path(src_path)
|
||||
abs_dst := myvfs.abs_path(dst_path)
|
||||
|
||||
if !os.exists(abs_src) {
|
||||
return error('Source path does not exist: ${src_path}')
|
||||
}
|
||||
if os.exists(abs_dst) {
|
||||
return error('Destination path already exists: ${dst_path}')
|
||||
}
|
||||
|
||||
os.mv(abs_src, abs_dst) or { return error('Failed to move ${src_path} to ${dst_path}: ${err}') }
|
||||
}
|
||||
|
||||
// Generic delete operation that handles all types
|
||||
pub fn (myvfs LocalVFS) delete(path string) ! {
|
||||
abs_path := myvfs.abs_path(path)
|
||||
|
||||
@@ -57,13 +57,13 @@ 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)
|
||||
mut impl, rel_path := self.find_vfs(path)!
|
||||
return impl.delete(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)
|
||||
mut impl, rel_path := self.find_vfs(path)!
|
||||
return impl.link_delete(rel_path)
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) file_create(path string) !vfscore.FSEntry {
|
||||
@@ -151,6 +151,22 @@ pub fn (mut self NestedVFS) copy(src_path string, dst_path string) ! {
|
||||
}
|
||||
|
||||
// Copy across different VFS implementations
|
||||
// TODO: Q: What if it's not file? What if it's a symlink or directory?
|
||||
data := src_impl.file_read(src_rel_path)!
|
||||
dst_impl.file_create(dst_rel_path)!
|
||||
return dst_impl.file_write(dst_rel_path, data)
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) move(src_path string, dst_path string) ! {
|
||||
mut src_impl, src_rel_path := self.find_vfs(src_path)!
|
||||
mut dst_impl, dst_rel_path := self.find_vfs(dst_path)!
|
||||
|
||||
if src_impl == dst_impl {
|
||||
return src_impl.move(src_rel_path, dst_rel_path)
|
||||
}
|
||||
|
||||
// Move across different VFS implementations
|
||||
// TODO: Q: What if it's not file? What if it's a symlink or directory?
|
||||
data := src_impl.file_read(src_rel_path)!
|
||||
dst_impl.file_create(dst_rel_path)!
|
||||
return dst_impl.file_write(dst_rel_path, data)
|
||||
@@ -185,6 +201,21 @@ fn (e &RootEntry) get_path() string {
|
||||
return '/'
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &RootEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &RootEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &RootEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
pub struct MountEntry {
|
||||
pub mut:
|
||||
metadata vfscore.Metadata
|
||||
@@ -198,3 +229,18 @@ fn (e &MountEntry) get_metadata() vfscore.Metadata {
|
||||
fn (e &MountEntry) get_path() string {
|
||||
return '/${e.metadata.name}'
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &MountEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &MountEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &MountEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
@@ -130,6 +130,10 @@ pub fn (mut self OurDBVFS) copy(src_path string, dst_path string) ! {
|
||||
return error('Not implemented')
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) move(src_path string, dst_path string) ! {
|
||||
return error('Not implemented')
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) link_create(target_path string, link_path string) !vfscore.FSEntry {
|
||||
parent_path := os.dir(link_path)
|
||||
link_name := os.base(link_path)
|
||||
@@ -270,6 +274,21 @@ fn (e &DirectoryEntry) get_path() string {
|
||||
return e.path
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &DirectoryEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &DirectoryEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &DirectoryEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
struct FileEntry {
|
||||
metadata vfscore.Metadata
|
||||
path string
|
||||
@@ -283,6 +302,21 @@ fn (e &FileEntry) get_path() string {
|
||||
return e.path
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &FileEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &FileEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &FileEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
struct SymlinkEntry {
|
||||
metadata vfscore.Metadata
|
||||
path string
|
||||
@@ -296,3 +330,18 @@ fn (e &SymlinkEntry) get_metadata() vfscore.Metadata {
|
||||
fn (e &SymlinkEntry) get_path() string {
|
||||
return e.path
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &SymlinkEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &SymlinkEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &SymlinkEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
module webdav
|
||||
|
||||
import vweb
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.vfs.vfscore
|
||||
|
||||
@@ -11,10 +10,10 @@ struct App {
|
||||
user_db map[string]string @[required]
|
||||
// root_dir pathlib.Path @[vweb_global]
|
||||
pub mut:
|
||||
// lock_manager LockManager
|
||||
vfs vfscore.VFSImplementation
|
||||
server_port int
|
||||
middlewares map[string][]vweb.Middleware
|
||||
lock_manager LockManager
|
||||
vfs vfscore.VFSImplementation
|
||||
server_port int
|
||||
middlewares map[string][]vweb.Middleware
|
||||
}
|
||||
|
||||
@[params]
|
||||
|
||||
@@ -1,106 +1,135 @@
|
||||
module webdav
|
||||
|
||||
// import vweb
|
||||
// import os
|
||||
// import freeflowuniverse.herolib.core.pathlib
|
||||
// import encoding.xml
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
// import net.urllib
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.vfs.ourdb_fs
|
||||
import encoding.xml
|
||||
import net.urllib
|
||||
import os
|
||||
import vweb
|
||||
|
||||
// // @['/:path...'; LOCK]
|
||||
// // fn (mut app App) lock_handler(path string) vweb.Result {
|
||||
// // // Not yet working
|
||||
// // // TODO: Test with multiple clients
|
||||
// // resource := app.req.url
|
||||
// // owner := app.get_header('Owner')
|
||||
// // if owner.len == 0 {
|
||||
// // return app.bad_request('Owner header is required.')
|
||||
// // }
|
||||
@['/:path...'; options]
|
||||
fn (mut app App) options(path string) vweb.Result {
|
||||
app.set_status(200, 'OK')
|
||||
app.add_header('DAV', '1,2')
|
||||
app.add_header('Allow', 'OPTIONS, PROPFIND, MKCOL, GET, HEAD, POST, PUT, DELETE, COPY, MOVE')
|
||||
app.add_header('MS-Author-Via', 'DAV')
|
||||
app.add_header('Access-Control-Allow-Origin', '*')
|
||||
app.add_header('Access-Control-Allow-Methods', 'OPTIONS, PROPFIND, MKCOL, GET, HEAD, POST, PUT, DELETE, COPY, MOVE')
|
||||
app.add_header('Access-Control-Allow-Headers', 'Authorization, Content-Type')
|
||||
app.send_response_to_client('text/plain', '')
|
||||
return vweb.not_found()
|
||||
}
|
||||
|
||||
// // depth := if app.get_header('Depth').len > 0 { app.get_header('Depth').int() } else { 0 }
|
||||
// // timeout := if app.get_header('Timeout').len > 0 { app.get_header('Timeout').int() } else { 3600 }
|
||||
@['/:path...'; LOCK]
|
||||
fn (mut app App) lock_handler(path string) vweb.Result {
|
||||
// Not yet working
|
||||
// TODO: Test with multiple clients
|
||||
resource := app.req.url
|
||||
owner := app.get_header('Owner')
|
||||
if owner.len == 0 {
|
||||
app.set_status(400, 'Bad Request')
|
||||
return app.text('Owner header is required.')
|
||||
}
|
||||
|
||||
// // token := app.lock_manager.lock(resource, owner, depth, timeout) or {
|
||||
// // app.set_status(423, 'Locked')
|
||||
// // return app.text('Resource is already locked.')
|
||||
// // }
|
||||
depth := if app.get_header('Depth').len > 0 { app.get_header('Depth').int() } else { 0 }
|
||||
timeout := if app.get_header('Timeout').len > 0 { app.get_header('Timeout').int() } else { 3600 }
|
||||
|
||||
// // app.set_status(200, 'OK')
|
||||
// // app.add_header('Lock-Token', token)
|
||||
// // return app.text('Lock granted with token: ${token}')
|
||||
// // }
|
||||
token := app.lock_manager.lock(resource, owner, depth, timeout) or {
|
||||
app.set_status(423, 'Locked')
|
||||
return app.text('Resource is already locked.')
|
||||
}
|
||||
|
||||
// // @['/:path...'; UNLOCK]
|
||||
// // fn (mut app App) unlock_handler(path string) vweb.Result {
|
||||
// // // Not yet working
|
||||
// // // TODO: Test with multiple clients
|
||||
// // resource := app.req.url
|
||||
// // token := app.get_header('Lock-Token')
|
||||
// // if token.len == 0 {
|
||||
// // console.print_stderr('Unlock failed: `Lock-Token` header required.')
|
||||
// // return app.bad_request('Unlock failed: `Lock-Token` header required.')
|
||||
// // }
|
||||
app.set_status(200, 'OK')
|
||||
app.add_header('Lock-Token', token)
|
||||
return app.text('Lock granted with token: ${token}')
|
||||
}
|
||||
|
||||
// // if app.lock_manager.unlock_with_token(resource, token) {
|
||||
// // app.set_status(204, 'No Content')
|
||||
// // return app.text('Lock successfully released')
|
||||
// // }
|
||||
@['/:path...'; UNLOCK]
|
||||
fn (mut app App) unlock_handler(path string) vweb.Result {
|
||||
// Not yet working
|
||||
// TODO: Test with multiple clients
|
||||
resource := app.req.url
|
||||
token := app.get_header('Lock-Token')
|
||||
if token.len == 0 {
|
||||
console.print_stderr('Unlock failed: `Lock-Token` header required.')
|
||||
app.set_status(400, 'Bad Request')
|
||||
return app.text('Lock failed: `Owner` header missing.')
|
||||
}
|
||||
|
||||
// // console.print_stderr('Resource is not locked or token mismatch.')
|
||||
// // app.set_status(409, 'Conflict')
|
||||
// // return app.text('Resource is not locked or token mismatch')
|
||||
// // }
|
||||
if app.lock_manager.unlock_with_token(resource, token) {
|
||||
app.set_status(204, 'No Content')
|
||||
return app.text('Lock successfully released')
|
||||
}
|
||||
|
||||
// @['/:path...'; get]
|
||||
// fn (mut app App) get_file(path string) vweb.Result {
|
||||
// mut file_path := pathlib.get_file(path: app.root_dir.path + path) or { return app.not_found() }
|
||||
// if !file_path.exists() {
|
||||
// return app.not_found()
|
||||
// }
|
||||
console.print_stderr('Resource is not locked or token mismatch.')
|
||||
app.set_status(409, 'Conflict')
|
||||
return app.text('Resource is not locked or token mismatch')
|
||||
}
|
||||
|
||||
// file_data := file_path.read() or {
|
||||
// console.print_stderr('failed to read file ${file_path.path}: ${err}')
|
||||
// return app.server_error()
|
||||
// }
|
||||
@['/:path...'; get]
|
||||
fn (mut app App) get_file(path string) vweb.Result {
|
||||
if !app.vfs.exists(path) {
|
||||
return app.not_found()
|
||||
}
|
||||
|
||||
// ext := os.file_ext(file_path.path)
|
||||
// content_type := if v := vweb.mime_types[ext] {
|
||||
// v
|
||||
// } else {
|
||||
// 'text/plain'
|
||||
// }
|
||||
fs_entry := app.vfs.get(path) or {
|
||||
console.print_stderr('failed to get FS Entry ${path}: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
// app.set_status(200, 'Ok')
|
||||
// app.send_response_to_client(content_type, file_data)
|
||||
file_data := app.vfs.file_read(fs_entry.get_path()) or { return app.server_error() }
|
||||
|
||||
// return vweb.not_found() // this is for returning a dummy result
|
||||
// }
|
||||
ext := fs_entry.get_metadata().name.all_after_last('.')
|
||||
content_type := if v := vweb.mime_types[ext] {
|
||||
v
|
||||
} else {
|
||||
'text/plain'
|
||||
}
|
||||
|
||||
// @['/:path...'; delete]
|
||||
// fn (mut app App) delete(path string) vweb.Result {
|
||||
// mut p := pathlib.get(app.root_dir.path + path)
|
||||
// if !p.exists() {
|
||||
// return app.not_found()
|
||||
// }
|
||||
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
|
||||
}
|
||||
|
||||
// if p.is_dir() {
|
||||
// console.print_debug('deleting directory: ${p.path}')
|
||||
// os.rmdir_all(p.path) or { return app.server_error() }
|
||||
// }
|
||||
@['/:path...'; delete]
|
||||
fn (mut app App) delete(path string) vweb.Result {
|
||||
if !app.vfs.exists(path) {
|
||||
return app.not_found()
|
||||
}
|
||||
|
||||
// if p.is_file() {
|
||||
// console.print_debug('deleting file: ${p.path}')
|
||||
// os.rm(p.path) or { return app.server_error() }
|
||||
// }
|
||||
fs_entry := app.vfs.get(path) or {
|
||||
console.print_stderr('failed to get FS Entry ${path}: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
// console.print_debug('entry: ${p.path} is deleted')
|
||||
// app.set_status(204, 'No Content')
|
||||
if fs_entry.is_dir() {
|
||||
console.print_debug('deleting directory: ${path}')
|
||||
app.vfs.dir_delete(path) or { return app.server_error() }
|
||||
}
|
||||
|
||||
// return app.text('entry ${p.path} is deleted')
|
||||
// }
|
||||
if fs_entry.is_file() {
|
||||
console.print_debug('deleting file: ${path}')
|
||||
app.vfs.file_delete(path) or { return app.server_error() }
|
||||
}
|
||||
|
||||
if fs_entry.is_symlink() {
|
||||
console.print_debug('deleting symlink: ${path}')
|
||||
app.vfs.link_delete(path) or { return app.server_error() }
|
||||
}
|
||||
|
||||
console.print_debug('entry: ${path} is deleted')
|
||||
app.set_status(204, 'No Content')
|
||||
return app.text('entry ${path} is deleted')
|
||||
}
|
||||
|
||||
// @['/:path...'; put]
|
||||
// fn (mut app App) create_or_update(path string) vweb.Result {
|
||||
// fs_entry := app.vfs.get(path) or {
|
||||
// console.print_stderr('failed to get FS Entry ${path}: ${err}')
|
||||
// return app.server_error()
|
||||
// }
|
||||
|
||||
// mut p := pathlib.get(app.root_dir.path + path)
|
||||
|
||||
// if p.is_dir() {
|
||||
@@ -124,136 +153,124 @@ module webdav
|
||||
// return app.text('HTTP 200: Successfully saved file: ${p.path}')
|
||||
// }
|
||||
|
||||
// @['/:path...'; copy]
|
||||
// fn (mut app App) copy(path string) vweb.Result {
|
||||
// mut p := pathlib.get(app.root_dir.path + path)
|
||||
// if !p.exists() {
|
||||
// return app.not_found()
|
||||
// }
|
||||
@['/:path...'; copy]
|
||||
fn (mut app App) copy(path string) vweb.Result {
|
||||
if !app.vfs.exists(path) {
|
||||
return app.not_found()
|
||||
}
|
||||
|
||||
// destination := app.get_header('Destination')
|
||||
// destination_url := urllib.parse(destination) or {
|
||||
// return app.bad_request('Invalid Destination ${destination}: ${err}')
|
||||
// }
|
||||
// destination_path_str := destination_url.path
|
||||
destination := app.get_header('Destination')
|
||||
destination_url := urllib.parse(destination) or {
|
||||
return app.bad_request('Invalid Destination ${destination}: ${err}')
|
||||
}
|
||||
destination_path_str := destination_url.path
|
||||
|
||||
// mut destination_path := pathlib.get(app.root_dir.path + destination_path_str)
|
||||
// if destination_path.exists() {
|
||||
// return app.bad_request('Destination ${destination_path.path} already exists')
|
||||
// }
|
||||
app.vfs.get(path) or {
|
||||
console.print_stderr('failed to get FS Entry ${path}: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
// os.cp_all(p.path, destination_path.path, false) or {
|
||||
// console.print_stderr('failed to copy: ${err}')
|
||||
// return app.server_error()
|
||||
// }
|
||||
app.vfs.copy(path, destination_path_str) or {
|
||||
console.print_stderr('failed to copy: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
// app.set_status(200, 'Successfully copied entry: ${p.path}')
|
||||
// return app.text('HTTP 200: Successfully copied entry: ${p.path}')
|
||||
app.set_status(200, 'Successfully copied entry: ${path}')
|
||||
return app.text('HTTP 200: Successfully copied entry: ${path}')
|
||||
}
|
||||
|
||||
@['/:path...'; move]
|
||||
fn (mut app App) move(path string) vweb.Result {
|
||||
if !app.vfs.exists(path) {
|
||||
return app.not_found()
|
||||
}
|
||||
|
||||
destination := app.get_header('Destination')
|
||||
destination_url := urllib.parse(destination) or {
|
||||
return app.bad_request('Invalid Destination ${destination}: ${err}')
|
||||
}
|
||||
destination_path_str := destination_url.path
|
||||
|
||||
app.vfs.move(path, destination_path_str) or {
|
||||
console.print_stderr('failed to move: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
app.set_status(200, 'Successfully moved entry: ${path}')
|
||||
return app.text('HTTP 200: Successfully moved entry: ${path}')
|
||||
}
|
||||
|
||||
@['/:path...'; mkcol]
|
||||
fn (mut app App) mkcol(path string) vweb.Result {
|
||||
if app.vfs.exists(path) {
|
||||
return app.bad_request('Another collection exists at ${path}')
|
||||
}
|
||||
|
||||
app.vfs.dir_create(path) or {
|
||||
console.print_stderr('failed to create directory ${path}: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
app.set_status(201, 'Created')
|
||||
return app.text('HTTP 201: Created')
|
||||
}
|
||||
|
||||
@['/:path...'; propfind]
|
||||
fn (mut app App) propfind(path string) vweb.Result {
|
||||
if !app.vfs.exists(path) {
|
||||
return app.not_found()
|
||||
}
|
||||
|
||||
depth := app.get_header('Depth').int()
|
||||
|
||||
responses := app.get_responses(path, depth) or {
|
||||
console.print_stderr('failed to get responses: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
doc := xml.XMLDocument{
|
||||
root: xml.XMLNode{
|
||||
name: 'D:multistatus'
|
||||
children: responses
|
||||
attributes: {
|
||||
'xmlns:D': 'DAV:'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := '<?xml version="1.0" encoding="UTF-8"?>${doc.pretty_str('').split('\n')[1..].join('')}'
|
||||
// println('res: ${res}')
|
||||
|
||||
app.set_status(207, 'Multi-Status')
|
||||
app.send_response_to_client('application/xml', res)
|
||||
return vweb.not_found()
|
||||
}
|
||||
|
||||
fn (mut app App) generate_resource_response(path string) string {
|
||||
mut response := ''
|
||||
response += app.generate_element('response', 2)
|
||||
response += app.generate_element('href', 4)
|
||||
response += app.generate_element('/href', 4)
|
||||
response += app.generate_element('/response', 2)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
fn (mut app App) generate_element(element string, space_cnt int) string {
|
||||
mut spaces := ''
|
||||
for i := 0; i < space_cnt; i++ {
|
||||
spaces += ' '
|
||||
}
|
||||
|
||||
return '${spaces}<${element}>\n'
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
// @['/'; proppatch]
|
||||
// fn (mut app App) prop_patch() vweb.Result {
|
||||
// }
|
||||
|
||||
// @['/:path...'; move]
|
||||
// fn (mut app App) move(path string) vweb.Result {
|
||||
// mut p := pathlib.get(app.root_dir.path + path)
|
||||
// if !p.exists() {
|
||||
// return app.not_found()
|
||||
// }
|
||||
|
||||
// destination := app.get_header('Destination')
|
||||
// destination_url := urllib.parse(destination) or {
|
||||
// return app.bad_request('Invalid Destination ${destination}: ${err}')
|
||||
// }
|
||||
// destination_path_str := destination_url.path
|
||||
|
||||
// mut destination_path := pathlib.get(app.root_dir.path + destination_path_str)
|
||||
// if destination_path.exists() {
|
||||
// return app.bad_request('Destination ${destination_path.path} already exists')
|
||||
// }
|
||||
|
||||
// os.mv(p.path, destination_path.path) or {
|
||||
// console.print_stderr('failed to copy: ${err}')
|
||||
// return app.server_error()
|
||||
// }
|
||||
|
||||
// app.set_status(200, 'Successfully moved entry: ${p.path}')
|
||||
// return app.text('HTTP 200: Successfully moved entry: ${p.path}')
|
||||
// TODO: implement, now it's used with PUT
|
||||
// @['/'; post]
|
||||
// fn (mut app App) post() vweb.Result {
|
||||
// }
|
||||
|
||||
// @['/:path...'; mkcol]
|
||||
// fn (mut app App) mkcol(path string) vweb.Result {
|
||||
// mut p := pathlib.get(app.root_dir.path + path)
|
||||
// if p.exists() {
|
||||
// return app.bad_request('Another collection exists at ${p.path}')
|
||||
// }
|
||||
|
||||
// p = pathlib.get_dir(path: p.path, create: true) or {
|
||||
// console.print_stderr('failed to create directory ${p.path}: ${err}')
|
||||
// return app.server_error()
|
||||
// }
|
||||
|
||||
// app.set_status(201, 'Created')
|
||||
// return app.text('HTTP 201: Created')
|
||||
// }
|
||||
|
||||
// @['/:path...'; options]
|
||||
// fn (mut app App) options(path string) vweb.Result {
|
||||
// app.set_status(200, 'OK')
|
||||
// app.add_header('DAV', '1,2')
|
||||
// app.add_header('Allow', 'OPTIONS, PROPFIND, MKCOL, GET, HEAD, POST, PUT, DELETE, COPY, MOVE')
|
||||
// app.add_header('MS-Author-Via', 'DAV')
|
||||
// app.add_header('Access-Control-Allow-Origin', '*')
|
||||
// app.add_header('Access-Control-Allow-Methods', 'OPTIONS, PROPFIND, MKCOL, GET, HEAD, POST, PUT, DELETE, COPY, MOVE')
|
||||
// app.add_header('Access-Control-Allow-Headers', 'Authorization, Content-Type')
|
||||
// app.send_response_to_client('text/plain', '')
|
||||
// return vweb.not_found()
|
||||
// }
|
||||
|
||||
// @['/:path...'; propfind]
|
||||
// fn (mut app App) propfind(path string) vweb.Result {
|
||||
// mut p := pathlib.get(app.root_dir.path + path)
|
||||
// if !p.exists() {
|
||||
// return app.not_found()
|
||||
// }
|
||||
|
||||
// depth := app.get_header('Depth').int()
|
||||
|
||||
// responses := app.get_responses(p.path, depth) or {
|
||||
// console.print_stderr('failed to get responses: ${err}')
|
||||
// return app.server_error()
|
||||
// }
|
||||
|
||||
// doc := xml.XMLDocument{
|
||||
// root: xml.XMLNode{
|
||||
// name: 'D:multistatus'
|
||||
// children: responses
|
||||
// attributes: {
|
||||
// 'xmlns:D': 'DAV:'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// res := '<?xml version="1.0" encoding="UTF-8"?>${doc.pretty_str('').split('\n')[1..].join('')}'
|
||||
// // println('res: ${res}')
|
||||
|
||||
// app.set_status(207, 'Multi-Status')
|
||||
// app.send_response_to_client('application/xml', res)
|
||||
// return vweb.not_found()
|
||||
// }
|
||||
|
||||
// fn (mut app App) generate_element(element string, space_cnt int) string {
|
||||
// mut spaces := ''
|
||||
// for i := 0; i < space_cnt; i++ {
|
||||
// spaces += ' '
|
||||
// }
|
||||
|
||||
// return '${spaces}<${element}>\n'
|
||||
// }
|
||||
|
||||
// // TODO: implement
|
||||
// // @['/'; proppatch]
|
||||
// // fn (mut app App) prop_patch() vweb.Result {
|
||||
// // }
|
||||
|
||||
// // TODO: implement, now it's used with PUT
|
||||
// // @['/'; post]
|
||||
// // fn (mut app App) post() vweb.Result {
|
||||
// // }
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
module webdav
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
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 {
|
||||
if !app.vfs.exists(path) {
|
||||
return app.not_found()
|
||||
}
|
||||
|
||||
fs_entry := app.vfs.get(path) or {
|
||||
console.print_stderr('failed to get FS Entry ${path}: ${err}')
|
||||
return app.server_error()
|
||||
}
|
||||
|
||||
file_data := app.vfs.file_read(fs_entry.get_path()) or { return app.server_error() }
|
||||
|
||||
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.str())
|
||||
return vweb.not_found() // this is for returning a dummy result
|
||||
}
|
||||
Reference in New Issue
Block a user