From 2c710e2e24c3b301374eb2323e824095d376a55a Mon Sep 17 00:00:00 2001 From: thomas Date: Sat, 23 May 2026 17:42:59 +0800 Subject: [PATCH] Same-origin API: empty VITE_API_URL with nginx proxy to backend-1. Frontends call /api/ on ark-library.com; nginx forwards internally to 100.93.205.19. Co-authored-by: Cursor --- .gitea/workflows/deploy.yml | 18 ++++++- deploy/nginx-frontend-locations.inc | 84 +++++++++++++++++++++++++++++ deploy/nginx-frontend-native.conf | 18 +++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 deploy/nginx-frontend-locations.inc create mode 100644 deploy/nginx-frontend-native.conf diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index cb27af7..3b5163a 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -34,7 +34,7 @@ jobs: - name: Build run: npm run build env: - VITE_API_URL: https://api.ark-library.com + VITE_API_URL: "" VITE_DISABLE_ADMIN: "true" - name: Setup SSH key @@ -54,6 +54,22 @@ jobs: -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \ dist/ \ ec2-user@${HOST}:/var/www/ark-library/ + rsync -avz \ + -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \ + deploy/nginx-frontend-locations.inc \ + ec2-user@${HOST}:/tmp/ark-library-frontend.inc + rsync -avz \ + -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \ + deploy/nginx-frontend-native.conf \ + ec2-user@${HOST}:/tmp/ark-library-native.conf + ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no ec2-user@${HOST} bash -s <<'REMOTE' + set -euo pipefail + sudo mkdir -p /etc/nginx/snippets + sudo install -m 0644 /tmp/ark-library-frontend.inc /etc/nginx/snippets/ark-library-frontend.inc + sudo install -m 0644 /tmp/ark-library-native.conf /etc/nginx/conf.d/ark-library.conf + rm -f /tmp/ark-library-frontend.inc /tmp/ark-library-native.conf + sudo nginx -t && sudo systemctl reload nginx + REMOTE echo ">>> $HOST 部署完成" } deploy_to "${{ secrets.FRONTEND_1_HOST }}" & diff --git a/deploy/nginx-frontend-locations.inc b/deploy/nginx-frontend-locations.inc new file mode 100644 index 0000000..1ccd934 --- /dev/null +++ b/deploy/nginx-frontend-locations.inc @@ -0,0 +1,84 @@ +# Shared SPA locations. Browser calls same-origin /api/ + /uploads/ (VITE_API_URL empty). +# Nginx proxies internally to ark-library-backend-1 (Tailscale); Host header for backend TLS. +gzip on; +gzip_vary on; +gzip_proxied any; +gzip_comp_level 5; +gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml; +gzip_min_length 256; + +location ^~ /api/admin { + return 404; +} +location ^~ /admin { + return 404; +} + +location ^~ /api/ { + proxy_pass https://100.93.205.19/api/; + proxy_http_version 1.1; + proxy_ssl_server_name on; + proxy_set_header Host api.ark-library.com; + 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; + client_max_body_size 512m; + client_body_timeout 600s; + proxy_read_timeout 600s; + proxy_send_timeout 600s; +} + +location ^~ /uploads/ { + proxy_pass https://100.93.205.19/uploads/; + proxy_http_version 1.1; + proxy_ssl_server_name on; + proxy_set_header Host api.ark-library.com; + 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; +} + +location = /health { + default_type text/plain; + return 200 "ok\n"; +} + +location = /healthz { + default_type text/plain; + return 200 "ok\n"; +} + +location = /index.html { + try_files $uri =404; + add_header Cache-Control "no-cache, no-store, must-revalidate" always; +} + +# Exact `/` so the HTML shell is never edge-cached without validators (avoids stale index.html → 404 on hashed /index-*.js or /assets/*). +location = / { + try_files /index.html =404; + add_header Cache-Control "no-cache, no-store, must-revalidate" always; +} + +location = /assets/logo-primary.webp { + try_files $uri =404; + add_header Cache-Control "public, max-age=3600, stale-while-revalidate=86400" always; +} +location ^~ /assets/ark-library/ { + try_files $uri =404; + add_header Cache-Control "public, max-age=86400, stale-while-revalidate=604800" always; +} + +location ^~ /assets/ { + try_files $uri =404; + add_header Cache-Control "public, max-age=31536000, immutable" always; +} + +# Hashed entry chunk at /index-[hash].js (Vite entryFileNames). Do not 308 to /assets — file lives here. +location ~* ^/index-[A-Za-z0-9_-]+\.(js|mjs)$ { + try_files $uri =404; + add_header Cache-Control "public, max-age=31536000, immutable" always; +} + +location / { + try_files $uri $uri/ /index.html; +} diff --git a/deploy/nginx-frontend-native.conf b/deploy/nginx-frontend-native.conf new file mode 100644 index 0000000..a62ba16 --- /dev/null +++ b/deploy/nginx-frontend-native.conf @@ -0,0 +1,18 @@ +# Native system nginx (not Docker). SPA root: /var/www/ark-library +# Snippet: /etc/nginx/snippets/ark-library-frontend.inc +# ALB terminates TLS; apex uses X-Forwarded-Proto so we do not 301-loop behind the LB. + +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name ark-library.com www.ark-library.com; + + if ($http_x_forwarded_proto != "https") { + return 301 https://$host$request_uri; + } + + root /var/www/ark-library; + index index.html; + + include /etc/nginx/snippets/ark-library-frontend.inc; +}