222c44c1f7
服务端 200+数据正常、debug/JIT 正常;release/AOT 的 Dart HttpClient 对 Content-Encoding: gzip 解压异常 → JSON 解析失败 → 显示 INTERNAL_ERROR。 nginx /v1/ 路径 gzip off(静态资源 gzip 不受影响);proguard keep 规则 一并保留(dio/flutter_secure_storage/全项目模型)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
107 lines
4.4 KiB
Plaintext
107 lines
4.4 KiB
Plaintext
# infra/deploy/nginx.conf.template —— photo-live-web nginx 配置模板。
|
||
# 官方 nginx 镜像启动脚本对 /etc/nginx/templates/*.template 执行 envsubst 并
|
||
# 输出到 /etc/nginx/conf.d/。模板变量仅 ${API_UPSTREAM}(compose environment
|
||
# 注入);envsubst 只替换已定义的环境变量,$uri/$host 等 nginx 变量不受影响。
|
||
#
|
||
# 路由布局(viewer history 路由 / admin hash 路由,路由模式核实自
|
||
# apps/viewer/src/router/index.ts createWebHistory 与 apps/admin/src/router/
|
||
# index.ts createWebHashHistory):
|
||
# / viewer SPA(try_files 回退 index.html)
|
||
# /admin/ admin 静态(hash 路由无需 history 回退;try_files 仅兜底)
|
||
# /assets/ viewer vite 内容寻址产物 → immutable 一年
|
||
# /admin/assets/ admin vite 内容寻址产物 → immutable 一年
|
||
# /embed/v1/ 嵌入 loader.js(版本目录即内容版本)→ immutable 一年;
|
||
# /embed/:slotToken 是 viewer SPA 路由,走 / 的回退,不被本条误捕
|
||
# /v1/ 反代 API_UPSTREAM(HTTP + WS /v1/realtime 升级透传)
|
||
|
||
map $http_upgrade $connection_upgrade {
|
||
default upgrade;
|
||
'' close;
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
server_name _;
|
||
|
||
root /usr/share/nginx/html/viewer;
|
||
index index.html;
|
||
|
||
# 跳转一律发相对 Location(absolute_redirect 默认 on 会按收到的 Host 头
|
||
# 绝对化——经反代丢端口,浏览器跳到无端口的 80;off 后浏览器按当前
|
||
# host:port 补全,2026-09-23 用户报告 32005/ 丢端口即此因)。
|
||
absolute_redirect off;
|
||
|
||
gzip on;
|
||
gzip_comp_level 5;
|
||
gzip_min_length 1024;
|
||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||
|
||
# API 路径禁用 gzip(2026-09-23:Flutter release AOT 的 HTTP 客户端
|
||
# 对 gzip 响应解压异常——debug/JIT 正常,release/AOT 全挂 INTERNAL_ERROR;
|
||
# 静态资源 gzip 不受影响)。
|
||
location ^~ /v1/ {
|
||
gzip off;
|
||
proxy_pass http://${API_UPSTREAM};
|
||
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 Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_read_timeout 300s;
|
||
proxy_send_timeout 300s;
|
||
}
|
||
|
||
;
|
||
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;
|
||
# WS(/v1/realtime)升级;普通 HTTP 请求不受影响。
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_read_timeout 75s;
|
||
proxy_send_timeout 75s;
|
||
}
|
||
|
||
# admin(hash 路由:静态直达即工作;try_files 兜底异常直链)。
|
||
location ^~ /admin/ {
|
||
alias /usr/share/nginx/html/admin/;
|
||
try_files $uri $uri/ /admin/index.html;
|
||
}
|
||
# admin 内容寻址资源:immutable(注意 alias 指到 assets/ 本身:location 前缀
|
||
# /admin/assets/ 被剥去后拼到 alias 末尾,多剥一段就 404——实测坑位)。
|
||
location ^~ /admin/assets/ {
|
||
alias /usr/share/nginx/html/admin/assets/;
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
try_files $uri =404;
|
||
}
|
||
|
||
# 嵌入 loader(版本化路径 /embed/v1/loader.js,apps/viewer/public 静态携带)。
|
||
location ^~ /embed/v1/ {
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
try_files $uri =404;
|
||
}
|
||
|
||
# viewer 内容寻址资源:immutable。
|
||
location ^~ /assets/ {
|
||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||
try_files $uri =404;
|
||
}
|
||
|
||
# 根路径直达(用户约定 2026-09-23:cn 入口 / 打开即管理后台)。
|
||
# ROOT_REDIRECT 由 compose 按栈注入:cn=/admin/(进后台);ov=/index.html
|
||
# (观众端安全自指,等价原 viewer 首页)。仅精确匹配 /,不影响 /a/<token>
|
||
# 等 viewer 路由与嵌入路径。
|
||
location = / {
|
||
return 302 ${ROOT_REDIRECT};
|
||
}
|
||
|
||
# viewer SPA history 路由回退。
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
}
|