diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 55e9e01..72df382 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -18,6 +18,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Log in to Container Registry uses: docker/login-action@v3 with: @@ -29,7 +35,9 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/${{ github.repository }} + images: | + tbphp/gpt-load + ghcr.io/${{ github.repository }} flavor: | latest=false tags: | diff --git a/README.md b/README.md index 8609a49..211493d 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ GPT-Load 采用双层配置架构: | 请求超时 | `request_timeout` | 600 | ✅ | 转发请求完整生命周期超时(秒) | | 连接超时 | `connect_timeout` | 15 | ✅ | 与上游服务建立连接超时(秒) | | 空闲连接超时 | `idle_conn_timeout` | 120 | ✅ | HTTP 客户端空闲连接超时(秒) | -| 响应头超时 | `response_header_timeout` | 15 | ✅ | 等待上游响应头超时(秒) | +| 响应头超时 | `response_header_timeout` | 600 | ✅ | 等待上游响应头超时(秒) | | 最大空闲连接数 | `max_idle_conns` | 100 | ✅ | 连接池最大空闲连接总数 | | 每主机最大空闲连接数 | `max_idle_conns_per_host` | 50 | ✅ | 每个上游主机最大空闲连接数 | diff --git a/README_EN.md b/README_EN.md index dc8eef3..f01510f 100644 --- a/README_EN.md +++ b/README_EN.md @@ -204,7 +204,7 @@ Dynamic configuration is stored in the database and supports real-time modificat | Request Timeout | `request_timeout` | 600 | ✅ | Forward request complete lifecycle timeout (seconds) | | Connection Timeout | `connect_timeout` | 15 | ✅ | Timeout for establishing connection with upstream service (seconds) | | Idle Connection Timeout | `idle_conn_timeout` | 120 | ✅ | HTTP client idle connection timeout (seconds) | -| Response Header Timeout | `response_header_timeout` | 15 | ✅ | Timeout for waiting upstream response headers (seconds) | +| Response Header Timeout | `response_header_timeout` | 600 | ✅ | Timeout for waiting upstream response headers (seconds) | | Max Idle Connections | `max_idle_conns` | 100 | ✅ | Connection pool maximum total idle connections | | Max Idle Connections Per Host | `max_idle_conns_per_host` | 50 | ✅ | Maximum idle connections per upstream host | diff --git a/internal/handler/group_handler.go b/internal/handler/group_handler.go index cbbaea1..ce6c57a 100644 --- a/internal/handler/group_handler.go +++ b/internal/handler/group_handler.go @@ -83,8 +83,8 @@ func isValidGroupName(name string) bool { if name == "" { return false } - // 允许使用小写字母、数字和下划线,长度在 3 到 30 个字符之间 - match, _ := regexp.MatchString("^[a-z0-9_]{3,30}$", name) + // 允许使用小写字母、数字、下划线和中划线,长度在 3 到 30 个字符之间 + match, _ := regexp.MatchString("^[a-z0-9_-]{3,30}$", name) return match } @@ -151,7 +151,7 @@ func (s *Server) CreateGroup(c *gin.Context) { // Data Cleaning and Validation name := strings.TrimSpace(req.Name) if !isValidGroupName(name) { - response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, "Invalid group name format. Use 3-30 lowercase letters, numbers, and underscores.")) + response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, "无效的分组名称。只能包含小写字母、数字、中划线或下划线,长度3-30位")) return } @@ -265,7 +265,7 @@ func (s *Server) UpdateGroup(c *gin.Context) { if req.Name != nil { cleanedName := strings.TrimSpace(*req.Name) if !isValidGroupName(cleanedName) { - response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, "Invalid group name format. Name is required and must be 3-30 lowercase letters, numbers, or underscores.")) + response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, "无效的分组名称格式。只能包含小写字母、数字、中划线或下划线,长度3-30位")) return } group.Name = cleanedName diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go index 25d6e91..3ca522b 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/middleware.go @@ -136,11 +136,6 @@ func Auth( if strings.HasPrefix(path, "/api") { // Handle backend API authentication key = extractBearerKey(c) - if key == "" || key != authConfig.Key { - response.Error(c, app_errors.ErrUnauthorized) - c.Abort() - return - } } else if strings.HasPrefix(path, "/proxy/") { // Handle proxy authentication key, err = extractProxyKey(c, groupManager, channelFactory) @@ -161,7 +156,7 @@ func Auth( return } - if key == "" { + if key == "" || key != authConfig.Key { response.Error(c, app_errors.ErrUnauthorized) c.Abort() return diff --git a/internal/proxy/server.go b/internal/proxy/server.go index 0437eb3..e116ced 100644 --- a/internal/proxy/server.go +++ b/internal/proxy/server.go @@ -188,7 +188,7 @@ func (ps *ProxyServer) executeRequestWithRetry( var parsedError string if err != nil { - statusCode = 0 + statusCode = 500 errorMessage = err.Error() logrus.Debugf("Request failed (attempt %d/%d) for key %s: %v", retryCount+1, cfg.MaxRetries, utils.MaskAPIKey(apiKey.KeyValue), err) } else { diff --git a/internal/types/types.go b/internal/types/types.go index ccee089..dcab432 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -23,10 +23,10 @@ type SystemSettings struct { RequestLogWriteIntervalMinutes int `json:"request_log_write_interval_minutes" default:"5" name:"日志延迟写入周期(分钟)" category:"基础参数" desc:"请求日志从缓存写入数据库的周期(分钟),0为实时写入数据。" validate:"min=0"` // 请求设置 - RequestTimeout int `json:"request_timeout" default:"600" name:"请求超时(秒)" category:"请求设置" desc:"转发请求的完整生命周期超时(秒),包括连接、重试等。" validate:"min=1"` + RequestTimeout int `json:"request_timeout" default:"600" name:"请求超时(秒)" category:"请求设置" desc:"转发请求的完整生命周期超时(秒)等。" validate:"min=1"` ConnectTimeout int `json:"connect_timeout" default:"15" name:"连接超时(秒)" category:"请求设置" desc:"与上游服务建立新连接的超时时间(秒)。" validate:"min=1"` IdleConnTimeout int `json:"idle_conn_timeout" default:"120" name:"空闲连接超时(秒)" category:"请求设置" desc:"HTTP 客户端中空闲连接的超时时间(秒)。" validate:"min=1"` - ResponseHeaderTimeout int `json:"response_header_timeout" default:"15" name:"响应头超时(秒)" category:"请求设置" desc:"等待上游服务响应头的最长时间(秒),用于流式请求。" validate:"min=1"` + ResponseHeaderTimeout int `json:"response_header_timeout" default:"600" name:"响应头超时(秒)" category:"请求设置" desc:"等待上游服务响应头的最长时间(秒)。" validate:"min=1"` MaxIdleConns int `json:"max_idle_conns" default:"100" name:"最大空闲连接数" category:"请求设置" desc:"HTTP 客户端连接池中允许的最大空闲连接总数。" validate:"min=1"` MaxIdleConnsPerHost int `json:"max_idle_conns_per_host" default:"50" name:"每主机最大空闲连接数" category:"请求设置" desc:"HTTP 客户端连接池对每个上游主机允许的最大空闲连接数。" validate:"min=1"` diff --git a/web/src/components/keys/GroupFormModal.vue b/web/src/components/keys/GroupFormModal.vue index 285445c..53ff94a 100644 --- a/web/src/components/keys/GroupFormModal.vue +++ b/web/src/components/keys/GroupFormModal.vue @@ -92,8 +92,8 @@ const rules: FormRules = { trigger: ["blur", "input"], }, { - pattern: /^[a-z]+$/, - message: "只能输入小写字母", + pattern: /^[a-z0-9_-]{3,30}$/, + message: "只能包含小写字母、数字、中划线或下划线,长度3-30位", trigger: ["blur", "input"], }, ], @@ -330,7 +330,10 @@ async function handleSubmit() {