- Block file downloads and previews with a 424 StatusFailedDependency if file processing failed or the box has issues. - Register routes for `/service-worker.js` and `/share-target` to support PWA features. - Update README.md with an AI usage disclosure.
111 lines
3.2 KiB
JavaScript
111 lines
3.2 KiB
JavaScript
self.addEventListener("fetch", (event) => {
|
|
const url = new URL(event.request.url);
|
|
if (event.request.method === "POST" && url.origin === self.location.origin && url.pathname === "/share-target") {
|
|
event.respondWith(handleShareTarget(event.request));
|
|
}
|
|
});
|
|
|
|
const SHARE_CACHE = "warpbox-share-target-v1";
|
|
const SHARE_PREFIX = "/__warpbox_share_target__/";
|
|
const LATEST_KEY = SHARE_PREFIX + "latest";
|
|
|
|
async function handleShareTarget(request) {
|
|
const id = Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10);
|
|
try {
|
|
const formData = await request.formData();
|
|
const files = collectSharedFiles(formData);
|
|
const cache = await caches.open(SHARE_CACHE);
|
|
const metadata = {
|
|
id,
|
|
title: stringValue(formData.get("title")),
|
|
text: stringValue(formData.get("text")),
|
|
url: stringValue(formData.get("url")),
|
|
createdAt: new Date().toISOString(),
|
|
files: [],
|
|
};
|
|
|
|
await deletePreviousShare(cache);
|
|
for (let index = 0; index < files.length; index += 1) {
|
|
const file = files[index];
|
|
const key = SHARE_PREFIX + "file/" + encodeURIComponent(id) + "/" + index;
|
|
metadata.files.push({
|
|
key,
|
|
name: file.name || "shared-file",
|
|
type: file.type || "application/octet-stream",
|
|
size: file.size || 0,
|
|
lastModified: file.lastModified || Date.now(),
|
|
});
|
|
await cache.put(key, new Response(file, {
|
|
headers: {
|
|
"Content-Type": file.type || "application/octet-stream",
|
|
"Cache-Control": "no-store",
|
|
},
|
|
}));
|
|
}
|
|
|
|
await cache.put(LATEST_KEY, jsonResponse(metadata));
|
|
await cache.put(SHARE_PREFIX + "meta/" + encodeURIComponent(id), jsonResponse(metadata));
|
|
} catch (error) {
|
|
await storeShareError(id, error);
|
|
}
|
|
|
|
return Response.redirect("/?share-target=1&share-id=" + encodeURIComponent(id), 303);
|
|
}
|
|
|
|
function collectSharedFiles(formData) {
|
|
const files = [];
|
|
["files", "file", "sharex"].forEach((name) => {
|
|
formData.getAll(name).forEach((value) => {
|
|
if (value instanceof File && value.size > 0) {
|
|
files.push(value);
|
|
}
|
|
});
|
|
});
|
|
return files;
|
|
}
|
|
|
|
function stringValue(value) {
|
|
return typeof value === "string" ? value : "";
|
|
}
|
|
|
|
function jsonResponse(payload) {
|
|
return new Response(JSON.stringify(payload), {
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Cache-Control": "no-store",
|
|
},
|
|
});
|
|
}
|
|
|
|
async function storeShareError(id, error) {
|
|
const cache = await caches.open(SHARE_CACHE);
|
|
await cache.put(LATEST_KEY, jsonResponse({
|
|
id,
|
|
error: error && error.message ? error.message : "Shared files could not be staged.",
|
|
createdAt: new Date().toISOString(),
|
|
files: [],
|
|
}));
|
|
}
|
|
|
|
async function deletePreviousShare(cache) {
|
|
const previous = await cache.match(LATEST_KEY);
|
|
if (!previous) {
|
|
return;
|
|
}
|
|
let metadata = null;
|
|
try {
|
|
metadata = await previous.json();
|
|
} catch (error) {
|
|
metadata = null;
|
|
}
|
|
for (const file of metadata && metadata.files ? metadata.files : []) {
|
|
if (file.key) {
|
|
await cache.delete(file.key);
|
|
}
|
|
}
|
|
if (metadata && metadata.id) {
|
|
await cache.delete(SHARE_PREFIX + "meta/" + encodeURIComponent(metadata.id));
|
|
}
|
|
await cache.delete(LATEST_KEY);
|
|
}
|