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); }