https 접속 허용
This commit is contained in:
@@ -15,6 +15,11 @@ BACKEND_PORT=8080
|
|||||||
|
|
||||||
# Frontend (외부 노출 포트)
|
# Frontend (외부 노출 포트)
|
||||||
FRONTEND_PORT=80
|
FRONTEND_PORT=80
|
||||||
|
FRONTEND_HTTPS_PORT=443
|
||||||
|
|
||||||
|
# HTTPS (선택) — scripts/setup-exdev-https.sh 로 발급 후 frontend 재시작
|
||||||
|
# SSL_CERT_DIR=./docker/nginx/ssl
|
||||||
|
# CERTBOT_WEBROOT_DIR=./docker/nginx/certbot-webroot
|
||||||
|
|
||||||
# Desktop Jenkins (웹 UI 「앱 빌드」 — exdev 서버 .env에 설정)
|
# Desktop Jenkins (웹 UI 「앱 빌드」 — exdev 서버 .env에 설정)
|
||||||
# GC_DESKTOP_JENKINS_URL=http://host.docker.internal:8090
|
# GC_DESKTOP_JENKINS_URL=http://host.docker.internal:8090
|
||||||
|
|||||||
@@ -28,4 +28,11 @@ desktop/updater.key
|
|||||||
**/.tauri/
|
**/.tauri/
|
||||||
data/mobile-releases/*.apk
|
data/mobile-releases/*.apk
|
||||||
frontend/public/desktop/updates/latest.json
|
frontend/public/desktop/updates/latest.json
|
||||||
|
frontend/public/desktop/updates/*.tar.gz
|
||||||
|
frontend/public/desktop/updates/*.exe
|
||||||
|
frontend/public/desktop/updates/*.dmg
|
||||||
|
|
||||||
|
# TLS (서버에서 발급 — git 제외)
|
||||||
|
docker/nginx/ssl/*.pem
|
||||||
|
docker/nginx/letsencrypt/
|
||||||
scripts/deploy-target.local.env
|
scripts/deploy-target.local.env
|
||||||
|
|||||||
+2
-1
@@ -305,7 +305,8 @@ echo -e "${GREEN}${BOLD}╔═════════════════
|
|||||||
echo -e "${GREEN}${BOLD}║ 배포 완료! 🎉 ║${NC}"
|
echo -e "${GREEN}${BOLD}║ 배포 완료! 🎉 ║${NC}"
|
||||||
echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════╝${NC}"
|
echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════╝${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " 접속 URL: ${CYAN}${BOLD}http://${REMOTE_HOST}:${FRONTEND_PORT}${NC}"
|
echo -e " HTTP: ${CYAN}${BOLD}http://${REMOTE_HOST}:${FRONTEND_PORT}${NC}"
|
||||||
|
echo -e " HTTPS: ${CYAN}${BOLD}https://${REMOTE_HOST}/${NC} ${YELLOW}(인증서: ./scripts/setup-exdev-https.sh)${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}자주 쓰는 명령어:${NC}"
|
echo -e "${YELLOW}자주 쓰는 명령어:${NC}"
|
||||||
echo " ./deploy-home.sh # 전체 재빌드 배포 (기본)"
|
echo " ./deploy-home.sh # 전체 재빌드 배포 (기본)"
|
||||||
|
|||||||
+41
-1
@@ -90,7 +90,7 @@ GoldenChart 배포는 **공통 스크립트 `deploy.sh`** 를 중심으로 두
|
|||||||
|
|
||||||
| 서비스 | 컨테이너 | 포트 (기본) | URL |
|
| 서비스 | 컨테이너 | 포트 (기본) | URL |
|
||||||
|--------|---------|------------|-----|
|
|--------|---------|------------|-----|
|
||||||
| Frontend (Nginx) | `gc-frontend` | 80 | http://exdev.co.kr/ |
|
| Frontend (Nginx) | `gc-frontend` | 80, **443** | http://exdev.co.kr/ · https://exdev.co.kr/ |
|
||||||
| Backend (Spring Boot) | `gc-backend` | 8080 | http://exdev.co.kr:8080 (직접) 또는 `/api` (Nginx 경유) |
|
| Backend (Spring Boot) | `gc-backend` | 8080 | http://exdev.co.kr:8080 (직접) 또는 `/api` (Nginx 경유) |
|
||||||
| MySQL | `gc-mysql` | 3306 | localhost:3306 (서버 내부) |
|
| MySQL | `gc-mysql` | 3306 | localhost:3306 (서버 내부) |
|
||||||
|
|
||||||
@@ -602,6 +602,46 @@ docker system prune -f
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## HTTPS (HTTP + HTTPS 동시 접속)
|
||||||
|
|
||||||
|
기본은 **HTTP(80)만** 동작합니다. `docker/nginx/ssl/` 에 인증서가 있으면 frontend 컨테이너가 **HTTPS(443)** 도 함께 엽니다. HTTP→HTTPS 강제 리다이렉트는 하지 않습니다.
|
||||||
|
|
||||||
|
### 최초 1회 (exdev.co.kr 서버)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/aidev/dev/goldenChart # work-tree 경로
|
||||||
|
|
||||||
|
# 1) frontend 가 80에서 동작 중인지 확인
|
||||||
|
docker compose up -d frontend
|
||||||
|
|
||||||
|
# 2) Let's Encrypt 발급 (도메인이 이 서버를 가리켜야 함, 443 방화벽 개방)
|
||||||
|
./scripts/setup-exdev-https.sh --email you@example.com --domain exdev.co.kr
|
||||||
|
|
||||||
|
# 테스트용 자체 서명 (브라우저 경고 발생)
|
||||||
|
# ./scripts/setup-exdev-https.sh --self-signed
|
||||||
|
```
|
||||||
|
|
||||||
|
### 검증
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -o /dev/null -w "HTTP: %{http_code}\n" http://exdev.co.kr/
|
||||||
|
curl -s -o /dev/null -w "HTTPS: %{http_code}\n" https://exdev.co.kr/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 인증서 갱신 (cron 예시, 매월)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm \
|
||||||
|
-v "$(pwd)/docker/nginx/certbot-webroot:/var/www/certbot" \
|
||||||
|
-v "$(pwd)/docker/nginx/letsencrypt:/etc/letsencrypt" \
|
||||||
|
certbot/certbot renew
|
||||||
|
docker compose exec frontend nginx -s reload
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Mac 방화벽:** 시스템 설정 → 네트워크 → 방화벽에서 **443/tcp** 허용. 공유기/클라우드에서도 443 포워딩이 필요할 수 있습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 12. 트러블슈팅
|
## 12. 트러블슈팅
|
||||||
|
|
||||||
### 12-1. git push 후 배포가 안 됨
|
### 12-1. git push 후 배포가 안 됨
|
||||||
|
|||||||
@@ -114,8 +114,13 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- "${FRONTEND_PORT:-80}:80"
|
- "${FRONTEND_PORT:-80}:80"
|
||||||
|
- "${FRONTEND_HTTPS_PORT:-443}:443"
|
||||||
volumes:
|
volumes:
|
||||||
# Desktop updater latest.json·설치 파일 — Jenkins publish + nginx reload
|
# Desktop updater latest.json·설치 파일 — Jenkins publish + nginx reload
|
||||||
- ./frontend/public/desktop/updates:/usr/share/nginx/html/desktop/updates:ro
|
- ./frontend/public/desktop/updates:/usr/share/nginx/html/desktop/updates:ro
|
||||||
|
# HTTPS — fullchain.pem + privkey.pem (scripts/setup-exdev-https.sh)
|
||||||
|
- ${SSL_CERT_DIR:-./docker/nginx/ssl}:/etc/nginx/ssl:ro
|
||||||
|
# Let's Encrypt HTTP-01 webroot
|
||||||
|
- ${CERTBOT_WEBROOT_DIR:-./docker/nginx/certbot-webroot}:/var/www/certbot:ro
|
||||||
networks:
|
networks:
|
||||||
- goldenchart-net
|
- goldenchart-net
|
||||||
|
|||||||
+5
-6
@@ -2,12 +2,9 @@
|
|||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# 의존성 레이어 캐시 (package*.json 변경 시만 재설치)
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# 소스 복사 후 프로덕션 빌드
|
|
||||||
# Docker 환경에서 /api 는 nginx 가 backend:8080 으로 프록시하므로 그대로 사용
|
|
||||||
COPY . .
|
COPY . .
|
||||||
ARG VITE_API_BASE_URL=/api
|
ARG VITE_API_BASE_URL=/api
|
||||||
ARG VITE_APP_VERSION=
|
ARG VITE_APP_VERSION=
|
||||||
@@ -18,11 +15,13 @@ RUN npm run build
|
|||||||
# ── 2단계: 서비스 ─────────────────────────────────────────────────────────────
|
# ── 2단계: 서비스 ─────────────────────────────────────────────────────────────
|
||||||
FROM nginx:1.27-alpine AS production
|
FROM nginx:1.27-alpine AS production
|
||||||
|
|
||||||
# 빌드 결과물 복사
|
|
||||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
# SPA 라우팅 + 정적 파일 캐시 설정
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY nginx-locations.inc /etc/nginx/gc-locations.inc
|
||||||
|
COPY nginx-https.conf /etc/nginx/nginx-https.conf
|
||||||
|
COPY docker-entrypoint.d/40-enable-https.sh /docker-entrypoint.d/40-enable-https.sh
|
||||||
|
RUN chmod +x /docker-entrypoint.d/40-enable-https.sh
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80 443
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
HTTPS_CONF="/etc/nginx/conf.d/https.conf"
|
||||||
|
HTTPS_TEMPLATE="/etc/nginx/nginx-https.conf"
|
||||||
|
|
||||||
|
if [ -f /etc/nginx/ssl/fullchain.pem ] && [ -f /etc/nginx/ssl/privkey.pem ]; then
|
||||||
|
cp "$HTTPS_TEMPLATE" "$HTTPS_CONF"
|
||||||
|
echo "[gc-frontend] HTTPS enabled — listening on 443"
|
||||||
|
else
|
||||||
|
rm -f "$HTTPS_CONF" 2>/dev/null || true
|
||||||
|
echo "[gc-frontend] HTTPS skipped — place fullchain.pem + privkey.pem in /etc/nginx/ssl"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# SSL 인증서가 /etc/nginx/ssl/ 에 있을 때 entrypoint 가 conf.d 에 복사한다.
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
http2 on;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/ssl/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
|
||||||
|
client_max_body_size 32m;
|
||||||
|
|
||||||
|
resolver 127.0.0.11 8.8.8.8 8.8.4.4 valid=30s;
|
||||||
|
resolver_timeout 5s;
|
||||||
|
|
||||||
|
include /etc/nginx/gc-locations.inc;
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
# Let's Encrypt HTTP-01 (certbot webroot)
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
allow all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── STOMP / SockJS (Spring context-path /api → /api/ws/trading) ───
|
||||||
|
location /api/ws/ {
|
||||||
|
proxy_pass http://backend:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $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_connect_timeout 7d;
|
||||||
|
proxy_send_timeout 7d;
|
||||||
|
proxy_read_timeout 7d;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
gzip off;
|
||||||
|
chunked_transfer_encoding on;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── GoldenChart Spring Boot Backend 프록시 ────────────────────────
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $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_connect_timeout 10s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Upbit REST API 역방향 프록시 ──────────────────────────────────────
|
||||||
|
location /upbit-api/ {
|
||||||
|
proxy_pass https://api.upbit.com/;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
proxy_set_header Host api.upbit.com;
|
||||||
|
proxy_set_header Accept application/json;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_hide_header Access-Control-Allow-Origin;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Upbit WebSocket 역방향 프록시 ─────────────────────────────────────
|
||||||
|
location /upbit-ws {
|
||||||
|
proxy_pass https://api.upbit.com/websocket/v1;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host api.upbit.com;
|
||||||
|
proxy_set_header Origin "https://upbit.com";
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Authorization "";
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── STOMP / SockJS WebSocket 프록시 (구버전 /ws/**) ─────────────────
|
||||||
|
location /ws/ {
|
||||||
|
proxy_pass http://backend:8080/api/ws/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $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_connect_timeout 7d;
|
||||||
|
proxy_send_timeout 7d;
|
||||||
|
proxy_read_timeout 7d;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 모바일 APK 다운로드 (대용량) ─────────────────────────────────────
|
||||||
|
location /api/mobile-app/download/ {
|
||||||
|
proxy_pass http://backend:8080;
|
||||||
|
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_send_timeout 600s;
|
||||||
|
proxy_buffering off;
|
||||||
|
client_max_body_size 200m;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── React SPA 폴백 ────────────────────────────────────────────────────
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(js|css|woff2?|ttf|eot|svg|ico|png|jpg|jpeg|gif|webp)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.html$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
|
# GoldenChart Desktop updater manifest + bundles
|
||||||
|
location /desktop/updates/ {
|
||||||
|
alias /usr/share/nginx/html/desktop/updates/;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
}
|
||||||
|
location = /desktop/updates/latest.json {
|
||||||
|
alias /usr/share/nginx/html/desktop/updates/latest.json;
|
||||||
|
default_type application/json;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types text/plain text/css text/javascript application/javascript
|
||||||
|
application/json image/svg+xml;
|
||||||
+1
-143
@@ -10,152 +10,10 @@ server {
|
|||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
# multipart 이미지 업로드 (Spring max-request-size 30MB와 맞춤)
|
|
||||||
client_max_body_size 32m;
|
client_max_body_size 32m;
|
||||||
|
|
||||||
# 외부 호스트명 DNS 조회용 resolver (Upbit + Backend 프록시에 필요)
|
|
||||||
resolver 127.0.0.11 8.8.8.8 8.8.4.4 valid=30s;
|
resolver 127.0.0.11 8.8.8.8 8.8.4.4 valid=30s;
|
||||||
resolver_timeout 5s;
|
resolver_timeout 5s;
|
||||||
|
|
||||||
# ── STOMP / SockJS (Spring context-path /api → /api/ws/trading) ───
|
include /etc/nginx/gc-locations.inc;
|
||||||
# SockJS 가 /api/ws/trading/{id}/{session}/websocket 으로 업그레이드하므로
|
|
||||||
# 일반 REST 프록시와 분리해 Upgrade 헤더·장시간 타임아웃을 반드시 설정한다.
|
|
||||||
location /api/ws/ {
|
|
||||||
proxy_pass http://backend:8080;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $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_connect_timeout 7d;
|
|
||||||
proxy_send_timeout 7d;
|
|
||||||
proxy_read_timeout 7d;
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_request_buffering off;
|
|
||||||
gzip off;
|
|
||||||
chunked_transfer_encoding on;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── GoldenChart Spring Boot Backend 프록시 ────────────────────────
|
|
||||||
# 브라우저 → /api/... → http://backend:8080/api/...
|
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://backend:8080;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $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_connect_timeout 10s;
|
|
||||||
proxy_read_timeout 60s;
|
|
||||||
proxy_send_timeout 60s;
|
|
||||||
proxy_buffering off;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Upbit REST API 역방향 프록시 ──────────────────────────────────────
|
|
||||||
# 브라우저 → /upbit-api/v1/... → https://api.upbit.com/v1/...
|
|
||||||
# CORS 없이 동일 오리진 요청으로 처리됨 (dev: Vite proxy, prod: nginx proxy)
|
|
||||||
location /upbit-api/ {
|
|
||||||
proxy_pass https://api.upbit.com/;
|
|
||||||
proxy_ssl_server_name on;
|
|
||||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
proxy_set_header Host api.upbit.com;
|
|
||||||
proxy_set_header Accept application/json;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_hide_header Access-Control-Allow-Origin;
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
proxy_cache_bypass $http_upgrade;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Upbit WebSocket 역방향 프록시 ─────────────────────────────────────
|
|
||||||
# 브라우저 → ws://HOST/upbit-ws → wss://api.upbit.com/websocket/v1
|
|
||||||
location /upbit-ws {
|
|
||||||
proxy_pass https://api.upbit.com/websocket/v1;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_ssl_server_name on;
|
|
||||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
proxy_ssl_verify off;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "Upgrade";
|
|
||||||
proxy_set_header Host api.upbit.com;
|
|
||||||
proxy_set_header Origin "https://upbit.com";
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# 쿠키/Authorization 헤더 제거 (업비트 WebSocket 인증 불필요)
|
|
||||||
proxy_set_header Authorization "";
|
|
||||||
proxy_read_timeout 3600s;
|
|
||||||
proxy_send_timeout 3600s;
|
|
||||||
proxy_buffering off;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── STOMP / SockJS WebSocket 프록시 ──────────────────────────────────
|
|
||||||
# Spring context-path /api → 실제 엔드포인트는 /api/ws/trading
|
|
||||||
# (구버전 클라이언트 /ws/** 요청도 /api/ws/** 로 전달)
|
|
||||||
location /ws/ {
|
|
||||||
proxy_pass http://backend:8080/api/ws/;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $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_connect_timeout 7d;
|
|
||||||
proxy_send_timeout 7d;
|
|
||||||
proxy_read_timeout 7d;
|
|
||||||
proxy_buffering off;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── 모바일 APK 다운로드 (대용량) ─────────────────────────────────────
|
|
||||||
location /api/mobile-app/download/ {
|
|
||||||
proxy_pass http://backend:8080;
|
|
||||||
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_send_timeout 600s;
|
|
||||||
proxy_buffering off;
|
|
||||||
client_max_body_size 200m;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── React SPA 폴백 ────────────────────────────────────────────────────
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# JS/CSS/이미지 등 정적 자산 — 1년 장기 캐시 (Vite 콘텐츠 해시 포함 파일명)
|
|
||||||
location ~* \.(js|css|woff2?|ttf|eot|svg|ico|png|jpg|jpeg|gif|webp)$ {
|
|
||||||
expires 1y;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
access_log off;
|
|
||||||
}
|
|
||||||
|
|
||||||
# HTML 은 캐시 안 함 (항상 최신 index.html 로드)
|
|
||||||
location ~* \.html$ {
|
|
||||||
expires -1;
|
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
|
||||||
}
|
|
||||||
|
|
||||||
# GoldenChart Desktop updater manifest + bundles
|
|
||||||
location /desktop/updates/ {
|
|
||||||
alias /usr/share/nginx/html/desktop/updates/;
|
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
||||||
}
|
|
||||||
location = /desktop/updates/latest.json {
|
|
||||||
alias /usr/share/nginx/html/desktop/updates/latest.json;
|
|
||||||
default_type application/json;
|
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
||||||
}
|
|
||||||
|
|
||||||
# gzip 압축
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_min_length 1024;
|
|
||||||
gzip_types text/plain text/css text/javascript application/javascript
|
|
||||||
application/json image/svg+xml;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+70
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# exdev.co.kr — HTTP(80) 유지 + HTTPS(443) Let's Encrypt 인증서 발급
|
||||||
|
#
|
||||||
|
# 사전: docker compose up -d frontend (80 포트 동작, /.well-known/acme-challenge/ 노출)
|
||||||
|
# 사용: ./scripts/setup-exdev-https.sh [--email you@example.com] [--domain exdev.co.kr]
|
||||||
|
# ./scripts/setup-exdev-https.sh --self-signed # 로컬/테스트용 자체 서명
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
DOMAIN="${DOMAIN:-exdev.co.kr}"
|
||||||
|
EMAIL="${CERTBOT_EMAIL:-}"
|
||||||
|
SELF_SIGNED=false
|
||||||
|
SSL_DIR="${SSL_CERT_DIR:-$ROOT/docker/nginx/ssl}"
|
||||||
|
WEBROOT="${CERTBOT_WEBROOT_DIR:-$ROOT/docker/nginx/certbot-webroot}"
|
||||||
|
LE_DIR="$ROOT/docker/nginx/letsencrypt"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--email) EMAIL="$2"; shift 2 ;;
|
||||||
|
--domain) DOMAIN="$2"; shift 2 ;;
|
||||||
|
--self-signed) SELF_SIGNED=true; shift ;;
|
||||||
|
-h|--help)
|
||||||
|
echo "Usage: $0 [--email E] [--domain D] [--self-signed]"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$SSL_DIR" "$WEBROOT" "$LE_DIR"
|
||||||
|
chmod 755 "$SSL_DIR" "$WEBROOT"
|
||||||
|
|
||||||
|
link_certs() {
|
||||||
|
ln -sf "../letsencrypt/live/$DOMAIN/fullchain.pem" "$SSL_DIR/fullchain.pem"
|
||||||
|
ln -sf "../letsencrypt/live/$DOMAIN/privkey.pem" "$SSL_DIR/privkey.pem"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$SELF_SIGNED" == true ]]; then
|
||||||
|
echo "[setup-https] self-signed certificate for $DOMAIN"
|
||||||
|
openssl req -x509 -nodes -days 825 -newkey rsa:2048 \
|
||||||
|
-keyout "$SSL_DIR/privkey.pem" \
|
||||||
|
-out "$SSL_DIR/fullchain.pem" \
|
||||||
|
-subj "/CN=$DOMAIN"
|
||||||
|
else
|
||||||
|
if [[ -z "$EMAIL" ]]; then
|
||||||
|
echo "[setup-https] ERROR: --email required for Let's Encrypt (or use --self-signed)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[setup-https] certbot webroot — domain=$DOMAIN email=$EMAIL"
|
||||||
|
docker run --rm \
|
||||||
|
-v "$WEBROOT:/var/www/certbot" \
|
||||||
|
-v "$LE_DIR:/etc/letsencrypt" \
|
||||||
|
certbot/certbot certonly --webroot -w /var/www/certbot \
|
||||||
|
-d "$DOMAIN" --email "$EMAIL" --agree-tos --non-interactive --keep-until-expiring
|
||||||
|
link_certs
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[setup-https] restarting frontend (443 enable)..."
|
||||||
|
docker compose up -d --force-recreate frontend
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done."
|
||||||
|
echo " HTTP: http://${DOMAIN}/"
|
||||||
|
echo " HTTPS: https://${DOMAIN}/"
|
||||||
|
echo ""
|
||||||
|
echo "Renew (cron monthly):"
|
||||||
|
echo " docker run --rm -v \"$WEBROOT:/var/www/certbot\" -v \"$LE_DIR:/etc/letsencrypt\" \\"
|
||||||
|
echo " certbot/certbot renew && docker compose exec frontend nginx -s reload"
|
||||||
Reference in New Issue
Block a user