@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/dist/index.cjs
ADDED
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
WebDAVServer: () => WebDAVServer,
|
|
33
|
+
default: () => index_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/operations/commands.ts
|
|
38
|
+
var commands_exports = {};
|
|
39
|
+
__export(commands_exports, {
|
|
40
|
+
Copy: () => copy_default,
|
|
41
|
+
Delete: () => delete_default,
|
|
42
|
+
Get: () => get_default,
|
|
43
|
+
Head: () => head_default,
|
|
44
|
+
Mkcol: () => mkcol_default,
|
|
45
|
+
Move: () => move_default,
|
|
46
|
+
NotImplemented: () => not_implemented_default,
|
|
47
|
+
Options: () => options_default,
|
|
48
|
+
Propfind: () => propfind_default,
|
|
49
|
+
Proppatch: () => proppatch_default,
|
|
50
|
+
Put: () => put_default
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// src/operations/get.ts
|
|
54
|
+
async function loadProxy() {
|
|
55
|
+
const proxy = await import("@filebox/proxy");
|
|
56
|
+
return proxy.default || proxy;
|
|
57
|
+
}
|
|
58
|
+
var get_default = async (ctx) => {
|
|
59
|
+
try {
|
|
60
|
+
const decodedPath = decodeURIComponent(ctx.path);
|
|
61
|
+
const stat = await ctx.driver.stat(decodedPath);
|
|
62
|
+
if (!stat || !stat.file) {
|
|
63
|
+
return {
|
|
64
|
+
status: "405",
|
|
65
|
+
body: "405 Method not allowed"
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const data = await ctx.driver.download(decodedPath);
|
|
69
|
+
if (!data || !data.length || !data[0].url) {
|
|
70
|
+
return {
|
|
71
|
+
status: "404",
|
|
72
|
+
body: "file not found"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const proxy = await loadProxy();
|
|
76
|
+
return proxy(ctx.req, {
|
|
77
|
+
...data[0],
|
|
78
|
+
fileName: decodedPath
|
|
79
|
+
});
|
|
80
|
+
} catch (_) {
|
|
81
|
+
return {
|
|
82
|
+
status: "500",
|
|
83
|
+
body: "Internal Server Error"
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/operations/put.ts
|
|
89
|
+
var import_pathe = require("pathe");
|
|
90
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
91
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
92
|
+
var put_default = async (ctx) => {
|
|
93
|
+
try {
|
|
94
|
+
const rawPath = decodeURIComponent(ctx.path);
|
|
95
|
+
const name = (0, import_pathe.basename)(rawPath);
|
|
96
|
+
const upath = (0, import_pathe.dirname)(rawPath);
|
|
97
|
+
const contentLength = parseInt(ctx.req.headers["content-length"] || 0);
|
|
98
|
+
const mimeType = ctx.req.headers["content-type"] || "application/octet-stream";
|
|
99
|
+
const tempDir = ctx.config?.temp;
|
|
100
|
+
if (!tempDir) {
|
|
101
|
+
return { status: "500", body: "temp dir not configured" };
|
|
102
|
+
}
|
|
103
|
+
const tempFile = import_node_path.default.join(tempDir, `webdav-upload-${Date.now()}-${name}`);
|
|
104
|
+
const uploadStream = ctx.req._webdavStream || (ctx.req.readable && !ctx.req.readableEnded ? ctx.req : null);
|
|
105
|
+
if (uploadStream && contentLength > 0) {
|
|
106
|
+
await new Promise((resolve, reject) => {
|
|
107
|
+
const ws = import_node_fs.default.createWriteStream(tempFile);
|
|
108
|
+
uploadStream.pipe(ws);
|
|
109
|
+
ws.on("finish", resolve);
|
|
110
|
+
ws.on("error", reject);
|
|
111
|
+
uploadStream.on("error", reject);
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
import_node_fs.default.writeFileSync(tempFile, "");
|
|
115
|
+
}
|
|
116
|
+
const tempStat = import_node_fs.default.statSync(tempFile);
|
|
117
|
+
if (tempStat.size === 0 && contentLength > 0) {
|
|
118
|
+
return { status: "502", body: "upload failed: empty file" };
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const result = await ctx.driver.upload(upath, {
|
|
122
|
+
name,
|
|
123
|
+
size: tempStat.size,
|
|
124
|
+
absolutePath: tempFile,
|
|
125
|
+
mimeType
|
|
126
|
+
});
|
|
127
|
+
if (result && typeof result.upload === "function") {
|
|
128
|
+
await result.upload();
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return { status: "502", body: String(error) };
|
|
132
|
+
} finally {
|
|
133
|
+
try {
|
|
134
|
+
import_node_fs.default.unlinkSync(tempFile);
|
|
135
|
+
} catch (_) {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { status: "200" };
|
|
139
|
+
} catch (err) {
|
|
140
|
+
return { status: "502", body: String(err?.message || err) };
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/operations/head.ts
|
|
145
|
+
var head_default = async (ctx) => {
|
|
146
|
+
try {
|
|
147
|
+
console.log(ctx.path, "head", 22222);
|
|
148
|
+
const stat = ctx.driver.stat(ctx.path);
|
|
149
|
+
const headers = {
|
|
150
|
+
"Content-Type": "application/octet-stream",
|
|
151
|
+
"Content-Length": stat.size,
|
|
152
|
+
"Last-Modified": stat.mtime,
|
|
153
|
+
"Accept-Ranges": "bytes"
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
status: "200",
|
|
157
|
+
headers,
|
|
158
|
+
body: null
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.log(error);
|
|
162
|
+
return {
|
|
163
|
+
status: "404"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// src/operations/move.ts
|
|
169
|
+
var import_pathe2 = require("pathe");
|
|
170
|
+
var move_default = async (ctx) => {
|
|
171
|
+
try {
|
|
172
|
+
const dst = new URL(ctx.req.headers?.destination).pathname.replace(
|
|
173
|
+
ctx.base,
|
|
174
|
+
""
|
|
175
|
+
);
|
|
176
|
+
const src = ctx.path;
|
|
177
|
+
if (!dst) {
|
|
178
|
+
return {
|
|
179
|
+
status: "400",
|
|
180
|
+
body: "Missing source or destination URI"
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const normalizedSrc = src.endsWith("/") ? src.slice(0, -1) : src;
|
|
184
|
+
const normalizedDst = dst.endsWith("/") ? dst.slice(0, -1) : dst;
|
|
185
|
+
if (normalizedSrc === normalizedDst) {
|
|
186
|
+
return { status: "403" };
|
|
187
|
+
}
|
|
188
|
+
const isRename = normalizedSrc.substring(0, normalizedSrc.lastIndexOf("/")) === normalizedDst.substring(0, normalizedDst.lastIndexOf("/"));
|
|
189
|
+
if (isRename) {
|
|
190
|
+
await ctx.driver?.rename(normalizedSrc, (0, import_pathe2.basename)(normalizedDst));
|
|
191
|
+
} else {
|
|
192
|
+
await ctx.driver?.move(normalizedSrc, (0, import_pathe2.dirname)(normalizedDst));
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
status: "201"
|
|
196
|
+
};
|
|
197
|
+
} catch (_) {
|
|
198
|
+
return {
|
|
199
|
+
status: "502",
|
|
200
|
+
body: null
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/operations/copy.ts
|
|
206
|
+
var import_pathe3 = require("pathe");
|
|
207
|
+
var copy_default = async (ctx) => {
|
|
208
|
+
try {
|
|
209
|
+
const dst = new URL(ctx.req.headers?.destination).pathname.replace(
|
|
210
|
+
ctx.base,
|
|
211
|
+
""
|
|
212
|
+
);
|
|
213
|
+
const src = ctx.path;
|
|
214
|
+
if (src === dst) {
|
|
215
|
+
return { status: "403" };
|
|
216
|
+
}
|
|
217
|
+
const stat = await ctx.driver.stat(src);
|
|
218
|
+
if (!stat || !stat.file) {
|
|
219
|
+
return { status: "404" };
|
|
220
|
+
}
|
|
221
|
+
const data = await ctx.driver.download(src);
|
|
222
|
+
if (!data || !data.length) {
|
|
223
|
+
return { status: "404" };
|
|
224
|
+
}
|
|
225
|
+
const upath = (0, import_pathe3.dirname)(dst);
|
|
226
|
+
const name = (0, import_pathe3.basename)(dst);
|
|
227
|
+
const resp = await fetch(data[0].url);
|
|
228
|
+
await ctx.driver.upload(upath, {
|
|
229
|
+
name,
|
|
230
|
+
size: parseInt(resp.headers.get("content-length") || "0"),
|
|
231
|
+
data: resp.body,
|
|
232
|
+
mimeType: stat.mime || "application/octet-stream"
|
|
233
|
+
});
|
|
234
|
+
return { status: "201" };
|
|
235
|
+
} catch (_) {
|
|
236
|
+
return { status: "502" };
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/operations/mkcol.ts
|
|
241
|
+
var mkcol_default = async (ctx) => {
|
|
242
|
+
try {
|
|
243
|
+
await ctx.driver.mkdir(ctx.path);
|
|
244
|
+
return {
|
|
245
|
+
status: "201",
|
|
246
|
+
body: null
|
|
247
|
+
};
|
|
248
|
+
} catch (_) {
|
|
249
|
+
return {
|
|
250
|
+
status: "500",
|
|
251
|
+
body: null
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/operations/delete.ts
|
|
257
|
+
var delete_default = async (ctx) => {
|
|
258
|
+
try {
|
|
259
|
+
await ctx.driver.remove(ctx.path);
|
|
260
|
+
return {
|
|
261
|
+
status: "204"
|
|
262
|
+
};
|
|
263
|
+
} catch (_) {
|
|
264
|
+
return {
|
|
265
|
+
status: "502"
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/operations/options.ts
|
|
271
|
+
var options_default = async (ctx) => {
|
|
272
|
+
const dav = [1];
|
|
273
|
+
if (ctx.allows?.includes("LOCK")) {
|
|
274
|
+
dav.push(2);
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
headers: {
|
|
278
|
+
// For Microsoft clients
|
|
279
|
+
"MS-Author-Via": "DAV",
|
|
280
|
+
DAV: dav.join(", "),
|
|
281
|
+
Allow: ctx.allows?.join(", ") || ""
|
|
282
|
+
},
|
|
283
|
+
status: "200"
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// src/operations/propfind.ts
|
|
288
|
+
var import_xml2js2 = __toESM(require("xml2js"), 1);
|
|
289
|
+
|
|
290
|
+
// src/operations/shared.ts
|
|
291
|
+
var import_xml2js = require("xml2js");
|
|
292
|
+
var parseBody = (req, charset) => {
|
|
293
|
+
return new Promise((resolve, reject) => {
|
|
294
|
+
const data = [];
|
|
295
|
+
const stream = req._webdavStream || req;
|
|
296
|
+
stream.on("data", (chunk) => {
|
|
297
|
+
data.push(chunk);
|
|
298
|
+
}).on("error", reject).on("end", () => resolve(Buffer.concat(data).toString(charset)));
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
var parseXML = async (req) => {
|
|
302
|
+
const txt = await parseBody(req);
|
|
303
|
+
if (!txt.trim()) return null;
|
|
304
|
+
return await (0, import_xml2js.parseStringPromise)(txt, {
|
|
305
|
+
// explicitChildren: true,
|
|
306
|
+
explicitArray: false,
|
|
307
|
+
tagNameProcessors: [import_xml2js.processors.stripPrefix]
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
var shared_default = parseXML;
|
|
311
|
+
|
|
312
|
+
// src/operations/propfind.ts
|
|
313
|
+
var DEFAULT_PROPS = [
|
|
314
|
+
"displayname",
|
|
315
|
+
"getcontentlength",
|
|
316
|
+
"resourcetype",
|
|
317
|
+
"getcontenttype",
|
|
318
|
+
"creationdate",
|
|
319
|
+
"getlastmodified"
|
|
320
|
+
];
|
|
321
|
+
var propParse = (data) => {
|
|
322
|
+
if (!data) {
|
|
323
|
+
return {
|
|
324
|
+
ns: { prefix: "D", uri: "DAV:" },
|
|
325
|
+
prop: [...DEFAULT_PROPS]
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
let prop = [...DEFAULT_PROPS];
|
|
329
|
+
const ns = { prefix: "D", uri: "DAV:" };
|
|
330
|
+
if (data.propfind.hasOwnProperty("prop")) {
|
|
331
|
+
if (data.propfind.prop.hasOwnProperty("allprop")) {
|
|
332
|
+
return {
|
|
333
|
+
ns,
|
|
334
|
+
prop: [...DEFAULT_PROPS]
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
prop = Object.keys(data.propfind.prop);
|
|
338
|
+
}
|
|
339
|
+
return { ns, prop };
|
|
340
|
+
};
|
|
341
|
+
var convData = (files, options) => {
|
|
342
|
+
const {
|
|
343
|
+
path: path2,
|
|
344
|
+
base = "",
|
|
345
|
+
depth,
|
|
346
|
+
prop,
|
|
347
|
+
ns: { prefix, uri }
|
|
348
|
+
} = options;
|
|
349
|
+
files = files.data || files.list || files;
|
|
350
|
+
return files.map((file) => {
|
|
351
|
+
const item = {};
|
|
352
|
+
for (const key of prop) {
|
|
353
|
+
if (key === "displayname") {
|
|
354
|
+
item[key] = file.name.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
355
|
+
}
|
|
356
|
+
if (key === "getcontentlength") {
|
|
357
|
+
item[key] = file.byte || 0;
|
|
358
|
+
}
|
|
359
|
+
if (key === "resourcetype") {
|
|
360
|
+
item[key] = file.type === "folder" ? { collection: "" } : "";
|
|
361
|
+
}
|
|
362
|
+
if (key == "getcontenttype") {
|
|
363
|
+
item[key] = file.mime;
|
|
364
|
+
}
|
|
365
|
+
if (key === "getetag") {
|
|
366
|
+
const stamp = file.etag || file.hash || file.mtime || file.updatedAt || "";
|
|
367
|
+
item[key] = `"${Buffer.from(`${file.name || ""}:${file.byte || 0}:${stamp}`).toString("base64url")}"`;
|
|
368
|
+
}
|
|
369
|
+
if (key === "creationdate" && file.ctime) {
|
|
370
|
+
item[key] = new Date(file.ctime).toUTCString();
|
|
371
|
+
}
|
|
372
|
+
if (key === "getlastmodified" && file.mtime) {
|
|
373
|
+
item[key] = new Date(file.mtime).toUTCString();
|
|
374
|
+
}
|
|
375
|
+
if (key === "quota-available-bytes") {
|
|
376
|
+
item[key] = -1;
|
|
377
|
+
}
|
|
378
|
+
if (key === "quota-used-bytes") {
|
|
379
|
+
item[key] = -1;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const isSelf = file._self === true;
|
|
383
|
+
let href = (isSelf ? base + path2 : base + path2 + "/" + encodeURIComponent(file.name)).replace(/\/{2,}/g, "/");
|
|
384
|
+
if (file.type === "folder" && !href.endsWith("/")) href += "/";
|
|
385
|
+
return {
|
|
386
|
+
href,
|
|
387
|
+
propstat: {
|
|
388
|
+
status: "HTTP/1.1 200 OK",
|
|
389
|
+
prop: item
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
var fixNs = (data, prefix) => {
|
|
395
|
+
if (!prefix) return data;
|
|
396
|
+
Object.keys(data).forEach((key) => {
|
|
397
|
+
const val = data[key];
|
|
398
|
+
if (key != "$" && prefix) {
|
|
399
|
+
if (Array.isArray(val) || typeof val == "object") {
|
|
400
|
+
fixNs(val, prefix);
|
|
401
|
+
}
|
|
402
|
+
delete data[key];
|
|
403
|
+
data[`${prefix}:${key}`] = val;
|
|
404
|
+
} else {
|
|
405
|
+
if (val.xmlns) {
|
|
406
|
+
val[`xmlns:${prefix}`] = val.xmlns;
|
|
407
|
+
delete val.xmlns;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
return data;
|
|
412
|
+
};
|
|
413
|
+
var createXML = (data, options) => {
|
|
414
|
+
const {
|
|
415
|
+
ns: { prefix, uri }
|
|
416
|
+
} = options;
|
|
417
|
+
const obj = {
|
|
418
|
+
multistatus: {
|
|
419
|
+
response: convData(data || [], options)
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
if (uri) {
|
|
423
|
+
obj.multistatus.$ = {
|
|
424
|
+
xmlns: uri
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const builder = new import_xml2js2.default.Builder({
|
|
428
|
+
renderOpts: { pretty: false },
|
|
429
|
+
xmldec: { version: "1.0", encoding: "UTF-8" }
|
|
430
|
+
});
|
|
431
|
+
const xml = builder.buildObject(fixNs(obj, prefix));
|
|
432
|
+
return xml;
|
|
433
|
+
};
|
|
434
|
+
var propfind_default = async (ctx) => {
|
|
435
|
+
const options = Object.assign(
|
|
436
|
+
{
|
|
437
|
+
path: ctx.path,
|
|
438
|
+
base: ctx.base,
|
|
439
|
+
depth: ctx.depth
|
|
440
|
+
},
|
|
441
|
+
propParse(await shared_default(ctx.req))
|
|
442
|
+
);
|
|
443
|
+
let data = {};
|
|
444
|
+
data.item = await ctx.driver.stat(ctx.path);
|
|
445
|
+
if (ctx.depth == "1") {
|
|
446
|
+
try {
|
|
447
|
+
data.files = await ctx.driver.list(ctx.path);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
return { status: "500" };
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!data) return { status: "404" };
|
|
453
|
+
if (data.error) {
|
|
454
|
+
if (data.error.code == 401) {
|
|
455
|
+
return {
|
|
456
|
+
headers: {
|
|
457
|
+
"WWW-Authenticate": `Basic realm="ShareList WebDAV"`
|
|
458
|
+
},
|
|
459
|
+
status: "401"
|
|
460
|
+
};
|
|
461
|
+
} else {
|
|
462
|
+
return {
|
|
463
|
+
status: "404"
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const selfItem = Object.assign({}, data.item, { _self: true });
|
|
468
|
+
if (ctx.depth == "0") {
|
|
469
|
+
return {
|
|
470
|
+
status: "207",
|
|
471
|
+
headers: {
|
|
472
|
+
"content-type": 'text/xml; charset="utf-8"'
|
|
473
|
+
},
|
|
474
|
+
body: createXML([selfItem], options)
|
|
475
|
+
};
|
|
476
|
+
} else if (ctx.depth == "1") {
|
|
477
|
+
const listResult = data.files;
|
|
478
|
+
const children = Array.isArray(listResult) ? listResult : Array.isArray(listResult && listResult.data) ? listResult.data : Array.isArray(listResult && listResult.list) ? listResult.list : [];
|
|
479
|
+
return {
|
|
480
|
+
status: "207",
|
|
481
|
+
headers: {
|
|
482
|
+
// "content-type": 'text/xml; charset="utf-8"',
|
|
483
|
+
"content-type": 'application/xml; charset="utf-8"'
|
|
484
|
+
},
|
|
485
|
+
body: createXML([selfItem].concat(children), options)
|
|
486
|
+
};
|
|
487
|
+
} else if (ctx.depth == "infinity") {
|
|
488
|
+
return {
|
|
489
|
+
status: "404"
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// src/operations/proppatch.ts
|
|
495
|
+
var proppatch_default = async (ctx) => {
|
|
496
|
+
return {
|
|
497
|
+
status: "200"
|
|
498
|
+
};
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// src/operations/not-implemented.ts
|
|
502
|
+
var not_implemented_default = async (ctx) => {
|
|
503
|
+
return {
|
|
504
|
+
status: "405 Method not allowed",
|
|
505
|
+
headers: {
|
|
506
|
+
Allow: Object.keys(commands_exports).join(", ")
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/context.ts
|
|
512
|
+
var createContext = (req, base, allows, temp) => {
|
|
513
|
+
const authorization = req.headers?.authorization?.split(" ")[1];
|
|
514
|
+
const path2 = new URL(req.url, `http://${req.headers.host}`).pathname;
|
|
515
|
+
const ctx = {
|
|
516
|
+
req,
|
|
517
|
+
depth: req.headers?.depth || 0,
|
|
518
|
+
method: (req.method || "").toLowerCase(),
|
|
519
|
+
path: path2.replace(base, "").replace("/k", ""),
|
|
520
|
+
// 临时这么做
|
|
521
|
+
base,
|
|
522
|
+
config: {
|
|
523
|
+
temp
|
|
524
|
+
},
|
|
525
|
+
auth: { user: void 0, pass: void 0 },
|
|
526
|
+
allows,
|
|
527
|
+
get(field) {
|
|
528
|
+
const req2 = this.req;
|
|
529
|
+
switch (field = field.toLowerCase()) {
|
|
530
|
+
case "referer":
|
|
531
|
+
case "referrer":
|
|
532
|
+
return req2.headers.referrer || req2.headers.referer || "";
|
|
533
|
+
default:
|
|
534
|
+
return req2.headers[field] || "";
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
if (authorization) {
|
|
539
|
+
const pairs = Buffer.from(authorization, "base64").toString("utf8").split(":");
|
|
540
|
+
ctx.auth = { user: pairs[0], pass: pairs[1] };
|
|
541
|
+
}
|
|
542
|
+
return ctx;
|
|
543
|
+
};
|
|
544
|
+
var context_default = createContext;
|
|
545
|
+
|
|
546
|
+
// src/index.ts
|
|
547
|
+
var StatusCodes = {
|
|
548
|
+
200: "OK",
|
|
549
|
+
201: "Created",
|
|
550
|
+
204: "No Content",
|
|
551
|
+
207: "Multi Status",
|
|
552
|
+
302: "Moved Temporarily",
|
|
553
|
+
401: "Unauthorized",
|
|
554
|
+
403: "Forbidden",
|
|
555
|
+
404: "Not Found",
|
|
556
|
+
409: "Conflict",
|
|
557
|
+
423: "Locked",
|
|
558
|
+
500: "Internal Server Error"
|
|
559
|
+
};
|
|
560
|
+
var VirtualDriver = {
|
|
561
|
+
async get() {
|
|
562
|
+
return {
|
|
563
|
+
status: "200",
|
|
564
|
+
body: "Hello World!",
|
|
565
|
+
headers: {
|
|
566
|
+
"Content-Type": "application/json"
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
// 其他方法...
|
|
571
|
+
};
|
|
572
|
+
var WebDAVServer = class {
|
|
573
|
+
constructor({ driver, base, redirect, auth } = { redirect: false, auth: () => true }) {
|
|
574
|
+
this.methods = {};
|
|
575
|
+
this.driver = driver || VirtualDriver;
|
|
576
|
+
this.base = base || "";
|
|
577
|
+
this.auth = auth;
|
|
578
|
+
this.config = { redirect };
|
|
579
|
+
const commands = commands_exports;
|
|
580
|
+
for (const k in commands) {
|
|
581
|
+
if (k === "NotImplemented") {
|
|
582
|
+
this.unknownMethod = commands[k];
|
|
583
|
+
} else {
|
|
584
|
+
this.methods[k.toLowerCase()] = commands[k];
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
this.allows = Object.keys(this.methods).map((i) => i.toUpperCase());
|
|
588
|
+
}
|
|
589
|
+
async request(req, options) {
|
|
590
|
+
const ctx = context_default(
|
|
591
|
+
req,
|
|
592
|
+
options?.base || this.base,
|
|
593
|
+
this.allows,
|
|
594
|
+
options.temp
|
|
595
|
+
);
|
|
596
|
+
if (!(ctx.method == "options" && !ctx.path) && !this?.auth(ctx.auth.user, ctx.auth.pass)) {
|
|
597
|
+
return {
|
|
598
|
+
headers: {
|
|
599
|
+
"X-WebDAV-Status": `401 ${StatusCodes[401]}`,
|
|
600
|
+
"www-Authenticate": 'Basic realm="Restricted"'
|
|
601
|
+
},
|
|
602
|
+
status: "401"
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
ctx.driver = options.driver;
|
|
606
|
+
ctx.config = { ...this.config, temp: options.temp };
|
|
607
|
+
const method = this.methods[ctx.method] || this.unknownMethod;
|
|
608
|
+
const res = await method(ctx);
|
|
609
|
+
res.headers = res.headers ?? {};
|
|
610
|
+
if (res.status) {
|
|
611
|
+
}
|
|
612
|
+
return res;
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
var index_default = {
|
|
616
|
+
WebDAVServer
|
|
617
|
+
};
|
|
618
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
619
|
+
0 && (module.exports = {
|
|
620
|
+
WebDAVServer
|
|
621
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface WebDAVServerOptions {
|
|
2
|
+
driver?: unknown;
|
|
3
|
+
base?: string;
|
|
4
|
+
redirect?: boolean;
|
|
5
|
+
auth?: (user?: string, pass?: string) => boolean | Promise<boolean>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface WebDAVRequestOptions {
|
|
9
|
+
base?: string;
|
|
10
|
+
driver?: unknown;
|
|
11
|
+
temp?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WebDAVResponse {
|
|
15
|
+
status?: string | number;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
body?: unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class WebDAVServer {
|
|
21
|
+
constructor(options?: WebDAVServerOptions);
|
|
22
|
+
request(req: unknown, options: WebDAVRequestOptions): Promise<WebDAVResponse>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare const api: {
|
|
26
|
+
WebDAVServer: typeof WebDAVServer;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default api;
|