diff --git a/GiteaHubSync/app/backend/panel.py b/GiteaHubSync/app/backend/panel.py index f98ae1f..b4502ea 100644 --- a/GiteaHubSync/app/backend/panel.py +++ b/GiteaHubSync/app/backend/panel.py @@ -97,17 +97,24 @@ def gitea_host(): return cfg.get("gitea_host") or DEFAULT_GITEA_HOST -def gitea_ui(host_header=""): - """「打开 Gitea」按钮地址:优先环境变量;否则用设置里的地址, - 但把 127.0.0.1/localhost 替换成访问面板的主机名(浏览器打不开本机回环)""" +def gitea_ui(host_header="", referer="", xfwd_host=""): + """「打开 Gitea」按钮地址:把 127.0.0.1/localhost 替换成真实远程主机名。 + 推断顺序:X-Forwarded-Host(CGI 转发) > Host > Referer(浏览器自带)。 + 注意:仅当推断出的真实主机名不是 loopback 时才替换(本地直连保持原样)。""" if GITEA_URL_ENV: return GITEA_URL_ENV base = gitea_host() + real = (xfwd_host or host_header or "").split(":")[0].strip() + if not real and referer: + m = re.match(r'^https?://([^/:/?#]+)', referer) + if m: + real = m.group(1) + if not real or real in ("127.0.0.1", "localhost", "0.0.0.0", "::1"): + return base # 本地/无法推断,保留 gitea_host 原值 try: p = urlsplit(base) - host = (host_header or "").split(":")[0].strip() - if host and p.hostname in ("127.0.0.1", "localhost", "0.0.0.0", "::1", ""): - return f"{p.scheme}://{host}" + (f":{p.port}" if p.port else "") + if p.hostname in ("127.0.0.1", "localhost", "0.0.0.0", "::1", ""): + return f"{p.scheme}://{real}" + (f":{p.port}" if p.port else "") except Exception: pass return base @@ -1661,7 +1668,10 @@ class Handler(BaseHTTPRequestHandler): path = self.path.split("?")[0].rstrip("/") or "/" if path == "/": acc = active_account() - body = (PAGE_HOME.replace("__GITEA__", gitea_ui(self.headers.get("Host"))) + body = (PAGE_HOME.replace("__GITEA__", gitea_ui( + self.headers.get("Host"), + self.headers.get("Referer"), + self.headers.get("X-Forwarded-Host"))) .replace("__GH__", f"https://github.com/{acc['gh_user']}?tab=repositories" if acc else "") .replace("__LOGPATH__", str(LOG_FILE))) self._html(page("控制面板", body, "home")) @@ -1685,7 +1695,10 @@ class Handler(BaseHTTPRequestHandler): "account": account_public(acc), "last_real": last_real_sync()}) elif path == "/api/status": - self._json(global_status()) + self._json(global_status(gitea_ui( + self.headers.get("Host"), + self.headers.get("Referer"), + self.headers.get("X-Forwarded-Host")))) elif path == "/api/settings": self._json({"gitea_user": cfg["gitea_user"], "gitea_pass": mask_token(cfg.get("gitea_pass")), @@ -1700,7 +1713,10 @@ class Handler(BaseHTTPRequestHandler): "max_retries": max_retries(), "retry_interval": RETRY_INTERVAL, "gitea_host": gitea_host(), - "gitea_url": gitea_ui(self.headers.get("Host")), + "gitea_url": gitea_ui( + self.headers.get("Host"), + self.headers.get("Referer"), + self.headers.get("X-Forwarded-Host")), "active": active_id, "accounts": [account_public(a) for a in accounts]}) elif path == "/api/log": diff --git a/GiteaHubSync/app/ui/index.cgi b/GiteaHubSync/app/ui/index.cgi index 9220a67..2cb73f0 100644 --- a/GiteaHubSync/app/ui/index.cgi +++ b/GiteaHubSync/app/ui/index.cgi @@ -19,9 +19,19 @@ CTYPE="${CONTENT_TYPE:-application/json}" HDR="$(mktemp)"; BODY="$(mktemp)"; INBODY="" CURL_ARGS=(-s -m 120 -X "$METHOD" -H "Content-Type: $CTYPE") -# 把真实访问 Host / 协议转发给面板:面板据此把「打开 Gitea」等远程可访问链接 -# 的主机名从 127.0.0.1/localhost 替换成用户实际访问的主机名 -[ -n "$HTTP_HOST" ] && CURL_ARGS+=(-H "Host: $HTTP_HOST") +# 把真实访问 Host / 协议转发给面板,面板据此把「打开 Gitea」等远程可访问 +# 链接的主机名从 127.0.0.1/localhost 替换成用户实际访问的主机名。 +# 真实主机从 X-Forwarded-Host / HTTP_HOST / Referer 中兜底提取(fnOS CGI +# 环境里 HTTP_HOST 可能为空)。 +REAL_HOST="" +[ -n "$HTTP_X_FORWARDED_HOST" ] && REAL_HOST="$HTTP_X_FORWARDED_HOST" +[ -z "$REAL_HOST" ] && [ -n "$HTTP_HOST" ] && REAL_HOST="$HTTP_HOST" +if [ -z "$REAL_HOST" ] && [ -n "$HTTP_REFERER" ]; then + REAL_HOST=$(printf '%s' "$HTTP_REFERER" | sed -nE 's|^https?://([^/:/?#]+).*|\1|p' | head -1) +fi +if [ -n "$REAL_HOST" ]; then + CURL_ARGS+=(-H "Host: $REAL_HOST" -H "X-Forwarded-Host: $REAL_HOST") +fi if [ "${HTTPS:-off}" = "on" ]; then CURL_ARGS+=(-H "X-Forwarded-Proto: https") else diff --git a/GiteaHubSync/manifest b/GiteaHubSync/manifest index 9d86a5e..6d5d2f9 100644 --- a/GiteaHubSync/manifest +++ b/GiteaHubSync/manifest @@ -1,5 +1,5 @@ appname = GiteaHubSync -version = 1.5.4 +version = 1.5.5 display_name = Gitea→GitHub 同步助手 desc = 每次推送到飞牛 Gitea 的代码,10 秒内自动镜像到 GitHub 的 7 个私有仓库。内置控制面板:自动同步开关、立即同步、仓库状态、同步日志,一键打开 Gitea。数据全部保存在 NAS 本地。 platform = all