一元网络论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 72|回复: 0

在国内快速访问 GitHub:自建镜像,告别延迟困扰。

[复制链接]

1万

主题

1万

帖子

5万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
56445
发表于 2024-8-14 21:22:20 | 显示全部楼层 |阅读模式
背景
在中国,访问 GitHub 速度受限,导致下载代码库和克隆项目缓慢。即使使用第三方镜像,也存在稳定性和可靠性问题。为了解决这一问题,我决定自部署一个 GitHub 镜像,并分享部署过程。
项目简介
我参考了前辈的镜像代码,但发现无法实现登录。因此,我使用 Cloudflare Workers 重新构建了镜像,通过 Cloudflare 全球 CDN 网络,将 GitHub 请求代理到最近的数据中心,从而加速访问速度。
下面是代码:
const upstream = 'github.com'
const upstream_path = '/'
const upstream_mobile = 'github.com'
const blocked_region = ['KP', 'SY', 'PK', 'CU']
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']
const https = true
const replace_dict = {
  '$upstream': '$custom_domain',
  '//github.com': '',
  'https://github.com': ''
}
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
  const region = request.headers.get('cf-ipcountry').toUpperCase()
  const ip_address = request.headers.get('cf-connecting-ip')
  const user_agent = request.headers.get('user-agent')
  
  if (blocked_region.includes(region)) {
    return new Response('Access denied: WorkersProxy is not available in your region yet.', { status: 403 })
  } else if (blocked_ip_address.includes(ip_address)) {
    return new Response('Access denied: Your IP address is blocked by WorkersProxy.', { status: 403 })
  }
  
  const requestURL = new URL(request.url)
  const path = requestURL.pathname
  const destURL = new URL((https ? 'https://' : 'http://') + upstream + path)
  
  // 添加原始查询参数
  destURL.search = requestURL.search
  
  const upstreamDomain = await device_status(user_agent) ? upstream : upstream_mobile
  const newRequestHeaders = new Headers(request.headers)
  newRequestHeaders.set('Host', upstreamDomain)
  newRequestHeaders.set('Referer', 'https://' + upstreamDomain)
  
  // 处理Origin头
  const origin = request.headers.get('Origin')
  if (origin) {
    newRequestHeaders.set('Origin', 'https://' + upstreamDomain)
  }
  
  // 处理ZIP下载请求
  if (path.endsWith('.zip') || path.includes('/archive/')) {
    return handleZipDownload(destURL, newRequestHeaders)
  }
  
  // 处理git操作
  if (path.includes('/info/refs') || path.includes('/git-upload-pack')) {
    return handleGitOperation(destURL, request, newRequestHeaders)
  }
  
  let response = await fetch(destURL.href, {
    method: request.method,
    headers: newRequestHeaders,
    body: request.body,
    redirect: 'manual',
  })
  
  let newResponseHeaders = new Headers(response.headers)
  newResponseHeaders.set('Access-Control-Allow-Origin', '*')
  newResponseHeaders.set('Access-Control-Allow-Credentials', 'true')
  newResponseHeaders.delete('Content-Security-Policy')
  newResponseHeaders.delete('Content-Security-Policy-Report-Only')
  newResponseHeaders.delete('Clear-Site-Data')
  
  // 处理重定向
  if ([301, 302, 307, 308].includes(response.status)) {
    const location = newResponseHeaders.get('Location')
    if (location) {
      const redirectURL = new URL(location)
      redirectURL.host = requestURL.host
      newResponseHeaders.set('Location', redirectURL.href)
    }
    return new Response(null, {
      status: response.status,
      headers: newResponseHeaders
    })
  }
  
  const content_type = newResponseHeaders.get('content-type')
  let body = await response.text()
  
  // 替换响应中的域名
  for (let key in replace_dict) {
    const replaceKey = key === '$upstream' ? upstreamDomain : key
    const replaceValue = replace_dict[key] === '$custom_domain' ? requestURL.host : replace_dict[key]
    body = body.replace(new RegExp(replaceKey, 'g'), replaceValue)
  }
  
  return new Response(body, {
    status: response.status,
    headers: newResponseHeaders
  })
}
async function handleZipDownload(destURL, headers) {
  const response = await fetch(destURL.href, {
    headers: headers,
    redirect: 'follow',
  })
  
  if (response.ok) {
    const newHeaders = new Headers(response.headers)
    newHeaders.set('Content-Disposition', response.headers.get('Content-Disposition') || 'attachment')
    return new Response(response.body, {
      status: response.status,
      headers: newHeaders
    })
  } else {
    return new Response('Failed to download ZIP file', { status: 404 })
  }
}
async function handleGitOperation(destURL, request, headers) {
  const response = await fetch(destURL.href, {
    method: request.method,
    headers: headers,
    body: request.body,
    redirect: 'follow',
  })
  
  if (response.ok) {
    const newHeaders = new Headers(response.headers)
    newHeaders.set('Cache-Control', 'no-cache')
    return new Response(response.body, {
      status: response.status,
      headers: newHeaders
    })
  } else {
    return new Response('Git operation failed', { status: response.status })
  }
}
async function device_status(user_agent_info) {
  const agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]
  return !agents.some(agent => user_agent_info.includes(agent))
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|一元网络论坛

GMT+8, 2024-9-20 08:36 , Processed in 0.221133 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表