Files
chunyu 24f200bfa6 feat(C-01):路由/代理前缀冲突修复
SPA冲突路由改名+旧路径Navigate重定向(/api-docs→/open-api-docs、/api-detail→/open-api-detail、/article→/post、/user→/user-home);vite代理宽前缀改精确正则白名单;nginx.conf同步同口径;新增86路由回归脚本+Playwright 3项(86/86全绿)。
2026-09-15 15:25:59 +08:00

251 lines
10 KiB
Nginx Configuration File
Raw Permalink 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.
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 开启gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
# WebSocket 反代(聊天 ws/chat、扫码登录 ws/qr-status → Granian + Channels)
location /ws/ {
proxy_pass http://backend:8000;
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;
proxy_send_timeout 3600s;
}
# API 反代 → Granian(ADRF 异步视图)
# C-01:精确收窄到后端真实存在的 API 前缀。SPA 路由(/open-api*、/post/*、
# /user-home*、/articles、/learn 列表页等)一律落到下面的 `location /` 回退,
# 不再被宽正则劫持。旧分享链接(/article/:id、/user/:id、/api-detail/* 等)
# 由前端 SPA 内 <Navigate> 重定向承接,nginx 直接放行到 index.html 即可。
location ~ ^/api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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_set_header Content-Type $http_content_type;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 user.urls(/user/<action>/):SPA 已迁到 /user-home*,旧 /user/:id 分享
# 链接由前端重定向承接;数字 id 形态放行到 SPA,其余代理到后端
location ~ ^/user/\d+/?$ {
try_files $uri $uri/ /index.html;
}
location ~ ^/user/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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_set_header Content-Type $http_content_type;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 bug.urls 仅 /bug/reports/*;SPA /bug 与 /bug-detail 放行
location ~ ^/bug/reports/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 article.urls 仅 articles|my-articles|comments;SPA /post/*、/articles 放行
location ~ ^/article/(articles|my-articles|comments)/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 learn.urls 子段;SPA /learn 列表页放行
location ~ ^/learn/(courses|chapters|my-courses|my-progress|materials|favorites|cdn)/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 message.urls(/message/*);SPA /messages(多 s)天然不命中
location ~ ^/message/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 chat.urls 子段;SPA /chat 本体放行
location ~ ^/chat/(friend-requests|friends|users|conversations|messages|upload|favorite-stickers)/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 tool.urls(/tool/*);SPA /tool-detail 放行(不以 /tool/ 开头)
location ~ ^/tool/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 history.urls 仅 records/;SPA /history 列表页放行
location ~ ^/history/records/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 后端 search 查询串形态(GET /search/?q=);裸 /search(页)与无参 /search/ 放行到 SPA。
# location 按 URI 匹配、看不见 query string,故用 $args 分流。
location = /search/ {
error_page 418 = @spa_fallback;
if ($args = "") { return 418; }
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
}
location ~ ^/search/(suggestions|hot-keywords)/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
}
# 后端 app(changelog)/ logs / shorturl
location ~ ^/(app|logs/api|shorturl)/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
client_max_body_size 50M;
}
# 短链跳转 /s/<code>/:仅短码形态代理,其余(/settings、/shorturl-detail)放行
location ~ ^/s/[^/]+/?$ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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 60s;
proxy_send_timeout 60s;
}
# Django admin / swagger / redoc / i18n / 测试页
location /admin/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 50M; }
location /swagger { proxy_pass http://backend:8000; proxy_set_header Host $host; }
location /swagger.json { proxy_pass http://backend:8000; proxy_set_header Host $host; }
location /redoc/ { proxy_pass http://backend:8000; proxy_set_header Host $host; }
location /i18n/ { proxy_pass http://backend:8000; proxy_set_header Host $host; }
location /test/ { proxy_pass http://backend:8000; proxy_set_header Host $host; }
# 带哈希指纹的构建产物:永久强缓存(immutable)
location ~* \assets/.*\.(js|css|png|jpg|jpeg|gif|webp|avif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 其他静态资源(public/ 目录:图片/字体等无指纹文件):适中缓存
location ~* \.(png|jpg|jpeg|gif|webp|ico|svg|woff|woff2|ttf|eot)$ {
expires 7d;
add_header Cache-Control "public";
access_log off;
}
# index.html 不缓存,保证发版后立即生效
location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# C-05 SEO 预渲染优先:蜘蛛/首屏直接拿到含正文的静态 HTML,
# React hydrate 后照常接管交互(预渲染页内含 location.replace 跳转到 SPA 路由)。
# 首页用 index.seo.html(不覆盖 SPA 外壳 index.html);工具页/聚合页为目录 index.html。
location = / {
try_files /index.seo.html /index.html;
}
location ~ ^/(utility/[a-z-]+|qrcode-generator|color-picker|baidu-translate|utility|top)/?$ {
try_files $uri $uri/index.html $uri/ /index.html;
add_header Cache-Control "public, max-age=3600";
}
# SPA 路由回退(放在最后,避免拦截上面的 API 请求)
location / {
try_files $uri $uri/ /index.html;
}
location @spa_fallback {
try_files $uri $uri/ /index.html;
}
}