limit_req_zone webai:5m rate=60r/m, burst=20 nodelay, return 429 on limit hit. Proxies to stock:8000 with X-Real-IP, X-Forwarded-For, and X-WebAI-Key headers preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
329 lines
10 KiB
Plaintext
329 lines
10 KiB
Plaintext
# /api/webai/* rate limit — web-ai pull worker (60 req/min, burst 20)
|
|
limit_req_zone $binary_remote_addr zone=webai:5m rate=60r/m;
|
|
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
# Security headers
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
# index.html은 캐시 금지 (배포 반영 핵심)
|
|
location = /index.html {
|
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# 정적 리소스는 장기 캐시 (Vite 해시 파일)
|
|
location /assets/ {
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# music media — Nginx가 직접 오디오 파일 서빙
|
|
location ^~ /media/music/ {
|
|
alias /data/music/;
|
|
|
|
expires 30d;
|
|
add_header Cache-Control "public, max-age=2592000" always;
|
|
add_header Accept-Ranges bytes always; # 오디오 스트리밍 범위 요청 지원
|
|
|
|
autoindex off;
|
|
}
|
|
|
|
# music videos — Nginx가 직접 비디오 파일 서빙
|
|
location ^~ /media/videos/ {
|
|
alias /data/videos/;
|
|
|
|
expires 1d;
|
|
add_header Cache-Control "public, max-age=86400" always;
|
|
add_header Accept-Ranges bytes always; # 비디오 스트리밍 범위 요청 지원
|
|
|
|
autoindex off;
|
|
}
|
|
|
|
# music API — 변수 기반 proxy_pass + $request_uri로 전체 경로 전달
|
|
location /api/music/ {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $music_backend music-lab: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 660s;
|
|
proxy_pass http://$music_backend$request_uri;
|
|
}
|
|
|
|
# realestate API
|
|
location /api/realestate/ {
|
|
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_pass http://realestate-lab:8000/api/realestate/;
|
|
}
|
|
|
|
# travel thumbnails (generated by travel-proxy, stored in /data/thumbs)
|
|
location ^~ /media/travel/.thumb/ {
|
|
alias /data/thumbs/;
|
|
|
|
expires 30d;
|
|
add_header Cache-Control "public, max-age=2592000, immutable" always;
|
|
|
|
autoindex off;
|
|
}
|
|
|
|
# travel media (images) - nginx가 직접 파일 서빙
|
|
location ^~ /media/travel/ {
|
|
alias /data/travel/; # ✅ /media/travel/... -> /data/travel/...
|
|
|
|
expires 7d;
|
|
add_header Cache-Control "public, max-age=604800" always;
|
|
|
|
# 옵션: 폴더리스팅 막기
|
|
autoindex off;
|
|
}
|
|
|
|
# 기타 정적 파일(예: vite.svg 등) 기본 캐시(원하면 조정)
|
|
location ~* \.(?:ico|png|jpg|jpeg|gif|svg|webp|css|js)$ {
|
|
add_header Cache-Control "public, max-age=604800";
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# todos API → personal
|
|
location /api/todos {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $personal_backend personal: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_pass http://$personal_backend$request_uri;
|
|
}
|
|
|
|
# blog API → personal
|
|
location /api/blog/ {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $personal_backend personal: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_pass http://$personal_backend$request_uri;
|
|
}
|
|
|
|
# travel API
|
|
location /api/travel/ {
|
|
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 600s;
|
|
proxy_pass http://travel-proxy:8000/api/travel/;
|
|
}
|
|
|
|
# webai API — rate limited web-ai pull worker
|
|
location /api/webai/ {
|
|
limit_req zone=webai burst=20 nodelay;
|
|
limit_req_status 429;
|
|
|
|
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 X-WebAI-Key $http_x_webai_key;
|
|
proxy_pass http://stock:8000;
|
|
}
|
|
|
|
# stock API
|
|
location /api/stock/ {
|
|
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_pass http://stock:8000/api/stock/;
|
|
}
|
|
|
|
# trade API (Stock Proxy)
|
|
location /api/trade/ {
|
|
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_pass http://stock:8000/api/trade/;
|
|
}
|
|
|
|
# blog-marketing API
|
|
location /api/blog-marketing/ {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $blog_backend blog-lab: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 120s;
|
|
proxy_pass http://$blog_backend$request_uri;
|
|
}
|
|
|
|
# portfolio API (Stock) — trailing slash 유무 모두 매칭
|
|
location /api/portfolio {
|
|
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_pass http://stock:8000/api/portfolio;
|
|
}
|
|
|
|
|
|
# profile API (Personal Service)
|
|
location /api/profile/ {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $personal_backend personal: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_pass http://$personal_backend$request_uri;
|
|
}
|
|
|
|
# packs-lab — admin upload (5GB body limit, request buffering off)
|
|
location ^~ /api/packs/upload {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $packs_backend packs-lab:8000;
|
|
client_max_body_size 5G;
|
|
proxy_request_buffering off;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_pass http://$packs_backend$request_uri;
|
|
proxy_read_timeout 1800s;
|
|
proxy_send_timeout 1800s;
|
|
}
|
|
|
|
# packs-lab — 사용자 다운로드 + list/sign-link
|
|
location /api/packs/ {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $packs_backend packs-lab:8000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_pass http://$packs_backend$request_uri;
|
|
}
|
|
|
|
# agent-office API + WebSocket
|
|
location /api/agent-office/ {
|
|
resolver 127.0.0.11 valid=10s;
|
|
set $agent_office_backend agent-office: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 Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_read_timeout 86400s;
|
|
proxy_pass http://$agent_office_backend$request_uri;
|
|
}
|
|
|
|
# API 프록시 (여기가 포인트: /api/ 중복 제거)
|
|
location /api/ {
|
|
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_pass http://lotto:8000;
|
|
}
|
|
|
|
# Fear & Greed Index (CNN 공개 API)
|
|
# 프로덕션 nginx에서는 아래 proxy_pass 추가 필요:
|
|
location /ext/feargreed {
|
|
proxy_pass https://production.dataviz.cnn.io/index/fearandgreed/graphdata;
|
|
proxy_set_header Host production.dataviz.cnn.io;
|
|
}
|
|
|
|
# VIX (CBOE 변동성 지수) — Yahoo Finance 공개 API
|
|
# 프로덕션 nginx에서는 아래 proxy_pass 추가 필요:
|
|
location /ext/vix {
|
|
proxy_pass https://query1.finance.yahoo.com/v8/finance/chart/%5EVIX?interval=1d&range=1d;
|
|
proxy_set_header Host query1.finance.yahoo.com;
|
|
}
|
|
|
|
# 미국 10년물 국채 금리 (^TNX) — Yahoo Finance
|
|
# 프로덕션 nginx 설정 필요:
|
|
location /ext/treasury {
|
|
proxy_pass https://query1.finance.yahoo.com/v8/finance/chart/%5ETNX?interval=1d&range=1d;
|
|
proxy_set_header Host query1.finance.yahoo.com;
|
|
}
|
|
|
|
# WTI 원유 선물 (CL=F) — Yahoo Finance
|
|
# 프로덕션 nginx 설정 필요:
|
|
location /ext/wti {
|
|
proxy_pass https://query1.finance.yahoo.com/v8/finance/chart/CL%3DF?interval=1d&range=1d;
|
|
proxy_set_header Host query1.finance.yahoo.com;
|
|
}
|
|
|
|
# Brent 원유 선물 (BZ=F) — Yahoo Finance
|
|
# 프로덕션 nginx 설정 필요:
|
|
location /ext/brent {
|
|
proxy_pass https://query1.finance.yahoo.com/v8/finance/chart/BZ%3DF?interval=1d&range=1d;
|
|
proxy_set_header Host query1.finance.yahoo.com;
|
|
}
|
|
|
|
# webhook receiver (handle both /webhook and /webhook/)
|
|
location = /webhook {
|
|
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_pass http://deployer:9000/webhook;
|
|
}
|
|
|
|
location /webhook/ {
|
|
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_pass http://deployer:9000/webhook;
|
|
}
|
|
|
|
# SPA 라우팅 (마지막에 두는 게 안전)
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
|
|
# gzip (옵션)
|
|
gzip on;
|
|
gzip_types text/plain text/css application/json application/javascript application/xml+rss;
|
|
gzip_min_length 1024;
|
|
}
|
|
|