Files
danyang-match/infrastructure/nginx.conf
T
WorkBuddy f96dcb8aa6 fix(admin): 管理端入口收敛到 /admin/ 子路径,staging 端口 13521,修复生产 /admin/ 假成功
承接 rc.15 的管理端端口收敛(3000 → 13520),本轮把「入口」也收敛到一处:

- staging admin 发布端口 3001 → 13521(保持「生产 + 1」错开),同步
  tests/uat/run-uat.sh 的默认 UAT_ADMIN_URL。
- 管理端产物改为 /admin/ 子路径部署:vite 构建 base=/admin/(dev 仍为 /,开发与
  排查习惯不变)、router history base 跟随 import.meta.env.BASE_URL、三处容器化
  入口(生产 nginx / 本地整套栈 nginx / admin-web 容器内 nginx)统一为「不剥离
  前缀」转发,形态一致。
- 修复生产 nginx /admin/ 的假成功:原为 alias /usr/share/nginx/html/,而那正是
  nginx:alpine 纯净镜像自带的默认页目录(管理端产物不在该容器内),访问返回
  200 + nginx 欢迎页 —— 浏览器不报错、状态码正常,只是打开的不是管理端,比 404
  更难发现。改为 upstream admin_web + proxy_pass,并以真实两跳容器实测:
  /admin/ → 200 且为管理端标题与 /admin/assets/*、/admin/dashboard → 200(SPA
  兜底)、/admin/brand/logo-mark.png → 200、根路径 → 显式 404(用 location = /
  钉住,不依赖「本配置恰好没有 server 级 root」这一偶然事实)。
- 品牌资源引用统一走新增的 src/brand.ts(public/ 资源不受 vite base 影响,原 5 处
  写死根路径,子路径部署下会裂图);favicon 由仓库内并不存在的 /favicon.svg 改为
  存在的品牌标。
- 补全 tests/uat/run-uat.sh 被截断的尾部:原文件停在 `if [ -`,bash -n 不通过、
  脚本根本无法运行;顺带把此前「定义了却无人消费」的 ADMIN_URL 接到管理端可达性
  实测上,并补结果汇总与非零退出码。
- ui-tests:ui-document-baseline-contracts 新增两类一致性断言(staging 发布端口 ⇄
  run-uat.sh 默认入口、子路径部署的五处约束);admin-reference-contracts 的资产
  断言适配 brand 模块(断言「组件经 brand 模块引用」+「brand 模块指向正式资产」)。
  6 条新增断言全部经负向注入验证(注入对应缺陷 → 守卫变红且信息指向正确对象)。

验证:UI 门控 14/14 绿;vue-tsc EXIT=0;vite build EXIT=0 且产物资源均为
/admin/ 前缀;三份 nginx 配置 nginx -t 通过。

注:docs/STATUS.md 含此前的在途文档改动(非本次工作,且其中引用了已被删除的
plans 文件),故未纳入本提交;本轮的口径更新已写入该文件待一并处理。
2026-09-18 20:51:12 +08:00

115 lines
3.9 KiB
Nginx Configuration File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'request_id=$upstream_http_x_request_id';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Rate limiting zone
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=upload:10m rate=10r/s;
upstream backend {
server backend:8080;
}
upstream admin_web {
server admin-web:80;
}
server {
listen 80;
server_name localhost;
# Health check for nginx itself
location /nginx-health {
return 200 'ok';
add_header Content-Type text/plain;
}
# API proxy
location /api/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://backend;
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;
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
add_header Pragma "no-cache";
}
# WebSocket proxy
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 3600s;
}
# Auth endpoints - stricter rate limit
location /api/v1/auth/ {
limit_req zone=auth burst=10 nodelay;
proxy_pass http://backend;
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;
}
# Admin web —— 管理端产物挂在 /admin/ 子路径下(构建 base=/admin/,
# 见 admin-web/vite.config.ts)。与生产 nginx 保持同一形态:**不剥离**前缀地
# 转发给 admin-web 容器,由容器内的 nginx(admin-web/nginx-admin.conf)
# 统一处理 /admin/ 前缀。三处入口(生产 / 本地整套栈 / staging 直连)因此一致。
location = /admin {
return 301 /admin/;
}
location /admin/ {
proxy_pass http://admin_web;
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;
}
# 其余未匹配路径(不含 /admin、/api、/ws)统一跳转到管理端入口。
# 根路径没有内容,显式跳转以避免"打根路径拿到一个空白/错页"的假成功。
location / {
return 301 /admin/;
}
}
}