From 8b9cdef0e4144bad728e99cdc03a9361901e9704 Mon Sep 17 00:00:00 2001 From: sagit <36596628+Sagit-chu@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:59:44 +0800 Subject: [PATCH] feat: add configurable GitHub proxy settings (#401) * docs: add GitHub proxy config design spec * docs: add GitHub proxy config implementation plan * feat(backend): use configurable github proxy for node upgrades * feat(backend): use configurable github proxy for node install command * feat(frontend): add github proxy config settings * feat(script): support configurable github proxy in installer flows Honor custom GitHub mirror settings across interactive and env-driven installer/update paths so script downloads match the panel configuration. Add shell regressions to lock down proxy prompting, URL recomputation, and non-interactive fallback behavior. --- .../plans/2026-04-01-github-proxy-config.md | 520 ++++++++++++++++++ .../2026-04-01-github-proxy-config-design.md | 274 +++++++++ go-backend/internal/http/handler/mutations.go | 11 +- go-backend/internal/http/handler/upgrade.go | 57 +- install.sh | 74 ++- panel_install.sh | 76 ++- test-install-scripts-proxy.sh | 314 +++++++++++ vite-frontend/src/pages/config.tsx | 17 + 8 files changed, 1315 insertions(+), 28 deletions(-) create mode 100644 docs/superpowers/plans/2026-04-01-github-proxy-config.md create mode 100644 docs/superpowers/specs/2026-04-01-github-proxy-config-design.md create mode 100644 test-install-scripts-proxy.sh diff --git a/docs/superpowers/plans/2026-04-01-github-proxy-config.md b/docs/superpowers/plans/2026-04-01-github-proxy-config.md new file mode 100644 index 0000000..f0c5cb8 --- /dev/null +++ b/docs/superpowers/plans/2026-04-01-github-proxy-config.md @@ -0,0 +1,520 @@ +# GitHub 加速地址自定义配置实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 允许用户在面板设置中自定义 GitHub 加速地址,支持开启/关闭加速,配置影响全部下载场景。 + +**Architecture:** 使用现有 `vite_config` 表存储配置,后端 Handler 读取配置替换硬编码,前端复用现有配置项渲染逻辑,安装脚本支持环境变量和交互式询问。 + +**Tech Stack:** Go 1.24, React/TypeScript, Shell/Bash + +--- + +## 文件结构 + +| 文件 | 操作 | 说明 | +|------|------|------| +| `go-backend/internal/http/handler/upgrade.go` | 修改 | 移除硬编码,添加配置读取函数 | +| `go-backend/internal/http/handler/mutations.go` | 修改 | `nodeInstall` 函数使用动态配置 | +| `vite-frontend/src/pages/config.tsx` | 修改 | 添加两个新配置项 | +| `install.sh` | 修改 | 支持交互式询问和环境变量 | +| `panel_install.sh` | 修改 | 支持交互式询问和环境变量 | +| `test-install-scripts-proxy.sh` | 新增 | 覆盖代理交互与下载 URL 回归 | + +--- + +## Task 1: 后端 - upgrade.go 修改 + +**Files:** +- Modify: `go-backend/internal/http/handler/upgrade.go` + +- [x] **Step 1: 移除硬编码常量,添加配置读取函数** + +在 `upgrade.go` 中,移除 `githubProxy` 常量,添加 `getGithubProxyConfig` 函数: + +找到第 16-26 行: +```go +const ( + githubRepo = "Sagit-chu/flvx" + githubProxy = "https://gcode.hostcentral.cc" + githubAPIBase = "https://api.github.com" + githubHTMLBase = "https://github.com" + upgradeTimeout = 5 * time.Minute + batchWorkers = 5 + + releaseChannelStable = "stable" + releaseChannelDev = "dev" +) +``` + +替换为: +```go +const ( + githubRepo = "Sagit-chu/flvx" + githubAPIBase = "https://api.github.com" + githubHTMLBase = "https://github.com" + upgradeTimeout = 5 * time.Minute + batchWorkers = 5 + + releaseChannelStable = "stable" + releaseChannelDev = "dev" + + defaultGithubProxyEnabled = true + defaultGithubProxyURL = "https://gcode.hostcentral.cc" +) +``` + +然后在 `releaseChannelLabel` 函数后(约第 71 行之后)添加新函数: +```go +// getGithubProxyConfig 获取 GitHub 加速配置 +// 返回: (是否开启加速, 加速地址) +func (h *Handler) getGithubProxyConfig() (enabled bool, proxyURL string) { + enabled = defaultGithubProxyEnabled + proxyURL = defaultGithubProxyURL + + if h == nil || h.repo == nil { + return + } + + // 读取开启状态 + if enabledCfg, err := h.repo.GetConfigByName("github_proxy_enabled"); err == nil && enabledCfg != nil { + enabled = enabledCfg.Value != "false" + } + + // 读取加速地址 + if urlCfg, err := h.repo.GetConfigByName("github_proxy_url"); err == nil && urlCfg != nil && urlCfg.Value != "" { + proxyURL = strings.TrimSpace(urlCfg.Value) + // 确保 URL 格式正确 + if !strings.HasPrefix(proxyURL, "http://") && !strings.HasPrefix(proxyURL, "https://") { + proxyURL = "https://" + proxyURL + } + proxyURL = strings.TrimSuffix(proxyURL, "/") + } + + return +} + +// buildGithubDownloadURL 构建 GitHub 下载地址 +func (h *Handler) buildGithubDownloadURL(version, filename string) string { + enabled, proxyURL := h.getGithubProxyConfig() + base := fmt.Sprintf("%s/%s/releases/download/%s/%s", githubHTMLBase, githubRepo, version, filename) + + if enabled { + return fmt.Sprintf("%s/%s", proxyURL, base) + } + return base +} +``` + +- [x] **Step 2: 修改 nodeUpgrade 函数使用动态配置** + +找到第 152-159 行: +```go + downloadURL := fmt.Sprintf( + githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}", + githubHTMLBase, githubRepo, version, + ) + checksumURL := fmt.Sprintf( + githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}.sha256", + githubHTMLBase, githubRepo, version, + ) +``` + +替换为: +```go + downloadURL := h.buildGithubDownloadURL(version, "gost-{ARCH}") + checksumURL := h.buildGithubDownloadURL(version, "gost-{ARCH}.sha256") +``` + +- [x] **Step 3: 修改 nodeBatchUpgrade 函数使用动态配置** + +找到第 216-223 行: +```go + downloadURL := fmt.Sprintf( + githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}", + githubHTMLBase, githubRepo, version, + ) + checksumURL := fmt.Sprintf( + githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}.sha256", + githubHTMLBase, githubRepo, version, + ) +``` + +替换为: +```go + downloadURL := h.buildGithubDownloadURL(version, "gost-{ARCH}") + checksumURL := h.buildGithubDownloadURL(version, "gost-{ARCH}.sha256") +``` + +- [x] **Step 4: 验证编译** + +Run: `cd go-backend && go build ./...` +Expected: 编译成功,无错误 + +- [x] **Step 5: 提交** + +```bash +git add go-backend/internal/http/handler/upgrade.go +git commit -m "feat(backend): use configurable github proxy for node upgrades" +``` + +--- + +## Task 2: 后端 - mutations.go 修改 + +**Files:** +- Modify: `go-backend/internal/http/handler/mutations.go` + +- [x] **Step 1: 修改 nodeInstall 函数使用动态配置** + +找到第 456 行: +```go + cmd := fmt.Sprintf("curl -L https://gcode.hostcentral.cc/https://github.com/Sagit-chu/flvx/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && VERSION=%s ./install.sh -a %s -s %s", version, version, processServerAddress(panelAddr), secret) +``` + +替换为: +```go + enabled, proxyURL := h.getGithubProxyConfig() + + var cmd string + if enabled { + cmd = fmt.Sprintf("curl -L %s/https://github.com/%s/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && PROXY_ENABLED=true PROXY_URL=%s VERSION=%s ./install.sh -a %s -s %s", + proxyURL, githubRepo, version, proxyURL, version, processServerAddress(panelAddr), secret) + } else { + cmd = fmt.Sprintf("curl -L https://github.com/%s/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && PROXY_ENABLED=false VERSION=%s ./install.sh -a %s -s %s", + githubRepo, version, version, processServerAddress(panelAddr), secret) + } +``` + +- [x] **Step 2: 验证编译** + +Run: `cd go-backend && go build ./...` +Expected: 编译成功,无错误 + +- [x] **Step 3: 提交** + +```bash +git add go-backend/internal/http/handler/mutations.go +git commit -m "feat(backend): use configurable github proxy for node install command" +``` + +--- + +## Task 3: 前端 - config.tsx 添加配置项 + +**Files:** +- Modify: `vite-frontend/src/pages/config.tsx` + +- [x] **Step 1: 在 CONFIG_ITEMS 数组中添加配置项** + +找到第 158 行(`CONFIG_ITEMS` 数组的结束位置): +```go + { + key: "cloudflare_secret_key", + label: "Cloudflare Secret Key", + placeholder: "请输入 Cloudflare Secret Key", + description: "Cloudflare Turnstile 密钥", + type: "input", + dependsOn: "captcha_enabled", + dependsValue: "true", + }, +]; +``` + +在 `];` 之前添加: +```typescript + { + key: "github_proxy_enabled", + label: "开启 GitHub 加速", + description: "用于节点更新和安装脚本下载,解决部分地区 GitHub 访问受限问题", + type: "switch", + }, + { + key: "github_proxy_url", + label: "加速地址", + placeholder: "https://gcode.hostcentral.cc", + description: "GitHub 下载加速代理地址,开启加速后生效", + type: "input", + dependsOn: "github_proxy_enabled", + dependsValue: "true", + }, +``` + +- [x] **Step 2: 在缓存键列表中添加新键** + +找到第 179-190 行: +```typescript + const configKeys = [ + "app_name", + "captcha_enabled", + "cloudflare_site_key", + "cloudflare_secret_key", + "forward_compact_mode", + "monitor_tunnel_quality_enabled", + "ip", + "panel_domain", + "app_logo", + "app_favicon", + ]; +``` + +在 `"app_favicon",` 之后添加: +```typescript + "github_proxy_enabled", + "github_proxy_url", +``` + +- [x] **Step 3: 验证前端编译** + +Run: `cd vite-frontend && npm run build` +Expected: 编译成功,无错误 + +- [x] **Step 4: 提交** + +```bash +git add vite-frontend/src/pages/config.tsx +git commit -m "feat(frontend): add github proxy config settings" +``` + +--- + +## Task 4: 安装脚本 - install.sh 修改 + +**Files:** +- Modify: `install.sh` + +- [x] **Step 1: 添加环境变量声明和修改 maybe_proxy_url 函数** + +找到第 28-32 行: +```bash +# 镜像加速(所有下载均经过镜像源,以支持 IPv6) +maybe_proxy_url() { + local url="$1" + echo "https://gcode.hostcentral.cc/${url}" +} +``` + +替换为: +```bash +# 镜像加速配置(可由面板传入或交互式询问) +PROXY_ENABLED="${PROXY_ENABLED:-}" +PROXY_URL="${PROXY_URL:-}" + +# 镜像加速 +maybe_proxy_url() { + local url="$1" + + # 如果明确关闭加速 + if [[ "$PROXY_ENABLED" == "false" ]]; then + echo "$url" + return + fi + + # 默认开启加速 + local proxy="${PROXY_URL:-gcode.hostcentral.cc}" + + # 处理 URL 格式 + if [[ "$proxy" == https://* || "$proxy" == http://* ]]; then + proxy="${proxy%/}" + else + proxy="https://${proxy}" + fi + + echo "${proxy}/${url}" +} + +# 询问加速配置(如果未由面板传入) +ask_proxy_config() { + if [[ -n "$PROXY_ENABLED" ]]; then + return + fi + + echo "" + echo "===============================================" + echo " GitHub 加速配置" + echo "===============================================" + read -p "是否开启 GitHub 加速? (Y/n): " proxy_choice + case "$proxy_choice" in + n|N) + PROXY_ENABLED="false" + echo "已关闭加速,将直连 GitHub" + ;; + *) + PROXY_ENABLED="true" + read -p "加速地址 (默认 gcode.hostcentral.cc): " input_url + PROXY_URL="${input_url:-gcode.hostcentral.cc}" + echo "已开启加速: $PROXY_URL" + ;; + esac + echo "===============================================" +} +``` + +- [x] **Step 2: 修改 install_flux_agent 函数添加询问** + +找到第 211-214 行: +```bash +# 安装功能 +install_flux_agent() { + echo "🚀 开始安装 flux_agent..." + get_config_params +``` + +替换为: +```bash +# 安装功能 +install_flux_agent() { + echo "🚀 开始安装 flux_agent..." + + # 询问加速配置(如果未由面板传入) + ask_proxy_config + + get_config_params +``` + +- [ ] **Step 3: 提交** + +```bash +git add install.sh +git commit -m "feat(script): add configurable github proxy for install.sh" +``` + +--- + +## Task 5: 安装脚本 - panel_install.sh 修改 + +**Files:** +- Modify: `panel_install.sh` + +- [x] **Step 1: 添加环境变量声明和修改 maybe_proxy_url 函数** + +找到第 16-20 行: +```bash +# 镜像加速(所有下载均经过镜像源,以支持 IPv6) +maybe_proxy_url() { + local url="$1" + echo "https://gcode.hostcentral.cc/${url}" +} +``` + +替换为: +```bash +# 镜像加速配置(可由面板传入或交互式询问) +PROXY_ENABLED="${PROXY_ENABLED:-}" +PROXY_URL="${PROXY_URL:-}" + +# 镜像加速 +maybe_proxy_url() { + local url="$1" + + # 如果明确关闭加速 + if [[ "$PROXY_ENABLED" == "false" ]]; then + echo "$url" + return + fi + + # 默认开启加速 + local proxy="${PROXY_URL:-gcode.hostcentral.cc}" + + # 处理 URL 格式 + if [[ "$proxy" == https://* || "$proxy" == http://* ]]; then + proxy="${proxy%/}" + else + proxy="https://${proxy}" + fi + + echo "${proxy}/${url}" +} + +# 询问加速配置(如果未由面板传入) +ask_proxy_config() { + if [[ -n "$PROXY_ENABLED" ]]; then + return + fi + + echo "" + echo "===============================================" + echo " GitHub 加速配置" + echo "===============================================" + read -p "是否开启 GitHub 加速? (Y/n): " proxy_choice + case "$proxy_choice" in + n|N) + PROXY_ENABLED="false" + echo "已关闭加速,将直连 GitHub" + ;; + *) + PROXY_ENABLED="true" + read -p "加速地址 (默认 gcode.hostcentral.cc): " input_url + PROXY_URL="${input_url:-gcode.hostcentral.cc}" + echo "已开启加速: $PROXY_URL" + ;; + esac + echo "===============================================" +} +``` + +- [x] **Step 2: 修改 install_panel 函数添加询问** + +找到第 375-378 行: +```bash +# 安装功能 +install_panel() { + echo "🚀 开始安装面板..." + check_docker + get_config_params +``` + +替换为: +```bash +# 安装功能 +install_panel() { + echo "🚀 开始安装面板..." + + # 询问加速配置(如果未由面板传入) + ask_proxy_config + + check_docker + get_config_params +``` + +- [ ] **Step 3: 提交** + +```bash +git add panel_install.sh +git commit -m "feat(script): add configurable github proxy for panel_install.sh" +``` + +--- + +## Task 6: 最终验证和提交 + +- [x] **Step 1: 验证后端编译** + +Run: `cd go-backend && go build ./...` +Expected: 编译成功 + +- [x] **Step 2: 验证前端编译** + +Run: `cd vite-frontend && npm run build` +Expected: 编译成功 + +- [x] **Step 3: 验证脚本语法** + +Run: `bash -n install.sh && bash -n panel_install.sh && bash test-install-scripts-proxy.sh` +Expected: 无语法错误,且脚本代理回归测试通过 + +- [ ] **Step 4: 推送所有提交** + +```bash +git push +``` + +--- + +## 验收标准 + +1. 面板设置页面显示 GitHub 加速配置项 +2. 开关关闭后,下载地址直连 GitHub +3. 自定义加速地址后,节点更新和安装命令使用自定义地址 +4. 安装脚本支持交互式询问加速配置 +5. 面板生成的安装命令包含加速配置环境变量 diff --git a/docs/superpowers/specs/2026-04-01-github-proxy-config-design.md b/docs/superpowers/specs/2026-04-01-github-proxy-config-design.md new file mode 100644 index 0000000..4c5f61a --- /dev/null +++ b/docs/superpowers/specs/2026-04-01-github-proxy-config-design.md @@ -0,0 +1,274 @@ +# GitHub 加速地址自定义配置设计 + +**日期**: 2026-04-01 +**状态**: 待审核 +**作者**: AI Assistant + +## 概述 + +允许用户在面板设置中自定义 GitHub 加速地址,支持开启/关闭加速功能。配置后,面板更新节点、生成安装命令以及安装脚本都使用配置的加速地址。 + +## 背景 + +当前 `gcode.hostcentral.cc` 硬编码在多个位置: +- `go-backend/internal/http/handler/upgrade.go` - 节点升级下载 URL +- `go-backend/internal/http/handler/mutations.go` - 节点安装命令生成 +- `install.sh` - 节点安装脚本 +- `panel_install.sh` - 面板安装脚本 + +用户无法自定义加速地址或关闭加速功能。 + +## 目标 + +1. 面板设置中支持配置加速开关和加速地址 +2. 配置影响全部下载场景(面板端 + 安装脚本) +3. 安装脚本支持交互式询问加速配置 +4. 面板生成的安装命令自动嵌入加速配置 + +## 影响范围 + +### 后端 +- `go-backend/internal/http/handler/upgrade.go` +- `go-backend/internal/http/handler/mutations.go` + +### 前端 +- `vite-frontend/src/pages/config.tsx` +- `vite-frontend/src/config/site.ts`(缓存配置键) + +### 安装脚本 +- `install.sh` +- `panel_install.sh` + +## 详细设计 + +### 1. 数据存储 + +使用现有 `vite_config` 表存储两个配置项: + +| name | value | 说明 | +|------|-------|------| +| `github_proxy_enabled` | `"true"` / `"false"` | 是否开启加速,默认 `"true"` | +| `github_proxy_url` | URL 字符串 | 加速地址,默认 `"https://gcode.hostcentral.cc"` | + +### 2. 后端 Handler 修改 + +#### upgrade.go + +移除硬编码常量,新增辅助函数: + +```go +// getGithubProxyConfig 获取 GitHub 加速配置 +// 返回: (是否开启, 加速地址) +func (h *Handler) getGithubProxyConfig() (enabled bool, proxyURL string) { + enabled = true // 默认开启 + proxyURL = "https://gcode.hostcentral.cc" // 默认地址 + + if h == nil || h.repo == nil { + return + } + + // 读取开启状态 + if enabledCfg, err := h.repo.GetConfigByName("github_proxy_enabled"); err == nil && enabledCfg != nil { + enabled = enabledCfg.Value != "false" + } + + // 读取加速地址 + if urlCfg, err := h.repo.GetConfigByName("github_proxy_url"); err == nil && urlCfg != nil && urlCfg.Value != "" { + proxyURL = strings.TrimSpace(urlCfg.Value) + // 确保 URL 格式正确 + if !strings.HasPrefix(proxyURL, "http://") && !strings.HasPrefix(proxyURL, "https://") { + proxyURL = "https://" + proxyURL + } + proxyURL = strings.TrimSuffix(proxyURL, "/") + } + + return +} + +// buildDownloadURL 构建下载地址 +func (h *Handler) buildDownloadURL(version, arch string) string { + enabled, proxyURL := h.getGithubProxyConfig() + base := fmt.Sprintf("https://github.com/%s/releases/download/%s/gost-%s", githubRepo, version, arch) + + if enabled { + return fmt.Sprintf("%s/%s", proxyURL, base) + } + return base +} +``` + +修改 `nodeUpgrade` 和 `nodeBatchUpgrade` 使用动态配置。 + +#### mutations.go + +修改 `getNodeInstallCmd` 函数(约第 440-456 行): + +```go +func (h *Handler) getNodeInstallCmd(w http.ResponseWriter, r *http.Request) { + // ... 现有逻辑 ... + + enabled, proxyURL := h.getGithubProxyConfig() + + var cmd string + if enabled { + cmd = fmt.Sprintf( + "curl -L %s/https://github.com/%s/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && PROXY_ENABLED=true PROXY_URL=%s VERSION=%s ./install.sh -a %s -s %s", + proxyURL, githubRepo, version, proxyURL, version, processServerAddress(panelAddr), secret, + ) + } else { + cmd = fmt.Sprintf( + "curl -L https://github.com/%s/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && PROXY_ENABLED=false VERSION=%s ./install.sh -a %s -s %s", + githubRepo, version, version, processServerAddress(panelAddr), secret, + ) + } + + response.WriteJSON(w, response.OK(cmd)) +} +``` + +### 3. 前端修改 + +#### config.tsx + +在 `CONFIG_ITEMS` 数组中添加配置项(约第 87-158 行之后): + +```typescript +{ + key: "github_proxy_enabled", + label: "开启 GitHub 加速", + description: "用于节点更新和安装脚本下载,解决部分地区 GitHub 访问受限问题", + type: "switch", +}, +{ + key: "github_proxy_url", + label: "加速地址", + placeholder: "https://gcode.hostcentral.cc", + description: "GitHub 下载加速代理地址,开启加速后生效", + type: "input", + dependsOn: "github_proxy_enabled", + dependsValue: "true", +}, +``` + +在 `getInitialConfigs` 函数的 `configKeys` 数组中添加缓存键: + +```typescript +"github_proxy_enabled", +"github_proxy_url", +``` + +### 4. 安装脚本修改 + +#### install.sh + +在脚本开头添加配置变量和环境变量读取: + +```bash +# 镜像加速配置(可由面板传入) +PROXY_ENABLED="${PROXY_ENABLED:-}" +PROXY_URL="${PROXY_URL:-}" +``` + +修改 `maybe_proxy_url` 函数: + +```bash +# 镜像加速 +maybe_proxy_url() { + local url="$1" + + # 如果明确关闭加速 + if [[ "$PROXY_ENABLED" == "false" ]]; then + echo "$url" + return + fi + + # 默认开启加速 + local proxy="${PROXY_URL:-gcode.hostcentral.cc}" + + # 处理 URL 格式 + if [[ "$proxy" == https://* || "$proxy" == http://* ]]; then + proxy="${proxy%/}" # 移除末尾斜杠 + else + proxy="https://${proxy}" + fi + + echo "${proxy}/${url}" +} +``` + +在 `install_flux_agent` 函数开头添加交互式询问: + +```bash +install_flux_agent() { + echo "🚀 开始安装 flux_agent..." + + # 询问加速配置(如果未由面板传入) + if [[ -z "$PROXY_ENABLED" ]]; then + echo "" + read -p "是否开启 GitHub 加速? (Y/n): " proxy_choice + case "$proxy_choice" in + n|N) PROXY_ENABLED="false" ;; + *) + PROXY_ENABLED="true" + read -p "加速地址 (默认 gcode.hostcentral.cc): " input_url + PROXY_URL="${input_url:-gcode.hostcentral.cc}" + ;; + esac + fi + + # ... 现有安装逻辑 ... +} +``` + +#### panel_install.sh + +类似修改,在 `install_panel` 函数开头添加询问逻辑。 + +### 5. 配置缓存 + +#### site.ts + +在配置缓存键列表中添加新键(如果需要前端缓存加速配置)。 + +## 默认行为 + +- `github_proxy_enabled`: 默认 `"true"`(开启加速) +- `github_proxy_url`: 默认 `"https://gcode.hostcentral.cc"` + +## 测试要点 + +1. **后端 API 测试**: + - 未配置时使用默认值 + - 配置后正确读取并应用 + - 关闭加速后直连 GitHub + +2. **前端 UI 测试**: + - Switch 开关正确切换 + - 关闭加速时隐藏地址输入框 + - 保存配置后正确持久化 + +3. **安装脚本测试**: + - 交互式询问正常工作 + - 环境变量传入时跳过询问 + - 加速关闭时直连 GitHub + +4. **集成测试**: + - 面板生成安装命令正确包含加速配置 + - 节点升级下载使用配置的加速地址 + +## 风险与缓解 + +| 风险 | 缓解措施 | +|------|----------| +| 用户输入无效加速地址 | 后端验证 URL 格式,前端添加格式提示 | +| 旧版本安装脚本不兼容 | 保持 `maybe_proxy_url` 函数签名不变,仅修改内部逻辑 | +| 配置缺失时行为不一致 | 在 `getGithubProxyConfig` 中提供合理的默认值 | + +## 任务清单 + +- [ ] 后端:upgrade.go 修改 +- [ ] 后端:mutations.go 修改 +- [ ] 前端:config.tsx 添加配置项 +- [ ] 脚本:install.sh 修改 +- [ ] 脚本:panel_install.sh 修改 +- [ ] 测试:验证功能正常 \ No newline at end of file diff --git a/go-backend/internal/http/handler/mutations.go b/go-backend/internal/http/handler/mutations.go index de0efec..3ad8f56 100644 --- a/go-backend/internal/http/handler/mutations.go +++ b/go-backend/internal/http/handler/mutations.go @@ -453,7 +453,16 @@ func (h *Handler) nodeInstall(w http.ResponseWriter, r *http.Request) { response.WriteJSON(w, response.Err(-2, err.Error())) return } - cmd := fmt.Sprintf("curl -L https://gcode.hostcentral.cc/https://github.com/Sagit-chu/flvx/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && VERSION=%s ./install.sh -a %s -s %s", version, version, processServerAddress(panelAddr), secret) + enabled, proxyURL := h.getGithubProxyConfig() + + var cmd string + if enabled { + cmd = fmt.Sprintf("curl -L %s/https://github.com/%s/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && PROXY_ENABLED=true PROXY_URL=%s VERSION=%s ./install.sh -a %s -s %s", + proxyURL, githubRepo, version, proxyURL, version, processServerAddress(panelAddr), secret) + } else { + cmd = fmt.Sprintf("curl -L https://github.com/%s/releases/download/%s/install.sh -o ./install.sh && chmod +x ./install.sh && PROXY_ENABLED=false VERSION=%s ./install.sh -a %s -s %s", + githubRepo, version, version, processServerAddress(panelAddr), secret) + } response.WriteJSON(w, response.OK(cmd)) } diff --git a/go-backend/internal/http/handler/upgrade.go b/go-backend/internal/http/handler/upgrade.go index cf2ae17..26ef384 100644 --- a/go-backend/internal/http/handler/upgrade.go +++ b/go-backend/internal/http/handler/upgrade.go @@ -15,7 +15,6 @@ import ( const ( githubRepo = "Sagit-chu/flvx" - githubProxy = "https://gcode.hostcentral.cc" githubAPIBase = "https://api.github.com" githubHTMLBase = "https://github.com" upgradeTimeout = 5 * time.Minute @@ -23,6 +22,9 @@ const ( releaseChannelStable = "stable" releaseChannelDev = "dev" + + defaultGithubProxyEnabled = true + defaultGithubProxyURL = "https://gcode.hostcentral.cc" ) var ( @@ -70,6 +72,39 @@ func releaseChannelLabel(channel string) string { return "正式版" } +func (h *Handler) getGithubProxyConfig() (enabled bool, proxyURL string) { + enabled = defaultGithubProxyEnabled + proxyURL = defaultGithubProxyURL + + if h == nil || h.repo == nil { + return + } + + if enabledCfg, err := h.repo.GetConfigByName("github_proxy_enabled"); err == nil && enabledCfg != nil { + enabled = enabledCfg.Value != "false" + } + + if urlCfg, err := h.repo.GetConfigByName("github_proxy_url"); err == nil && urlCfg != nil && urlCfg.Value != "" { + proxyURL = strings.TrimSpace(urlCfg.Value) + if !strings.HasPrefix(proxyURL, "http://") && !strings.HasPrefix(proxyURL, "https://") { + proxyURL = "https://" + proxyURL + } + proxyURL = strings.TrimSuffix(proxyURL, "/") + } + + return +} + +func (h *Handler) buildGithubDownloadURL(version, filename string) string { + enabled, proxyURL := h.getGithubProxyConfig() + base := fmt.Sprintf("%s/%s/releases/download/%s/%s", githubHTMLBase, githubRepo, version, filename) + + if enabled { + return fmt.Sprintf("%s/%s", proxyURL, base) + } + return base +} + func fetchGitHubReleases(perPage int) ([]githubRelease, error) { if perPage <= 0 { perPage = 20 @@ -149,14 +184,8 @@ func (h *Handler) nodeUpgrade(w http.ResponseWriter, r *http.Request) { } } - downloadURL := fmt.Sprintf( - githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}", - githubHTMLBase, githubRepo, version, - ) - checksumURL := fmt.Sprintf( - githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}.sha256", - githubHTMLBase, githubRepo, version, - ) + downloadURL := h.buildGithubDownloadURL(version, "gost-{ARCH}") + checksumURL := h.buildGithubDownloadURL(version, "gost-{ARCH}.sha256") result, err := h.wsServer.SendCommand(req.ID, "UpgradeAgent", map[string]interface{}{ "downloadUrl": downloadURL, @@ -213,14 +242,8 @@ func (h *Handler) nodeBatchUpgrade(w http.ResponseWriter, r *http.Request) { } } - downloadURL := fmt.Sprintf( - githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}", - githubHTMLBase, githubRepo, version, - ) - checksumURL := fmt.Sprintf( - githubProxy+"/%s/%s/releases/download/%s/gost-{ARCH}.sha256", - githubHTMLBase, githubRepo, version, - ) + downloadURL := h.buildGithubDownloadURL(version, "gost-{ARCH}") + checksumURL := h.buildGithubDownloadURL(version, "gost-{ARCH}.sha256") type upgradeResult struct { ID int64 `json:"id"` diff --git a/install.sh b/install.sh index d496ecf..bddc1c5 100644 --- a/install.sh +++ b/install.sh @@ -25,10 +25,62 @@ get_architecture() { # 安装目录 INSTALL_DIR="/etc/flux_agent" -# 镜像加速(所有下载均经过镜像源,以支持 IPv6) +# 镜像加速配置(可由面板传入或交互式询问) +PROXY_ENABLED="${PROXY_ENABLED:-}" +PROXY_URL="${PROXY_URL:-}" + +# 镜像加速 maybe_proxy_url() { local url="$1" - echo "https://gcode.hostcentral.cc/${url}" + + if [[ "$PROXY_ENABLED" == "false" ]]; then + echo "$url" + return + fi + + local proxy="${PROXY_URL:-gcode.hostcentral.cc}" + + if [[ "$proxy" == https://* || "$proxy" == http://* ]]; then + proxy="${proxy%/}" + else + proxy="https://${proxy%/}" + fi + + echo "${proxy}/${url}" +} + +ask_proxy_config() { + if [[ -n "$PROXY_ENABLED" ]]; then + return + fi + + if [[ -n "$PROXY_URL" ]]; then + PROXY_ENABLED="true" + return + fi + + echo "" + echo "===============================================" + echo " GitHub 加速配置" + echo "===============================================" + if ! read -r -p "是否开启 GitHub 加速? (Y/n): " proxy_choice; then + proxy_choice="" + fi + case "$proxy_choice" in + n|N) + PROXY_ENABLED="false" + echo "已关闭加速,将直连 GitHub" + ;; + *) + PROXY_ENABLED="true" + if ! read -r -p "加速地址 (默认 gcode.hostcentral.cc): " input_url; then + input_url="" + fi + PROXY_URL="${input_url:-gcode.hostcentral.cc}" + echo "已开启加速: $PROXY_URL" + ;; + esac + echo "===============================================" } resolve_latest_release_tag() { @@ -81,9 +133,14 @@ build_download_url() { echo "https://github.com/${REPO}/releases/download/${RESOLVED_VERSION}/gost-${ARCH}" } -# 解析版本并构建下载地址 -RESOLVED_VERSION=$(resolve_version) || exit 1 -DOWNLOAD_URL=$(maybe_proxy_url "$(build_download_url)") +ensure_download_url_initialized() { + if [[ -n "${DOWNLOAD_URL:-}" ]]; then + return 0 + fi + + RESOLVED_VERSION=$(resolve_version) || return 1 + DOWNLOAD_URL=$(maybe_proxy_url "$(build_download_url)") +} @@ -210,6 +267,10 @@ done # 安装功能 install_flux_agent() { echo "🚀 开始安装 flux_agent..." + + ask_proxy_config + ensure_download_url_initialized || exit 1 + get_config_params # 检查并安装 tcpkill @@ -308,6 +369,9 @@ update_flux_agent() { echo "❌ flux_agent 未安装,请先选择安装。" return 1 fi + + ask_proxy_config + ensure_download_url_initialized || return 1 echo "📥 使用下载地址: $DOWNLOAD_URL" diff --git a/panel_install.sh b/panel_install.sh index ca70f48..4b47aca 100755 --- a/panel_install.sh +++ b/panel_install.sh @@ -13,10 +13,62 @@ REPO="Sagit-chu/flux-panel" # 固定版本号(Release 构建时自动填充,留空则获取最新版) PINNED_VERSION="" -# 镜像加速(所有下载均经过镜像源,以支持 IPv6) +# 镜像加速配置(可由面板传入或交互式询问) +PROXY_ENABLED="${PROXY_ENABLED:-}" +PROXY_URL="${PROXY_URL:-}" + +# 镜像加速 maybe_proxy_url() { local url="$1" - echo "https://gcode.hostcentral.cc/${url}" + + if [[ "$PROXY_ENABLED" == "false" ]]; then + echo "$url" + return + fi + + local proxy="${PROXY_URL:-gcode.hostcentral.cc}" + + if [[ "$proxy" == https://* || "$proxy" == http://* ]]; then + proxy="${proxy%/}" + else + proxy="https://${proxy%/}" + fi + + echo "${proxy}/${url}" +} + +ask_proxy_config() { + if [[ -n "$PROXY_ENABLED" ]]; then + return + fi + + if [[ -n "$PROXY_URL" ]]; then + PROXY_ENABLED="true" + return + fi + + echo "" + echo "===============================================" + echo " GitHub 加速配置" + echo "===============================================" + if ! read -r -p "是否开启 GitHub 加速? (Y/n): " proxy_choice; then + proxy_choice="" + fi + case "$proxy_choice" in + n|N) + PROXY_ENABLED="false" + echo "已关闭加速,将直连 GitHub" + ;; + *) + PROXY_ENABLED="true" + if ! read -r -p "加速地址 (默认 gcode.hostcentral.cc): " input_url; then + input_url="" + fi + PROXY_URL="${input_url:-gcode.hostcentral.cc}" + echo "已开启加速: $PROXY_URL" + ;; + esac + echo "===============================================" } resolve_latest_release_tag() { @@ -70,9 +122,14 @@ set_compose_urls_by_version() { DOCKER_COMPOSEV6_URL=$(maybe_proxy_url "https://github.com/${REPO}/releases/download/${version}/docker-compose-v6.yml") } -# 全局下载地址配置(默认获取最新版本;也可用 VERSION=... 覆盖) -RESOLVED_VERSION=$(resolve_version) || exit 1 -set_compose_urls_by_version "$RESOLVED_VERSION" +ensure_compose_urls_initialized() { + if [[ -n "${DOCKER_COMPOSEV4_URL:-}" && -n "${DOCKER_COMPOSEV6_URL:-}" ]]; then + return 0 + fi + + RESOLVED_VERSION=$(resolve_version) || return 1 + set_compose_urls_by_version "$RESOLVED_VERSION" +} @@ -374,6 +431,10 @@ get_config_params() { # 安装功能 install_panel() { echo "🚀 开始安装面板..." + + ask_proxy_config + ensure_compose_urls_initialized || return 1 + check_docker get_config_params @@ -425,6 +486,7 @@ EOF # 更新功能 update_panel() { echo "🔄 开始更新面板..." + ask_proxy_config check_docker if [[ ! -f ".env" ]]; then @@ -506,6 +568,8 @@ migrate_to_postgres() { if [[ ! -f "docker-compose.yml" ]]; then echo "⚠️ 未找到 docker-compose.yml 文件,正在下载..." + ask_proxy_config + ensure_compose_urls_initialized || return 1 DOCKER_COMPOSE_URL=$(get_docker_compose_url) echo "📡 选择配置文件:$(basename "$DOCKER_COMPOSE_URL")" curl -L -o docker-compose.yml "$DOCKER_COMPOSE_URL" @@ -581,6 +645,8 @@ uninstall_panel() { if [[ ! -f "docker-compose.yml" ]]; then echo "⚠️ 未找到 docker-compose.yml 文件,正在下载以完成卸载..." + ask_proxy_config + ensure_compose_urls_initialized || return 1 DOCKER_COMPOSE_URL=$(get_docker_compose_url) echo "📡 选择配置文件:$(basename "$DOCKER_COMPOSE_URL")" curl -L -o docker-compose.yml "$DOCKER_COMPOSE_URL" diff --git a/test-install-scripts-proxy.sh b/test-install-scripts-proxy.sh new file mode 100644 index 0000000..544f024 --- /dev/null +++ b/test-install-scripts-proxy.sh @@ -0,0 +1,314 @@ +#!/bin/bash +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +fail() { + echo "FAIL: $1" >&2 + exit 1 +} + +assert_equals() { + local expected="$1" + local actual="$2" + local message="$3" + + if [[ "$actual" != "$expected" ]]; then + fail "$message (expected: $expected, actual: $actual)" + fi +} + +load_script_without_main() { + local script_path="$1" + local temp_file + + temp_file=$(mktemp) + sed '/^# 执行主函数$/,$d' "$script_path" > "$temp_file" + VERSION="v-test" + source "$temp_file" + rm -f "$temp_file" +} + +test_install_script_respects_disabled_proxy() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/install.sh" + + PROXY_ENABLED="false" + PROXY_URL="" + + local actual + actual=$(maybe_proxy_url "https://github.com/example/release") + + assert_equals \ + "https://github.com/example/release" \ + "$actual" \ + "install.sh should bypass the proxy when disabled" +) + +test_install_script_asks_for_proxy_config() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/install.sh" + + PROXY_ENABLED="" + PROXY_URL="" + + ask_proxy_config >/dev/null < <(printf '\nmirror.example.com/\n') + + assert_equals "true" "$PROXY_ENABLED" "install.sh should enable proxy by default" + assert_equals "mirror.example.com/" "$PROXY_URL" "install.sh should keep the entered proxy URL" + + local actual + actual=$(maybe_proxy_url "https://github.com/example/release") + + assert_equals \ + "https://mirror.example.com/https://github.com/example/release" \ + "$actual" \ + "install.sh should normalize the entered proxy URL" +) + +test_install_script_recomputes_download_url_after_prompt() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/install.sh" + + PROXY_ENABLED="" + PROXY_URL="" + DOWNLOAD_URL="" + + ask_proxy_config >/dev/null < <(printf 'n\n') + ensure_download_url_initialized + + local expected + expected=$(build_download_url) + + assert_equals \ + "$expected" \ + "$DOWNLOAD_URL" \ + "install.sh should build the final download URL after the interactive proxy choice" +) + +test_update_flux_agent_asks_for_proxy_config() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/install.sh" + + INSTALL_DIR=$(mktemp -d) + cat > "$INSTALL_DIR/flux_agent" <<'EOF' +#!/bin/bash +echo "old version" +EOF + chmod +x "$INSTALL_DIR/flux_agent" + + local ask_called="0" + + ask_proxy_config() { + ask_called="1" + PROXY_ENABLED="false" + DOWNLOAD_URL="" + } + + check_and_install_tcpkill() { :; } + + systemctl() { + return 0 + } + + curl() { + local output="" + + while [[ $# -gt 0 ]]; do + if [[ "$1" == "-o" ]]; then + output="$2" + shift 2 + continue + fi + shift + done + + cat > "$output" <<'EOF' +#!/bin/bash +echo "new version" +EOF + chmod +x "$output" + } + + update_flux_agent >/dev/null + + assert_equals "1" "$ask_called" "update_flux_agent should ask for proxy config before downloading" + assert_equals "$(build_download_url)" "$DOWNLOAD_URL" "update_flux_agent should honor the prompted proxy choice" +) + +test_update_flux_agent_skips_proxy_prompt_when_not_installed() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/install.sh" + + INSTALL_DIR=$(mktemp -u) + + local ask_called="0" + + ask_proxy_config() { + ask_called="1" + } + + local rc="0" + update_flux_agent >/dev/null || rc="$?" + + assert_equals "1" "$rc" "update_flux_agent should fail when the agent is not installed" + assert_equals "0" "$ask_called" "update_flux_agent should not prompt for proxy config when the agent is missing" +) + +test_install_script_accepts_proxy_url_env_without_prompt() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/install.sh" + + PROXY_ENABLED="" + PROXY_URL="mirror.example.com" + + ask_proxy_config >/dev/null < <(printf '\n\n') + + assert_equals "true" "$PROXY_ENABLED" "install.sh should treat PROXY_URL as enabling the proxy" + assert_equals "mirror.example.com" "$PROXY_URL" "install.sh should preserve PROXY_URL when provided via env" +) + +test_panel_install_script_can_disable_proxy() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/panel_install.sh" + + PROXY_ENABLED="" + PROXY_URL="" + + ask_proxy_config >/dev/null < <(printf 'n\n') + + assert_equals "false" "$PROXY_ENABLED" "panel_install.sh should allow disabling proxy" + + local actual + actual=$(maybe_proxy_url "https://github.com/example/release") + + assert_equals \ + "https://github.com/example/release" \ + "$actual" \ + "panel_install.sh should bypass the proxy after disabling it" +) + +test_panel_install_script_recomputes_compose_urls_after_prompt() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/panel_install.sh" + + PROXY_ENABLED="" + PROXY_URL="" + DOCKER_COMPOSEV4_URL="" + DOCKER_COMPOSEV6_URL="" + + ask_proxy_config >/dev/null < <(printf 'n\n') + ensure_compose_urls_initialized + + assert_equals \ + "https://github.com/${REPO}/releases/download/${RESOLVED_VERSION}/docker-compose-v4.yml" \ + "$DOCKER_COMPOSEV4_URL" \ + "panel_install.sh should build the compose URL after the interactive proxy choice" +) + +test_update_panel_asks_for_proxy_config() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/panel_install.sh" + + local ask_called="0" + + ask_proxy_config() { + ask_called="1" + PROXY_ENABLED="false" + DOCKER_COMPOSEV4_URL="" + DOCKER_COMPOSEV6_URL="" + } + + check_docker() { + DOCKER_CMD="true" + } + + get_current_db_type() { + echo "sqlite" + } + + resolve_latest_release_tag() { + echo "v-test" + } + + upsert_env_var() { :; } + check_ipv6_support() { return 1; } + configure_docker_ipv6() { :; } + docker() { return 0; } + wait_for_backend_healthy() { return 0; } + sleep() { :; } + curl() { :; } + + update_panel >/dev/null + + assert_equals "1" "$ask_called" "update_panel should ask for proxy config before downloading" + assert_equals \ + "https://github.com/${REPO}/releases/download/v-test/docker-compose-v4.yml" \ + "$DOCKER_COMPOSEV4_URL" \ + "update_panel should honor the prompted proxy choice" +) + +test_panel_install_script_accepts_proxy_url_env_without_prompt() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/panel_install.sh" + + PROXY_ENABLED="" + PROXY_URL="mirror.example.com" + + ask_proxy_config >/dev/null < <(printf '\n\n') + + assert_equals "true" "$PROXY_ENABLED" "panel_install.sh should treat PROXY_URL as enabling the proxy" + assert_equals "mirror.example.com" "$PROXY_URL" "panel_install.sh should preserve PROXY_URL when provided via env" +) + +test_panel_install_script_defaults_proxy_on_eof() { + local rc="0" + local output + local temp_script + + temp_script=$(mktemp) + sed '/^# 执行主函数$/,$d' "$ROOT_DIR/panel_install.sh" > "$temp_script" + cat >> "$temp_script" <<'EOF' +PROXY_ENABLED="" +PROXY_URL="" +ask_proxy_config >/dev/null < /dev/null +printf '%s\n%s\n' "$PROXY_ENABLED" "$PROXY_URL" +EOF + + output=$(bash "$temp_script") || rc="$?" + rm -f "$temp_script" + + assert_equals "0" "$rc" "panel_install.sh should not fail when proxy prompt receives EOF" + assert_equals $'true\ngcode.hostcentral.cc' "$output" "panel_install.sh should fall back to the default proxy on EOF" +} + +test_panel_install_script_uses_default_proxy() ( + set -euo pipefail + load_script_without_main "$ROOT_DIR/panel_install.sh" + + PROXY_ENABLED="true" + PROXY_URL="" + + local actual + actual=$(maybe_proxy_url "https://github.com/example/release") + + assert_equals \ + "https://gcode.hostcentral.cc/https://github.com/example/release" \ + "$actual" \ + "panel_install.sh should keep the default proxy when enabled" +) + +test_install_script_respects_disabled_proxy +test_install_script_asks_for_proxy_config +test_install_script_recomputes_download_url_after_prompt +test_update_flux_agent_asks_for_proxy_config +test_update_flux_agent_skips_proxy_prompt_when_not_installed +test_install_script_accepts_proxy_url_env_without_prompt +test_panel_install_script_can_disable_proxy +test_panel_install_script_recomputes_compose_urls_after_prompt +test_update_panel_asks_for_proxy_config +test_panel_install_script_uses_default_proxy +test_panel_install_script_accepts_proxy_url_env_without_prompt +test_panel_install_script_defaults_proxy_on_eof + +echo "install script proxy tests passed" diff --git a/vite-frontend/src/pages/config.tsx b/vite-frontend/src/pages/config.tsx index 662fdd8..2014eda 100644 --- a/vite-frontend/src/pages/config.tsx +++ b/vite-frontend/src/pages/config.tsx @@ -155,6 +155,21 @@ const CONFIG_ITEMS: ConfigItem[] = [ dependsOn: "captcha_enabled", dependsValue: "true", }, + { + key: "github_proxy_enabled", + label: "开启 GitHub 加速", + description: "用于节点更新和安装脚本下载,解决部分地区 GitHub 访问受限问题", + type: "switch", + }, + { + key: "github_proxy_url", + label: "加速地址", + placeholder: "https://gcode.hostcentral.cc", + description: "GitHub 下载加速代理地址,开启加速后生效", + type: "input", + dependsOn: "github_proxy_enabled", + dependsValue: "true", + }, ]; const BACKUP_TYPE_OPTIONS = [ @@ -187,6 +202,8 @@ const getInitialConfigs = (): Record => { "panel_domain", "app_logo", "app_favicon", + "github_proxy_enabled", + "github_proxy_url", ]; const initialConfigs: Record = {};