1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| export default { async fetch(request, env, ctx) { const cache = caches.default; const kv = env.SCREENSHOT;
const requestUrl = new URL(request.url); const targetUrl = requestUrl.searchParams.get("url");
if (!targetUrl) { return new Response("Missing 'url' parameter", { status: 400 }); }
const referer = request.headers.get("referer") || ""; const origin = request.headers.get("origin") || ""; const allowedDomains = env.ALLOWED_DOMAINS ? env.ALLOWED_DOMAINS.split(",") : [];
const isAllowed = allowedDomains.some(domain => { const trimmedDomain = domain.trim(); return referer.includes(trimmedDomain) || origin.includes(trimmedDomain); });
if (allowedDomains.length > 0 && !isAllowed) { return new Response("Access denied: request not from allowed domain", { status: 403 }); }
const date = new Date().toISOString().split("T")[0]; const cacheKey = targetUrl; const datedKey = `${targetUrl}?${date}`;
const buildResponse = (buffer) => new Response(buffer, { headers: { "content-type": "image/png", "cache-control": "public, max-age=86400, immutable", }, });
const tryGetCachedResponse = async (key) => { let res = await cache.match(key); if (res) return res;
const kvData = await kv.get(key, { type: "arrayBuffer" }); if (kvData) { res = buildResponse(kvData); ctx.waitUntil(cache.put(key, res.clone())); return res; } return null; };
let res = await tryGetCachedResponse(datedKey); if (res) return res;
try { const payload = { url: targetUrl, viewport: { width: 1200, height: 800 }, gotoOptions: { waitUntil: "networkidle0" }, };
const apiRes = await fetch( `https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/browser-rendering/screenshot?cacheTTL=86400`, { method: "POST", headers: { Authorization: `Bearer ${env.CF_API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(payload), } );
if (!apiRes.ok) throw new Error(`API returned ${apiRes.status}`);
const buffer = await apiRes.arrayBuffer(); res = buildResponse(buffer);
ctx.waitUntil(Promise.all([ kv.put(cacheKey, buffer), kv.put(datedKey, buffer, { expirationTtl: 86400 }), cache.put(cacheKey, res.clone()), cache.put(datedKey, res.clone()), ]));
return res; } catch (err) { console.error("Screenshot generation failed:", err);
res = await tryGetCachedResponse(cacheKey); if (res) return res;
return new Response("Screenshot generation failed", { status: 502 }); } }, };
|