Monorepo for Tangled
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102export default { async fetch(request, env) { const url = new URL(request.url);
if (url.pathname === "/" || url.pathname === "") { return new Response( "This is Tangled's Camo service. It proxies images served from knots via Cloudflare.", ); }
const cache = caches.default;
const pathParts = url.pathname.slice(1).split("/"); if (pathParts.length < 2) { return new Response("Bad URL", { status: 400 }); }
const [signatureHex, ...hexUrlParts] = pathParts; const hexUrl = hexUrlParts.join(""); const urlBytes = Uint8Array.from( hexUrl.match(/.{2}/g).map((b) => parseInt(b, 16)), ); const targetUrl = new TextDecoder().decode(urlBytes);
// check if we have an entry in the cache with the target url let cacheKey = new Request(targetUrl); let response = await cache.match(cacheKey); if (response) { return response; }
// else compute the signature const key = await crypto.subtle.importKey( "raw", new TextEncoder().encode(env.CAMO_SHARED_SECRET), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"], );
const computedSigBuffer = await crypto.subtle.sign("HMAC", key, urlBytes); const computedSig = Array.from(new Uint8Array(computedSigBuffer)) .map((b) => b.toString(16).padStart(2, "0")) .join("");
console.log({ level: "debug", message: "camo target: " + targetUrl, computedSignature: computedSig, providedSignature: signatureHex, targetUrl: targetUrl, });
const sigBytes = Uint8Array.from( signatureHex.match(/.{2}/g).map((b) => parseInt(b, 16)), ); const valid = await crypto.subtle.verify("HMAC", key, sigBytes, urlBytes);
if (!valid) { return new Response("Invalid signature", { status: 403 }); }
let parsedUrl; try { parsedUrl = new URL(targetUrl); if (!["https:", "http:"].includes(parsedUrl.protocol)) { return new Response("Only HTTP(S) allowed", { status: 400 }); } } catch { return new Response("Malformed URL", { status: 400 }); }
// fetch from the parsed URL const res = await fetch(parsedUrl.toString(), { headers: { "User-Agent": "Tangled Camo v0.1.0" }, });
const allowedMimeTypes = require("./mimetypes.json");
const contentType = res.headers.get("Content-Type") || "application/octet-stream";
if (!allowedMimeTypes.includes(contentType.split(";")[0].trim())) { return new Response("Unsupported media type", { status: 415 }); }
const headers = new Headers(); headers.set("Content-Type", contentType); headers.set("Cache-Control", "public, max-age=86400, immutable");
// serve and cache it with cf response = new Response(await res.arrayBuffer(), { status: res.status, headers, });
await cache.put(cacheKey, response.clone());
return response; },};