极简 GitHub Porxy 支持GitHub脚本的无限嵌套调用

前言

当我看到这个DD系统的项目时 https://github.com/bin456789/reinstall
我被它的安装命令吸引了.
curl -O https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh || wget -O ${_##*/} $_
找GPT解析了一下原理, 是为了考虑到有些linux系统默认有wget,有些默认有curl .
我准备把我的极简一键脚本的安装方式都改成这样.
curl -LO https://github.com/crazypeace/xray-vless-reality/raw/main/install.sh || wget -O ${_##*/}$_ && bash install.sh 4 8443
然后在脚本的一开始, 安装 curl 和 wget.
apt-get -y install curl wget -qq
我倾向于显示错误内容, 这样在小白反馈问题的时候, 直接发截图或日志就行了. 所以curl和wget没有加静默参数.


复杂的脚本命令, 在结合我的ghproxy时, 如果要实现嵌套github脚本调用, 现有的方案会比较难处理.
我准备改用 WJQSERVER-ghproxy 的思路.
在ghproxy 本身的处理过程中, 针对  .sh 的资源, 全量查找替换, 把访问github资源的链接, 都再套一次自己 ghproxy. 然后再返回.

不影响原来使用 gh-proxy 的用户.

worker.js

我想了一下, 
基于 cloudflare 的 woker, 开发 一个专门 反向代理  github 的工具
1. 本代理 接收的 path部分 应该是一个 http:// 或者 https://
2. 如果 path部分 不是 http:// 或者 https:// 开头
那么加上 http:// 或者 https:// 
3. 判断 本代理 接收的 链接 是否 github
判断方法为:
链接 的域名部分 应该是 git 开头的主域名
如 
github.com
raw.githubusercontent.com
api.github.com
gist.github.com
codeload.github.com
avatars.githubusercontent.com
assets-cdn.github.com
这些域名的 主域名 都是  git 开头的
4. 在获取需要反向代理的内容后
检查 path 是否以 .sh 结尾, 来判断 是否 脚本文件
5. 对于 .sh 结尾的脚本文件
对文本内容进行查找替换
将 github 的链接前面都加上 本代理的域名, 
这样可以解决脚本嵌套使用的场景
判断 是否 github 链接的方法 参考 第3步
把上面这些需求发给GPT. 
走读代码, 提一些小修小改的要求.
就得到了我们的 worker.js
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

/**  * 处理所有传入的请求  * @param {Request} request  */ async function handleRequest(request) {   const url = new URL(request.url);   const workerUrl = url.origin; // 获取 worker 自己的域名, e.g., https://my-worker.example.com
  // 1. 本代理 接受的 path 部分 应该是一个 http:// 或者 https://   // 我们从 path 中提取目标 URL   let path = url.pathname.substring(1); // 移除开头的 '/'
  if (!path || path === 'favicon.ico') {     return new Response('使用方法: ' + workerUrl + '/<您要代理的GitHub链接>', { status: 400 });   }
  // 2. 如果 path 部分 不是 http:// 或者 https:// 开头, 那么加上 https://   if (!path.startsWith('http://') && !path.startsWith('https://')) {     path = 'https://' + path;   }
  let targetUrl;   try {     targetUrl = new URL(path);   } catch (e) {     return new Response('路径中包含无效的 URL', { status: 400 });   }
  // 3. 判断 本代理 接受的 链接 是否 github   if (!isGitHubDomain(targetUrl.hostname)) {     return new Response('访问被拒绝:此代理仅支持 GitHub 相关域名。', { status: 403 });   }
  // 准备转发请求   // GET 或 HEAD 方法不能有 body   const hasBody = request.method === 'POST' || request.method === 'PUT' || request.method === 'PATCH';      const response = await fetch(targetUrl.toString(), {     method: request.method,     headers: request.headers,     body: hasBody ? request.body : null,     redirect: 'follow',   });
  // 复制响应头,并设置 CORS   const newHeaders = new Headers(response.headers);   newHeaders.set('access-control-allow-origin', '*');   newHeaders.set('access-control-allow-headers', '*');   newHeaders.set('access-control-allow-methods', '*');
  // 4. 检查 path 是否以 .sh 结尾   const finalUrl = new URL(response.url);   const isScript = finalUrl.pathname.endsWith('.sh');
  // 5. 对于 .sh 结尾的脚本文件 (并且请求成功)   if (isScript && response.status === 200) {     let bodyText = await response.text();
    // ********** git.io 短链 ************     // 修复 git.io 链接:[空格]git.io 替换为 [空格]https://git.io     bodyText = bodyText.replace(/(\s)(git\.io)/g, '$1https://$2');     // **********************************
    // 对所有 GitHub 链接进行查找替换 (嵌套代理)     // 匹配所有 https?://... 链接     const urlRegex = /(https?:\/\/[^\s"'`()<>]+)/g;
    bodyText = bodyText.replace(urlRegex, (match) => {       try {         // 'match' 是一个完整的 URL, e.g., "https://github.com/foo"         const linkUrl = new URL(match);                  // 使用 isGitHubDomain 函数来判断         if (isGitHubDomain(linkUrl.hostname)) {           // 如果是 GitHub 链接,添加代理前缀           return `${workerUrl}/${match}`;         } else {           // 如果不是,保持原样           return match;         }       } catch (e) {         // 如果 URL 解析失败 (例如,它可能只是看起来像 URL 的文本),保持原样         return match;       }     });
    // 因为修改了内容,所以 content-length 头部失效了,删除它     newHeaders.delete('content-length');
    return new Response(bodyText, {       status: response.status,       statusText: response.statusText,       headers: newHeaders,     });   }
  // 对于非 .sh 文件或非 200 状态码,直接返回(已修改 CORS 和 Location 头部)   return new Response(response.body, {     status: response.status,     statusText: response.statusText,     headers: newHeaders,   }); }
/**  * 助手函数:判断是否为目标的 GitHub 域名  * 域名是否以 git 开头  * @param {string} hostname  * @returns {boolean}  */ function isGitHubDomain(hostname) { // 这个正则表达式检查:   // 1. (^|\.) : 字符串是否以...开头 (^) 或 ( | ) 以一个点 (.) 开头   // 2. git     : 后面紧跟着 'git'   //   // 示例:   // - "github.com"      -> 匹配 (^git)   // - "api.github.com"  -> 匹配 (.git)hub.com   // - "gitlab.com"      -> 匹配 (^git)   // - "my.gitee.com"    -> 匹配 (.git)ee.com   // - "my-git.com"      -> 不匹配 (因为 'g' 前面是 '-' 而不是 '.' 或开头)   return /(^|\.)git/.test(hostname); }

index.html 

工具页面 index.html 本身与 github proxy 是相对独立的.
html内容很少, 也很简单.
参考之前的 gh-proxy 的 index.html 删掉一些界面元素, 调整一些文字说明就行了.

main.js

工具页面 index.html上点击按钮时, 将链接添加ghproxy的工作是在 main.js 中完成的.
借用 worker.js 中的实现, 正好都是JS, 代码直接复制粘贴就行了.

访问worker时, 显示index.html

处理 path 为空时, path 为 styles.css 时, path 为 main.js 时, 应该去访问 github page
// 如果path为空,返回主页
if (!path) {
  return fetch(ASSET_URL)
}
if (path === 'styles.css') {
  return fetch(ASSET_URL + path)
}
if (path === 'main.js') {
  return fetch(ASSET_URL + path)
}

处理嵌套的代理调用

防止出现 https://ghproxy.icdyct.nyc.mn/https://ghproxy.icdyct.nyc.mn/https://api.github.com/repos/XTLS/Xray-core/releases/latest
const selfPrefixFull = workerUrl + '/'; 

while (true) {   if (path.startsWith(selfPrefixFull)) {     path = path.substring(selfPrefixFull.length);   } else {     // 当 path 不再以任何一个前缀开头时,跳出循环     break;   } }

上传 Github


演示站


演示视频


部署方法


========

后记

在本次 (2025-11-6) 面向GPT开发中最好用的GPT是 

========

相关推荐


评论