diff --git a/app/.env.example b/app/.env.example index 7eebaaa..a584485 100644 --- a/app/.env.example +++ b/app/.env.example @@ -2,4 +2,4 @@ # VITE_API_BASE_URL=http://localhost:8080/api # 모바일 APK / exdev (HTTPS 443 미사용 — http 만) -VITE_API_BASE_URL=http://exdev.co.kr/api +VITE_API_BASE_URL=https://stock.exdev.co.kr/api diff --git a/app/README.md b/app/README.md index c5a6003..eb0a0c1 100644 --- a/app/README.md +++ b/app/README.md @@ -26,7 +26,7 @@ npm run cap:android # or cap:ios ./scripts/upload-android-apk.sh --build --version 1.0.0 ``` -`app/.env` 예: `VITE_API_BASE_URL=http://exdev.co.kr/api` +`app/.env` 예: `VITE_API_BASE_URL=https://stock.exdev.co.kr/api` 자세한 내용: [`data/mobile-releases/README.md`](../data/mobile-releases/README.md) @@ -36,7 +36,7 @@ Copy `.env.example` to `.env` and set `VITE_API_BASE_URL`. ### 로그인 시 "Failed to fetch" / "서버 응답이 느립니다" -- API는 **`http://exdev.co.kr/api`** 만 사용 (`https://exdev.co.kr` 은 443 미개방). +- API는 **`https://stock.exdev.co.kr/api`** 만 사용 (`https://stock.exdev.co.kr` 은 443 미개방). - exdev 로그인은 **10~30초** 걸릴 수 있음 — 2분 타임아웃까지 대기. - APK는 `app/.env`의 `VITE_API_BASE_URL`이 빌드에 포함되어야 함. - `androidScheme`은 **`http`**. `CapacitorHttp`는 비활성(로그인 fetch 안정화). diff --git a/app_golden/src/main/java/com/analysis/goldenanalysis/ui/chart/ChartFragment.kt b/app_golden/src/main/java/com/analysis/goldenanalysis/ui/chart/ChartFragment.kt index 341051d..00a257b 100644 --- a/app_golden/src/main/java/com/analysis/goldenanalysis/ui/chart/ChartFragment.kt +++ b/app_golden/src/main/java/com/analysis/goldenanalysis/ui/chart/ChartFragment.kt @@ -34,8 +34,8 @@ class ChartFragment : Fragment() { private fun getFrontendBaseUrl(): String { val prefs = requireActivity().getSharedPreferences("app_prefs", Context.MODE_PRIVATE) - return prefs.getString("frontend_url", "http://exdev.co.kr/mobile/chart") - ?: "http://exdev.co.kr/mobile/chart" + return prefs.getString("frontend_url", "https://stock.exdev.co.kr/mobile/chart") + ?: "https://stock.exdev.co.kr/mobile/chart" } /** 알림에서 저장한 심볼이 있으면 쿼리 gaSymbol 붙이고 prefs 에서 제거 (프론트 연동 시 사용). */ diff --git a/app_golden/src/main/java/com/analysis/goldenanalysis/ui/settings/SettingsFragment.kt b/app_golden/src/main/java/com/analysis/goldenanalysis/ui/settings/SettingsFragment.kt index 2d96493..89000c3 100644 --- a/app_golden/src/main/java/com/analysis/goldenanalysis/ui/settings/SettingsFragment.kt +++ b/app_golden/src/main/java/com/analysis/goldenanalysis/ui/settings/SettingsFragment.kt @@ -39,8 +39,8 @@ class SettingsFragment : Fragment() { binding.switchDarkMode.isChecked = prefs.getBoolean("dark_mode", false) // Frontend URL 설정 (차트 페이지) // 로컬 Docker: http://192.168.219.106/mobile/chart - // 운영: https://exdev.co.kr/mobile/chart - val defaultFrontendUrl = "https://exdev.co.kr/mobile/chart" + // 운영: https://stock.exdev.co.kr/mobile/chart + val defaultFrontendUrl = "https://stock.exdev.co.kr/mobile/chart" binding.etApiServer.setText(prefs.getString("frontend_url", defaultFrontendUrl)) binding.etApiBackend.setText(prefs.getString("api_base_url", "")) diff --git a/app_golden/src/main/java/com/analysis/goldenanalysis/util/ApiPrefs.kt b/app_golden/src/main/java/com/analysis/goldenanalysis/util/ApiPrefs.kt index 2f5a682..6907564 100644 --- a/app_golden/src/main/java/com/analysis/goldenanalysis/util/ApiPrefs.kt +++ b/app_golden/src/main/java/com/analysis/goldenanalysis/util/ApiPrefs.kt @@ -24,8 +24,8 @@ object ApiPrefs { if (!b.endsWith("/api")) b = "$b/api" return "$b/" } - val front = p.getString(KEY_FRONTEND, "https://exdev.co.kr/mobile/chart") - ?: "https://exdev.co.kr/mobile/chart" + val front = p.getString(KEY_FRONTEND, "https://stock.exdev.co.kr/mobile/chart") + ?: "https://stock.exdev.co.kr/mobile/chart" val uri = Uri.parse(front) val scheme = uri.scheme ?: "https" val host = uri.host ?: "exdev.co.kr" diff --git a/backend/FCM_BACKEND.md b/backend/FCM_BACKEND.md index de5ee88..619b84a 100644 --- a/backend/FCM_BACKEND.md +++ b/backend/FCM_BACKEND.md @@ -35,11 +35,11 @@ goldenApp과 동일 프로젝트 **goldenanalysisapp** 서비스 계정 JSON 사 ## 확인 ```bash -curl http://exdev.co.kr/api/fcm/status +curl https://stock.exdev.co.kr/api/fcm/status # {"available":true} # 앱에서 FCM 푸시 ON 후 -curl -X POST http://exdev.co.kr/api/fcm/test \ +curl -X POST https://stock.exdev.co.kr/api/fcm/test \ -H "x-user-id: 1" \ -H "x-device-id: <앱 device id>" ``` diff --git a/backend/src/main/java/com/goldenchart/service/DesktopAppReleaseService.java b/backend/src/main/java/com/goldenchart/service/DesktopAppReleaseService.java index 82680e3..96771b5 100644 --- a/backend/src/main/java/com/goldenchart/service/DesktopAppReleaseService.java +++ b/backend/src/main/java/com/goldenchart/service/DesktopAppReleaseService.java @@ -320,12 +320,12 @@ public class DesktopAppReleaseService { if (requestBaseUrl != null && !requestBaseUrl.isBlank()) { return requestBaseUrl.trim(); } - return "http://exdev.co.kr"; + return "https://stock.exdev.co.kr"; } private static String normalizeBase(String publicBaseUrl) { if (publicBaseUrl == null || publicBaseUrl.isBlank()) { - return "http://exdev.co.kr"; + return "https://stock.exdev.co.kr"; } return publicBaseUrl.replaceAll("/+$", ""); } diff --git a/backend/src/main/java/com/goldenchart/service/MobileAppReleaseService.java b/backend/src/main/java/com/goldenchart/service/MobileAppReleaseService.java index 20ffb21..59de15a 100644 --- a/backend/src/main/java/com/goldenchart/service/MobileAppReleaseService.java +++ b/backend/src/main/java/com/goldenchart/service/MobileAppReleaseService.java @@ -131,12 +131,12 @@ public class MobileAppReleaseService { if (requestBaseUrl != null && !requestBaseUrl.isBlank()) { return requestBaseUrl.trim(); } - return "http://exdev.co.kr"; + return "https://stock.exdev.co.kr"; } private static String normalizeBase(String publicBaseUrl) { if (publicBaseUrl == null || publicBaseUrl.isBlank()) { - return "http://exdev.co.kr"; + return "https://stock.exdev.co.kr"; } return publicBaseUrl.replaceAll("/+$", ""); } diff --git a/backend/src/main/resources/application-docker.yml b/backend/src/main/resources/application-docker.yml index a261b65..13175d2 100644 --- a/backend/src/main/resources/application-docker.yml +++ b/backend/src/main/resources/application-docker.yml @@ -29,7 +29,7 @@ goldenchart: desktop-app: release-dir: ${GC_DESKTOP_APP_RELEASE_DIR:/app/data/desktop-releases} updates-dir: ${GC_DESKTOP_UPDATES_DIR:/app/data/desktop-updates} - public-base-url: ${GC_DESKTOP_APP_PUBLIC_BASE_URL:http://exdev.co.kr} + public-base-url: ${GC_DESKTOP_APP_PUBLIC_BASE_URL:https://stock.exdev.co.kr} jenkins: enabled: ${GC_DESKTOP_JENKINS_ENABLED:true} # Docker 컨테이너 → 호스트 Jenkins (127.0.0.1:8090 은 컨테이너 자신을 가리킴) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 4404451..1577cd3 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -111,7 +111,7 @@ goldenchart: release-dir: ${GC_MOBILE_APP_RELEASE_DIR:data/mobile-releases} version: ${GC_MOBILE_APP_VERSION:} # QR·APK 다운로드 절대 URL (로컬 개발 시에도 exdev 서버 APK 사용) - public-base-url: ${GC_MOBILE_APP_PUBLIC_BASE_URL:http://exdev.co.kr} + public-base-url: ${GC_MOBILE_APP_PUBLIC_BASE_URL:https://stock.exdev.co.kr} ios-testflight-url: ${GC_MOBILE_APP_IOS_TESTFLIGHT_URL:} jenkins: enabled: ${GC_MOBILE_JENKINS_ENABLED:true} @@ -123,7 +123,7 @@ goldenchart: desktop-app: release-dir: ${GC_DESKTOP_APP_RELEASE_DIR:data/desktop-releases} updates-dir: ${GC_DESKTOP_UPDATES_DIR:} - public-base-url: ${GC_DESKTOP_APP_PUBLIC_BASE_URL:http://exdev.co.kr} + public-base-url: ${GC_DESKTOP_APP_PUBLIC_BASE_URL:https://stock.exdev.co.kr} git-bare-dir: ${GC_GIT_BARE_DIR:} work-tree: ${GC_WORK_TREE:} jenkins: diff --git a/data/desktop-releases/README.md b/data/desktop-releases/README.md index 913e0c3..4c0754c 100644 --- a/data/desktop-releases/README.md +++ b/data/desktop-releases/README.md @@ -9,4 +9,4 @@ Jenkins 빌드 후 `scripts/publish-desktop-update.sh`가 이 디렉터리와 | `GoldenChart_*-setup.exe` | Windows | - API: `GET /api/desktop-app/info` -- 다운로드 URL: `http://exdev.co.kr/desktop/updates/<파일명>` +- 다운로드 URL: `https://stock.exdev.co.kr/desktop/updates/<파일명>` diff --git a/data/mobile-releases/README.md b/data/mobile-releases/README.md index 7b1c1ed..8dd92f3 100644 --- a/data/mobile-releases/README.md +++ b/data/mobile-releases/README.md @@ -39,7 +39,7 @@ chmod +x scripts/upload-android-apk.sh ```bash REMOTE_PROJECT_DIR=/Users/aidev/apps/goldenChart -PUBLIC_URL=http://exdev.co.kr +PUBLIC_URL=https://stock.exdev.co.kr ``` ## 서버 파일 위치 @@ -51,8 +51,8 @@ PUBLIC_URL=http://exdev.co.kr Docker backend가 `/app/data/mobile-releases`로 마운트해 제공합니다. - API: `GET /api/mobile-app/info` -- 다운로드: `GET /api/mobile-app/download/android` (절대 URL: `http://exdev.co.kr`, HTTPS 443 미사용) -- 설치 페이지: `http://exdev.co.kr/install.html` +- 다운로드: `GET /api/mobile-app/download/android` (절대 URL: `https://stock.exdev.co.kr`, HTTPS 443 미사용) +- 설치 페이지: `https://stock.exdev.co.kr/install.html` exdev.co.kr은 **HTTP(80)만** 열려 있습니다. QR URL에 `https://` 를 쓰면 휴대폰에서 연결되지 않습니다. @@ -76,7 +76,7 @@ docker compose up -d backend # 볼륨 마운트 후 모바일 앱은 `app/.env`의 `VITE_API_BASE_URL`이 **빌드 시** 박힙니다. exdev 배포용: ```env -VITE_API_BASE_URL=http://exdev.co.kr/api +VITE_API_BASE_URL=https://stock.exdev.co.kr/api ``` 변경 후 `npm run cap:sync` → APK 재빌드 → 업로드. diff --git a/deploy_server_guide.md b/deploy_server_guide.md index 28582cc..5000b3c 100644 --- a/deploy_server_guide.md +++ b/deploy_server_guide.md @@ -90,8 +90,8 @@ GoldenChart 배포는 **공통 스크립트 `deploy.sh`** 를 중심으로 두 | 서비스 | 컨테이너 | 포트 (기본) | URL | |--------|---------|------------|-----| -| 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 경유) | +| Frontend (Nginx) | `gc-frontend` | **80** (443 금지) | http://stock.exdev.co.kr/ · https://stock.exdev.co.kr/ (Caddy 경유) | +| Backend (Spring Boot) | `gc-backend` | 8080 | http://stock.exdev.co.kr:8080 (직접) 또는 `/api` (Nginx 경유) | | MySQL | `gc-mysql` | 3306 | localhost:3306 (서버 내부) | 브라우저에서는 Nginx가 `/api` → `backend:8080` 으로 프록시합니다. @@ -541,7 +541,7 @@ curl -s -o /dev/null -w "Frontend HTTP: %{http_code}\n" http://localhost:80/ # 기대: 200 ``` -브라우저: http://exdev.co.kr/ +브라우저: http://stock.exdev.co.kr/ ### 10-3. Backend API @@ -604,22 +604,33 @@ docker system prune -f ## HTTPS (HTTP + HTTPS 동시 접속) -### exdev.co.kr (운영 — Caddy + gc-frontend) +### exdev.co.kr (운영 — Caddy + stock / chatbot 서브도메인) -이 서버는 **443 포트를 ex-chatbot Caddy**가 사용합니다. GoldenChart HTTPS는 Caddy → `host.docker.internal:80` → **gc-frontend** 경로입니다. +443 포트는 **ex-chatbot Caddy**만 사용. GoldenChart·Chatbot은 **443 직접 바인딩 금지**. -| URL | 경로 | +| URL | 처리 | |-----|------| -| `http://exdev.co.kr/` | gc-frontend `:80` 직접 | -| `https://exdev.co.kr/` | Caddy `:443` (TLS) → gc-frontend `:80` | +| https://stock.exdev.co.kr | Caddy → gc-frontend `:80` (GoldenChart) | +| https://chatbot.exdev.co.kr/chatbot/* | Caddy → rag-api + Admin | +| https://exdev.co.kr/chatbot/* | → chatbot.exdev.co.kr 301 | +| https://exdev.co.kr/* (그 외) | → stock.exdev.co.kr 301 | -**주의:** `docker-compose.yml` 에 frontend `443:443` 매핑을 넣으면 Caddy 와 충돌해 **frontend 가 기동되지 않습니다** (HTTP·HTTPS 모두 502). frontend 는 **80만** 노출합니다. +Caddyfile: `exAIChatbot2.0/docker/caddy/Caddyfile.exdev` +절차: [docs/exdev-subdomain-setup.md](../docs/exdev-subdomain-setup.md) + +```bash +curl -sS -o /dev/null -w "stock %{size_download}B\n" https://stock.exdev.co.kr/ +curl -sS -o /dev/null -w "chatbot %{size_download}B\n" https://chatbot.exdev.co.kr/chatbot/health +DESKTOP_UPDATE_BASE_URL=https://stock.exdev.co.kr/desktop/updates ./scripts/verify-desktop-updater-endpoint.sh +``` + +**주의:** gc-frontend `443:443` 매핑 금지 (Caddy 충돌). frontend는 **80만** 노출. ```bash # frontend 기동 확인 docker ps --filter name=gc-frontend -curl -s -o /dev/null -w "HTTP:%{http_code}\n" http://exdev.co.kr/ -curl -sk -o /dev/null -w "HTTPS:%{http_code}\n" https://exdev.co.kr/ +curl -s -o /dev/null -w "HTTP:%{http_code}\n" http://stock.exdev.co.kr/ +curl -sk -o /dev/null -w "HTTPS:%{http_code}\n" https://stock.exdev.co.kr/ ``` Caddy TLS 인증서: `exAIChatbot2.0/docker/nginx/certs/` (`obtain-cert-exdev.sh`) @@ -688,6 +699,34 @@ docker compose up -d --no-deps backend docker compose up -d --no-deps frontend ``` +### 12-5a. PC 앱 자동 업데이트 — latest.json 형식 오류 / HTTPS 빈 응답 + +데스크톱 앱(Tauri)은 **HTTPS** `https://stock.exdev.co.kr/desktop/updates/latest.json` 만 사용합니다. + +**증상:** 앱 업데이트 모달에 `latest.json 형식 오류` 또는 JSON parse 실패. + +**원인 1 (가장 흔함):** ex-chatbot **Caddy(443)** 가 gc-frontend 로 프록시할 때 **HTTP 200 + 본문 0바이트** 를 반환. nginx(:80) 직접 접속은 정상. + +```bash +# 진단 (로컬·서버 공통) +DESKTOP_UPDATE_BASE_URL=https://stock.exdev.co.kr/desktop/updates ./scripts/verify-desktop-updater-endpoint.sh + +# 수동 비교 +curl -sS -o /dev/null -w "HTTPS size=%{size_download}\n" https://stock.exdev.co.kr/desktop/updates/latest.json +curl -sS -o /dev/null -w "HTTP size=%{size_download}\n" http://stock.exdev.co.kr/desktop/updates/latest.json +curl -sS http://stock.exdev.co.kr/desktop/updates/latest.json | head +``` + +| 결과 | 의미 | +|------|------| +| HTTPS 0B, HTTP 1KB+ | **Caddy 역프록시** 설정 오류 — `stock.exdev.co.kr` 블록에서 `reverse_proxy host.docker.internal:80` 및 `flush_interval -1` 확인 | +| HTTPS·HTTP 모두 0B | `frontend/public/desktop/updates/latest.json` 미생성 — Desktop Jenkins `goldenChart-Desktop-Pipeline` publish 실행 | +| HTTPS OK, platforms `{}` | manifest 는 있으나 서명 bundle 없음 — `publish-desktop-update.sh` 재실행 | + +**Caddy 수정 후:** `curl -sS https://stock.exdev.co.kr/desktop/updates/latest.json | jq .version` 로 v0.1.x 확인. + +**임시 우회:** PC 프로그램 탭에서 dmg/exe 수동 설치. + ### 12-6. deploy-home.sh SSH 실패 ```bash diff --git a/desktop/.env.example b/desktop/.env.example index 7d3ff0d..d7fc510 100644 --- a/desktop/.env.example +++ b/desktop/.env.example @@ -1 +1 @@ -VITE_API_BASE_URL=http://exdev.co.kr/api +VITE_API_BASE_URL=https://stock.exdev.co.kr/api diff --git a/desktop/src-tauri/Info.plist b/desktop/src-tauri/Info.plist index e9618bc..6c0f0c9 100644 --- a/desktop/src-tauri/Info.plist +++ b/desktop/src-tauri/Info.plist @@ -8,7 +8,7 @@ NSExceptionDomains - exdev.co.kr + stock.exdev.co.kr NSExceptionAllowsInsecureHTTPLoads diff --git a/desktop/src-tauri/capabilities/default.json b/desktop/src-tauri/capabilities/default.json index 2423888..d61e6fd 100644 --- a/desktop/src-tauri/capabilities/default.json +++ b/desktop/src-tauri/capabilities/default.json @@ -35,10 +35,10 @@ { "identifier": "http:default", "allow": [ - { "url": "http://exdev.co.kr/**" }, + { "url": "https://stock.exdev.co.kr/**" }, { "url": "http://localhost:8080/**" }, { "url": "http://127.0.0.1:8080/**" }, - { "url": "https://exdev.co.kr/**" }, + { "url": "https://stock.exdev.co.kr/**" }, { "url": "https://api.upbit.com/**" } ] } diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 19cf123..7512096 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -25,7 +25,7 @@ } ], "security": { - "csp": "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; connect-src 'self' http://localhost:8080 http://127.0.0.1:8080 http://exdev.co.kr https://exdev.co.kr ws://exdev.co.kr wss://exdev.co.kr ws://localhost:8080 https://api.upbit.com wss://api.upbit.com https://fonts.googleapis.com https://fonts.gstatic.com; img-src 'self' data: blob: https:; font-src 'self' data: https://fonts.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" + "csp": "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; connect-src 'self' http://localhost:8080 http://127.0.0.1:8080 https://stock.exdev.co.kr wss://stock.exdev.co.kr ws://localhost:8080 https://api.upbit.com wss://api.upbit.com https://fonts.googleapis.com https://fonts.gstatic.com; img-src 'self' data: blob: https:; font-src 'self' data: https://fonts.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" }, "trayIcon": { "iconPath": "icons/icon.png", @@ -44,7 +44,7 @@ "icons/icon.ico" ], "macOS": { - "exceptionDomain": "exdev.co.kr", + "exceptionDomain": "stock.exdev.co.kr", "infoPlist": "Info.plist", "signingIdentity": "-", "entitlements": "./Entitlements.plist" @@ -58,7 +58,7 @@ "plugins": { "updater": { "endpoints": [ - "https://exdev.co.kr/desktop/updates/latest.json" + "https://stock.exdev.co.kr/desktop/updates/latest.json" ], "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDg3MUJCMjIyRkIyNzdFMUUKUldRZWZpZjdJckliaDlMS3pvRHpmQ2lnSFNjaDQrT1pEZTdDZEJ5S2RhOXV5aU1QY3JObEEwaFUK", "windows": { diff --git a/desktop/src/bridge/updater.ts b/desktop/src/bridge/updater.ts index 9305417..4f1a698 100644 --- a/desktop/src/bridge/updater.ts +++ b/desktop/src/bridge/updater.ts @@ -149,8 +149,8 @@ async function formatUpdateError(e: unknown): Promise { if (!msg.trim()) return '업데이트 확인에 실패했습니다.'; if (/unexpected end of json|eof|empty|content-length.*0|^$/.test(lower) && msg.trim().length < 80) { return onMac - ? '서버 latest.json 이 비어 있습니다. PC 프로그램 탭에서 dmg를 받아 다시 설치해 주세요. (관리자: Desktop Jenkins publish 필요)' - : '서버 latest.json 이 비어 있습니다. PC 프로그램 탭에서 exe를 받아 다시 설치해 주세요. (관리자: Desktop Jenkins publish 필요)'; + ? '서버 latest.json 이 비어 있습니다. HTTPS(Caddy) 역프록시가 본문 없이 200을 반환하는 경우가 많습니다. 관리자에게 Caddy→gc-frontend:80 설정 점검을 요청하거나, PC 프로그램 탭에서 dmg를 받아 다시 설치해 주세요.' + : '서버 latest.json 이 비어 있습니다. HTTPS(Caddy) 역프록시가 본문 없이 200을 반환하는 경우가 많습니다. 관리자에게 Caddy→gc-frontend:80 설정 점검을 요청하거나, PC 프로그램 탭에서 exe를 받아 다시 설치해 주세요.'; } if (/could not fetch a valid release json|release not found/.test(lower)) { return onMac diff --git a/desktop/vite.config.ts b/desktop/vite.config.ts index 6f15e26..5a9f1b5 100644 --- a/desktop/vite.config.ts +++ b/desktop/vite.config.ts @@ -47,7 +47,7 @@ export default defineConfig({ define: { global: 'globalThis', /** desktop은 항상 exdev 서버 (localhost 금지) */ - 'import.meta.env.VITE_API_BASE_URL': JSON.stringify('http://exdev.co.kr/api'), + 'import.meta.env.VITE_API_BASE_URL': JSON.stringify('https://stock.exdev.co.kr/api'), __DESKTOP_CLIENT__: 'true', __DESKTOP_APP_VERSION__: JSON.stringify(readDesktopAppVersion()), }, diff --git a/docker-compose.yml b/docker-compose.yml index bb4d32d..0c37f5a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,10 +68,10 @@ services: GC_VERIFICATION_UPLOAD_DIR: /app/data/verification-images GC_MOBILE_APP_RELEASE_DIR: /app/data/mobile-releases GC_MOBILE_APP_VERSION: ${GC_MOBILE_APP_VERSION:-} - GC_MOBILE_APP_PUBLIC_BASE_URL: ${GC_MOBILE_APP_PUBLIC_BASE_URL:-http://exdev.co.kr} + GC_MOBILE_APP_PUBLIC_BASE_URL: ${GC_MOBILE_APP_PUBLIC_BASE_URL:-https://stock.exdev.co.kr} GC_MOBILE_APP_IOS_TESTFLIGHT_URL: ${GC_MOBILE_APP_IOS_TESTFLIGHT_URL:-} GC_DESKTOP_APP_RELEASE_DIR: /app/data/desktop-releases - GC_DESKTOP_APP_PUBLIC_BASE_URL: ${GC_DESKTOP_APP_PUBLIC_BASE_URL:-http://exdev.co.kr} + GC_DESKTOP_APP_PUBLIC_BASE_URL: ${GC_DESKTOP_APP_PUBLIC_BASE_URL:-https://stock.exdev.co.kr} GC_DESKTOP_JENKINS_ENABLED: ${GC_DESKTOP_JENKINS_ENABLED:-true} GC_DESKTOP_JENKINS_URL: ${GC_DESKTOP_JENKINS_URL:-http://host.docker.internal:8090} GC_DESKTOP_JENKINS_JOB: ${GC_DESKTOP_JENKINS_JOB:-goldenChart-Desktop-Pipeline} diff --git a/docs/desktop-pilot.md b/docs/desktop-pilot.md index 28aa964..ee56709 100644 --- a/docs/desktop-pilot.md +++ b/docs/desktop-pilot.md @@ -83,7 +83,7 @@ CI 빌드 시 `latest.json` 버전 기준 **patch 자동 bump** (예: 0.1.3 → ## 4. 앱 기능 -- **서버**: `http://exdev.co.kr/api` (웹과 동일 계정·DB) +- **서버**: `https://stock.exdev.co.kr/api` (웹과 동일 계정·DB) - **위젯**: OS 별도 창 (`WebviewWindow`) - **알림**: 창 닫기 → 트레이 상주, STOMP + OS 알림 - **업데이트**: 설정 → 일반 → PC 앱 → **업데이트 확인** diff --git a/docs/exdev-subdomain-setup.md b/docs/exdev-subdomain-setup.md new file mode 100644 index 0000000..7a24083 --- /dev/null +++ b/docs/exdev-subdomain-setup.md @@ -0,0 +1,72 @@ +# exdev.co.kr 서브도메인 분리 — stock / chatbot + +| 서비스 | URL | +|--------|-----| +| **GoldenChart** | https://stock.exdev.co.kr | +| **Chatbot** | https://chatbot.exdev.co.kr/chatbot/* | +| **Legacy apex** | https://exdev.co.kr → stock 또는 chatbot으로 301 | + +## 1. DNS (Gabia 등) + +| 호스트 | 타입 | 값 | +|--------|------|-----| +| `stock` | A | 서버 공인 IP | +| `chatbot` | A | 서버 공인 IP | + +## 2. SSL 인증서 (exAIChatbot2.0 서버) + +```bash +cd /path/to/exAIChatbot2.0 +# env: STOCK_DOMAIN, CHATBOT_DOMAIN (env.exdev 참고) +bash scripts/exdev/obtain-cert-exdev.sh # exdev + stock + chatbot SAN +bash scripts/exdev/start-https-caddy.sh +``` + +Caddyfile: `docker/caddy/Caddyfile.exdev` + +## 3. GoldenChart + +서버 `.env`: + +```env +GC_DESKTOP_APP_PUBLIC_BASE_URL=https://stock.exdev.co.kr +GC_MOBILE_APP_PUBLIC_BASE_URL=https://stock.exdev.co.kr +``` + +Desktop updater / Jenkins: + +```bash +export DESKTOP_UPDATE_BASE_URL=https://stock.exdev.co.kr/desktop/updates +``` + +검증: + +```bash +curl -sS https://stock.exdev.co.kr/ | head +DESKTOP_UPDATE_BASE_URL=https://stock.exdev.co.kr/desktop/updates ./scripts/verify-desktop-updater-endpoint.sh +``` + +## 4. Chatbot + +`.env` (env.exdev): + +```env +PUBLIC_BASE_URL=https://chatbot.exdev.co.kr +CHATBOT_DOMAIN=chatbot.exdev.co.kr +``` + +카카오 스킬 URL 예: + +- Q&A: `https://chatbot.exdev.co.kr/chatbot/kakao/skill` +- 챗봇홈: `https://chatbot.exdev.co.kr/chatbot/app/` + +검증: + +```bash +curl -sS https://chatbot.exdev.co.kr/chatbot/health +curl -sS https://chatbot.exdev.co.kr/chatbot/app/ | head +``` + +## 5. SSH / git remote + +서버 호스트명 `exdev.co.kr`·`REMOTE_HOST=exdev.co.kr` 는 **변경하지 않음** (배포·SSH용). diff --git a/docs/mobile-tablet-phase1.md b/docs/mobile-tablet-phase1.md index 5ef8b99..c9c4461 100644 --- a/docs/mobile-tablet-phase1.md +++ b/docs/mobile-tablet-phase1.md @@ -49,7 +49,7 @@ git push main |------|------| | `GC_MOBILE_APP_VERSION` | API·앱 업데이트 비교용 버전 | | `GC_MOBILE_JENKINS_*` | Jenkins mobile job | -| `GC_MOBILE_APP_PUBLIC_BASE_URL` | 다운로드 URL base (기본 `http://exdev.co.kr`) | +| `GC_MOBILE_APP_PUBLIC_BASE_URL` | 다운로드 URL base (기본 `https://stock.exdev.co.kr`) | ## 수동 빌드 diff --git a/frontend/.env.example b/frontend/.env.example index 1e08d7b..086c2ec 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -5,4 +5,4 @@ VITE_API_BASE_URL=/api # 로컬 dev: /api/mobile-app → exdev (QR·APK 다운로드 URL). vite.config.ts 기본값과 동일 -# VITE_MOBILE_APP_PROXY_TARGET=http://exdev.co.kr +# VITE_MOBILE_APP_PROXY_TARGET=https://stock.exdev.co.kr diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 2ae4cc7..f85a04e 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -82,7 +82,7 @@ export default defineConfig({ proxy: { // ── 모바일 APK 정보/다운로드 → exdev (로컬 QR·PC 다운로드가 exdev APK 사용) ── '/api/mobile-app': { - target: process.env.VITE_MOBILE_APP_PROXY_TARGET || 'http://exdev.co.kr', + target: process.env.VITE_MOBILE_APP_PROXY_TARGET || 'https://stock.exdev.co.kr', changeOrigin: true, secure: true, }, diff --git a/packages/shared/src/api/backendApi.ts b/packages/shared/src/api/backendApi.ts index 273ddef..784017a 100644 --- a/packages/shared/src/api/backendApi.ts +++ b/packages/shared/src/api/backendApi.ts @@ -12,10 +12,10 @@ import { getRuntimePlatform } from '../platform.js'; export type StrategyFlowLayoutStore = Record; /** 배포 APK·exdev (모바일 APK — http) */ -export const PRODUCTION_API_BASE = 'http://exdev.co.kr/api'; +export const PRODUCTION_API_BASE = 'https://stock.exdev.co.kr/api'; /** Tauri desktop — exdev HTTP (HTTPS /api는 Caddy에서 빈 응답) */ -export const DESKTOP_API_BASE = 'http://exdev.co.kr/api'; +export const DESKTOP_API_BASE = 'https://stock.exdev.co.kr/api'; function viteEnv(): Record | undefined { return typeof import.meta !== 'undefined' diff --git a/scripts/bump-desktop-version-before-build.sh b/scripts/bump-desktop-version-before-build.sh index 09552dc..9379215 100755 --- a/scripts/bump-desktop-version-before-build.sh +++ b/scripts/bump-desktop-version-before-build.sh @@ -5,7 +5,7 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" WORK_TREE="${WORK_TREE:-$ROOT}" export WORK_TREE -export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" CONF="$WORK_TREE/desktop/src-tauri/tauri.conf.json" CARGO="$WORK_TREE/desktop/src-tauri/Cargo.toml" diff --git a/scripts/bump-desktop-version-for-push.sh b/scripts/bump-desktop-version-for-push.sh index a76709f..17a4ec0 100755 --- a/scripts/bump-desktop-version-for-push.sh +++ b/scripts/bump-desktop-version-for-push.sh @@ -4,7 +4,7 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" export WORK_TREE="${WORK_TREE:-$ROOT}" -export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" export DESKTOP_BUMP_IGNORE_BUILD_INFO=1 "$ROOT/scripts/bump-desktop-version-from-release.sh" diff --git a/scripts/bump-desktop-version-from-release.sh b/scripts/bump-desktop-version-from-release.sh index 357721c..61a613f 100755 --- a/scripts/bump-desktop-version-from-release.sh +++ b/scripts/bump-desktop-version-from-release.sh @@ -10,7 +10,7 @@ UPDATES_DIR="${WORK_TREE}/frontend/public/desktop/updates" BUILD_INFO="${WORK_TREE}/dist-desktop/build-info.json" RELEASE_DIR="${GC_DESKTOP_APP_RELEASE_DIR:-$WORK_TREE/data/desktop-releases}" -PUBLISHED_LATEST_URL="${DESKTOP_PUBLISHED_LATEST_URL:-${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}/latest.json}" +PUBLISHED_LATEST_URL="${DESKTOP_PUBLISHED_LATEST_URL:-${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}/latest.json}" REMOTE_PUB_VER="" if REMOTE_JSON="$(curl -sf --connect-timeout 5 --max-time 12 "$PUBLISHED_LATEST_URL" 2>/dev/null)"; then REMOTE_PUB_VER="$(printf '%s' "$REMOTE_JSON" | node -e " diff --git a/scripts/deploy-target.defaults b/scripts/deploy-target.defaults index d54f655..0e2feb1 100644 --- a/scripts/deploy-target.defaults +++ b/scripts/deploy-target.defaults @@ -15,4 +15,4 @@ APK_REMOTE_SUBDIR=data/mobile-releases APK_REMOTE_NAME=goldenchart-android.apk # 업로드 후 API 확인 URL -PUBLIC_URL=http://exdev.co.kr +PUBLIC_URL=https://stock.exdev.co.kr diff --git a/scripts/deploy-target.local.env.example b/scripts/deploy-target.local.env.example index 3b68352..7b7b392 100644 --- a/scripts/deploy-target.local.env.example +++ b/scripts/deploy-target.local.env.example @@ -4,4 +4,4 @@ # REMOTE_PROJECT_DIR=/Users/aidev/apps/goldenChart # REMOTE_HOST=exdev.co.kr # REMOTE_USER=aidev -# PUBLIC_URL=https://exdev.co.kr +# PUBLIC_URL=https://stock.exdev.co.kr diff --git a/scripts/deploy.sh b/scripts/deploy.sh index c671217..f8125d5 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -85,7 +85,7 @@ UPDATES_DIR="$WORK_TREE/frontend/public/desktop/updates" if [[ -f "$ENSURE" && -d "$UPDATES_DIR" ]]; then DESKTOP_VER="$(node -p "require('$WORK_TREE/desktop/src-tauri/tauri.conf.json').version" 2>/dev/null || echo '0.1.0')" if ! node "$ENSURE" --dir "$UPDATES_DIR" --version "$DESKTOP_VER" \ - --base-url "${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" 2>/dev/null; then + --base-url "${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" 2>/dev/null; then warn "latest.json platforms 비어 있음 — Desktop Jenkins 파이프라인 publish 필요" fi LATEST="$UPDATES_DIR/latest.json" @@ -169,6 +169,14 @@ case "$SERVICE_NAME" in ;; esac +VERIFY_UPDATER="$WORK_TREE/scripts/verify-desktop-updater-endpoint.sh" +if [[ -x "$VERIFY_UPDATER" ]]; then + info "[Phase 4b] Desktop updater latest.json (HTTPS)" + if ! WORK_TREE="$WORK_TREE" "$VERIFY_UPDATER"; then + warn "Desktop 자동 업데이트 엔드포인트 이상 — 배포는 완료됐으나 Caddy HTTPS 또는 Jenkins desktop publish 점검 필요" + fi +fi + log "======================================================" log " 배포 완료: $(date '+%Y-%m-%d %H:%M:%S')" log " 커밋: $(git --git-dir="$GIT_DIR" log --oneline -1 2>/dev/null || echo N/A)" diff --git a/scripts/ensure-desktop-latest-json.mjs b/scripts/ensure-desktop-latest-json.mjs index 769a601..fb68c36 100755 --- a/scripts/ensure-desktop-latest-json.mjs +++ b/scripts/ensure-desktop-latest-json.mjs @@ -16,7 +16,7 @@ function arg(name, fallback) { const dir = path.resolve(arg('--dir', 'frontend/public/desktop/updates')); const version = arg('--version', '0.1.0'); -const rawBaseUrl = arg('--base-url', 'https://exdev.co.kr/desktop/updates'); +const rawBaseUrl = arg('--base-url', 'https://stock.exdev.co.kr/desktop/updates'); const baseUrl = rawBaseUrl.replace(/\/$/, '').replace(/^http:\/\//i, 'https://'); const notes = arg('--notes', `GoldenChart Desktop ${version}`); const latestPath = path.join(dir, 'latest.json'); diff --git a/scripts/exdev-caddy-coexist.Caddyfile b/scripts/exdev-caddy-coexist.Caddyfile new file mode 100644 index 0000000..e9156bd --- /dev/null +++ b/scripts/exdev-caddy-coexist.Caddyfile @@ -0,0 +1,79 @@ +# exdev.co.kr — GoldenChart + Chatbot HTTPS 공존 (Caddy v2 예시) +# +# 원칙: +# - 443 포트는 Caddy만 사용 (gc-frontend·chatbot은 443 바인딩 금지) +# - GoldenChart: 호스트/ Docker gc-frontend :80 +# - Chatbot: 별도 컨테이너 내부 포트 (아래 CHATBOT_UPSTREAM 수정) +# +# 설치 위치 (ex-chatbot 프로젝트): docker/nginx/Caddyfile 또는 Caddyfile +# 적용 후: docker compose restart caddy (프로젝트명에 맞게 조정) +# +# 검증: +# curl -sS -o /dev/null -w "GC HTTPS %{size_download}\n" https://exdev.co.kr/ +# curl -sS -o /dev/null -w "CB HTTPS %{size_download}\n" https://exdev.co.kr/chatbot/ +# ./scripts/verify-desktop-updater-endpoint.sh + +{ + # HTTP/2·HTTP/3 — 본문 버퍼링 이슈 완화 + servers { + protocols h1 h2 h3 + } +} + +(exdev_tls) { + # Let's Encrypt — ex-chatbot obtain-cert-exdev.sh 로 발급한 인증서 경로에 맞게 수정 + tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/privkey.pem +} + +# ── Chatbot upstream (실제 포트·호스트명은 환경에 맞게) ───────────────────── +# Docker: chatbot 컨테이너명:내부포트 / 호스트: host.docker.internal:PORT +(chatbot_upstream) { + reverse_proxy host.docker.internal:8081 { + flush_interval -1 + transport http { + versions 1.1 2 + } + header_up Host {host} + header_up X-Forwarded-Proto https + header_up X-Forwarded-Prefix /chatbot + } +} + +# ── GoldenChart (gc-frontend nginx :80) ───────────────────────────────────── +(goldenchart_upstream) { + reverse_proxy host.docker.internal:80 { + flush_interval -1 + transport http { + versions 1.1 2 + } + header_up Host {host} + header_up X-Real-IP {remote_host} + header_up X-Forwarded-For {remote_host} + header_up X-Forwarded-Proto https + } +} + +exdev.co.kr { + import exdev_tls + + # 1) Chatbot — /chatbot/* (경로 유지; 앱이 /chatbot base path 를 기대할 때) + @chatbot path /chatbot /chatbot/* + handle @chatbot { + import chatbot_upstream + } + + # 2) GoldenChart — 나머지 전부 (SPA, /api 는 nginx→backend, /desktop/updates 등) + handle { + import goldenchart_upstream + } +} + +# ── 대안: 서브도메인 분리 (경로 충돌이 없어 운영이 단순함) ───────────────────── +# chat.exdev.co.kr { +# import exdev_tls +# import chatbot_upstream +# } +# chart.exdev.co.kr { +# import exdev_tls +# import goldenchart_upstream +# } diff --git a/scripts/exdev-caddy-subdomains.Caddyfile b/scripts/exdev-caddy-subdomains.Caddyfile new file mode 100644 index 0000000..9a8aff9 --- /dev/null +++ b/scripts/exdev-caddy-subdomains.Caddyfile @@ -0,0 +1,6 @@ +# GoldenChart + Chatbot 서브도메인 (ex-chatbot Caddy에 통합됨) +# 실제 운영 Caddyfile: exAIChatbot2.0/docker/caddy/Caddyfile.exdev +# +# stock.exdev.co.kr → gc-frontend :80 +# chatbot.exdev.co.kr → rag-api + Admin +# exdev.co.kr → /chatbot/* 리다이렉트 + 나머지 stock 리다이렉트 diff --git a/scripts/generate-desktop-latest-json.mjs b/scripts/generate-desktop-latest-json.mjs index 0c69de8..8794159 100644 --- a/scripts/generate-desktop-latest-json.mjs +++ b/scripts/generate-desktop-latest-json.mjs @@ -6,7 +6,7 @@ * node scripts/generate-desktop-latest-json.mjs \ * --version 0.1.0 \ * --dir dist-desktop/updates \ - * --base-url https://exdev.co.kr/desktop/updates \ + * --base-url https://stock.exdev.co.kr/desktop/updates \ * --notes "Release notes" */ import fs from 'node:fs'; @@ -19,7 +19,7 @@ function arg(name, fallback) { const version = arg('--version', '0.1.0'); const dir = path.resolve(arg('--dir', 'dist-desktop/updates')); -const rawBaseUrl = arg('--base-url', 'https://exdev.co.kr/desktop/updates'); +const rawBaseUrl = arg('--base-url', 'https://stock.exdev.co.kr/desktop/updates'); const baseUrl = rawBaseUrl.replace(/\/$/, '').replace(/^http:\/\//i, 'https://'); const notes = arg('--notes', `GoldenChart Desktop ${version}`); diff --git a/scripts/jenkins-desktop-pipeline.sh b/scripts/jenkins-desktop-pipeline.sh index a6a1748..244fb54 100755 --- a/scripts/jenkins-desktop-pipeline.sh +++ b/scripts/jenkins-desktop-pipeline.sh @@ -58,7 +58,7 @@ fi export WORK_TREE export BUILD_NUMBER="${BUILD_NUMBER:-}" -export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" GIT_VER="$(node -p "require('$WORK_TREE/desktop/src-tauri/tauri.conf.json').version" 2>/dev/null || echo '?')" log "Desktop git tauri.conf: v${GIT_VER}" @@ -71,7 +71,7 @@ log "Built version: v${BUILT_VER}" WORK_TREE="$WORK_TREE" \ ARTIFACT_DIR="$WORK_TREE/dist-desktop" \ -DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" \ +DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" \ TAURI_SIGNER_KEY="${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" \ DESKTOP_RELEASE_NOTES="${DESKTOP_RELEASE_NOTES:-Build ${BUILD_NUMBER:-local} v${BUILT_VER}}" \ "$WORK_TREE/scripts/publish-desktop-update.sh" diff --git a/scripts/prepare-tauri-build.sh b/scripts/prepare-tauri-build.sh index 1736a0e..85b7027 100755 --- a/scripts/prepare-tauri-build.sh +++ b/scripts/prepare-tauri-build.sh @@ -27,9 +27,9 @@ conf.bundle.createUpdaterArtifacts = enable; conf.plugins = conf.plugins || {}; conf.plugins.updater = conf.plugins.updater || {}; conf.plugins.updater.endpoints = (conf.plugins.updater.endpoints || []) - .map((u) => String(u).replace(/^http:\\/\\/exdev\\.co\\.kr\\//, 'https://exdev.co.kr/')); + .map((u) => String(u).replace(/^http:\\/\\/exdev\\.co\\.kr\\//, 'https://stock.exdev.co.kr/')); if (!conf.plugins.updater.endpoints.length) { - conf.plugins.updater.endpoints = ['https://exdev.co.kr/desktop/updates/latest.json']; + conf.plugins.updater.endpoints = ['https://stock.exdev.co.kr/desktop/updates/latest.json']; } if (!conf.plugins.updater.pubkey) { conf.plugins.updater.pubkey = 'REPLACE_WITH_TAURI_SIGNER_PUBKEY'; diff --git a/scripts/publish-desktop-update.sh b/scripts/publish-desktop-update.sh index 114a090..98f82ee 100755 --- a/scripts/publish-desktop-update.sh +++ b/scripts/publish-desktop-update.sh @@ -8,7 +8,7 @@ ARTIFACT_DIR="${ARTIFACT_DIR:-$WORK_TREE/dist-desktop}" UPDATES_SRC="${UPDATES_SRC:-$ARTIFACT_DIR/updates}" UPDATES_DIR="${UPDATES_DIR:-$WORK_TREE/frontend/public/desktop/updates}" WEB_STATIC_DIR="${WEB_STATIC_DIR:-$UPDATES_DIR}" -BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" BASE_URL="${BASE_URL/http:\/\//https:\/\/}" DESKTOP_RELEASE_DIR="${GC_DESKTOP_APP_RELEASE_DIR:-$WORK_TREE/data/desktop-releases}" KEY_PATH="${TAURI_SIGNER_KEY:-$HOME/.tauri/goldenchart.key}" diff --git a/scripts/reconcile-desktop-latest-json.sh b/scripts/reconcile-desktop-latest-json.sh index 1b125a8..50f225f 100755 --- a/scripts/reconcile-desktop-latest-json.sh +++ b/scripts/reconcile-desktop-latest-json.sh @@ -5,7 +5,7 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" export PATH="/opt/homebrew/bin:/usr/local/bin:${PATH:-/usr/bin:/bin}" UPDATES_DIR="${UPDATES_DIR:-${WORK_TREE:-$ROOT}/frontend/public/desktop/updates}" -BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" BASE_URL="${BASE_URL/http:\/\//https:\/\/}" if [[ ! -d "$UPDATES_DIR" ]]; then diff --git a/scripts/server-install-desktop-jenkins.sh b/scripts/server-install-desktop-jenkins.sh index b658b2b..395bbdd 100755 --- a/scripts/server-install-desktop-jenkins.sh +++ b/scripts/server-install-desktop-jenkins.sh @@ -54,7 +54,7 @@ export PATH="/opt/homebrew/opt/llvm/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/us export WORK_TREE="${WORK_TREE}" export GIT_DIR="${GIT_DIR}" export BUILD_NUMBER="\${BUILD_NUMBER}" -export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +export DESKTOP_UPDATE_BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" export TAURI_SIGNER_KEY="\${TAURI_SIGNER_KEY:-\$HOME/.tauri/goldenchart.key}" export DESKTOP_DEPLOY_WEB=0 "${WORK_TREE}/scripts/jenkins-desktop-pipeline.sh" diff --git a/scripts/server-setup-mobile-phase1.sh b/scripts/server-setup-mobile-phase1.sh index f46e03f..db9c2e0 100755 --- a/scripts/server-setup-mobile-phase1.sh +++ b/scripts/server-setup-mobile-phase1.sh @@ -62,6 +62,6 @@ export GC_MOBILE_JENKINS_TOKEN="${DESKTOP_TOKEN:-}" log "API 확인" sleep 4 curl -fsSL http://127.0.0.1:8080/api/mobile-app/info 2>/dev/null | python3 -m json.tool 2>/dev/null || \ - curl -fsSL http://exdev.co.kr/api/mobile-app/info | python3 -m json.tool 2>/dev/null || true + curl -fsSL https://stock.exdev.co.kr/api/mobile-app/info | python3 -m json.tool 2>/dev/null || true log "완료 (Android APK · iPad는 향후 TestFlight)" diff --git a/scripts/smoke-test-desktop.sh b/scripts/smoke-test-desktop.sh index 348f29d..d330d6e 100755 --- a/scripts/smoke-test-desktop.sh +++ b/scripts/smoke-test-desktop.sh @@ -3,7 +3,7 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" -BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" FAIL=0 ok() { echo "[OK] $*"; } @@ -43,8 +43,12 @@ fi if curl -sf "${BASE_URL}/latest.json" >/dev/null 2>&1; then REMOTE="$(curl -sf "${BASE_URL}/latest.json" 2>/dev/null || true)" if [[ -z "$REMOTE" || "${#REMOTE}" -lt 8 ]]; then - echo "[FAIL] remote latest.json empty (0-byte)" - exit 1 + HTTP_BASE="$(printf '%s' "$BASE_URL" | sed 's|^https://|http://|')" + HTTP_REMOTE="$(curl -sf "${HTTP_BASE}/latest.json" 2>/dev/null || true)" + if [[ -n "$HTTP_REMOTE" && "${#HTTP_REMOTE}" -ge 8 ]]; then + fail "remote HTTPS latest.json empty — HTTP는 정상 (${#HTTP_REMOTE}B). Caddy→gc-frontend:80 역프록시 점검" + fi + fail "remote latest.json empty (0-byte)" fi node -e "JSON.parse(process.argv[1])" "$REMOTE" && ok "remote latest.json ${BASE_URL}/latest.json" else diff --git a/scripts/upload-android-apk.sh b/scripts/upload-android-apk.sh index 04a74a5..0e2eabd 100755 --- a/scripts/upload-android-apk.sh +++ b/scripts/upload-android-apk.sh @@ -26,7 +26,7 @@ REMOTE_USER="${REMOTE_USER:-aidev}" REMOTE_PORT="${REMOTE_PORT:-22}" APK_REMOTE_SUBDIR="${APK_REMOTE_SUBDIR:-data/mobile-releases}" APK_REMOTE_NAME="${APK_REMOTE_NAME:-goldenchart-android.apk}" -PUBLIC_URL="${PUBLIC_URL:-http://exdev.co.kr}" +PUBLIC_URL="${PUBLIC_URL:-https://stock.exdev.co.kr}" LOCAL_APK="${LOCAL_APK:-$ROOT/data/mobile-releases/$APK_REMOTE_NAME}" DO_BUILD=false @@ -169,7 +169,7 @@ build_apk_locally() { "$ROOT/scripts/ensure-android-google-services.sh" if [[ ! -f "$ROOT/app/.env" ]] || ! grep -q 'VITE_API_BASE_URL=http' "$ROOT/app/.env" 2>/dev/null; then - echo -e "${YELLOW}⚠ app/.env 에 VITE_API_BASE_URL=http://exdev.co.kr/api 확인 (HTTPS 아님)${NC}" + echo -e "${YELLOW}⚠ app/.env 에 VITE_API_BASE_URL=https://stock.exdev.co.kr/api 확인 (HTTPS 아님)${NC}" fi echo -e "${CYAN}[빌드] npm run cap:sync${NC}" diff --git a/scripts/verify-desktop-published-version.sh b/scripts/verify-desktop-published-version.sh index 4806baf..1d01217 100755 --- a/scripts/verify-desktop-published-version.sh +++ b/scripts/verify-desktop-published-version.sh @@ -3,7 +3,7 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" -BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://exdev.co.kr/desktop/updates}" +BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" LATEST_URL="${DESKTOP_PUBLISHED_LATEST_URL:-${BASE_URL}/latest.json}" UPDATES_DIR="${UPDATES_DIR:-$ROOT/frontend/public/desktop/updates}" RELEASE_DIR="${GC_DESKTOP_APP_RELEASE_DIR:-$ROOT/data/desktop-releases}" @@ -19,6 +19,11 @@ REMOTE_PLATFORMS="0" if REMOTE_JSON="$(curl -sf --connect-timeout 5 --max-time 12 "$LATEST_URL" 2>/dev/null)"; then if [[ -z "$REMOTE_JSON" || "${#REMOTE_JSON}" -lt 8 ]]; then log "remote latest.json → EMPTY (0-byte — updater JSON parse 실패 원인)" + HTTP_URL="$(printf '%s' "$LATEST_URL" | sed 's|^https://|http://|')" + HTTP_JSON="$(curl -sf --connect-timeout 5 --max-time 12 "$HTTP_URL" 2>/dev/null || true)" + if [[ -n "$HTTP_JSON" && "${#HTTP_JSON}" -ge 8 ]]; then + log "remote HTTP latest → OK (${#HTTP_JSON}B) — HTTPS(Caddy) 역프록시가 빈 200을 반환 중일 가능성" + fi else read -r REMOTE_VER REMOTE_PLATFORMS <<< "$(printf '%s' "$REMOTE_JSON" | node -e " let d=''; process.stdin.on('data',c=>d+=c); diff --git a/scripts/verify-desktop-updater-endpoint.sh b/scripts/verify-desktop-updater-endpoint.sh new file mode 100755 index 0000000..39a30fb --- /dev/null +++ b/scripts/verify-desktop-updater-endpoint.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Desktop updater latest.json — HTTPS·HTTP 무결성 (Caddy 역프록시 빈 응답 감지) +set -euo pipefail + +BASE_URL="${DESKTOP_UPDATE_BASE_URL:-https://stock.exdev.co.kr/desktop/updates}" +BASE_URL="${BASE_URL%/}" +HTTPS_URL="${DESKTOP_PUBLISHED_LATEST_URL:-${BASE_URL}/latest.json}" +HTTP_URL="$(printf '%s' "$HTTPS_URL" | sed 's|^https://|http://|')" + +log() { echo "[verify-updater-endpoint] $*"; } +warn() { log "WARN: $*"; } +fail() { log "FAIL: $*"; exit 1; } + +fetch_body() { + local url="$1" + curl -sf --connect-timeout 8 --max-time 15 "$url" 2>/dev/null || true +} + +parse_manifest() { + local body="$1" + printf '%s' "$body" | node -e " +let d=''; process.stdin.on('data',c=>d+=c); +process.stdin.on('end',()=>{ + try { + const j=JSON.parse(d); + const n=Object.keys(j.platforms||{}).length; + console.log((j.version||'')+' '+n); + } catch { console.log('PARSE_ERR 0'); } +}); +" 2>/dev/null || echo "PARSE_ERR 0" +} + +log "HTTPS → $HTTPS_URL" +log "HTTP → $HTTP_URL" + +HTTPS_BODY="$(fetch_body "$HTTPS_URL")" +HTTP_BODY="$(fetch_body "$HTTP_URL")" +HTTPS_SIZE="${#HTTPS_BODY}" +HTTP_SIZE="${#HTTP_BODY}" + +log "HTTPS size=${HTTPS_SIZE}B HTTP size=${HTTP_SIZE}B" + +if [[ "$HTTPS_SIZE" -lt 8 && "$HTTP_SIZE" -ge 8 ]]; then + read -r HTTP_VER HTTP_PLAT <<< "$(parse_manifest "$HTTP_BODY")" + fail "HTTPS latest.json 이 비어 있습니다 (${HTTPS_SIZE}B). HTTP는 v${HTTP_VER} (${HTTP_SIZE}B, platforms=${HTTP_PLAT}) — Caddy(443) → gc-frontend:80 역프록시를 점검하세요." +fi + +if [[ "$HTTPS_SIZE" -lt 8 && "$HTTP_SIZE" -lt 8 ]]; then + fail "latest.json 이 HTTPS·HTTP 모두 비어 있습니다 — Desktop Jenkins publish 후 scripts/publish-desktop-update.sh 실행 필요" +fi + +BODY="$HTTPS_BODY" +[[ "$HTTPS_SIZE" -lt 8 ]] && BODY="$HTTP_BODY" + +read -r VER PLAT <<< "$(parse_manifest "$BODY")" +if [[ "$VER" == "PARSE_ERR" || -z "$VER" ]]; then + fail "latest.json JSON 파싱 실패 (HTTPS ${HTTPS_SIZE}B / HTTP ${HTTP_SIZE}B)" +fi + +if [[ "${PLAT:-0}" -eq 0 ]]; then + warn "latest.json v${VER} — platforms 비어 있음 (서명된 updater bundle 없음). 자동 업데이트는 실패할 수 있습니다." + exit 2 +fi + +log "OK v${VER} platforms=${PLAT} (HTTPS ${HTTPS_SIZE}B)" +exit 0