Last active
June 24, 2025 17:40
-
-
Save chenxuan520/7f6c0017b774955726eaca2a20d6b5ba to your computer and use it in GitHub Desktop.
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
| 'use strict' | |
| /** | |
| * this file from https://github.com/hunshcn/gh-proxy | |
| */ | |
| // index.html interface | |
| async function indexInterface() { | |
| const text = `<!DOCTYPE html> | |
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>github clone/文件下载</title> | |
| <!-- Tailwind CSS v3 --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- Font Awesome --> | |
| <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"> | |
| <!-- Tailwind配置 --> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#0D1117', | |
| secondary: '#58A6FF', | |
| accent: '#238636', | |
| neutral: '#161B22', | |
| 'neutral-dark': '#30363D', | |
| 'text-light': '#C9D1D9', | |
| 'text-muted': '#8B949E', | |
| }, | |
| fontFamily: { | |
| inter: ['Inter', 'system-ui', 'sans-serif'], | |
| }, | |
| } | |
| } | |
| } | |
| </script> | |
| <!-- 自定义工具类 --> | |
| <style type="text/tailwindcss"> | |
| @layer utilities { | |
| .content-auto { | |
| content-visibility: auto; | |
| } | |
| .shadow-github { | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25); | |
| } | |
| .border-glow { | |
| box-shadow: 0 0 0 3px rgba(56, 139, 253, 0.3); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-primary text-text-light min-h-screen flex flex-col"> | |
| <!-- 顶部导航 --> | |
| <header class="bg-neutral text-text-light py-4 px-6 shadow-md border-b border-neutral-dark"> | |
| <div class="container mx-auto flex justify-between items-center"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fa fa-github text-2xl text-white"></i> | |
| <span class="font-bold text-xl">011203.xyz</span> | |
| </div> | |
| <nav> | |
| <ul class="flex space-x-6"> | |
| </ul> | |
| </nav> | |
| </div> | |
| </header> | |
| <!-- 主要内容 --> | |
| <main class="flex-grow flex items-center justify-center p-6"> | |
| <div class="container mx-auto max-w-3xl"> | |
| <div class="text-center mb-10"> | |
| <h1 class="text-[clamp(2rem,5vw,3.5rem)] font-bold text-white mb-4">clone.011203.xyz</h1> | |
| <p class="text-text-muted text-lg max-w-2xl mx-auto">github 文件下载</p> | |
| </div> | |
| <div class="bg-neutral rounded-xl shadow-github p-8 border border-neutral-dark"> | |
| <!-- 内容输入框 --> | |
| <div class="mb-6"> | |
| <label for="content" class="block text-sm font-medium text-text-light mb-2">请输入github文件或仓库链接</label> | |
| <textarea | |
| id="content" | |
| class="w-full p-4 bg-primary border border-neutral-dark rounded-lg text-text-light focus:ring-2 focus:ring-secondary focus:border-secondary transition-all duration-200 h-24 resize-none" | |
| placeholder="请输入要添加到URL的内容..." | |
| ></textarea> | |
| </div> | |
| <!-- 链接显示区域 --> | |
| <div class="space-y-4 mb-6"> | |
| <!-- 文件下载链接 --> | |
| <div> | |
| <label class="block text-sm font-medium text-text-light mb-2">文件下载链接</label> | |
| <div class="flex items-center"> | |
| <input | |
| id="downloadUrl" | |
| type="text" | |
| class="flex-grow p-2 bg-primary border border-neutral-dark rounded-l-lg text-text-light focus:ring-2 focus:ring-secondary focus:border-secondary transition-all duration-200" | |
| readonly | |
| placeholder="生成的链接将显示在这里" | |
| > | |
| <button | |
| id="copyDownloadBtn" | |
| class="bg-neutral-dark hover:bg-neutral-dark/80 text-text-light px-3 py-2 rounded-r-lg transition-colors duration-200" | |
| > | |
| <i class="fa fa-copy"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Clone链接 --> | |
| <div> | |
| <label class="block text-sm font-medium text-text-light mb-2">Clone链接</label> | |
| <div class="flex items-center"> | |
| <input | |
| id="cloneUrl" | |
| type="text" | |
| class="flex-grow p-2 bg-primary border border-neutral-dark rounded-l-lg text-text-light focus:ring-2 focus:ring-secondary focus:border-secondary transition-all duration-200" | |
| readonly | |
| placeholder="生成的链接将显示在这里" | |
| > | |
| <button | |
| id="copyCloneBtn" | |
| class="bg-neutral-dark hover:bg-neutral-dark/80 text-text-light px-3 py-2 rounded-r-lg transition-colors duration-200" | |
| > | |
| <i class="fa fa-copy"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 生成按钮 --> | |
| <div class="flex justify-center"> | |
| <button | |
| id="generateBtn" | |
| class="bg-accent hover:bg-accent/90 text-white font-medium py-3 px-8 rounded-lg transition-all duration-200 flex items-center shadow-md hover:shadow-lg transform hover:-translate-y-0.5" | |
| > | |
| <i class="fa fa-link mr-2"></i> | |
| 生成链接 | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- 页脚 --> | |
| <footer class="bg-neutral text-text-muted py-6 px-6 border-t border-neutral-dark"> | |
| <div class="container mx-auto text-center"> | |
| <p>仅供学习, 请勿用于任何其他用途</p> | |
| <div class="mt-4 flex justify-center space-x-4"> | |
| <a href="https://gist.github.com/chenxuan520/7f6c0017b774955726eaca2a20d6b5ba" class="hover:text-secondary transition-colors duration-200"><i class="fa fa-github"></i></a> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- JavaScript --> | |
| <script> | |
| // 获取元素 | |
| const contentTextarea = document.getElementById('content'); | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const downloadUrl = document.getElementById('downloadUrl'); | |
| const cloneUrl = document.getElementById('cloneUrl'); | |
| const copyDownloadBtn = document.getElementById('copyDownloadBtn'); | |
| const copyCloneBtn = document.getElementById('copyCloneBtn'); | |
| // 生成按钮点击事件 | |
| generateBtn.addEventListener('click', () => { | |
| const content = contentTextarea.value.trim(); | |
| if (content === '') { | |
| alert('请输入内容后再生成链接!'); | |
| return; | |
| } | |
| // 使用更健壮的URL构建方法 | |
| const baseUrl = new URL(window.location.href).origin; | |
| const contentPath = content; | |
| const fullDownloadUrl = baseUrl+'/'+contentPath; | |
| const fullCloneUrl = \`git clone \${fullDownloadUrl}\`; | |
| // 填充到输入框 | |
| downloadUrl.value = fullDownloadUrl; | |
| cloneUrl.value = fullCloneUrl; | |
| // 显示成功消息 | |
| showNotification('链接生成成功!'); | |
| // 滚动到链接区域 | |
| downloadUrl.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | |
| }); | |
| // 复制下载链接 | |
| copyDownloadBtn.addEventListener('click', () => { | |
| copyToClipboard(downloadUrl, copyDownloadBtn); | |
| }); | |
| // 复制Clone链接 | |
| copyCloneBtn.addEventListener('click', () => { | |
| copyToClipboard(cloneUrl, copyCloneBtn); | |
| }); | |
| // 复制到剪贴板的辅助函数 | |
| function copyToClipboard(inputElement, buttonElement) { | |
| if (!inputElement.value) { | |
| alert('没有可复制的链接!'); | |
| return; | |
| } | |
| inputElement.select(); | |
| document.execCommand('copy'); | |
| // 显示复制成功图标 | |
| const originalHTML = buttonElement.innerHTML; | |
| buttonElement.innerHTML = '<i class="fa fa-check"></i>'; | |
| setTimeout(() => { | |
| buttonElement.innerHTML = originalHTML; | |
| }, 2000); | |
| } | |
| // 显示通知消息 | |
| function showNotification(message) { | |
| // 创建通知元素 | |
| const notification = document.createElement('div'); | |
| notification.className = 'fixed bottom-4 right-4 bg-accent text-white px-4 py-2 rounded-lg shadow-lg z-50 transform transition-all duration-300 opacity-0 translate-y-4'; | |
| notification.textContent = message; | |
| // 添加到页面 | |
| document.body.appendChild(notification); | |
| // 显示通知 | |
| setTimeout(() => { | |
| notification.classList.remove('opacity-0', 'translate-y-4'); | |
| }, 10); | |
| // 3秒后隐藏 | |
| setTimeout(() => { | |
| notification.classList.add('opacity-0', 'translate-y-4'); | |
| setTimeout(() => { | |
| document.body.removeChild(notification); | |
| }, 300); | |
| }, 3000); | |
| } | |
| // 文本框输入事件 - 自动调整高度 | |
| contentTextarea.addEventListener('input', function() { | |
| this.style.height = 'auto'; | |
| this.style.height = (this.scrollHeight) + 'px'; | |
| }); | |
| </script> | |
| </body> | |
| </html>`; | |
| return text; | |
| } | |
| // 前缀,如果自定义路由为example.com/gh/*,将PREFIX改为 '/gh/',注意,少一个杠都会错! | |
| const PREFIX = '/' | |
| // 分支文件使用jsDelivr镜像的开关,0为关闭,默认关闭 | |
| const Config = { | |
| jsdelivr: 0 | |
| } | |
| const whiteList = [] // 白名单,路径里面有包含字符的才会通过,e.g. ['/username/'] | |
| /** @type {ResponseInit} */ | |
| const PREFLIGHT_INIT = { | |
| status: 204, | |
| headers: new Headers({ | |
| 'access-control-allow-origin': '*', | |
| 'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS', | |
| 'access-control-max-age': '1728000', | |
| }), | |
| } | |
| const exp1 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:releases|archive)\/.*$/i | |
| const exp2 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:blob|raw)\/.*$/i | |
| const exp3 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:info|git-).*$/i | |
| const exp4 = /^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+?\/.+$/i | |
| const exp5 = /^(?:https?:\/\/)?gist\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+$/i | |
| const exp6 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/tags.*$/i | |
| /** | |
| * @param {any} body | |
| * @param {number} status | |
| * @param {Object<string, string>} headers | |
| */ | |
| function makeRes(body, status = 200, headers = {}) { | |
| headers['access-control-allow-origin'] = '*' | |
| return new Response(body, {status, headers}) | |
| } | |
| /** | |
| * @param {string} urlStr | |
| */ | |
| function newUrl(urlStr) { | |
| try { | |
| return new URL(urlStr) | |
| } catch (err) { | |
| return null | |
| } | |
| } | |
| addEventListener('fetch', e => { | |
| const ret = fetchHandler(e) | |
| .catch(err => makeRes('cfworker error:\n' + err.stack, 502)) | |
| e.respondWith(ret) | |
| }) | |
| function checkUrl(u) { | |
| for (let i of [exp1, exp2, exp3, exp4, exp5, exp6]) { | |
| if (u.search(i) === 0) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| /** | |
| * @param {FetchEvent} e | |
| */ | |
| async function fetchHandler(e) { | |
| const req = e.request | |
| const urlStr = req.url | |
| const urlObj = new URL(urlStr) | |
| let path = urlObj.searchParams.get('q') | |
| if (path) { | |
| return Response.redirect('https://' + urlObj.host + PREFIX + path, 301) | |
| } | |
| // cfworker 会把路径中的 `//` 合并成 `/` | |
| path = urlObj.href.substr(urlObj.origin.length + PREFIX.length).replace(/^https?:\/+/, 'https://') | |
| if (path.search(exp1) === 0 || path.search(exp5) === 0 || path.search(exp6) === 0 || path.search(exp3) === 0 || path.search(exp4) === 0) { | |
| return httpHandler(req, path) | |
| } else if (path.search(exp2) === 0) { | |
| if (Config.jsdelivr) { | |
| const newUrl = path.replace('/blob/', '@').replace(/^(?:https?:\/\/)?github\.com/, 'https://cdn.jsdelivr.net/gh') | |
| return Response.redirect(newUrl, 302) | |
| } else { | |
| path = path.replace('/blob/', '/raw/') | |
| return httpHandler(req, path) | |
| } | |
| } else if (path.search(exp4) === 0) { | |
| const newUrl = path.replace(/(?<=com\/.+?\/.+?)\/(.+?\/)/, '@$1').replace(/^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com/, 'https://cdn.jsdelivr.net/gh') | |
| return Response.redirect(newUrl, 302) | |
| } else { | |
| return new Response(await indexInterface(), { | |
| headers: { | |
| 'Content-Type': 'text/html; charset=UTF-8', | |
| }, | |
| }); | |
| } | |
| } | |
| /** | |
| * @param {Request} req | |
| * @param {string} pathname | |
| */ | |
| function httpHandler(req, pathname) { | |
| const reqHdrRaw = req.headers | |
| // preflight | |
| if (req.method === 'OPTIONS' && | |
| reqHdrRaw.has('access-control-request-headers') | |
| ) { | |
| return new Response(null, PREFLIGHT_INIT) | |
| } | |
| const reqHdrNew = new Headers(reqHdrRaw) | |
| let urlStr = pathname | |
| let flag = !Boolean(whiteList.length) | |
| for (let i of whiteList) { | |
| if (urlStr.includes(i)) { | |
| flag = true | |
| break | |
| } | |
| } | |
| if (!flag) { | |
| return new Response("blocked", {status: 403}) | |
| } | |
| if (urlStr.search(/^https?:\/\//) !== 0) { | |
| urlStr = 'https://' + urlStr | |
| } | |
| const urlObj = newUrl(urlStr) | |
| /** @type {RequestInit} */ | |
| const reqInit = { | |
| method: req.method, | |
| headers: reqHdrNew, | |
| redirect: 'manual', | |
| body: req.body | |
| } | |
| return proxy(urlObj, reqInit) | |
| } | |
| /** | |
| * | |
| * @param {URL} urlObj | |
| * @param {RequestInit} reqInit | |
| */ | |
| async function proxy(urlObj, reqInit) { | |
| const res = await fetch(urlObj.href, reqInit) | |
| const resHdrOld = res.headers | |
| const resHdrNew = new Headers(resHdrOld) | |
| const status = res.status | |
| if (resHdrNew.has('location')) { | |
| let _location = resHdrNew.get('location') | |
| if (checkUrl(_location)) | |
| resHdrNew.set('location', PREFIX + _location) | |
| else { | |
| reqInit.redirect = 'follow' | |
| return proxy(newUrl(_location), reqInit) | |
| } | |
| } | |
| resHdrNew.set('access-control-expose-headers', '*') | |
| resHdrNew.set('access-control-allow-origin', '*') | |
| resHdrNew.delete('content-security-policy') | |
| resHdrNew.delete('content-security-policy-report-only') | |
| resHdrNew.delete('clear-site-data') | |
| return new Response(res.body, { | |
| status, | |
| headers: resHdrNew, | |
| }) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment