admin 发表于 2024-8-14 21:22:20

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

背景
在中国,访问 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 (.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 === '$custom_domain' ? requestURL.host : replace_dict
    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))
}
页: [1]
查看完整版本: 在国内快速访问 GitHub:自建镜像,告别延迟困扰。