@filebox/webdav-server 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +621 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.mjs +591 -0
- package/package.json +17 -9
- package/context.js +0 -37
- package/index.js +0 -88
- package/operations/commands.js +0 -31
- package/operations/copy.js +0 -37
- package/operations/delete.js +0 -12
- package/operations/get.js +0 -34
- package/operations/head.js +0 -23
- package/operations/lock.js +0 -1
- package/operations/mkcol.js +0 -14
- package/operations/move.js +0 -46
- package/operations/not-implemented.js +0 -10
- package/operations/options.js +0 -17
- package/operations/post.js +0 -10
- package/operations/propfind.js +0 -323
- package/operations/proppatch.js +0 -5
- package/operations/put.js +0 -66
- package/operations/shared.js +0 -26
- package/operations/unlock.js +0 -1
package/index.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
const Commands = require("./operations/commands");
|
|
2
|
-
const createContext = require("./context");
|
|
3
|
-
|
|
4
|
-
const StatusCodes = {
|
|
5
|
-
200: "OK",
|
|
6
|
-
201: "Created",
|
|
7
|
-
204: "No Content",
|
|
8
|
-
207: "Multi Status",
|
|
9
|
-
302: "Moved Temporarily",
|
|
10
|
-
401: "Unauthorized",
|
|
11
|
-
403: "Forbidden",
|
|
12
|
-
404: "Not Found",
|
|
13
|
-
409: "Conflict",
|
|
14
|
-
423: "Locked",
|
|
15
|
-
500: "Internal Server Error",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const VirtualDriver = {
|
|
19
|
-
async get() {
|
|
20
|
-
return {
|
|
21
|
-
status: "200",
|
|
22
|
-
body: "Hello World!",
|
|
23
|
-
headers: {
|
|
24
|
-
"Content-Type": "application/json",
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
},
|
|
28
|
-
// 其他方法...
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
class WebDAVServer {
|
|
32
|
-
constructor(
|
|
33
|
-
{ driver, base, redirect, auth } = { redirect: false, auth: () => true },
|
|
34
|
-
) {
|
|
35
|
-
this.methods = {};
|
|
36
|
-
this.driver = driver || VirtualDriver;
|
|
37
|
-
this.base = base || "";
|
|
38
|
-
this.auth = auth;
|
|
39
|
-
this.config = { redirect };
|
|
40
|
-
const commands = Commands;
|
|
41
|
-
for (const k in commands) {
|
|
42
|
-
if (k === "NotImplemented") {
|
|
43
|
-
this.unknownMethod = commands[k];
|
|
44
|
-
} else {
|
|
45
|
-
this.methods[k.toLowerCase()] = commands[k];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
this.allows = Object.keys(this.methods).map((i) => i.toUpperCase());
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async request(req, options) {
|
|
52
|
-
const ctx = createContext(
|
|
53
|
-
req,
|
|
54
|
-
options?.base || this.base,
|
|
55
|
-
this.allows,
|
|
56
|
-
options.temp,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
!(ctx.method == "options" && !ctx.path) &&
|
|
61
|
-
!this?.auth(ctx.auth.user, ctx.auth.pass)
|
|
62
|
-
) {
|
|
63
|
-
return {
|
|
64
|
-
headers: {
|
|
65
|
-
"X-WebDAV-Status": `401 ${StatusCodes[401]}`,
|
|
66
|
-
"www-Authenticate": 'Basic realm="Restricted"',
|
|
67
|
-
},
|
|
68
|
-
status: "401",
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ctx.driver = this.driver;
|
|
73
|
-
ctx.driver = options.driver;
|
|
74
|
-
ctx.config = { ...this.config, temp: options.temp };
|
|
75
|
-
const method = this.methods[ctx.method] || this.unknownMethod;
|
|
76
|
-
const res = await method(ctx);
|
|
77
|
-
res.headers = res.headers ?? {};
|
|
78
|
-
if (res.status) {
|
|
79
|
-
// res.headers["X-WebDAV-Status"] =
|
|
80
|
-
// res.status + " " + StatusCodes[res.status];
|
|
81
|
-
}
|
|
82
|
-
return res;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
module.exports = {
|
|
87
|
-
WebDAVServer,
|
|
88
|
-
};
|
package/operations/commands.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const Get = require("./get");
|
|
2
|
-
const Put = require("./put");
|
|
3
|
-
const Post = require("./post");
|
|
4
|
-
const Head = require("./head");
|
|
5
|
-
const Move = require("./move");
|
|
6
|
-
const Lock = require("./lock");
|
|
7
|
-
const Copy = require("./copy");
|
|
8
|
-
const Mkcol = require("./mkcol");
|
|
9
|
-
const Unlock = require("./unlock");
|
|
10
|
-
const Delete = require("./delete");
|
|
11
|
-
const Options = require("./options");
|
|
12
|
-
const Propfind = require("./propfind");
|
|
13
|
-
const Proppatch = require("./proppatch");
|
|
14
|
-
const NotImplemented = require("./not-implemented");
|
|
15
|
-
|
|
16
|
-
module.exports = {
|
|
17
|
-
NotImplemented,
|
|
18
|
-
Proppatch,
|
|
19
|
-
Propfind,
|
|
20
|
-
Options,
|
|
21
|
-
Delete,
|
|
22
|
-
// Unlock,
|
|
23
|
-
Mkcol,
|
|
24
|
-
Copy,
|
|
25
|
-
// Lock,
|
|
26
|
-
Move,
|
|
27
|
-
Head,
|
|
28
|
-
// Post,
|
|
29
|
-
Put,
|
|
30
|
-
Get,
|
|
31
|
-
};
|
package/operations/copy.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
module.exports = async (ctx) => {
|
|
2
|
-
try {
|
|
3
|
-
const dst = new URL(ctx.req.headers?.destination).pathname.replace(
|
|
4
|
-
ctx.base,
|
|
5
|
-
"",
|
|
6
|
-
);
|
|
7
|
-
const src = ctx.path;
|
|
8
|
-
if (src === dst) {
|
|
9
|
-
return { status: "403" };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const stat = await ctx.driver.stat(src);
|
|
13
|
-
if (!stat || !stat.file) {
|
|
14
|
-
return { status: "404" };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const data = await ctx.driver.download(src);
|
|
18
|
-
if (!data || !data.length) {
|
|
19
|
-
return { status: "404" };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const { basename, dirname } = require("pathe");
|
|
23
|
-
const upath = dirname(dst);
|
|
24
|
-
const name = basename(dst);
|
|
25
|
-
const resp = await fetch(data[0].url);
|
|
26
|
-
await ctx.driver.upload(upath, {
|
|
27
|
-
name,
|
|
28
|
-
size: parseInt(resp.headers.get("content-length") || "0"),
|
|
29
|
-
data: resp.body,
|
|
30
|
-
mimeType: stat.mime || "application/octet-stream",
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return { status: "201" };
|
|
34
|
-
} catch (_) {
|
|
35
|
-
return { status: "502" };
|
|
36
|
-
}
|
|
37
|
-
};
|
package/operations/delete.js
DELETED
package/operations/get.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
async function loadProxy() {
|
|
2
|
-
const proxy = await import("@filebox/proxy");
|
|
3
|
-
return proxy.default || proxy;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
module.exports = async (ctx) => {
|
|
7
|
-
try {
|
|
8
|
-
const decodedPath = decodeURIComponent(ctx.path);
|
|
9
|
-
const stat = await ctx.driver.stat(decodedPath);
|
|
10
|
-
if (!stat || !stat.file) {
|
|
11
|
-
return {
|
|
12
|
-
status: "405",
|
|
13
|
-
body: "405 Method not allowed",
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
const data = await ctx.driver.download(decodedPath);
|
|
17
|
-
if (!data || !data.length || !data[0].url) {
|
|
18
|
-
return {
|
|
19
|
-
status: "404",
|
|
20
|
-
body: "file not found",
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
const proxy = await loadProxy();
|
|
24
|
-
return proxy(ctx.req, {
|
|
25
|
-
...data[0],
|
|
26
|
-
fileName: decodedPath,
|
|
27
|
-
});
|
|
28
|
-
} catch (_) {
|
|
29
|
-
return {
|
|
30
|
-
status: "500",
|
|
31
|
-
body: "Internal Server Error",
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
};
|
package/operations/head.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
module.exports = async (ctx) => {
|
|
2
|
-
try {
|
|
3
|
-
console.log(ctx.path, "head", 22222);
|
|
4
|
-
const stat = ctx.driver.stat(ctx.path);
|
|
5
|
-
const headers = {
|
|
6
|
-
"Content-Type": "application/octet-stream",
|
|
7
|
-
"Content-Length": stat.size,
|
|
8
|
-
"Last-Modified": stat.mtime,
|
|
9
|
-
"Accept-Ranges": "bytes",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
status: "200",
|
|
14
|
-
headers: headers,
|
|
15
|
-
body: null,
|
|
16
|
-
};
|
|
17
|
-
} catch (error) {
|
|
18
|
-
console.log(error);
|
|
19
|
-
return {
|
|
20
|
-
status: "404",
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
};
|
package/operations/lock.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = {};
|
package/operations/mkcol.js
DELETED
package/operations/move.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const { basename, dirname } = require("pathe");
|
|
2
|
-
|
|
3
|
-
module.exports = async (ctx) => {
|
|
4
|
-
try {
|
|
5
|
-
const dst = new URL(ctx.req.headers?.destination).pathname.replace(
|
|
6
|
-
ctx.base,
|
|
7
|
-
"",
|
|
8
|
-
);
|
|
9
|
-
const src = ctx.path;
|
|
10
|
-
|
|
11
|
-
if (!dst) {
|
|
12
|
-
return {
|
|
13
|
-
status: "400",
|
|
14
|
-
body: "Missing source or destination URI",
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const normalizedSrc = src.endsWith("/") ? src.slice(0, -1) : src;
|
|
19
|
-
const normalizedDst = dst.endsWith("/") ? dst.slice(0, -1) : dst;
|
|
20
|
-
|
|
21
|
-
if (normalizedSrc === normalizedDst) {
|
|
22
|
-
return { status: "403" };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const isRename =
|
|
26
|
-
normalizedSrc.substring(0, normalizedSrc.lastIndexOf("/")) ===
|
|
27
|
-
normalizedDst.substring(0, normalizedDst.lastIndexOf("/"));
|
|
28
|
-
|
|
29
|
-
if (isRename) {
|
|
30
|
-
// 处理重命名
|
|
31
|
-
await ctx.driver?.rename(normalizedSrc, basename(normalizedDst));
|
|
32
|
-
} else {
|
|
33
|
-
// 处理移动
|
|
34
|
-
await ctx.driver?.move(normalizedSrc, dirname(normalizedDst));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
status: "201",
|
|
39
|
-
};
|
|
40
|
-
} catch (_) {
|
|
41
|
-
return {
|
|
42
|
-
status: "502",
|
|
43
|
-
body: null,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
};
|
package/operations/options.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module.exports = async (ctx) => {
|
|
2
|
-
const dav = [1];
|
|
3
|
-
|
|
4
|
-
if (ctx.allows?.includes("LOCK")) {
|
|
5
|
-
dav.push(2);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
headers: {
|
|
10
|
-
// For Microsoft clients
|
|
11
|
-
"MS-Author-Via": "DAV",
|
|
12
|
-
DAV: dav.join(", "),
|
|
13
|
-
Allow: ctx.allows?.join(", ") || "",
|
|
14
|
-
},
|
|
15
|
-
status: "200",
|
|
16
|
-
};
|
|
17
|
-
};
|
package/operations/post.js
DELETED
package/operations/propfind.js
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
const parseXML = require("./shared");
|
|
2
|
-
const xml2js = require("xml2js");
|
|
3
|
-
|
|
4
|
-
const DEFAULT_PROPS = [
|
|
5
|
-
"displayname",
|
|
6
|
-
"getcontentlength",
|
|
7
|
-
"resourcetype",
|
|
8
|
-
"getcontenttype",
|
|
9
|
-
"creationdate",
|
|
10
|
-
"getlastmodified",
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Parse props from webdav request
|
|
15
|
-
*
|
|
16
|
-
* @param {object} [data]
|
|
17
|
-
* @return {object|boolean}
|
|
18
|
-
*/
|
|
19
|
-
// const propParse = (data) => {
|
|
20
|
-
// if (!data) return {
|
|
21
|
-
// ns: { prefix: 'D', uri: 'DAV:' },
|
|
22
|
-
// prop: [...DEFAULT_PROPS]
|
|
23
|
-
// }
|
|
24
|
-
// let prop = [...DEFAULT_PROPS]
|
|
25
|
-
// const prefix = Object.keys(data.propfind.$).find(i => i.startsWith('xmlns:'))?.split(':')[1] || ''
|
|
26
|
-
// const uri = data.propfind.$?.[`xmlns${prefix ? `:${prefix}` : ''}`] || ''
|
|
27
|
-
// if (data.propfind.hasOwnProperty('prop')) {
|
|
28
|
-
// prop = Object.keys(data.propfind.prop)
|
|
29
|
-
// }
|
|
30
|
-
// return { ns: { prefix, uri }, prop }
|
|
31
|
-
// }
|
|
32
|
-
|
|
33
|
-
const propParse = (data) => {
|
|
34
|
-
if (!data) {
|
|
35
|
-
return {
|
|
36
|
-
ns: { prefix: "D", uri: "DAV:" },
|
|
37
|
-
prop: [...DEFAULT_PROPS],
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let prop = [...DEFAULT_PROPS];
|
|
42
|
-
const ns = { prefix: "D", uri: "DAV:" };
|
|
43
|
-
|
|
44
|
-
if (data.propfind.hasOwnProperty("prop")) {
|
|
45
|
-
if (data.propfind.prop.hasOwnProperty("allprop")) {
|
|
46
|
-
return {
|
|
47
|
-
ns,
|
|
48
|
-
prop: [...DEFAULT_PROPS],
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
prop = Object.keys(data.propfind.prop);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return { ns, prop };
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Create webdav responese xml by data and props options
|
|
60
|
-
*
|
|
61
|
-
* @param {object} [data] file data
|
|
62
|
-
* @param {object} [options]
|
|
63
|
-
* @param {object} [options.props] Available props
|
|
64
|
-
* @param {object} [options.path] Current folder path
|
|
65
|
-
* @param {object} [options.ns]
|
|
66
|
-
* @return {string} XML string
|
|
67
|
-
*/
|
|
68
|
-
|
|
69
|
-
// const convData = (files, options) => {
|
|
70
|
-
// const {
|
|
71
|
-
// path,
|
|
72
|
-
// base = "",
|
|
73
|
-
// depth,
|
|
74
|
-
// prop,
|
|
75
|
-
// ns: { prefix, uri },
|
|
76
|
-
// } = options;
|
|
77
|
-
// console.log(options, 2222);
|
|
78
|
-
// console.log(files, 222);
|
|
79
|
-
// return files.map((file) => {
|
|
80
|
-
// const item = {};
|
|
81
|
-
// for (const key of prop) {
|
|
82
|
-
// item[key] = file.name
|
|
83
|
-
// .replace(/&/g, "&")
|
|
84
|
-
// .replace(/</g, "<")
|
|
85
|
-
// .replace(/>/g, ">")
|
|
86
|
-
// .replace(/"/g, """)
|
|
87
|
-
// .replace(/'/g, "'");
|
|
88
|
-
|
|
89
|
-
// if (key == "getcontentlength") {
|
|
90
|
-
// item[key] = parseInt(file.size || 0);
|
|
91
|
-
// } else if (key == "resourcetype") {
|
|
92
|
-
// item[key] = file.type == "folder" ? { collection: "" } : "";
|
|
93
|
-
// // } else if (key == 'getcontenttype') {
|
|
94
|
-
// // item[key] = file.mime
|
|
95
|
-
// } else if (key == "creationdate" && file.ctime) {
|
|
96
|
-
// item[key] = new Date(file.ctime).toUTCString();
|
|
97
|
-
// } else if (key == "getlastmodified" && file.lastModifiedDateTime) {
|
|
98
|
-
// item[key] = new Date(file.lastModifiedDateTime).toUTCString();
|
|
99
|
-
// }
|
|
100
|
-
// }
|
|
101
|
-
|
|
102
|
-
// const href = (
|
|
103
|
-
// base +
|
|
104
|
-
// path +
|
|
105
|
-
// (depth == "0" ? "" : "/" + encodeURIComponent(file.name))
|
|
106
|
-
// ).replace(/\/{2,}/g, "/");
|
|
107
|
-
// //if (file.type == 'file' && file.download_url) href = file.download_url
|
|
108
|
-
// return {
|
|
109
|
-
// href,
|
|
110
|
-
// propstat: {
|
|
111
|
-
// status: "HTTP/1.1 200 OK",
|
|
112
|
-
// prop: item,
|
|
113
|
-
// },
|
|
114
|
-
// };
|
|
115
|
-
// });
|
|
116
|
-
// };
|
|
117
|
-
|
|
118
|
-
const convData = (files, options) => {
|
|
119
|
-
const {
|
|
120
|
-
path,
|
|
121
|
-
base = "",
|
|
122
|
-
depth,
|
|
123
|
-
prop,
|
|
124
|
-
ns: { prefix, uri },
|
|
125
|
-
} = options;
|
|
126
|
-
// console.log(prop, 1111)
|
|
127
|
-
files = files.data || files.list || files;
|
|
128
|
-
return files.map((file) => {
|
|
129
|
-
const item = {};
|
|
130
|
-
for (const key of prop) {
|
|
131
|
-
// Encode special characters for XML for displayname property
|
|
132
|
-
if (key === "displayname") {
|
|
133
|
-
item[key] = file.name
|
|
134
|
-
.replace(/&/g, "&")
|
|
135
|
-
.replace(/</g, "<")
|
|
136
|
-
.replace(/>/g, ">")
|
|
137
|
-
.replace(/"/g, """)
|
|
138
|
-
.replace(/'/g, "'");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Add the file size
|
|
142
|
-
if (key === "getcontentlength") {
|
|
143
|
-
item[key] = file.byte || 0;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Add the resource type
|
|
147
|
-
if (key === "resourcetype") {
|
|
148
|
-
item[key] = file.type === "folder" ? { collection: "" } : "";
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Add the content type
|
|
152
|
-
if (key == "getcontenttype") {
|
|
153
|
-
item[key] = file.mime;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (key === "getetag") {
|
|
157
|
-
const stamp =
|
|
158
|
-
file.etag || file.hash || file.mtime || file.updatedAt || "";
|
|
159
|
-
item[key] =
|
|
160
|
-
`"${Buffer.from(`${file.name || ""}:${file.byte || 0}:${stamp}`).toString("base64url")}"`;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Add the creation date
|
|
164
|
-
if (key === "creationdate" && file.ctime) {
|
|
165
|
-
item[key] = new Date(file.ctime).toUTCString();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Add the last modified date
|
|
169
|
-
if (key === "getlastmodified" && file.mtime) {
|
|
170
|
-
item[key] = new Date(file.mtime).toUTCString();
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Set the available quota bytes to indicate unlimited
|
|
174
|
-
if (key === "quota-available-bytes") {
|
|
175
|
-
item[key] = -1;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Set the used quota bytes to indicate unlimited
|
|
179
|
-
if (key === "quota-used-bytes") {
|
|
180
|
-
item[key] = -1;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Construct the href.
|
|
185
|
-
// The collection itself uses base+path; its children append /name.
|
|
186
|
-
// Collections get a trailing slash (RFC 4918 §5.2).
|
|
187
|
-
const isSelf = file._self === true;
|
|
188
|
-
let href = (
|
|
189
|
-
isSelf ? base + path : base + path + "/" + encodeURIComponent(file.name)
|
|
190
|
-
).replace(/\/{2,}/g, "/");
|
|
191
|
-
if (file.type === "folder" && !href.endsWith("/")) href += "/";
|
|
192
|
-
// Return the WebDAV propstat object
|
|
193
|
-
return {
|
|
194
|
-
href,
|
|
195
|
-
propstat: {
|
|
196
|
-
status: "HTTP/1.1 200 OK",
|
|
197
|
-
prop: item,
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
});
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const fixNs = (data, prefix) => {
|
|
204
|
-
if (!prefix) return data;
|
|
205
|
-
Object.keys(data).forEach((key) => {
|
|
206
|
-
const val = data[key];
|
|
207
|
-
if (key != "$" && prefix) {
|
|
208
|
-
if (Array.isArray(val) || typeof val == "object") {
|
|
209
|
-
fixNs(val, prefix);
|
|
210
|
-
}
|
|
211
|
-
delete data[key];
|
|
212
|
-
data[`${prefix}:${key}`] = val;
|
|
213
|
-
} else {
|
|
214
|
-
if (val.xmlns) {
|
|
215
|
-
val[`xmlns:${prefix}`] = val.xmlns;
|
|
216
|
-
delete val.xmlns;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
return data;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const createXML = (data, options) => {
|
|
224
|
-
const {
|
|
225
|
-
ns: { prefix, uri },
|
|
226
|
-
} = options;
|
|
227
|
-
|
|
228
|
-
const obj = {
|
|
229
|
-
multistatus: {
|
|
230
|
-
response: convData(data || [], options),
|
|
231
|
-
},
|
|
232
|
-
};
|
|
233
|
-
if (uri) {
|
|
234
|
-
obj.multistatus.$ = {
|
|
235
|
-
xmlns: uri,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const builder = new xml2js.Builder({
|
|
240
|
-
renderOpts: { pretty: false },
|
|
241
|
-
xmldec: { version: "1.0", encoding: "UTF-8" },
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
const xml = builder.buildObject(fixNs(obj, prefix));
|
|
245
|
-
return xml;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
module.exports = async (ctx) => {
|
|
249
|
-
const options = Object.assign(
|
|
250
|
-
{
|
|
251
|
-
path: ctx.path,
|
|
252
|
-
base: ctx.base,
|
|
253
|
-
depth: ctx.depth,
|
|
254
|
-
},
|
|
255
|
-
propParse(await parseXML(ctx.req)),
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
let data = {};
|
|
259
|
-
data.item = await ctx.driver.stat(ctx.path);
|
|
260
|
-
// console.log(data.item);
|
|
261
|
-
if (ctx.depth == "1") {
|
|
262
|
-
try {
|
|
263
|
-
data.files = await ctx.driver.list(ctx.path);
|
|
264
|
-
} catch (error) {
|
|
265
|
-
return { status: "500" };
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (!data) return { status: "404" };
|
|
270
|
-
|
|
271
|
-
if (data.error) {
|
|
272
|
-
if (data.error.code == 401) {
|
|
273
|
-
// Windows seems to require this being the last header sent
|
|
274
|
-
// (changed according to PECL bug #3138)
|
|
275
|
-
return {
|
|
276
|
-
headers: {
|
|
277
|
-
"WWW-Authenticate": `Basic realm="ShareList WebDAV"`,
|
|
278
|
-
},
|
|
279
|
-
status: "401",
|
|
280
|
-
};
|
|
281
|
-
} else {
|
|
282
|
-
return {
|
|
283
|
-
status: "404",
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
//return itself
|
|
289
|
-
// Mark the requested resource as "self" so its href is built as base+path
|
|
290
|
-
// (children append /name). Depth:1 must include the collection itself
|
|
291
|
-
// followed by its members (RFC 4918 §9.1).
|
|
292
|
-
const selfItem = Object.assign({}, data.item, { _self: true });
|
|
293
|
-
if (ctx.depth == "0") {
|
|
294
|
-
return {
|
|
295
|
-
status: "207",
|
|
296
|
-
headers: {
|
|
297
|
-
"content-type": 'text/xml; charset="utf-8"',
|
|
298
|
-
},
|
|
299
|
-
body: createXML([selfItem], options),
|
|
300
|
-
};
|
|
301
|
-
} else if (ctx.depth == "1") {
|
|
302
|
-
const listResult = data.files;
|
|
303
|
-
const children = Array.isArray(listResult)
|
|
304
|
-
? listResult
|
|
305
|
-
: Array.isArray(listResult && listResult.data)
|
|
306
|
-
? listResult.data
|
|
307
|
-
: Array.isArray(listResult && listResult.list)
|
|
308
|
-
? listResult.list
|
|
309
|
-
: [];
|
|
310
|
-
return {
|
|
311
|
-
status: "207",
|
|
312
|
-
headers: {
|
|
313
|
-
// "content-type": 'text/xml; charset="utf-8"',
|
|
314
|
-
"content-type": 'application/xml; charset="utf-8"',
|
|
315
|
-
},
|
|
316
|
-
body: createXML([selfItem].concat(children), options),
|
|
317
|
-
};
|
|
318
|
-
} else if (ctx.depth == "infinity") {
|
|
319
|
-
return {
|
|
320
|
-
status: "404",
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
};
|