feat(backend): handle processing errors and add PWA routes
- 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.
This commit is contained in:
110
backend/static/js/service-worker.js
Normal file
110
backend/static/js/service-worker.js
Normal file
@@ -0,0 +1,110 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user