Skip to content

Instantly share code, notes, and snippets.

@umutbasal
Created February 26, 2025 01:39
Show Gist options
  • Save umutbasal/9db0da22fff9fea10f0b966f318ebb44 to your computer and use it in GitHub Desktop.
Save umutbasal/9db0da22fff9fea10f0b966f318ebb44 to your computer and use it in GitHub Desktop.
securityfocus.com.worker.js
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