feat: Refactor WebDAV server to use VFS core
- Replace custom VFS implementations with the core VFS module - Simplify VFS setup and configuration in example code - Improve code maintainability and consistency
This commit is contained in:
20
examples/vfs/example.vsh
Normal file → Executable file
20
examples/vfs/example.vsh
Normal file → Executable file
@@ -3,22 +3,14 @@
|
|||||||
import freeflowuniverse.herolib.vfs.webdav
|
import freeflowuniverse.herolib.vfs.webdav
|
||||||
import freeflowuniverse.herolib.vfs.vfsnested
|
import freeflowuniverse.herolib.vfs.vfsnested
|
||||||
import freeflowuniverse.herolib.vfs.ourdb_fs
|
import freeflowuniverse.herolib.vfs.ourdb_fs
|
||||||
|
import freeflowuniverse.herolib.vfs.vfscore
|
||||||
|
|
||||||
high_level_vfs := vfsnested.new()
|
mut high_level_vfs := vfsnested.new()
|
||||||
|
|
||||||
// lower level VFS Implementations that use OurDB
|
// lower level VFS Implementations that use OurDB
|
||||||
mut vfs1 := ourdb_fs.new(
|
mut vfs1 := vfscore.new_local_vfs('/tmp/test_webdav_ourdbvfs/vfs1')!
|
||||||
data_dir: '/tmp/test_webdav_ourdbvfs/vfs1'
|
mut vfs2 := vfscore.new_local_vfs('/tmp/test_webdav_ourdbvfs/vfs2')!
|
||||||
metadata_dir: '/tmp/test_webdav_ourdbvfs/vfs1'
|
mut vfs3 := vfscore.new_local_vfs('/tmp/test_webdav_ourdbvfs/vfs3')!
|
||||||
)!
|
|
||||||
mut vfs2 := ourdb_fs.new(
|
|
||||||
data_dir: '/tmp/test_webdav_ourdbvfs/vfs2'
|
|
||||||
metadata_dir: '/tmp/test_webdav_ourdbvfs/vfs2'
|
|
||||||
)!
|
|
||||||
mut vfs3 := ourdb_fs.new(
|
|
||||||
data_dir: '/tmp/test_webdav_ourdbvfs/vfs3'
|
|
||||||
metadata_dir: '/tmp/test_webdav_ourdbvfs/vfs3'
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Nest OurDB VFS instances at different paths
|
// Nest OurDB VFS instances at different paths
|
||||||
high_level_vfs.add_vfs('/data', vfs1) or { panic(err) }
|
high_level_vfs.add_vfs('/data', vfs1) or { panic(err) }
|
||||||
@@ -28,4 +20,4 @@ high_level_vfs.add_vfs('/data/backup', vfs3) or { panic(err) } // Nested under /
|
|||||||
// Create WebDAV Server that uses high level VFS
|
// Create WebDAV Server that uses high level VFS
|
||||||
webdav_server := webdav.new_app(
|
webdav_server := webdav.new_app(
|
||||||
vfs: high_level_vfs
|
vfs: high_level_vfs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct App {
|
|||||||
root_dir pathlib.Path @[vweb_global]
|
root_dir pathlib.Path @[vweb_global]
|
||||||
pub mut:
|
pub mut:
|
||||||
// lock_manager LockManager
|
// lock_manager LockManager
|
||||||
vfs VFSImplementation
|
vfs vfscore.VFSImplementation
|
||||||
server_port int
|
server_port int
|
||||||
middlewares map[string][]vweb.Middleware
|
middlewares map[string][]vweb.Middleware
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ pub mut:
|
|||||||
server_port int = 8080
|
server_port int = 8080
|
||||||
root_dir string @[required]
|
root_dir string @[required]
|
||||||
user_db map[string]string @[required]
|
user_db map[string]string @[required]
|
||||||
vfs VFSImplementation
|
vfs vfscore.VFSImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_app(args AppArgs) !&App {
|
pub fn new_app(args AppArgs) !&App {
|
||||||
@@ -32,7 +32,7 @@ pub fn new_app(args AppArgs) !&App {
|
|||||||
user_db: args.user_db.clone()
|
user_db: args.user_db.clone()
|
||||||
root_dir: root_dir
|
root_dir: root_dir
|
||||||
server_port: args.server_port
|
server_port: args.server_port
|
||||||
vfs: args.vfs
|
vfs: args.vfs
|
||||||
}
|
}
|
||||||
|
|
||||||
app.middlewares['/'] << logging_middleware
|
app.middlewares['/'] << logging_middleware
|
||||||
|
|||||||
@@ -1,259 +1,259 @@
|
|||||||
module webdav
|
module webdav
|
||||||
|
|
||||||
import vweb
|
// import vweb
|
||||||
import os
|
// import os
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
// import freeflowuniverse.herolib.core.pathlib
|
||||||
import encoding.xml
|
// import encoding.xml
|
||||||
import freeflowuniverse.herolib.ui.console
|
// import freeflowuniverse.herolib.ui.console
|
||||||
import net.urllib
|
// import net.urllib
|
||||||
|
|
||||||
// @['/:path...'; LOCK]
|
// // @['/:path...'; LOCK]
|
||||||
// fn (mut app App) lock_handler(path string) vweb.Result {
|
// // fn (mut app App) lock_handler(path string) vweb.Result {
|
||||||
// // Not yet working
|
// // // Not yet working
|
||||||
// // TODO: Test with multiple clients
|
// // // TODO: Test with multiple clients
|
||||||
// resource := app.req.url
|
// // resource := app.req.url
|
||||||
// owner := app.get_header('Owner')
|
// // owner := app.get_header('Owner')
|
||||||
// if owner.len == 0 {
|
// // if owner.len == 0 {
|
||||||
// return app.bad_request('Owner header is required.')
|
// // return app.bad_request('Owner header is required.')
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // 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 }
|
||||||
|
|
||||||
|
// // token := app.lock_manager.lock(resource, owner, depth, timeout) or {
|
||||||
|
// // app.set_status(423, 'Locked')
|
||||||
|
// // return app.text('Resource is already locked.')
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // app.set_status(200, 'OK')
|
||||||
|
// // app.add_header('Lock-Token', token)
|
||||||
|
// // return app.text('Lock granted with token: ${token}')
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // @['/: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.')
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // if app.lock_manager.unlock_with_token(resource, token) {
|
||||||
|
// // app.set_status(204, 'No Content')
|
||||||
|
// // return app.text('Lock successfully released')
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // 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')
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// @['/: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()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// depth := if app.get_header('Depth').len > 0 { app.get_header('Depth').int() } else { 0 }
|
// file_data := file_path.read() or {
|
||||||
// timeout := if app.get_header('Timeout').len > 0 { app.get_header('Timeout').int() } else { 3600 }
|
// console.print_stderr('failed to read file ${file_path.path}: ${err}')
|
||||||
|
// return app.server_error()
|
||||||
// token := app.lock_manager.lock(resource, owner, depth, timeout) or {
|
|
||||||
// app.set_status(423, 'Locked')
|
|
||||||
// return app.text('Resource is already locked.')
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// ext := os.file_ext(file_path.path)
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// return vweb.not_found() // this is for returning a dummy result
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @['/: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()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if p.is_dir() {
|
||||||
|
// console.print_debug('deleting directory: ${p.path}')
|
||||||
|
// os.rmdir_all(p.path) or { return app.server_error() }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if p.is_file() {
|
||||||
|
// console.print_debug('deleting file: ${p.path}')
|
||||||
|
// os.rm(p.path) or { return app.server_error() }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.print_debug('entry: ${p.path} is deleted')
|
||||||
|
// app.set_status(204, 'No Content')
|
||||||
|
|
||||||
|
// return app.text('entry ${p.path} is deleted')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @['/:path...'; put]
|
||||||
|
// fn (mut app App) create_or_update(path string) vweb.Result {
|
||||||
|
// mut p := pathlib.get(app.root_dir.path + path)
|
||||||
|
|
||||||
|
// if p.is_dir() {
|
||||||
|
// console.print_stderr('Cannot PUT to a directory: ${p.path}')
|
||||||
|
// app.set_status(405, 'Method Not Allowed')
|
||||||
|
// return app.text('HTTP 405: Method Not Allowed')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// file_data := app.req.data
|
||||||
|
// p = pathlib.get_file(path: p.path, create: true) or {
|
||||||
|
// console.print_stderr('failed to get file ${p.path}: ${err}')
|
||||||
|
// return app.server_error()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// p.write(file_data) or {
|
||||||
|
// console.print_stderr('failed to write file data ${p.path}: ${err}')
|
||||||
|
// return app.server_error()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// app.set_status(200, 'Successfully saved file: ${p.path}')
|
||||||
|
// 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()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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.cp_all(p.path, destination_path.path, false) 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}')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @['/: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}')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @['/: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.set_status(200, 'OK')
|
||||||
// app.add_header('Lock-Token', token)
|
// app.add_header('DAV', '1,2')
|
||||||
// return app.text('Lock granted with token: ${token}')
|
// 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...'; UNLOCK]
|
// @['/:path...'; propfind]
|
||||||
// fn (mut app App) unlock_handler(path string) vweb.Result {
|
// fn (mut app App) propfind(path string) vweb.Result {
|
||||||
// // Not yet working
|
// mut p := pathlib.get(app.root_dir.path + path)
|
||||||
// // TODO: Test with multiple clients
|
// if !p.exists() {
|
||||||
// resource := app.req.url
|
// return app.not_found()
|
||||||
// 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.')
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if app.lock_manager.unlock_with_token(resource, token) {
|
// depth := app.get_header('Depth').int()
|
||||||
// app.set_status(204, 'No Content')
|
|
||||||
// return app.text('Lock successfully released')
|
// responses := app.get_responses(p.path, depth) or {
|
||||||
|
// console.print_stderr('failed to get responses: ${err}')
|
||||||
|
// return app.server_error()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// console.print_stderr('Resource is not locked or token mismatch.')
|
// doc := xml.XMLDocument{
|
||||||
// app.set_status(409, 'Conflict')
|
// root: xml.XMLNode{
|
||||||
// return app.text('Resource is not locked or token mismatch')
|
// 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()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@['/:path...'; get]
|
// fn (mut app App) generate_element(element string, space_cnt int) string {
|
||||||
fn (mut app App) get_file(path string) vweb.Result {
|
// mut spaces := ''
|
||||||
mut file_path := pathlib.get_file(path: app.root_dir.path + path) or { return app.not_found() }
|
// for i := 0; i < space_cnt; i++ {
|
||||||
if !file_path.exists() {
|
// spaces += ' '
|
||||||
return app.not_found()
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
file_data := file_path.read() or {
|
// return '${spaces}<${element}>\n'
|
||||||
console.print_stderr('failed to read file ${file_path.path}: ${err}')
|
|
||||||
return app.server_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
ext := os.file_ext(file_path.path)
|
|
||||||
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)
|
|
||||||
|
|
||||||
return vweb.not_found() // this is for returning a dummy result
|
|
||||||
}
|
|
||||||
|
|
||||||
@['/: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()
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.is_dir() {
|
|
||||||
console.print_debug('deleting directory: ${p.path}')
|
|
||||||
os.rmdir_all(p.path) or { return app.server_error() }
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.is_file() {
|
|
||||||
console.print_debug('deleting file: ${p.path}')
|
|
||||||
os.rm(p.path) or { return app.server_error() }
|
|
||||||
}
|
|
||||||
|
|
||||||
console.print_debug('entry: ${p.path} is deleted')
|
|
||||||
app.set_status(204, 'No Content')
|
|
||||||
|
|
||||||
return app.text('entry ${p.path} is deleted')
|
|
||||||
}
|
|
||||||
|
|
||||||
@['/:path...'; put]
|
|
||||||
fn (mut app App) create_or_update(path string) vweb.Result {
|
|
||||||
mut p := pathlib.get(app.root_dir.path + path)
|
|
||||||
|
|
||||||
if p.is_dir() {
|
|
||||||
console.print_stderr('Cannot PUT to a directory: ${p.path}')
|
|
||||||
app.set_status(405, 'Method Not Allowed')
|
|
||||||
return app.text('HTTP 405: Method Not Allowed')
|
|
||||||
}
|
|
||||||
|
|
||||||
file_data := app.req.data
|
|
||||||
p = pathlib.get_file(path: p.path, create: true) or {
|
|
||||||
console.print_stderr('failed to get file ${p.path}: ${err}')
|
|
||||||
return app.server_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
p.write(file_data) or {
|
|
||||||
console.print_stderr('failed to write file data ${p.path}: ${err}')
|
|
||||||
return app.server_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
app.set_status(200, 'Successfully saved file: ${p.path}')
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
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.cp_all(p.path, destination_path.path, false) 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}')
|
|
||||||
}
|
|
||||||
|
|
||||||
@['/: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}')
|
|
||||||
}
|
|
||||||
|
|
||||||
@['/: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
|
// // TODO: implement
|
||||||
// @['/'; post]
|
// // @['/'; proppatch]
|
||||||
// fn (mut app App) post() vweb.Result {
|
// // fn (mut app App) prop_patch() vweb.Result {
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
|
// // TODO: implement, now it's used with PUT
|
||||||
|
// // @['/'; post]
|
||||||
|
// // fn (mut app App) post() vweb.Result {
|
||||||
|
// // }
|
||||||
|
|||||||
@@ -18,16 +18,18 @@ fn (mut app App) get_file(path string) vweb.Result {
|
|||||||
return app.server_error()
|
return app.server_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
file_data := app.vfs.file_read(fs_entry.path)
|
println('fs_entry: ${fs_entry}')
|
||||||
|
|
||||||
ext := fs_entry.get_metadata().name.all_after_last('.')
|
// file_data := app.vfs.file_read(fs_entry.path)
|
||||||
content_type := if v := vweb.mime_types[ext] {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
'text/plain'
|
|
||||||
}
|
|
||||||
|
|
||||||
app.set_status(200, 'Ok')
|
// ext := fs_entry.get_metadata().name.all_after_last('.')
|
||||||
app.send_response_to_client(content_type, file_data)
|
// 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)
|
||||||
return vweb.not_found() // this is for returning a dummy result
|
return vweb.not_found() // this is for returning a dummy result
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user