Implement HTML-based downloading
This commit is contained in:
parent
885434b87e
commit
1e48f6ead6
@ -1,27 +1,39 @@
|
||||
import { getHashString } from "./utils";
|
||||
|
||||
export async function downloadDispatcher(request, env, ctx) {
|
||||
const url = new URL(request.url);
|
||||
const uri = url.pathname;
|
||||
|
||||
const bucket = env.EXP_DATA;
|
||||
const signatureBase = env.SIGNATURE_BASE;
|
||||
const adminSecret = env.ADMIN_SECRET;
|
||||
|
||||
if (request.headers.get('x-access-token') !== adminSecret)
|
||||
return new Response(null, { status: 401, statusText: 'Unauthorized' });
|
||||
try {
|
||||
const key = uri.slice(10); // '/download/'.length
|
||||
const signature = url.searchParams.get('signature');
|
||||
const timestamp = url.searchParams.get('timestamp');
|
||||
|
||||
if (!signature && request.headers.get('x-access-token') !== adminSecret)
|
||||
return new Response(null, { status: 403, statusText: 'Forbidden' });
|
||||
else {
|
||||
const hash = await getHashString(`${key}${timestamp}${signatureBase}`, 'SHA-1');
|
||||
if (signature !== hash)
|
||||
return new Response(null, { status: 403, statusText: 'Forbidden' });
|
||||
}
|
||||
|
||||
try {
|
||||
const object = await bucket.get(key);
|
||||
if (object === null)
|
||||
return new Response(null, { status: 404, statusText: 'Not Found' });
|
||||
|
||||
const { body, etag, httpMetadata, customMetadata } = object;
|
||||
return new Response(body, { headers: {
|
||||
etag: etag,
|
||||
etag,
|
||||
'content-type': httpMetadata.contentType,
|
||||
'content-length': body.length,
|
||||
'x-metadata': JSON.stringify(customMetadata),
|
||||
}});
|
||||
|
||||
} catch (e) { return new Response(null, { status: 400 }); }
|
||||
} catch (e) { return new Response(null, { status: 500 }); }
|
||||
}
|
||||
|
||||
export default downloadDispatcher;
|
65
src/list.js
65
src/list.js
@ -1,17 +1,29 @@
|
||||
import { getHashString, adaptiveFilesize } from "./utils";
|
||||
|
||||
export async function listDispatcher(request, env, ctx) {
|
||||
const url = new URL(request.url);
|
||||
const headers = request.headers;
|
||||
|
||||
const bucket = env.EXP_DATA;
|
||||
const signatureBase = env.SIGNATURE_BASE;
|
||||
const adminSecret = env.ADMIN_SECRET;
|
||||
const badRequest = new Response(null, { status: 400 });
|
||||
|
||||
if (request.headers.get('x-access-token') !== adminSecret)
|
||||
const token = url.searchParams.get('accessToken') || headers.get('x-access-token');
|
||||
if (token !== adminSecret)
|
||||
return new Response(null, { status: 401, statusText: 'Unauthorized' });
|
||||
|
||||
const resType = url.searchParams.get('resType') || headers.get('x-res-type') || 'html';
|
||||
if (!['json', 'html'].includes(resType))
|
||||
return badRequest;
|
||||
|
||||
try {
|
||||
const type = request.headers.get('x-list-type') || 'prefix';
|
||||
let pattern = request.headers.get('x-pattern') || '';
|
||||
const type = url.searchParams.get('type') || headers.get('x-type') || 'prefix';
|
||||
let pattern = url.searchParams.get('pattern') || headers.get('x-pattern') || '';
|
||||
|
||||
let objects = [];
|
||||
let truncated = true;
|
||||
let cursor = "";
|
||||
let cursor;
|
||||
switch (type) {
|
||||
case 'prefix':
|
||||
while (truncated) {
|
||||
@ -37,12 +49,47 @@ export async function listDispatcher(request, env, ctx) {
|
||||
return badRequest;
|
||||
}
|
||||
|
||||
const res = JSON.stringify(objects, ['key', 'etag', 'size', 'uploaded']);
|
||||
return new Response(res, { status: 200 });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return badRequest;
|
||||
const timestamp = Date.now();
|
||||
const baseUrl = `${url.protocol}//${url.hostname}:${url.port}`;
|
||||
const res = await Promise.all(objects.map(async object => {
|
||||
const { key, etag, size, uploaded } = object;
|
||||
const hash = await getHashString(`${key}${timestamp}${signatureBase}`, 'SHA-1');
|
||||
return { key, etag, size, uploaded,
|
||||
presignedUrl: `${baseUrl}/download/${key}?signature=${hash}×tamp=${timestamp}` };
|
||||
}));
|
||||
console.log(objects[0]);
|
||||
if (resType === 'json')
|
||||
return new Response(JSON.stringify(res), { status: 200 });
|
||||
else if (resType === 'html') {
|
||||
// Generate a table of links with URLs, sizes, and upload dates
|
||||
const body = res.map(obj => `
|
||||
<tr>
|
||||
<td><a href="${obj.presignedUrl}">${obj.key}</a></td>
|
||||
<td>${adaptiveFilesize(obj.size)}</td>
|
||||
<td>${obj.uploaded.toISOString()}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
return new Response(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Object List</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Size</th>
|
||||
<th>Uploaded</th>
|
||||
</tr>
|
||||
${body}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`, { status: 200, headers: { 'content-type': 'text/html' } });
|
||||
}
|
||||
|
||||
} catch (e) { console.log(e); return badRequest; }
|
||||
}
|
||||
|
||||
export default listDispatcher;
|
@ -5,3 +5,9 @@ export async function getHashString(string, method = 'SHA-256') {
|
||||
const hashArray = Array.from(new Uint8Array(hash));
|
||||
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
export function adaptiveFilesize(bytes, digits = 2) {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
else if (bytes < 1048576) return `${(bytes / 1024).toFixed(digits)} KB`;
|
||||
else return `${(bytes / 1048576).toFixed(digits)} MB`;
|
||||
}
|
Loading…
Reference in New Issue
Block a user