Repository: AlistGo/alist (language: go)

Issue:
alist存储：github发布、github API  能否增加 ghproxy、CDN加速 通用的代理，而不是alist-proxy，因为ghproxy、CDN加速 实在是太多了
### Please make sure of the following things

- [x] I have read the [documentation](https://alist.nn.ci).
- [x] I'm sure there are no duplicate issues or discussions.
- [x] I'm sure this feature is not implemented.
- [x] I'm sure it's a reasonable and popular requirement.

### Description of the feature / 需求描述

https://github.com/AlistGo/alist/pull/7844#issuecomment-2617847411 这里提到[AlistGo/alist-proxy](https://github.com/AlistGo/alist-proxy/blob/main/alist-proxy.js) 作为代理，但是有一个问题， 非80和443端口，无法用 cloudflare搭建的 alist代理。

因为 宽带运营商不给80和443端口，我家里有公网ip，在小主机上搭建alist 用的5244端口，无法用 cloudflare搭建的 alist代理（已实测5244不可以）。

**建议  _github 发布_  存储 增加一项 ghproxy的地址栏，这种github专用代理非常多，失效可以随时换成其他的。**


**_GitHub API_  采用 CDN加速github仓库里的文件**
https://cdn.jsdelivr.net/gh/{github用户名}/{仓库名}@{分支名}/，其中 {github用户名}、{仓库名}、{分支名}分别对应Github用户名、仓库名、分支名。 这个是 jsdelivr 提供的加速CDN，有点不稳定，但有N个 jsdelivr 开源项目可用。
某个仓库里的一个iptv文件直链加速链接是   https://cdn.jsdmirror.cn/gh/wwb521/live/movies.json ，这个链接可以直接用在 iptv app里面。
洛雪音乐源 https://fastly.jsdelivr.net/gh/Huibq/keep-alive/render_api.js


![Image](https://github.com/user-attachments/assets/19e676df-f425-46ea-85de-e35c6316a560)


ghproxy 加速releases文件
ghproxy 原作者 https://github.com/hunshcn/gh-proxy    
ghproxy各种代理合集、测速  https://yishijie.gitlab.io/ziyuan/

CDN加速 仓库里的文件
免费的CDN加速网址（有12个） https://github.com/jsdmirror/JSDMirror 或 https://cnb.cool/jsdmirror/jsdmirror  
利用vercel一键搭建CDN稳定（要绑定自己的域名） https://github.com/rong6/jsd

github releases有一个实用的案例，就是 把常用的github开源软件 搭建到alist里，如果软件有新版，直接到alist去找。
github api 实用案例，加速一个在 github仓库里的 iptv文件或者 在线音乐源文件给 空壳播放器用。

### Suggested solution / 实现思路

_No response_

### Additional context / 附件

_No response_


Discussion / hints:
你可以选择使用代理地址,然后代理地址再重定向到ghproxy曲线救国
> 你可以选择使用代理地址,然后代理地址再重定向到ghproxy

![Image](https://github.com/user-attachments/assets/a715b1b1-2132-4445-8b60-bd45c5c59fe9)

麻烦问一下 在alist里 如何操作？
打开web代理 然后填入你的代理url(随便写一个你能用的) 例如说我这边是https://sub.example.com
然后随便点一个文件下载,肯定会无法下载的,然后记下获取到的链接,例如
https://test2.yyyyt.top/test/ghr/alist-web-gh/3.42.0/dist.zip?sign=sIEvFcGnLSDGTUdOvadldOy2Q775Omjv-kO4mvuZ8OI=:1739037789
紧接着建一个站点在sub.example.com 让nginx重定向请求(也可以用cf的重定向功能)保留url参数. 例如说我这边挂在到的是/test/ghr 挂载alist的web的发布到alist-web-gh文件夹下那么就让nginx重定向请求并保留url

让nginx把https://sub.example.com/test/ghr/alist-web-gh/*的请求重定向到 https://ghmirror.cn/https://github.com/此处省略一大堆/
同时保留url
这个不太方便用语言说...可能会说不清楚
反正就是利用后面的路径都一样 只是前面不一样 然后让alist返回代理地址,代理地址再依靠后面的路径重定向到ghmirror的地址来实现的
比如说上面那个例子实现起来就是这样
alist重定向到https://test2.yyyyt.top/test/ghr/alist-web-gh/3.42.0/dist.zip?sign=sIEvFcGnLSDGTUdOvadldOy2Q775Omjv-kO4mvuZ8OI=:1739037789
然后test2.yyyyt.top/test/ghr/alist-web-gh/3.42.0/dist.zip?xxxxx
再重定向给https://ghmirror/https://github.com/AlistGo/alist-web/releases/download/3.42.0/dist.zip
实现调用ghmirror
说白了就是根据后面的路径如/3.42.0/dist.zip 都是一样的然后可以靠代理地址二次重定向来实现的
> 打开web代理 然后填入你的代理url(随便写一个你能用的) 例如说我这边是https://sub.example.com 然后随便点一个文件下载,肯定会无法下载的,然后记下获取到的链接,例如 https://test2.yyyyt.top/test/ghr/alist-web-gh/3.42.0/dist.zip?sign=sIEvFcGnLSDGTUdOvadldOy2Q775Omjv-kO4mvuZ8OI=:1739037789 紧接着建一个站点在sub.example.com 让nginx重定向请求(也可以用cf的重定向功能)保留url参数. 例如说我这边挂在到的是/test/ghr 挂载alist的web的发布到alist-web-gh文件夹下那么就让nginx重定向请求并保留url
> 
> 让nginx把[https://sub.example.com/test/ghr/alist-web-gh/*的请求重定向到](https://sub.example.com/test/ghr/alist-web-gh/*%E7%9A%84%E8%AF%B7%E6%B1%82%E9%87%8D%E5%AE%9A%E5%90%91%E5%88%B0) [https://ghmirror.cn/https://github.com/此处省略一大堆/](https://ghmirror.cn/https://github.com/%E6%AD%A4%E5%A4%84%E7%9C%81%E7%95%A5%E4%B8%80%E5%A4%A7%E5%A0%86/) 同时保留url 这个不太方便用语言说...可能会说不清楚 反正就是利用后面的路径都一样 只是前面不一样 然后让alist返回代理地址,代理地址再依靠后面的路径重定向到ghmirror的地址来实现的 比如说上面那个例子实现起来就是这样 alist重定向到https://test2.yyyyt.top/test/ghr/alist-web-gh/3.42.0/dist.zip?sign=sIEvFcGnLSDGTUdOvadldOy2Q775Omjv-kO4mvuZ8OI=:1739037789 然后test2.yyyyt.top/test/ghr/alist-web-gh/3.42.0/dist.zip?xxxxx 再重定向给https://ghmirror/https://github.com/AlistGo/alist-web/releases/download/3.42.0/dist.zip 实现调用ghmirror 说白了就是根据后面的路径如/3.42.0/dist.zip 都是一样的然后可以靠代理地址二次重定向来实现的

谢谢，我试试，也许搞不定
新 pr 合并了 [#7938](https://github.com/AlistGo/alist/pull/7938#:~:text=alist%2Dorg/alist-,4.%20%E5%A2%9E%E5%8A%A0%20gh%2Dproxy%20%E6%94%AF%E6%8C%81,-%E5%A1%AB%E5%85%A5%20https%3A//ghproxy)。
beta 可用了

可以填入 https://ghproxy.net/github.com
会将原下载链接 https://github.com/AlistGo/alist/releases/download/v3.42.0/alist-linux-amd64.tar.gz
替换为 https://ghproxy.net/github.com/AlistGo/alist/releases/download/v3.42.0/alist-linux-amd64.tar.gz

一些公开示例：
+ `https://ghproxy.net/github.com`
+ `https://gh-proxy.com/github.com`
+ `https://ghproxy.cn/https://github.com`

也可以使用 [ymyuuu/Cloudflare-Workers-Proxy](https://github.com/ymyuuu/Cloudflare-Workers-Proxy)
+ 填入 `https://example.com/https://github.com`
> 新 pr 合并了 [#7938](https://github.com/AlistGo/alist/pull/7938#:~:text=alist%2Dorg/alist-,4.%20%E5%A2%9E%E5%8A%A0%20gh%2Dproxy%20%E6%94%AF%E6%8C%81,-%E5%A1%AB%E5%85%A5%20https%3A//ghproxy)。 beta 可用了
> 
> 可以填入 https://ghproxy.net/github.com 会将原下载链接 https://github.com/AlistGo/alist/releases/download/v3.42.0/alist-linux-amd64.tar.gz 替换为 https://ghproxy.net/github.com/AlistGo/alist/releases/download/v3.42.0/alist-linux-amd64.tar.gz
> 
> 一些公开示例：
> 
> * `https://ghproxy.net/github.com`
> * `https://gh-proxy.com/github.com`
> * `https://ghproxy.cn/https://github.com`
> 
> 也可以使用 [ymyuuu/Cloudflare-Workers-Proxy](https://github.com/ymyuuu/Cloudflare-Workers-Proxy)
> 
> * 填入 `https://example.com/https://github.com`

docker更新到main后 支持gh-proxy了，非常感谢

![Image](https://github.com/user-attachments/assets/af856073-c409-4580-8f51-265151ea24f2)

github api 能考虑增加 jsDelivr 一类的cdn吗？

Fix patch:
diff --git a/drivers/github/driver.go b/drivers/github/driver.go
index 996c79c74e9..dee4cbbf2db 100644
--- a/drivers/github/driver.go
+++ b/drivers/github/driver.go
@@ -85,10 +85,13 @@ func (d *Github) Init(ctx context.Context) error {
 	}
 	d.client = base.NewRestyClient().
 		SetHeader("Accept", "application/vnd.github.object+json").
-		SetHeader("Authorization", "Bearer "+d.Token).
 		SetHeader("X-GitHub-Api-Version", "2022-11-28").
 		SetLogger(log.StandardLogger()).
 		SetDebug(false)
+	token := strings.TrimSpace(d.Token)
+	if token != "" {
+		d.client = d.client.SetHeader("Authorization", "Bearer "+token)
+	}
 	if d.Ref == "" {
 		repo, err := d.getRepo()
 		if err != nil {
@@ -149,8 +152,13 @@ func (d *Github) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
 	if obj.Type == "submodule" {
 		return nil, errors.New("cannot download a submodule")
 	}
+	url := obj.DownloadURL
+	ghProxy := strings.TrimSpace(d.Addition.GitHubProxy)
+	if ghProxy != "" {
+		url = strings.Replace(url, "https://raw.githubusercontent.com", ghProxy, 1)
+	}
 	return &model.Link{
-		URL: obj.DownloadURL,
+		URL: url,
 	}, nil
 }
 
@@ -679,8 +687,11 @@ func (d *Github) putBlob(ctx context.Context, s model.FileStreamer, up driver.Up
 		return "", err
 	}
 	req.Header.Set("Accept", "application/vnd.github+json")
-	req.Header.Set("Authorization", "Bearer "+d.Token)
 	req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
+	token := strings.TrimSpace(d.Token)
+	if token != "" {
+		req.Header.Set("Authorization", "Bearer "+token)
+	}
 	req.ContentLength = length
 
 	res, err := base.HttpClient.Do(req)
diff --git a/drivers/github/meta.go b/drivers/github/meta.go
index 0df4aa60988..05e704be8e0 100644
--- a/drivers/github/meta.go
+++ b/drivers/github/meta.go
@@ -7,10 +7,11 @@ import (
 
 type Addition struct {
 	driver.RootPath
-	Token           string `json:"token" type:"string" required:"true"`
+	Token           string `json:"token" type:"string"`
 	Owner           string `json:"owner" type:"string" required:"true"`
 	Repo            string `json:"repo" type:"string" required:"true"`
 	Ref             string `json:"ref" type:"string" help:"A branch, a tag or a commit SHA, main branch by default."`
+	GitHubProxy     string `json:"gh_proxy" type:"string" help:"GitHub proxy, e.g. https://ghproxy.net/raw.githubusercontent.com or https://gh-proxy.com/raw.githubusercontent.com"`
 	CommitterName   string `json:"committer_name" type:"string"`
 	CommitterEmail  string `json:"committer_email" type:"string"`
 	AuthorName      string `json:"author_name" type:"string"`


Test patch: