# cx-web:SPA 静态托管 + /api、/websocket 反代 cx-gateway(compose 服务名直连) # 本文件被 compose 挂载到容器 /etc/nginx/conf.d/default.conf(镜像内亦有同份 COPY 兜底); # 现场改反代/开 TLS 直接编辑后 docker compose restart cx-web,无需重构建镜像。 server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # 上传经 /api 反代(分片上传单片默认几 MB,放宽到 512m 兜底整文件直传场景) client_max_body_size 512m; gzip on; gzip_min_length 1k; gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml font/woff2; # 变量 proxy_pass + docker 内嵌 DNS 惰性解析: # ① gateway 容器未起时 nginx 也能正常启动(静态 proxy_pass 会在启动期解析失败直接退出) # ② gateway 重建换 IP 后按 valid=30s 重新解析,不会缓存旧 IP 一直 502 resolver 127.0.0.11 valid=30s ipv6=off; set $gw http://cx-gateway:30000; # API:完整路径透传(proxy_pass 为变量且不带 URI => 原样转发,/api 前缀保留,网关按前缀路由) # ^~:命中此前缀后不再参与正则匹配——否则 /api/file/Image/xxx.jpg 会被下方 # 扩展名长缓存正则劫走当本地静态文件找而 404(图片预览经 80 反代的场景,2026-07-18 现场实证) location ^~ /api/ { proxy_pass $gw; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 🔴 2026-08-11 补:本 location 原先没设读超时,走 nginx 默认 60s #(那个 3600s 只写在下方 /websocket/ 里,对 /api/ 不生效)。 # 后果:任何 /api/** 超过 60 秒必然 504,且 nginx 只回一个自造的错误页, # 前端看不到后端的任何信息。实测撞上的是 # POST /api/workflow/operator/CandidateUser/0(选审批人)首次调用, # nginx error log 原文 "upstream timed out ... while reading response header"。 # 取 300s 与 Feign 的 client.config.default.readTimeout: 300000 对齐 #(config/shared/frame-config.yaml),使超时判定发生在后端而非 nginx—— # 后端超时至少能落日志、能回结构化错误。 # ⚠️ 这只是不让它 504,**没有解决"接口慢"本身**: # 流程相关接口偏慢是既有问题,定位与优化见 tasks/todo.md 的待优化章节。 proxy_read_timeout 300s; proxy_send_timeout 300s; } # 报表设计(Univer 版):前端 VITE_GLOB_REPORT_API_URL 生产构建为空,报表请求落到本域 # /api/Report,而网关 router.yaml 无此路由(该服务是独立发行包、不注册 Nacos)→ 404 # "No static resource api/Report."。此处按最长前缀优先(比下方 ^~ /api/ 更长,与顺序无关) # 单独截走,直连服务。 # ⚠️ 临时联调态:现指向宿主 java -jar 起的 32000;容器化(cx-report)后改成 $gw 同款 # 变量形式 http://cx-report:32000。host.docker.internal 走容器 /etc/hosts, # 故此处用静态 proxy_pass(变量形式会绕过 hosts 直查 DNS 解析不到)。 location ^~ /api/Report { proxy_pass http://host.docker.internal:32000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # WebSocket:前端生产环境连 ${location.origin}/websocket/, # 网关实际路由是 /api/message/websocket/**(2026-07-17 curl 实证:前者 200、后者 101), # 故重写补前缀再反代(rewrite 的 URI 会替换 proxy_pass 转发路径) location ^~ /websocket/ { rewrite ^/websocket/(.*)$ /api/message/websocket/$1 break; proxy_pass $gw; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # 运行时配置注入点(VITE_GLOB_* 三地址):改此文件免重构建生效,绝不能被长缓存 location = /_app.config.js { add_header Cache-Control "no-cache"; } # vite 产物带内容 hash(分布在 assets/ js/ css/ cdn/ 等目录),按扩展名长缓存; # html 不在列(index.html 走下方 no-cache);_app.config.js 由上方精确匹配优先兜住 location ~* \.(js|mjs|css|woff2?|ttf|eot|png|jpe?g|gif|svg|ico|mp3|mp4)$ { add_header Cache-Control "public, max-age=2592000, immutable"; } # history 路由:内页刷新/直达一律回落 index.html;index 本身不缓存(发版即生效) location / { try_files $uri $uri/ /index.html; add_header Cache-Control "no-cache"; } } # ── TLS(本期不启用,现场有证书后三步开启,详见交付包 INSTALL.md §5)────────── # ① 证书放到仓库/交付包根 docker/frontend/certs/{server.crt,server.key} # ② docker-compose.yml 的 cx-web 解开 "443:443" 端口与 certs 挂载两行注释 # ③ 解开下方 server 块注释后 docker compose restart cx-web # # server { # listen 443 ssl; # server_name _; # ssl_certificate /etc/nginx/certs/server.crt; # ssl_certificate_key /etc/nginx/certs/server.key; # ssl_protocols TLSv1.2 TLSv1.3; # # # 与 80 端口 server 块保持一致(root/反代/缓存各 location 原样复制到此处) # }