Refactor code to improve performance and readability

This commit is contained in:
abersheeran 2023-12-18 16:06:01 +08:00
parent cfc8b0085b
commit 2eae791a33

View File

@ -76,14 +76,14 @@ type DavProperties = {
function fromR2Object(object: R2Object | null | undefined): DavProperties { function fromR2Object(object: R2Object | null | undefined): DavProperties {
if (object === null || object === undefined) { if (object === null || object === undefined) {
return { return {
creationdate: undefined, creationdate: new Date().toUTCString(),
displayname: undefined, displayname: undefined,
getcontentlanguage: undefined, getcontentlanguage: undefined,
getcontentlength: undefined, getcontentlength: "0",
getcontenttype: undefined, getcontenttype: undefined,
getetag: undefined, getetag: undefined,
getlastmodified: undefined, getlastmodified: new Date().toUTCString(),
resourcetype: '', resourcetype: "<collection />",
}; };
} }
@ -101,7 +101,9 @@ function fromR2Object(object: R2Object | null | undefined): DavProperties {
function make_resource_path(request: Request): string { function make_resource_path(request: Request): string {
return new URL(request.url).pathname.slice(1) let path = new URL(request.url).pathname.slice(1);
path = path.endsWith('/') ? path.slice(0, -1) : path;
return path;
} }
async function handle_options(request: Request, bucket: R2Bucket): Promise<Response> { async function handle_options(request: Request, bucket: R2Bucket): Promise<Response> {
@ -180,12 +182,12 @@ async function handle_get(request: Request, bucket: R2Bucket): Promise<Response>
} }
async function handle_put(request: Request, bucket: R2Bucket): Promise<Response> { async function handle_put(request: Request, bucket: R2Bucket): Promise<Response> {
let resource_path = make_resource_path(request); if (request.url.endsWith('/')) {
if (resource_path.endsWith('/')) {
return new Response('Method Not Allowed', { status: 405 }); return new Response('Method Not Allowed', { status: 405 });
} }
let resource_path = make_resource_path(request);
// Check if the parent directory exists // Check if the parent directory exists
let dirpath = resource_path.split('/').slice(0, -1).join('/'); let dirpath = resource_path.split('/').slice(0, -1).join('/');
if (dirpath !== '') { if (dirpath !== '') {
@ -205,7 +207,6 @@ async function handle_put(request: Request, bucket: R2Bucket): Promise<Response>
async function handle_delete(request: Request, bucket: R2Bucket): Promise<Response> { async function handle_delete(request: Request, bucket: R2Bucket): Promise<Response> {
let resource_path = make_resource_path(request); let resource_path = make_resource_path(request);
resource_path = resource_path.endsWith('/') ? resource_path.slice(0, -1) : resource_path;
if (resource_path === '') { if (resource_path === '') {
let r2_objects, cursor: string | undefined = undefined; let r2_objects, cursor: string | undefined = undefined;
@ -258,7 +259,6 @@ async function handle_mkcol(request: Request, bucket: R2Bucket): Promise<Respons
} }
let resource_path = make_resource_path(request); let resource_path = make_resource_path(request);
resource_path = resource_path.endsWith('/') ? resource_path.slice(0, -1) : resource_path;
// Check if the resource already exists // Check if the resource already exists
let resource = await bucket.head(resource_path); let resource = await bucket.head(resource_path);
@ -282,105 +282,54 @@ async function handle_mkcol(request: Request, bucket: R2Bucket): Promise<Respons
async function handle_propfind(request: Request, bucket: R2Bucket): Promise<Response> { async function handle_propfind(request: Request, bucket: R2Bucket): Promise<Response> {
let resource_path = make_resource_path(request); let resource_path = make_resource_path(request);
let depth = request.headers.get('Depth') ?? 'infinity';
switch (depth) { let is_collection: boolean;
case '0': { let page = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">`;
if (resource_path === "") { if (resource_path === "") {
return new Response(`<?xml version="1.0" encoding="utf-8"?> page += `
<multistatus xmlns="DAV:">
<response> <response>
<href>/</href> <href>/</href>
<propstat> <propstat>
<prop> <prop>
<resourcetype><collection /></resourcetype> ${Object.entries(fromR2Object(null))
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => `<${key}>${value}</${key}>`)
.join('\n')}
</prop> </prop>
<status>HTTP/1.1 200 OK</status> <status>HTTP/1.1 200 OK</status>
</propstat> </propstat>
</response> </response>`;
</multistatus> is_collection = true;
`, { } else {
status: 207,
headers: {
'Content-Type': 'text/xml',
},
});
}
let object = await bucket.head(resource_path); let object = await bucket.head(resource_path);
if (object === null && resource_path.endsWith('/')) {
object = await bucket.head(resource_path.slice(0, -1));
}
if (object === null) { if (object === null) {
return new Response('Not Found', { status: 404 }); return new Response('Not Found', { status: 404 });
} }
is_collection = object.customMetadata?.resourcetype === '<collection />';
let page = `<?xml version="1.0" encoding="utf-8"?> let href = `/${object.key + (object.customMetadata?.resourcetype === '<collection />' ? '/' : '')}`;
<multistatus xmlns="DAV:"> page += `
<response> <response>
<href>/${resource_path}</href> <href>${href}</href>
<propstat> <propstat>
<prop> <prop>
${Object.entries(fromR2Object(object)) ${Object.entries(fromR2Object(object))
.filter(([_, value]) => value !== undefined) .filter(([_, value]) => value !== undefined)
.map(([key, value]) => `<${key}>${value}</${key}>`) .map(([key, value]) => `<${key}>${value}</${key}>`)
.join('\n') .join('\n')}
}
</prop> </prop>
<status>HTTP/1.1 200 OK</status> <status>HTTP/1.1 200 OK</status>
</propstat> </propstat>
</response> </response>`
</multistatus> };
`;
return new Response(page, { if (is_collection) {
status: 207, let depth = request.headers.get('Depth') ?? 'infinity';
headers: { switch (depth) {
'Content-Type': 'text/xml', case '0': break;
},
});
}
case '1': { case '1': {
if (resource_path !== "") { let prefix = resource_path === "" ? resource_path : resource_path + '/';
let object = await bucket.head(resource_path);
if (object === null && resource_path.endsWith('/')) {
object = await bucket.head(resource_path.slice(0, -1));
}
if (object === null) {
return new Response('Not Found', { status: 404 });
}
if (object.customMetadata?.resourcetype !== '<collection />') {
let page = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">
<response>
<href>/${resource_path}</href>
<propstat>
<prop>
${Object.entries(fromR2Object(object))
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => `<${key}>${value}</${key}>`)
.join('\n ')
}
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>
`;
return new Response(page, {
status: 207,
headers: {
'Content-Type': 'text/xml',
},
});
}
}
let page = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">`;
let prefix = resource_path.endsWith('/') || resource_path === "" ? resource_path : resource_path + '/';
for await (let object of listAll(bucket, prefix)) { for await (let object of listAll(bucket, prefix)) {
let href = `/${object.key + (object.customMetadata?.resourcetype === '<collection />' ? '/' : '')}`; let href = `/${object.key + (object.customMetadata?.resourcetype === '<collection />' ? '/' : '')}`;
page += ` page += `
@ -398,57 +347,10 @@ async function handle_propfind(request: Request, bucket: R2Bucket): Promise<Resp
</propstat> </propstat>
</response>`; </response>`;
} }
page += '\n</multistatus>\n';
return new Response(page, {
status: 207,
headers: {
'Content-Type': 'text/xml',
},
});
} }
break;
case 'infinity': { case 'infinity': {
if (resource_path !== "") { let prefix = resource_path === "" ? resource_path : resource_path + '/';
let object = await bucket.head(resource_path);
if (object === null && resource_path.endsWith('/')) {
object = await bucket.head(resource_path.slice(0, -1));
}
if (object === null) {
return new Response('Not Found', { status: 404 });
}
if (object.customMetadata?.resourcetype !== '<collection />') {
let page = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">
<response>
<href>/${resource_path}</href>
<propstat>
<prop>
${Object.entries(fromR2Object(object))
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => `<${key}>${value}</${key}>`)
.join('\n ')
}
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>
`;
return new Response(page, {
status: 207,
headers: {
'Content-Type': 'text/xml',
},
});
}
}
let page = `<?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:">`;
let prefix = resource_path.endsWith('/') || resource_path === "" ? resource_path : resource_path + '/';
for await (let object of listAll(bucket, prefix, true)) { for await (let object of listAll(bucket, prefix, true)) {
let href = `/${object.key + (object.customMetadata?.resourcetype === '<collection />' ? '/' : '')}`; let href = `/${object.key + (object.customMetadata?.resourcetype === '<collection />' ? '/' : '')}`;
page += ` page += `
@ -466,6 +368,13 @@ async function handle_propfind(request: Request, bucket: R2Bucket): Promise<Resp
</propstat> </propstat>
</response>`; </response>`;
} }
}
break;
default: {
return new Response('Forbidden', { status: 403 });
}
}
}
page += '\n</multistatus>\n'; page += '\n</multistatus>\n';
return new Response(page, { return new Response(page, {
@ -474,11 +383,6 @@ async function handle_propfind(request: Request, bucket: R2Bucket): Promise<Resp
'Content-Type': 'text/xml', 'Content-Type': 'text/xml',
}, },
}); });
}
default: {
return new Response('Forbidden', { status: 403 });
}
}
} }
async function handle_copy(request: Request, bucket: R2Bucket): Promise<Response> { async function handle_copy(request: Request, bucket: R2Bucket): Promise<Response> {
@ -503,7 +407,7 @@ async function handle_copy(request: Request, bucket: R2Bucket): Promise<Response
return new Response('Precondition Failed', { status: 412 }); return new Response('Precondition Failed', { status: 412 });
} }
let resource = await bucket.head(resource_path.endsWith("/") ? resource_path.slice(0, -1) : resource_path); let resource = await bucket.head(resource_path);
if (resource === null) { if (resource === null) {
return new Response('Not Found', { status: 404 }); return new Response('Not Found', { status: 404 });
} }
@ -514,7 +418,7 @@ async function handle_copy(request: Request, bucket: R2Bucket): Promise<Response
let depth = request.headers.get('Depth') ?? 'infinity'; let depth = request.headers.get('Depth') ?? 'infinity';
switch (depth) { switch (depth) {
case 'infinity': { case 'infinity': {
let prefix = resource_path.endsWith("/") ? resource_path : resource_path + "/"; let prefix = resource_path + "/";
const copy = async (object: R2Object) => { const copy = async (object: R2Object) => {
let target = destination + "/" + object.key.slice(prefix.length); let target = destination + "/" + object.key.slice(prefix.length);
target = target.endsWith("/") ? target.slice(0, -1) : target; target = target.endsWith("/") ? target.slice(0, -1) : target;
@ -595,7 +499,7 @@ async function handle_move(request: Request, bucket: R2Bucket): Promise<Response
return new Response('Precondition Failed', { status: 412 }); return new Response('Precondition Failed', { status: 412 });
} }
let resource = await bucket.head(resource_path.endsWith("/") ? resource_path.slice(0, -1) : resource_path); let resource = await bucket.head(resource_path);
if (resource === null) { if (resource === null) {
return new Response('Not Found', { status: 404 }); return new Response('Not Found', { status: 404 });
} }
@ -613,7 +517,7 @@ async function handle_move(request: Request, bucket: R2Bucket): Promise<Response
let depth = request.headers.get('Depth') ?? 'infinity'; let depth = request.headers.get('Depth') ?? 'infinity';
switch (depth) { switch (depth) {
case 'infinity': { case 'infinity': {
let prefix = resource_path.endsWith("/") ? resource_path : resource_path + "/"; let prefix = resource_path + "/";
const copy = async (object: R2Object) => { const copy = async (object: R2Object) => {
let target = destination + "/" + object.key.slice(prefix.length); let target = destination + "/" + object.key.slice(prefix.length);
target = target.endsWith("/") ? target.slice(0, -1) : target; target = target.endsWith("/") ? target.slice(0, -1) : target;