-
-
Save DoingDog/79fcd195189e436e2af4af825cdaee7a to your computer and use it in GitHub Desktop.
A proxy download cloudflare worker
This file contains 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
addEventListener("fetch", (event) => { | |
event.respondWith(handleRequest(event.request).catch(({ stack }) => new Response(stack, { status: 500 }))); | |
}); | |
function getQueryParam(url, paramName) { | |
const queryStartIdx = url.indexOf("?"); | |
if (queryStartIdx < 0) { | |
return null; | |
} | |
const query = url.slice(queryStartIdx + 1); | |
const params = new URLSearchParams(query); | |
const paramValue = params.get(paramName); | |
if (!paramValue || paramValue === "") { | |
return null; | |
} | |
return paramValue; | |
} | |
async function handleRequest(request) { | |
const url = getUrl(request); | |
if (!url) { | |
return new Response(Page, { | |
headers: { "Content-Type": "text/html" }, | |
status: 404, | |
}); | |
} | |
let response = await fetch( | |
new Request(url, { | |
body: request.body, | |
headers: request.headers, | |
method: request.method, | |
redirect: "follow", | |
}) | |
); | |
const modType = getQueryParam(request.url, "type"); | |
const headers = new Headers(response.headers); | |
if (modType === "reverse") { | |
const fileType = response.headers.get("Content-Type"); | |
if (fileType) { | |
function getHost(str) { | |
return str.replace(/^(https?:\/\/[^/]+)(\/.*)?$/, "$1"); | |
} | |
const cloneBody = await response.clone().text(); | |
const proxyHost = getHost(request.url); | |
function processUrl(str, proxy, proxyUrl) { | |
const addProxy = `${proxy}/?type=reverse&url=`; | |
if (str.startsWith("http")) { | |
return addProxy + encodeURIComponent(str); | |
} else if (str.startsWith("//")) { | |
return addProxy + encodeURIComponent(`https:${str}`); | |
} else if (str.startsWith("./")) { | |
return addProxy + encodeURIComponent(proxyUrl.replace(/^(https?:\/\/.+)\/[^\/]*$/, "$1") + str.substring(1)); | |
} else if (str.startsWith("/")) { | |
return addProxy + encodeURIComponent(getHost(proxyUrl) + str); | |
} else { | |
return addProxy + encodeURIComponent(proxyUrl.replace(/^(https?:\/\/.+)\/[^\/]*$/, "$1/") + str); | |
} | |
} | |
if (fileType.startsWith("text/html")) { | |
const regex = /((?:href|src|action)=["'])(.*?)["']/gi; | |
const replacedBody = cloneBody.replace(regex, (match, p1, p2) => `${p1}${processUrl(p2, proxyHost, url)}"`); | |
if (headers.get("Content-Security-Policy")) { | |
headers.set("Content-Security-Policy", `default-src *;`); | |
} | |
return new Response(replacedBody, { | |
headers, | |
status: response.status, | |
}); | |
} else if (fileType.startsWith("text/css")) { | |
const cssRegex = /url\(["'](.*?)["']\)/gi; | |
const replacedCss = cloneBody.replace(cssRegex, (match, p1) => `url("${processUrl(p1, proxyHost, url)}")`); | |
return new Response(replacedCss, { | |
headers, | |
status: response.status, | |
}); | |
} | |
} | |
return new Response(response.body, { | |
headers, | |
status: response.status, | |
}); | |
} | |
if (modType) { | |
headers.set("Content-Type", `${modType}`); | |
} | |
if (getQueryParam(request.url, "download")) { | |
const contentDisposition = headers.get("Content-Disposition"); | |
if (!contentDisposition) { | |
const filenameinurl = url.split("/").pop(); | |
headers.set("Content-Disposition", `attachment; filename="${filenameinurl}"`); | |
} | |
} else { | |
headers.delete("Content-Disposition"); | |
} | |
if (getQueryParam(request.url, "fetch")) { | |
return new Response(response.body, { | |
headers, | |
status: response.status, | |
}); | |
} else { | |
let { readable, writable } = new TransformStream(); | |
response.body.pipeTo(writable); | |
return new Response(readable, { | |
headers, | |
status: response.status, | |
}); | |
} | |
} | |
const getUrl = ({ url }) => { | |
const { pathname, searchParams } = new URL(url); | |
if (pathname.startsWith("/http")) { | |
return pathname | |
.slice(1) | |
.replace(/http:\/(?!\/)/, "http://") | |
.replace(/https:\/(?!\/)/, "https://"); | |
} | |
const searchParamsUrl = searchParams.get("url"); | |
if (searchParamsUrl?.startsWith("http")) { | |
return searchParamsUrl; | |
} | |
}; | |
const Page = ` | |
<!doctype html> | |
<html> | |
<head> | |
<link rel="icon" href="data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-flower1" viewBox="0 0 16 16"><path d="M6.174 1.184a2 2 0 0 1 3.652 0A2 2 0 0 1 12.99 3.01a2 2 0 0 1 1.826 3.164 2 2 0 0 1 0 3.652 2 2 0 0 1-1.826 3.164 2 2 0 0 1-3.164 1.826 2 2 0 0 1-3.652 0A2 2 0 0 1 3.01 12.99a2 2 0 0 1-1.826-3.164 2 2 0 0 1 0-3.652A2 2 0 0 1 3.01 3.01a2 2 0 0 1 3.164-1.826zM8 1a1 1 0 0 0-.998 1.03l.01.091c.012.077.029.176.054.296.049.241.122.542.213.887.182.688.428 1.513.676 2.314L8 5.762l.045-.144c.248-.8.494-1.626.676-2.314.091-.345.164-.646.213-.887a4.997 4.997 0 0 0 .064-.386L9 2a1 1 0 0 0-1-1zM2 9l.03-.002.091-.01a4.99 4.99 0 0 0 .296-.054c.241-.049.542-.122.887-.213a60.59 60.59 0 0 0 2.314-.676L5.762 8l-.144-.045a60.59 60.59 0 0 0-2.314-.676 16.705 16.705 0 0 0-.887-.213 4.99 4.99 0 0 0-.386-.064L2 7a1 1 0 1 0 0 2zm7 5-.002-.03a5.005 5.005 0 0 0-.064-.386 16.398 16.398 0 0 0-.213-.888 60.582 60.582 0 0 0-.676-2.314L8 10.238l-.045.144c-.248.8-.494 1.626-.676 2.314-.091.345-.164.646-.213.887a4.996 4.996 0 0 0-.064.386L7 14a1 1 0 1 0 2 0zm-5.696-2.134.025-.017a5.001 5.001 0 0 0 .303-.248c.184-.164.408-.377.661-.629A60.614 60.614 0 0 0 5.96 9.23l.103-.111-.147.033a60.88 60.88 0 0 0-2.343.572c-.344.093-.64.18-.874.258a5.063 5.063 0 0 0-.367.138l-.027.014a1 1 0 1 0 1 1.732zM4.5 14.062a1 1 0 0 0 1.366-.366l.014-.027c.01-.02.021-.048.036-.084a5.09 5.09 0 0 0 .102-.283c.078-.233.165-.53.258-.874a60.6 60.6 0 0 0 .572-2.343l.033-.147-.11.102a60.848 60.848 0 0 0-1.743 1.667 17.07 17.07 0 0 0-.629.66 5.06 5.06 0 0 0-.248.304l-.017.025a1 1 0 0 0 .366 1.366zm9.196-8.196a1 1 0 0 0-1-1.732l-.025.017a4.951 4.951 0 0 0-.303.248 16.69 16.69 0 0 0-.661.629A60.72 60.72 0 0 0 10.04 6.77l-.102.111.147-.033a60.6 60.6 0 0 0 2.342-.572c.345-.093.642-.18.875-.258a4.993 4.993 0 0 0 .367-.138.53.53 0 0 0 .027-.014zM11.5 1.938a1 1 0 0 0-1.366.366l-.014.027c-.01.02-.021.048-.036.084a5.09 5.09 0 0 0-.102.283c-.078.233-.165.53-.258.875a60.62 60.62 0 0 0-.572 2.342l-.033.147.11-.102a60.848 60.848 0 0 0 1.743-1.667c.252-.253.465-.477.629-.66a5.001 5.001 0 0 0 .248-.304l.017-.025a1 1 0 0 0-.366-1.366zM14 9a1 1 0 0 0 0-2l-.03.002a4.996 4.996 0 0 0-.386.064c-.242.049-.543.122-.888.213-.688.182-1.513.428-2.314.676L10.238 8l.144.045c.8.248 1.626.494 2.314.676.345.091.646.164.887.213a4.996 4.996 0 0 0 .386.064L14 9zM1.938 4.5a1 1 0 0 0 .393 1.38l.084.035c.072.03.166.064.283.103.233.078.53.165.874.258a60.88 60.88 0 0 0 2.343.572l.147.033-.103-.111a60.584 60.584 0 0 0-1.666-1.742 16.705 16.705 0 0 0-.66-.629 4.996 4.996 0 0 0-.304-.248l-.025-.017a1 1 0 0 0-1.366.366zm2.196-1.196.017.025a4.996 4.996 0 0 0 .248.303c.164.184.377.408.629.661A60.597 60.597 0 0 0 6.77 5.96l.111.102-.033-.147a60.602 60.602 0 0 0-.572-2.342c-.093-.345-.18-.642-.258-.875a5.006 5.006 0 0 0-.138-.367l-.014-.027a1 1 0 1 0-1.732 1zm9.928 8.196a1 1 0 0 0-.366-1.366l-.027-.014a5 5 0 0 0-.367-.138c-.233-.078-.53-.165-.875-.258a60.619 60.619 0 0 0-2.342-.572l-.147-.033.102.111a60.73 60.73 0 0 0 1.667 1.742c.253.252.477.465.66.629a4.946 4.946 0 0 0 .304.248l.025.017a1 1 0 0 0 1.366-.366zm-3.928 2.196a1 1 0 0 0 1.732-1l-.017-.025a5.065 5.065 0 0 0-.248-.303 16.705 16.705 0 0 0-.629-.661A60.462 60.462 0 0 0 9.23 10.04l-.111-.102.033.147a60.6 60.6 0 0 0 .572 2.342c.093.345.18.642.258.875a4.985 4.985 0 0 0 .138.367.575.575 0 0 0 .014.027zM8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/></svg>"> | |
<meta charset=UTF-8> | |
<meta http-equiv=X-UA-Compatible content="IE=edge"> | |
<meta name=viewport content="width=device-width,initial-scale=1"> | |
<title>Proxy</title> | |
<style>body,div,form{display:flex}body,select[name=type]{color:#fff;background-color:#000}body,html{height:100%;overflow:hidden}body{justify-content:center;align-items:center;height:100vh;margin:0;font-family:Arial,'Helvetica Neue',Helvetica,sans-serif}form{flex-direction:column;align-items:center}input[name=url]{margin-right:10px;flex-grow:1;height:30px;font-size:16px;border-radius:5px;border:2px solid #fff;padding:5px;color:#fff;background-color:transparent}button#download,button#fetch{background-color:red;color:#fff;border:none;border-radius:5px;cursor:pointer;height:30px;padding:5px;margin:10px}input#download:checked+button#download,input#fetch:checked+button#fetch{background-color:green;color:#fff}div{align-items:center}button[type=button],button[type=submit]{height:30px;font-size:16px;border-radius:5px;border:none;padding:5px 10px;cursor:pointer}button[type=submit]{background-color:#007bff;color:#fff;margin-right:10px}button[type=button]{background-color:#333;color:#eee}button[type=button]:hover,button[type=submit]:hover{opacity:.8}select[name=type]{margin-top:10px;height:30px;font-size:16px;border-radius:5px;border:2px solid #fff;padding:5px}</style> | |
</head> | |
<body> | |
<form> | |
<div> | |
<input name=url type=url required> | |
<button type=submit>→</button> | |
<button type=button onclick=clearInput()>Clear</button> | |
</div> | |
<br> | |
<select name=type id=type> | |
<option value="" selected>As Is</option> | |
<option value=application/json>application/json</option> | |
<option value=application/msword>application/msword</option> | |
<option value=application/octet-stream>application/octet-stream</option> | |
<option value=application/pdf>application/pdf</option> | |
<option value=application/xml>application/xml</option> | |
<option value=application/zip>application/zip</option> | |
<option value=audio/mp3>audio/mp3</option> | |
<option value=audio/ogg>audio/ogg</option> | |
<option value=audio/wav>audio/wav</option> | |
<option value=audio/webm>audio/webm</option> | |
<option value=image/avif>image/avif</option> | |
<option value=image/bmp>image/bmp</option> | |
<option value=image/gif>image/gif</option> | |
<option value=image/jpeg>image/jpeg</option> | |
<option value=image/png>image/png</option> | |
<option value=image/svg+xml>image/svg+xml</option> | |
<option value=image/webp>image/webp</option> | |
<option value=image/webp>image/webp</option> | |
<option value="text/css;charset=utf8">text/css</option> | |
<option value="text/html;charset=utf8">text/html</option> | |
<option value="text/javascript;charset=utf8">text/javascript</option> | |
<option value="text/plain;charset=utf8">text/plain</option> | |
<option value=video/mp2t>video/mp2t</option> | |
<option value=video/mp4>video/mp4</option> | |
<option value=video/ogg>video/ogg</option> | |
<option value=video/quicktime>video/quicktime</option> | |
<option value=video/webm>video/webm</option> | |
<option value=video/x-msvideo>video/x-msvideo</option> | |
<option value=reverse>Reverse</option> | |
</select><br> | |
<div> | |
<input type=checkbox id=download name=download value=true style=display:none> | |
<button type=button id=download onclick=toggleDownload()>As File</button> | |
<input type=checkbox id=fetch name=fetch value=true style=display:none> | |
<button type=button id=fetch onclick=toggleFetch()>Use Fetch</button> | |
</div> | |
</form> | |
<script>function clearInput(){document.querySelector("input[name='url']").value=""}function toggleDownload(){var e=document.getElementById("download"),c=document.querySelector("input[name='download']");c.checked?(e.style.backgroundColor="red",c.checked=!1):(e.style.backgroundColor="green",c.checked=!0)}function toggleFetch(){var e=document.getElementById("fetch"),c=document.querySelector("input[name='fetch']");c.checked?(e.style.backgroundColor="red",c.checked=!1):(e.style.backgroundColor="green",c.checked=!0)}</script> | |
</body> | |
</html> | |
`; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment