Created
February 26, 2025 01:39
-
-
Save umutbasal/9db0da22fff9fea10f0b966f318ebb44 to your computer and use it in GitHub Desktop.
securityfocus.com.worker.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const TIME_TRAVEL_DATETIME = "20250101"; // Format: yyyyMMdd | |
const WAYBACK_URL = "https://web.archive.org/web/"; | |
const WAYBACK_URL_FORMAT = /https:\/\/web\.archive\.org\/web\/([0-9a-z_]+)\/(.*)/i; | |
const BASE_DOMAIN = "http://securityfocus.com"; | |
export default { | |
async fetch(request, env, ctx) { | |
const url = new URL(request.url); | |
const path = url.pathname; | |
const cache = caches.default; | |
const cacheKey = new Request(url, request); | |
let response = await cache.match(cacheKey); | |
if (response) { | |
return response; // Serve cached response | |
} | |
// Handle resource requests (e.g., /web/20200101123456/http://securityfocus.com/asdasd.css) | |
if (path.startsWith("/web/")) { | |
const archiveUrl = "https://web.archive.org" + path; | |
try { | |
const response = await fetch(archiveUrl); | |
return new Response(response.body, { | |
status: response.status, | |
statusText: response.statusText, | |
headers: response.headers, | |
}); | |
} catch (error) { | |
return createErrorResponse(500, "Internal Server Error", `Error fetching resource from archive: ${error.message}`); | |
} | |
} | |
// Handle initial page requests (e.g., /path) | |
if (request.method.toLowerCase() !== "get") { | |
return createErrorResponse(400, "Bad Request", "Only GET requests are supported."); | |
} | |
const originalUrl = BASE_DOMAIN + path; | |
try { | |
// Get the Wayback Machine URL for the requested path | |
const waybackUrl = await getTargetUrl(originalUrl); | |
if (!waybackUrl) { | |
return createErrorResponse(404, "Not Found", `No archived snapshot available for ${originalUrl}`); | |
} | |
// Fetch the archived content | |
const archivedResponse = await fetch(waybackUrl, { | |
headers: { | |
"User-Agent": "Cloudflare-Worker-Time-Travel-Proxy/0.1", | |
}, | |
}); | |
// Handle redirects | |
if (archivedResponse.status === 301 || archivedResponse.status === 302) { | |
const location = archivedResponse.headers.get("location"); | |
const originalRedirectUrl = getOriginalUrl(location); | |
return new Response(null, { | |
status: archivedResponse.status, | |
statusText: archivedResponse.status === 301 ? "Moved Permanently" : "Found", | |
headers: { "Location": originalRedirectUrl }, | |
}); | |
} | |
// Return the archived content | |
ctx.waitUntil(cache.put(cacheKey, archivedResponse.clone())); | |
return new Response(archivedResponse.body, { | |
status: 200, | |
statusText: "OK", | |
headers: { | |
"Content-Type": archivedResponse.headers.get("content-type") || "text/html", | |
"Server": "Cloudflare-Worker-Time-Travel-Proxy/0.1", | |
}, | |
}); | |
} catch (error) { | |
console.error(`Error processing request for ${originalUrl}: ${error.message}`); | |
return createErrorResponse(502, "Bad Gateway", `Failed to fetch content: ${error.message}`); | |
} | |
} | |
}; | |
/** | |
* Queries the Wayback Machine API to find the closest archived snapshot | |
* @param {string} sourceUrl - The original URL to look up in the archive | |
* @returns {Promise<string|null>} - The archived URL or null if not found | |
*/ | |
async function getTargetUrl(sourceUrl) { | |
// Default redirect URL (can lead to loops, so we’ll refine it) | |
let result = `${WAYBACK_URL}${TIME_TRAVEL_DATETIME}id_/${sourceUrl}`; | |
try { | |
// Query the Wayback Machine API for the closest snapshot | |
const apiUrl = `https://archive.org/wayback/available?url=${sourceUrl}`; | |
const apiUrl2 = `https://archive.org/wayback/available?url=${sourceUrl}/`; | |
const apiResponses = await Promise.all([fetch(apiUrl),fetch(apiUrl2)]); | |
const ress = await Promise.all(apiResponses.map(x=>x.json())) | |
const json = ress.filter(x=>x.archived_snapshots.closest.status==200)[0]; | |
if (json.archived_snapshots && json.archived_snapshots.closest) { | |
const availableTimestamp = json.archived_snapshots.closest.timestamp; | |
result = `${WAYBACK_URL}${availableTimestamp}id_/${sourceUrl}`; | |
} else { | |
return null; // No snapshot available | |
} | |
} catch (error) { | |
console.error(`Error querying Wayback API for ${sourceUrl}: ${error.message}`); | |
// Fallback to the default URL if the API fails | |
} | |
return result; | |
} | |
/** | |
* Extracts the original URL from a Wayback Machine URL | |
* @param {string} translatedUrl - The full Wayback Machine URL | |
* @returns {string} - The original URL | |
*/ | |
function getOriginalUrl(translatedUrl) { | |
const match = WAYBACK_URL_FORMAT.exec(translatedUrl); | |
return match && match[2] ? match[2] : translatedUrl; | |
} | |
/** | |
* Creates an error response with HTML content | |
* @param {number} status - HTTP status code | |
* @param {string} statusText - HTTP status text | |
* @param {string} message - Detailed error message | |
* @returns {Response} - The error response object | |
*/ | |
function createErrorResponse(status, statusText, message) { | |
const html = ` | |
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> | |
<html><head> | |
<title>${status} ${statusText}</title> | |
</head><body> | |
<h1>${statusText}</h1> | |
<p>${message}</p> | |
</body></html> | |
`; | |
return new Response(html, { | |
status, | |
statusText, | |
headers: { "Content-Type": "text/html" }, | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment