diff --git a/internal/agent/route_test.go b/internal/agent/route_test.go index 5a888fc..9f2cc6c 100644 --- a/internal/agent/route_test.go +++ b/internal/agent/route_test.go @@ -81,6 +81,16 @@ func TestNginxRouteSharedTemplateSemantics(t *testing.T) { } } denyIdx := strings.Index(out, "deny 8.8.8.8;") + // WebSocket 头必须落在 location 内部:location 自定义 proxy_set_header 后, + // server 级的那组不会被继承,写在 server 块里等于没生效(升级请求会一直挂起) + locationIdx := strings.Index(out, "location / {") + upgradeIdx := strings.Index(out, "proxy_set_header Upgrade $http_upgrade;") + if locationIdx < 0 || upgradeIdx < 0 || upgradeIdx < locationIdx { + t.Fatalf("Upgrade 头必须写在 location 内:\n%s", out) + } + if !strings.Contains(out, "proxy_read_timeout 3600s;") { + t.Fatalf("WebSocket 未配读超时时应给长连接默认超时:\n%s", out) + } allowIdx := strings.Index(out, "allow 192.168.1.0/24;") denyAllIdx := strings.Index(out, "deny all;") if denyIdx < 0 || allowIdx < 0 || denyAllIdx < 0 || !(denyIdx < allowIdx && allowIdx < denyAllIdx) { diff --git a/internal/agent/server.go b/internal/agent/server.go index 3a280e5..94f61c9 100644 --- a/internal/agent/server.go +++ b/internal/agent/server.go @@ -31,7 +31,7 @@ import ( "github.com/havline/havline/internal/sysinfo" ) -const AgentVersion = "0.15.3" +const AgentVersion = "0.15.4" // Config 是 havline-agent 的运行配置。agent 只在 VPS 本地管理自己的 Nginx 片段、证书和 frps 配置。 type Config struct { @@ -1656,10 +1656,6 @@ func nginxRoute(req routeRequest, certDir string) string { if req.ConnLimitMax > 0 { b.WriteString(" limit_conn_status 503;\n") } - if req.WebSocket { - b.WriteString(" proxy_http_version 1.1;\n") - b.WriteString(nginxtmpl.UpgradeHeaders(" ", "\"upgrade\"")) - } // IP 访问控制:白名单(allow + deny all)与黑名单(deny)可同时生效; // 顺序由共享模板统一:deny → 仅大陆规则 → allow → deny all。 chinaDeny := "" @@ -1690,7 +1686,17 @@ func nginxRoute(req routeRequest, certDir string) string { Burst: req.RateLimitBurst, Conn: req.ConnLimitMax, }, " ") - fmt.Fprintf(&b, " location / {\n%s%s%s proxy_pass http://%s;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_http_version 1.1;\n }\n}\n", aclText, authLine, limitText, req.Upstream) + // WebSocket 必须写在 location 里:location 一旦自定义了 proxy_set_header, + // server 级的那组不会被继承——写在 server 块等于没写,表现就是升级请求一直挂起。 + wsText := "" + if req.WebSocket { + wsText = nginxtmpl.UpgradeHeaders(" ", "\"upgrade\"") + // 规则没显式配 proxy_read_timeout 时给长连接留足空闲时间,避免 60 秒被 Nginx 掐断 + if strings.TrimSpace(req.ProxyReadTimeout) == "" { + wsText += " proxy_read_timeout 3600s;\n" + } + } + fmt.Fprintf(&b, " location / {\n%s%s%s proxy_pass http://%s;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_http_version 1.1;\n%s }\n}\n", aclText, authLine, limitText, req.Upstream, wsText) return b.String() } diff --git a/internal/frp/agentbin/havline-agent-linux-amd64 b/internal/frp/agentbin/havline-agent-linux-amd64 index 2df0f1f..63caf54 100644 Binary files a/internal/frp/agentbin/havline-agent-linux-amd64 and b/internal/frp/agentbin/havline-agent-linux-amd64 differ diff --git a/internal/frp/agentbin/havline-agent-linux-arm64 b/internal/frp/agentbin/havline-agent-linux-arm64 index 16aece0..6e36edd 100644 Binary files a/internal/frp/agentbin/havline-agent-linux-arm64 and b/internal/frp/agentbin/havline-agent-linux-arm64 differ diff --git a/scripts/push-oci-manual.ps1 b/scripts/push-oci-manual.ps1 index 0eb7a0d..8d5b76a 100644 --- a/scripts/push-oci-manual.ps1 +++ b/scripts/push-oci-manual.ps1 @@ -4,14 +4,21 @@ Manual OCI layout pusher for registries whose token realm is misconfigured. This script is an emergency fallback. It reads Docker's OCI layout output, obtains a Bearer token over HTTPS from the Git credential store, and uploads the blobs and image manifest directly through the Registry HTTP API. + +默认面向 GitHub Container Registry(ghcr.io)。如果要用回自建 Gitea registry: + -Registry jayhub.top -Repository mrlee/havline -CredentialHost jayhub.top ` + -TokenPath /v2/token -TokenService container_registry #> param( [Parameter(Mandatory = $true)][string]$LayoutDir, [Parameter(Mandatory = $true)][string]$Platform, [Parameter(Mandatory = $true)][string]$Tag, - [string]$Registry = "jayhub.top", - [string]$Repository = "mrlee/havline", - [string]$CredentialHost = "jayhub.top" + [string]$Registry = "ghcr.io", + [string]$Repository = "mrlee233/havline", + [string]$CredentialHost = "ghcr.io", + # GHCR: /token?service=ghcr.io;Gitea: /v2/token?service=container_registry + [string]$TokenPath = "/token", + [string]$TokenService = "ghcr.io" ) $ErrorActionPreference = "Stop" @@ -33,7 +40,7 @@ function Get-RegistryBasicAuth { function Get-BearerToken([string]$Scope) { $basic = Get-RegistryBasicAuth - $url = "https://$Registry/v2/token?service=container_registry&scope=$Scope" + $url = "https://$Registry$TokenPath?service=$TokenService&scope=$Scope" $response = Invoke-RestMethod -Uri $url -Headers @{ Authorization = $basic } if (-not $response.token) { throw "Registry did not return a token" } return $response.token diff --git a/scripts/upgrade-hub.sh b/scripts/upgrade-hub.sh index b02083f..a82cb1d 100644 --- a/scripts/upgrade-hub.sh +++ b/scripts/upgrade-hub.sh @@ -5,7 +5,7 @@ # # 可选环境变量: # COMPOSE_FILE=docker-compose.hub.yml # 要用的编排文件 -# REPO=http://.../mrlee/Havline.git # 源码仓库 +# REPO=https://github.com/mrlee233/havline.git # 源码仓库 # BRANCH=main # 分支 # # 它做四件事: @@ -16,7 +16,7 @@ # 4) 打印「回滚到上一个 tag」的命令 set -euo pipefail -REPO="${REPO:-https://jayhub.top/mrlee/Havline.git}" +REPO="${REPO:-https://github.com/mrlee233/havline.git}" BRANCH="${BRANCH:-main}" COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.hub.yml}" APP_CONTAINER="${APP_CONTAINER:-havline}"