release: prepare v0.1.133
This commit is contained in:
@@ -1 +1 @@
|
|||||||
0.1.132
|
0.1.133
|
||||||
|
|||||||
@@ -150,6 +150,9 @@ type GeminiTierQuotaConfig struct {
|
|||||||
type UpdateConfig struct {
|
type UpdateConfig struct {
|
||||||
// GitHubRepo 用于在线更新的 GitHub 仓库,格式 owner/repo
|
// GitHubRepo 用于在线更新的 GitHub 仓库,格式 owner/repo
|
||||||
GitHubRepo string `mapstructure:"github_repo"`
|
GitHubRepo string `mapstructure:"github_repo"`
|
||||||
|
// GitHubMirrorBaseURL 用于通过 GitHub 镜像站访问 API 和 Release 下载地址。
|
||||||
|
// 为空时不启用;不含 {url} 时会拼接为 <base>/<original-url>。
|
||||||
|
GitHubMirrorBaseURL string `mapstructure:"github_mirror_base_url"`
|
||||||
// ProxyURL 用于访问 GitHub 的代理地址
|
// ProxyURL 用于访问 GitHub 的代理地址
|
||||||
// 支持 http/https/socks5/socks5h 协议
|
// 支持 http/https/socks5/socks5h 协议
|
||||||
// 例如: "http://127.0.0.1:7890", "socks5://127.0.0.1:1080"
|
// 例如: "http://127.0.0.1:7890", "socks5://127.0.0.1:1080"
|
||||||
@@ -1613,6 +1616,7 @@ func setDefaults() {
|
|||||||
|
|
||||||
// Update
|
// Update
|
||||||
viper.SetDefault("update.github_repo", "man209111-cpu/sub2api")
|
viper.SetDefault("update.github_repo", "man209111-cpu/sub2api")
|
||||||
|
viper.SetDefault("update.github_mirror_base_url", "")
|
||||||
viper.SetDefault("update.proxy_url", "")
|
viper.SetDefault("update.proxy_url", "")
|
||||||
|
|
||||||
// Timezone (default to Asia/Shanghai for Chinese users)
|
// Timezone (default to Asia/Shanghai for Chinese users)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
type githubReleaseClient struct {
|
type githubReleaseClient struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
downloadHTTPClient *http.Client
|
downloadHTTPClient *http.Client
|
||||||
|
mirrorBaseURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
type githubReleaseClientError struct {
|
type githubReleaseClientError struct {
|
||||||
@@ -29,7 +31,7 @@ type githubReleaseClientError struct {
|
|||||||
// 代理配置失败时行为由 allowDirectOnProxyError 控制:
|
// 代理配置失败时行为由 allowDirectOnProxyError 控制:
|
||||||
// - false(默认):返回错误占位客户端,禁止回退到直连
|
// - false(默认):返回错误占位客户端,禁止回退到直连
|
||||||
// - true:回退到直连(仅限管理员显式开启)
|
// - true:回退到直连(仅限管理员显式开启)
|
||||||
func NewGitHubReleaseClient(proxyURL string, allowDirectOnProxyError bool) service.GitHubReleaseClient {
|
func NewGitHubReleaseClient(proxyURL string, githubMirrorBaseURL string, allowDirectOnProxyError bool) service.GitHubReleaseClient {
|
||||||
// 安全说明:httpclient.GetClient 的错误链(url.Parse / proxyutil)不含明文代理凭据,
|
// 安全说明:httpclient.GetClient 的错误链(url.Parse / proxyutil)不含明文代理凭据,
|
||||||
// 但仍通过 slog 仅在服务端日志记录,不会暴露给 HTTP 响应。
|
// 但仍通过 slog 仅在服务端日志记录,不会暴露给 HTTP 响应。
|
||||||
sharedClient, err := httpclient.GetClient(httpclient.Options{
|
sharedClient, err := httpclient.GetClient(httpclient.Options{
|
||||||
@@ -60,6 +62,7 @@ func NewGitHubReleaseClient(proxyURL string, allowDirectOnProxyError bool) servi
|
|||||||
return &githubReleaseClient{
|
return &githubReleaseClient{
|
||||||
httpClient: sharedClient,
|
httpClient: sharedClient,
|
||||||
downloadHTTPClient: downloadClient,
|
downloadHTTPClient: downloadClient,
|
||||||
|
mirrorBaseURL: normalizeMirrorBaseURL(githubMirrorBaseURL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +81,7 @@ func (c *githubReleaseClientError) FetchChecksumFile(ctx context.Context, url st
|
|||||||
func (c *githubReleaseClient) FetchLatestRelease(ctx context.Context, repo string) (*service.GitHubRelease, error) {
|
func (c *githubReleaseClient) FetchLatestRelease(ctx context.Context, repo string) (*service.GitHubRelease, error) {
|
||||||
url := fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", repo)
|
url := fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", repo)
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.rewriteURL(url), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -104,7 +107,7 @@ func (c *githubReleaseClient) FetchLatestRelease(ctx context.Context, repo strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *githubReleaseClient) DownloadFile(ctx context.Context, url, dest string, maxSize int64) error {
|
func (c *githubReleaseClient) DownloadFile(ctx context.Context, url, dest string, maxSize int64) error {
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.rewriteURL(url), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -152,7 +155,7 @@ func (c *githubReleaseClient) DownloadFile(ctx context.Context, url, dest string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *githubReleaseClient) FetchChecksumFile(ctx context.Context, url string) ([]byte, error) {
|
func (c *githubReleaseClient) FetchChecksumFile(ctx context.Context, url string) ([]byte, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.rewriteURL(url), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -169,3 +172,26 @@ func (c *githubReleaseClient) FetchChecksumFile(ctx context.Context, url string)
|
|||||||
|
|
||||||
return io.ReadAll(resp.Body)
|
return io.ReadAll(resp.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *githubReleaseClient) rewriteURL(originalURL string) string {
|
||||||
|
if c == nil || c.mirrorBaseURL == "" {
|
||||||
|
return originalURL
|
||||||
|
}
|
||||||
|
if strings.Contains(c.mirrorBaseURL, "{url}") {
|
||||||
|
return strings.ReplaceAll(c.mirrorBaseURL, "{url}", originalURL)
|
||||||
|
}
|
||||||
|
return strings.TrimRight(c.mirrorBaseURL, "/") + "/" + originalURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeMirrorBaseURL(raw string) string {
|
||||||
|
raw = strings.TrimSpace(raw)
|
||||||
|
if raw == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parsed, err := url.Parse(raw)
|
||||||
|
if err != nil || parsed.Scheme != "https" || parsed.Host == "" {
|
||||||
|
slog.Warn("invalid github mirror base url, mirror disabled", "service", "github_release")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimRight(raw, "/")
|
||||||
|
}
|
||||||
|
|||||||
@@ -245,6 +245,42 @@ func (s *GitHubReleaseServiceSuite) TestFetchLatestRelease_Success() {
|
|||||||
require.Equal(s.T(), "app-linux-amd64.tar.gz", release.Assets[0].Name)
|
require.Equal(s.T(), "app-linux-amd64.tar.gz", release.Assets[0].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *GitHubReleaseServiceSuite) TestFetchLatestRelease_UsesMirrorBaseURL() {
|
||||||
|
releaseJSON := `{
|
||||||
|
"tag_name": "v1.0.0",
|
||||||
|
"name": "Release 1.0.0",
|
||||||
|
"body": "Release notes",
|
||||||
|
"html_url": "https://github.com/test/repo/releases/v1.0.0",
|
||||||
|
"assets": []
|
||||||
|
}`
|
||||||
|
|
||||||
|
s.srv = newLocalTestServer(s.T(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
require.Equal(s.T(), "/https://api.github.com/repos/test/repo/releases/latest", r.URL.Path)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = w.Write([]byte(releaseJSON))
|
||||||
|
}))
|
||||||
|
|
||||||
|
s.client = &githubReleaseClient{
|
||||||
|
httpClient: s.srv.Client(),
|
||||||
|
downloadHTTPClient: &http.Client{},
|
||||||
|
mirrorBaseURL: s.srv.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
release, err := s.client.FetchLatestRelease(context.Background(), "test/repo")
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
require.Equal(s.T(), "v1.0.0", release.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GitHubReleaseServiceSuite) TestRewriteURL_UsesTemplateMirror() {
|
||||||
|
s.client = &githubReleaseClient{
|
||||||
|
mirrorBaseURL: "https://mirror.example.com/{url}",
|
||||||
|
}
|
||||||
|
|
||||||
|
got := s.client.rewriteURL("https://github.com/test/repo/releases/download/v1/app.tar.gz")
|
||||||
|
require.Equal(s.T(), "https://mirror.example.com/https://github.com/test/repo/releases/download/v1/app.tar.gz", got)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GitHubReleaseServiceSuite) TestFetchLatestRelease_Non200() {
|
func (s *GitHubReleaseServiceSuite) TestFetchLatestRelease_Non200() {
|
||||||
s.srv = newLocalTestServer(s.T(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
s.srv = newLocalTestServer(s.T(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func ProvideConcurrencyCache(rdb *redis.Client, cfg *config.Config) service.Conc
|
|||||||
// ProvideGitHubReleaseClient 创建 GitHub Release 客户端
|
// ProvideGitHubReleaseClient 创建 GitHub Release 客户端
|
||||||
// 从配置中读取代理设置,支持国内服务器通过代理访问 GitHub
|
// 从配置中读取代理设置,支持国内服务器通过代理访问 GitHub
|
||||||
func ProvideGitHubReleaseClient(cfg *config.Config) service.GitHubReleaseClient {
|
func ProvideGitHubReleaseClient(cfg *config.Config) service.GitHubReleaseClient {
|
||||||
return NewGitHubReleaseClient(cfg.Update.ProxyURL, cfg.Security.ProxyFallback.AllowDirectOnError)
|
return NewGitHubReleaseClient(cfg.Update.ProxyURL, cfg.Update.GitHubMirrorBaseURL, cfg.Security.ProxyFallback.AllowDirectOnError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvidePricingRemoteClient 创建定价数据远程客户端
|
// ProvidePricingRemoteClient 创建定价数据远程客户端
|
||||||
|
|||||||
@@ -1084,6 +1084,14 @@ update:
|
|||||||
# GitHub repository used for online update checks and binary downloads.
|
# GitHub repository used for online update checks and binary downloads.
|
||||||
# 在线更新检查和二进制下载使用的 GitHub 仓库。
|
# 在线更新检查和二进制下载使用的 GitHub 仓库。
|
||||||
github_repo: "man209111-cpu/sub2api"
|
github_repo: "man209111-cpu/sub2api"
|
||||||
|
# GitHub mirror base URL for online updates.
|
||||||
|
# 在线更新使用的 GitHub 镜像站前缀。
|
||||||
|
# Examples:
|
||||||
|
# - "https://gh.llkk.cc"
|
||||||
|
# - "https://mirror.example.com/{url}"
|
||||||
|
# Leave empty for direct GitHub URLs.
|
||||||
|
# 留空表示使用原始 GitHub 地址。
|
||||||
|
github_mirror_base_url: ""
|
||||||
# Proxy URL for accessing GitHub (used for online updates and pricing data)
|
# Proxy URL for accessing GitHub (used for online updates and pricing data)
|
||||||
# 用于访问 GitHub 的代理地址(用于在线更新和定价数据获取)
|
# 用于访问 GitHub 的代理地址(用于在线更新和定价数据获取)
|
||||||
# Supports: http, https, socks5, socks5h
|
# Supports: http, https, socks5, socks5h
|
||||||
|
|||||||
@@ -146,6 +146,9 @@ services:
|
|||||||
# =======================================================================
|
# =======================================================================
|
||||||
# GitHub repo for online update checks and binary downloads
|
# GitHub repo for online update checks and binary downloads
|
||||||
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
|
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
|
||||||
|
# GitHub mirror base URL for online updates.
|
||||||
|
# Example: https://gh.llkk.cc or https://mirror.example.com/{url}
|
||||||
|
- UPDATE_GITHUB_MIRROR_BASE_URL=${UPDATE_GITHUB_MIRROR_BASE_URL:-}
|
||||||
# Proxy for accessing GitHub (online updates + pricing data)
|
# Proxy for accessing GitHub (online updates + pricing data)
|
||||||
# Examples: http://host:port, socks5://host:port
|
# Examples: http://host:port, socks5://host:port
|
||||||
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
|
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ services:
|
|||||||
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
|
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
|
||||||
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
|
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# Update Configuration (在线更新配置)
|
||||||
|
# =======================================================================
|
||||||
|
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
|
||||||
|
- UPDATE_GITHUB_MIRROR_BASE_URL=${UPDATE_GITHUB_MIRROR_BASE_URL:-}
|
||||||
|
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
|
||||||
|
|
||||||
# Built-in OAuth client secrets (optional)
|
# Built-in OAuth client secrets (optional)
|
||||||
# SECURITY: This repo does not embed third-party client_secret.
|
# SECURITY: This repo does not embed third-party client_secret.
|
||||||
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
|
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
|
||||||
|
|||||||
@@ -142,6 +142,9 @@ services:
|
|||||||
# =======================================================================
|
# =======================================================================
|
||||||
# GitHub repo for online update checks and binary downloads
|
# GitHub repo for online update checks and binary downloads
|
||||||
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
|
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
|
||||||
|
# GitHub mirror base URL for online updates.
|
||||||
|
# Example: https://gh.llkk.cc or https://mirror.example.com/{url}
|
||||||
|
- UPDATE_GITHUB_MIRROR_BASE_URL=${UPDATE_GITHUB_MIRROR_BASE_URL:-}
|
||||||
# Proxy for accessing GitHub (online updates + pricing data)
|
# Proxy for accessing GitHub (online updates + pricing data)
|
||||||
# Examples: http://host:port, socks5://host:port
|
# Examples: http://host:port, socks5://host:port
|
||||||
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
|
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
|
||||||
|
|||||||
Reference in New Issue
Block a user