diff --git a/README.md b/README.md index 52ba901..3bddb9c 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,35 @@ CGO_ENABLED=0 go build -tags embedui -o artex ./cmd/artex **常用参数**:`./start.sh -addr :8787 -proxy :8788`(`-addr` 前端+API,`-proxy` 流量录制代理)。启动脚本会把参数原样透传给 `artex`。 +### 反向代理部署(HTTPS / 只开放 443) + +前端和 API/SSE 都由同一个后端端口(默认 `:8787`)提供,实时活动流默认走**同源**地址,因此**无需配置 `NEXT_PUBLIC_SSE_BASE`**,公网只开放 443、把 8787 留在内网即可。 + +SSE 是长连接 + 持续推送,反代**必须关闭缓冲**,否则浏览器能连上却收不到事件(表现为活动流一直转圈)。Nginx 示例: + +```nginx +server { + listen 443 ssl; + server_name your.domain.com; + # ssl_certificate / ssl_certificate_key ... + + location / { + proxy_pass http://127.0.0.1:8787; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + + # SSE 关键项:关缓冲、长超时、HTTP/1.1 + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 3600s; + proxy_http_version 1.1; + proxy_set_header Connection ""; + } +} +``` + +> 仅当 SSE 需要走与页面不同的来源(如独立子域)时,才在**构建期**设置 `NEXT_PUBLIC_SSE_BASE`(该变量在 `next build` 时固化进静态包,容器运行时再设无效)。 + --- diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 6fe8a9e..1bd4f4b 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -146,14 +146,22 @@ export async function http(path: string, init?: RequestInit): Promise { return r.json(); } -// sseUrl builds a URL for Server-Sent Events streams. SSE must NOT go through the -// Next.js dev `/api` rewrite: that proxy buffers the streamed response, so event -// frames never reach the browser (the EventSource opens but receives 0 messages). -// We therefore connect straight to the Go backend, whose CORS is open. Override -// with NEXT_PUBLIC_SSE_BASE; set it to "" to force same-origin (e.g. behind a -// production reverse proxy that flushes SSE correctly). -// Token is appended as ?token= because SSE bypasses the Next.js proxy and the -// browser does not send cookies cross-port. +// sseUrl builds a URL for Server-Sent Events streams. +// +// Production (static export / Docker): the Go backend serves the web UI *and* the +// SSE streams on the same port, so we default to a same-origin (relative) URL. +// This is what makes reverse-proxy deploys work: a page loaded from +// https:/// connects to https:///api/..., which the proxy forwards +// to the backend — no need to expose :8787 publicly. Hardcoding :8787 here used to +// break exactly that (https://:8787/... is unreachable when only 443 is open). +// +// Dev (next dev): SSE must NOT go through the Next.js `/api` rewrite — that proxy +// buffers the streamed response, so event frames never reach the browser (the +// EventSource opens but receives 0 messages). So in dev only we connect straight +// to the Go backend on :8787, whose CORS is open. +// +// Override either default with NEXT_PUBLIC_SSE_BASE (set it to "" to force same-origin). +// Token is appended as ?token= because SSE can't carry cookies cross-origin. // mockReport returns a canned Markdown report for the demo. function mockReport(_task?: string): string { return `# ARTEX 渗透测试报告 — Acme Corp @@ -180,7 +188,9 @@ function mockReport(_task?: string): string { export function sseUrl(path: string): string { const base = process.env.NEXT_PUBLIC_SSE_BASE ?? - (typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8787` : ""); + (process.env.NODE_ENV !== "production" && typeof window !== "undefined" + ? `${window.location.protocol}//${window.location.hostname}:8787` + : ""); const token = getToken(); const sep = path.includes("?") ? "&" : "?"; return token ? `${base}${path}${sep}token=${encodeURIComponent(token)}` : `${base}${path}`;