diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..def9b1c --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,108 @@ +# TK Factory Services — 시스템 아키텍처 + +## 전체 구조 + +21개 컨테이너, Docker Compose 기반, Cloudflare Tunnel로 외부 노출. + +``` +[Cloudflare Tunnel] → tk-cloudflared + ├── tkds.technicalkorea.net → tk-gateway:80 (로그인 + 대시보드 + 공유JS) + ├── tkfb.technicalkorea.net → tk-system1-web:80 (공장관리) + ├── tkreport.technicalkorea.net → tk-system2-web:80 (신고) + ├── tkqc.technicalkorea.net → tk-system3-web:80 (부적합관리) + ├── tkuser.technicalkorea.net → tk-tkuser-web:80 (통합관리) + ├── tkpurchase.technicalkorea.net → tk-tkpurchase-web:80 (구매관리) + ├── tksafety.technicalkorea.net → tk-tksafety-web:80 (안전관리) + └── tksupport.technicalkorea.net → tk-tksupport-web:80 (행정지원) +``` + +## 서비스 목록 + +| 서비스 | 컨테이너명 | 로컬 포트 | 내부 포트 | 역할 | +|---|---|---|---|---| +| **Gateway** | tk-gateway | 30000 | 80 | 로그인/대시보드/공유JS + SSO·API 프록시 | +| **System1 API** | tk-system1-api | 30005 | 3005 | 공장관리 백엔드 (Node.js) | +| **System1 Web** | tk-system1-web | 30080 | 80 | 공장관리 프론트엔드 (nginx) | +| **System1 FastAPI** | tk-system1-fastapi | 30008 | 8000 | FastAPI Bridge | +| **System2 API** | tk-system2-api | 30105 | 3005 | 신고 백엔드 (Node.js) | +| **System2 Web** | tk-system2-web | 30180 | 80 | 신고 프론트엔드 (nginx) | +| **System3 API** | tk-system3-api | 30200 | 8000 | 부적합관리 백엔드 (FastAPI) | +| **System3 Web** | tk-system3-web | 30280 | 80 | 부적합관리 프론트엔드 (nginx) | +| **User API** | tk-tkuser-api | 30300 | 3000 | 사용자관리 백엔드 (Node.js) | +| **User Web** | tk-tkuser-web | 30380 | 80 | 사용자관리 프론트엔드 (nginx) | +| **Purchase API** | tk-tkpurchase-api | 30400 | 3000 | 구매관리 백엔드 (Node.js) | +| **Purchase Web** | tk-tkpurchase-web | 30480 | 80 | 구매관리 프론트엔드 (nginx) | +| **Safety API** | tk-tksafety-api | 30500 | 3000 | 안전관리 백엔드 (Node.js) | +| **Safety Web** | tk-tksafety-web | 30580 | 80 | 안전관리 프론트엔드 (nginx) | +| **Support API** | tk-tksupport-api | 30600 | 3000 | 행정지원 백엔드 (Node.js) | +| **Support Web** | tk-tksupport-web | 30680 | 80 | 행정지원 프론트엔드 (nginx) | +| **SSO Auth** | tk-sso-auth | 30050 | 3000 | SSO 인증 서비스 (Node.js) | +| **MariaDB** | tk-mariadb | 30306 | 3306 | 메인 데이터베이스 | +| **Redis** | tk-redis | — | 6379 | 세션/캐시 | +| **phpMyAdmin** | tk-phpmyadmin | 30880 | 80 | DB 관리 도구 | +| **Cloudflared** | tk-cloudflared | — | — | Cloudflare Tunnel 에이전트 | + +## 요청 흐름 + +``` +브라우저 → Cloudflare DNS → Cloudflare Tunnel → cloudflared 컨테이너 + → 서브도메인별 라우팅 → 해당 web 컨테이너 (nginx) + → /api/ → 해당 API 컨테이너 + → /auth/ → sso-auth 컨테이너 + → /uploads/ → API 컨테이너 (파일 서빙) + → 나머지 → 정적 파일 (SPA fallback) +``` + +## SSO 인증 흐름 + +1. 사용자가 아무 서비스 접근 → 프론트엔드 JS가 `sso_token` 쿠키 확인 +2. 토큰 없음/만료 → `tkds.technicalkorea.net/dashboard`로 리다이렉트 (redirect 파라미터 포함) +3. 로그인 폼 제출 → `POST /auth/login` → sso-auth가 JWT 발급 +4. 쿠키 설정: `sso_token`, `sso_user`, `sso_refresh_token` (domain=`.technicalkorea.net`) +5. redirect 파라미터가 있으면 원래 페이지로, 없으면 대시보드 표시 +6. 각 서비스의 API는 `Authorization: Bearer ` 헤더로 인증 검증 + +## CORS 관리 + +- **sso-auth**: `sso-auth-service/config/` — 모든 `*.technicalkorea.net` 서브도메인 허용 +- **system1-factory API**: `system1-factory/api/config/cors.js` — 허용 origin 명시적 관리 +- 로컬 네트워크(192.168.x.x)는 자동 허용 + +## 공유 JS + +Gateway(`/shared/`)에서 서빙: +- `notification-bell.js` — 알림 벨 UI, 모든 서비스에서 로딩 +- `nav-header.js` — 공통 네비게이션 헤더 + +각 서비스의 core.js에서 동적 로딩: +``` +프로덕션: https://tkds.technicalkorea.net/shared/notification-bell.js +로컬: http://localhost:30000/shared/notification-bell.js +``` + +## 신규 서비스 추가 체크리스트 + +1. `/api/` + `/web/` 디렉토리 생성 (Dockerfile 포함) +2. `docker-compose.yml`에 api + web 서비스 추가 (포트 할당) +3. `cloudflared.depends_on`에 web 서비스 추가 +4. Cloudflare Tunnel 대시보드에서 서브도메인 → 컨테이너 라우팅 추가 +5. `sso-auth-service/config/`에 새 origin 추가 +6. `system1-factory/api/config/cors.js`에 새 origin 추가 (API 호출 시) +7. 알림 벨 사용 시: core.js에 `_loadNotificationBell()` 함수 추가 +8. 로그인 리다이렉트: `tkds.technicalkorea.net/dashboard?redirect=` 패턴 사용 + +## 배포 절차 + +```bash +# 로컬에서 push +git push + +# NAS에서 pull + rebuild +ssh hyungi@100.71.132.52 "cd /volume1/docker/tk-factory-services && \ + git pull && \ + export PATH=\$PATH:/volume2/@appstore/ContainerManager/usr/bin && \ + docker compose up -d --build <서비스명>" +``` + +전체 재시작: `docker compose up -d --build` +특정 서비스만: `docker compose up -d --build gateway system1-web` diff --git a/docker-compose.yml b/docker-compose.yml index 5f880fd..85fe276 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -130,6 +130,7 @@ services: - ./system1-factory/web:/usr/share/nginx/html:ro depends_on: - system1-api + - sso-auth networks: - tk-network @@ -417,22 +418,7 @@ services: # ================================================================= # ================================================================= - # Dashboard (tkds) - # ================================================================= - - tkds-web: - build: - context: ./tkds/web - dockerfile: Dockerfile - container_name: tk-tkds-web - restart: unless-stopped - ports: - - "30780:80" - networks: - - tk-network - - # ================================================================= - # Gateway + # Gateway (로그인 + 대시보드 + 공유JS) # ================================================================= gateway: @@ -445,9 +431,7 @@ services: - "30000:80" depends_on: - sso-auth - - system1-web - - system2-web - - system3-web + - system1-api networks: - tk-network @@ -485,7 +469,7 @@ services: - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} depends_on: - gateway - - tkds-web + - system1-web - system2-web - system3-web - tkpurchase-web diff --git a/tkds/web/dashboard.html b/gateway/html/dashboard.html similarity index 99% rename from tkds/web/dashboard.html rename to gateway/html/dashboard.html index 0e3a2a9..0ed4e96 100644 --- a/tkds/web/dashboard.html +++ b/gateway/html/dashboard.html @@ -344,7 +344,7 @@ if (hostname.includes('technicalkorea.net')) { return protocol + '//' + name + '.technicalkorea.net'; } - var ports = { tkfb: 30000, tkreport: 30180, tkqc: 30280, tkuser: 30380, tkpurchase: 30480, tksafety: 30580, tksupport: 30680 }; + var ports = { tkfb: 30080, tkreport: 30180, tkqc: 30280, tkuser: 30380, tkpurchase: 30480, tksafety: 30580, tksupport: 30680 }; return protocol + '//' + hostname + ':' + (ports[name] || 30000); } diff --git a/gateway/html/login.html b/gateway/html/login.html deleted file mode 100644 index 7874a03..0000000 --- a/gateway/html/login.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - 로그인 - TK 공장관리 - - - - - - - - diff --git a/gateway/html/portal.html b/gateway/html/portal.html deleted file mode 100644 index ec23c8b..0000000 --- a/gateway/html/portal.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - TK 공장관리 시스템 - - - -
-

TK 공장관리 시스템

- -
- - - - - - - - - diff --git a/gateway/nginx.conf b/gateway/nginx.conf index ca51262..c365922 100644 --- a/gateway/nginx.conf +++ b/gateway/nginx.conf @@ -2,29 +2,32 @@ server { listen 80; server_name _; - client_max_body_size 50M; - - # ===== Gateway 자체 페이지 (포털, 로그인) ===== root /usr/share/nginx/html; - # 로그인 페이지 - location = /login { + # 대시보드 (로그인 포함) + location = /dashboard { add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma "no-cache"; - try_files /login.html =404; + try_files /dashboard.html =404; } - # 대시보드 → tkds로 리다이렉트 (북마크 깨짐 방지) - location = /dashboard { - return 301 $scheme://tkds.technicalkorea.net/dashboard; + location = / { + return 302 /dashboard$is_args$args; } - # 공유 JS/CSS (nav-header 등) + # 레거시 /login → /dashboard 리다이렉트 + location = /login { + return 302 /dashboard$is_args$args; + } + + # 공유 JS (notification-bell.js, nav-header.js) location /shared/ { alias /usr/share/nginx/html/shared/; + expires 1h; + add_header Cache-Control "public, no-transform"; } - # ===== SSO Auth API ===== + # SSO Auth 프록시 location /auth/ { proxy_pass http://sso-auth:3000/api/auth/; proxy_set_header Host $host; @@ -33,7 +36,7 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # ===== System 1 API 프록시 ===== + # System1 API 프록시 (대시보드 배너용: 알림/TBM/휴가 카운트) location /api/ { proxy_pass http://system1-api:3005/api/; proxy_http_version 1.1; @@ -43,50 +46,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # ===== System 1 업로드 파일 ===== - location /uploads/ { - proxy_pass http://system1-api:3005/uploads/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } - - # ===== System 1 FastAPI Bridge ===== - location /fastapi/ { - proxy_pass http://system1-fastapi: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; - } - - # ===== AI Service API (맥미니 home-service-proxy 경유) ===== - location /ai-api/ { - resolver 8.8.8.8 valid=300s ipv6=off; - set $ai_upstream https://ai.hyungi.net; - rewrite ^/ai-api/(.*) /api/ai/$1 break; - proxy_pass $ai_upstream; - proxy_http_version 1.1; - proxy_set_header Host ai.hyungi.net; - 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_ssl_server_name on; - proxy_read_timeout 180s; - proxy_send_timeout 180s; - } - - # ===== System 1 Web (나머지 모든 경로) ===== - location / { - proxy_pass http://system1-web:80; - 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; - } - - # ===== Health Check ===== + # Health check location /health { + access_log off; return 200 '{"status":"ok","service":"gateway"}'; add_header Content-Type application/json; } diff --git a/system1-factory/api/config/cors.js b/system1-factory/api/config/cors.js index 0cdd6dd..8affcc1 100644 --- a/system1-factory/api/config/cors.js +++ b/system1-factory/api/config/cors.js @@ -13,12 +13,14 @@ const logger = require('../utils/logger'); * 허용된 Origin 목록 */ const allowedOrigins = [ - 'https://tkfb.technicalkorea.net', // Gateway (프로덕션) + 'https://tkfb.technicalkorea.net', // System 1 (공장관리) + 'https://tkds.technicalkorea.net', // Gateway/Dashboard 'https://tkreport.technicalkorea.net', // System 2 'https://tkqc.technicalkorea.net', // System 3 'https://tkuser.technicalkorea.net', // User Management 'https://tkpurchase.technicalkorea.net', // Purchase Management 'https://tksafety.technicalkorea.net', // Safety Management + 'https://tksupport.technicalkorea.net', // Support Management 'http://localhost:20000', // 웹 UI (로컬) 'http://localhost:30080', // 웹 UI (Docker) 'http://localhost:3005', // API 서버 diff --git a/system1-factory/web/nginx.conf b/system1-factory/web/nginx.conf index 8e64979..9e9e5d9 100644 --- a/system1-factory/web/nginx.conf +++ b/system1-factory/web/nginx.conf @@ -46,6 +46,46 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } + # SSO Auth 프록시 (gateway에서 이관) + location /auth/ { + proxy_pass http://sso-auth:3000/api/auth/; + 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; + } + + # AI Service 프록시 (gateway에서 이관) + location /ai-api/ { + resolver 8.8.8.8 valid=300s ipv6=off; + set $ai_upstream https://ai.hyungi.net; + rewrite ^/ai-api/(.*) /api/ai/$1 break; + proxy_pass $ai_upstream; + proxy_http_version 1.1; + proxy_set_header Host ai.hyungi.net; + 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_ssl_server_name on; + proxy_read_timeout 180s; + proxy_send_timeout 180s; + } + + # 레거시 /login, /dashboard → gateway(tkds) 리다이렉트 + location = /login { + return 302 $scheme://tkds.technicalkorea.net/dashboard$is_args$args; + } + location = /dashboard { + return 301 $scheme://tkds.technicalkorea.net/dashboard; + } + + # Health check + location /health { + access_log off; + return 200 '{"status":"ok","service":"system1-web"}'; + add_header Content-Type application/json; + } + # Static files (new Tailwind UI) location /static/ { expires 1h; diff --git a/system1-factory/web/static/js/tkfb-core.js b/system1-factory/web/static/js/tkfb-core.js index 078e854..116e9e0 100644 --- a/system1-factory/web/static/js/tkfb-core.js +++ b/system1-factory/web/static/js/tkfb-core.js @@ -288,6 +288,6 @@ async function initAuth() { /* ===== 알림 벨 ===== */ function _loadNotificationBell() { const s = document.createElement('script'); - s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); } diff --git a/system2-report/web/js/api-base.js b/system2-report/web/js/api-base.js index 4e9e1d7..b256703 100644 --- a/system2-report/web/js/api-base.js +++ b/system2-report/web/js/api-base.js @@ -153,7 +153,7 @@ if ('serviceWorker' in navigator) { window._loadNotificationBell = function() { var h = window.location.hostname; var s = document.createElement('script'); - s.src = (h.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : window.location.protocol + '//' + h + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (h.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : window.location.protocol + '//' + h + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); }; diff --git a/system3-nonconformance/web/static/js/api.js b/system3-nonconformance/web/static/js/api.js index 2f6659b..00ede07 100644 --- a/system3-nonconformance/web/static/js/api.js +++ b/system3-nonconformance/web/static/js/api.js @@ -16,9 +16,9 @@ function _getLoginUrl() { const hostname = window.location.hostname; const t = Date.now(); if (hostname.includes('technicalkorea.net')) { - return window.location.protocol + '//tkfb.technicalkorea.net/login?redirect=' + encodeURIComponent(window.location.href) + '&_t=' + t; + return window.location.protocol + '//tkds.technicalkorea.net/dashboard?redirect=' + encodeURIComponent(window.location.href) + '&_t=' + t; } - return window.location.protocol + '//' + hostname + ':30000/login?redirect=' + encodeURIComponent(window.location.href) + '&_t=' + t; + return window.location.protocol + '//' + hostname + ':30000/dashboard?redirect=' + encodeURIComponent(window.location.href) + '&_t=' + t; } // API 기본 설정 (통합 환경 지원) diff --git a/system3-nonconformance/web/static/js/app.js b/system3-nonconformance/web/static/js/app.js index 41df9ba..3f06a9a 100644 --- a/system3-nonconformance/web/static/js/app.js +++ b/system3-nonconformance/web/static/js/app.js @@ -389,9 +389,9 @@ class App { redirectToLogin() { const hostname = window.location.hostname; if (hostname.includes('technicalkorea.net')) { - window.location.href = window.location.protocol + '//tkfb.technicalkorea.net/login?redirect=' + encodeURIComponent(window.location.href); + window.location.href = window.location.protocol + '//tkds.technicalkorea.net/dashboard?redirect=' + encodeURIComponent(window.location.href); } else { - window.location.href = window.location.protocol + '//' + hostname + ':30000/login?redirect=' + encodeURIComponent(window.location.href); + window.location.href = window.location.protocol + '//' + hostname + ':30000/dashboard?redirect=' + encodeURIComponent(window.location.href); } } @@ -401,7 +401,7 @@ class App { _loadNotificationBell() { var h = window.location.hostname; var s = document.createElement('script'); - s.src = (h.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : window.location.protocol + '//' + h + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (h.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : window.location.protocol + '//' + h + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); } diff --git a/system3-nonconformance/web/static/js/components/common-header.js b/system3-nonconformance/web/static/js/components/common-header.js index 1b1af01..cc28491 100644 --- a/system3-nonconformance/web/static/js/components/common-header.js +++ b/system3-nonconformance/web/static/js/components/common-header.js @@ -668,9 +668,9 @@ class CommonHeader { localStorage.removeItem('sso_user'); var hostname = window.location.hostname; if (hostname.includes('technicalkorea.net')) { - window.location.href = window.location.protocol + '//tkfb.technicalkorea.net/login'; + window.location.href = window.location.protocol + '//tkds.technicalkorea.net/dashboard'; } else { - window.location.href = window.location.protocol + '//' + hostname + ':30000/login'; + window.location.href = window.location.protocol + '//' + hostname + ':30000/dashboard'; } } } diff --git a/tkds/web/Dockerfile b/tkds/web/Dockerfile deleted file mode 100644 index 5ab7db1..0000000 --- a/tkds/web/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM nginx:alpine -COPY nginx.conf /etc/nginx/conf.d/default.conf -COPY dashboard.html /usr/share/nginx/html/dashboard.html -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] diff --git a/tkds/web/nginx.conf b/tkds/web/nginx.conf deleted file mode 100644 index 63e1147..0000000 --- a/tkds/web/nginx.conf +++ /dev/null @@ -1,38 +0,0 @@ -server { - listen 80; - server_name _; - - location = /dashboard { - add_header Cache-Control "no-store, no-cache, must-revalidate"; - add_header Pragma "no-cache"; - root /usr/share/nginx/html; - try_files /dashboard.html =404; - } - - location = / { - return 302 /dashboard$is_args$args; - } - - location /auth/ { - proxy_pass http://sso-auth:3000/api/auth/; - 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; - } - - location /api/ { - proxy_pass http://system1-api:3005/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; - } - - location /health { - access_log off; - return 200 '{"status":"ok","service":"tkds"}'; - add_header Content-Type application/json; - } -} diff --git a/tkpurchase/web/static/js/tkpurchase-core.js b/tkpurchase/web/static/js/tkpurchase-core.js index d2110f0..e6cf166 100644 --- a/tkpurchase/web/static/js/tkpurchase-core.js +++ b/tkpurchase/web/static/js/tkpurchase-core.js @@ -156,6 +156,6 @@ function initAuth() { /* ===== 알림 벨 ===== */ function _loadNotificationBell() { const s = document.createElement('script'); - s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); } diff --git a/tksafety/web/static/js/tksafety-core.js b/tksafety/web/static/js/tksafety-core.js index 0b201da..f5249c4 100644 --- a/tksafety/web/static/js/tksafety-core.js +++ b/tksafety/web/static/js/tksafety-core.js @@ -147,6 +147,6 @@ function initAuth() { /* ===== 알림 벨 ===== */ function _loadNotificationBell() { const s = document.createElement('script'); - s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); } diff --git a/tksupport/web/static/js/tksupport-core.js b/tksupport/web/static/js/tksupport-core.js index f1df96e..de954b6 100644 --- a/tksupport/web/static/js/tksupport-core.js +++ b/tksupport/web/static/js/tksupport-core.js @@ -140,6 +140,6 @@ function initAuth() { /* ===== 알림 벨 ===== */ function _loadNotificationBell() { const s = document.createElement('script'); - s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); } diff --git a/user-management/web/static/js/tkuser-core.js b/user-management/web/static/js/tkuser-core.js index 962c702..7d8149c 100644 --- a/user-management/web/static/js/tkuser-core.js +++ b/user-management/web/static/js/tkuser-core.js @@ -192,6 +192,6 @@ async function init() { /* ===== 알림 벨 ===== */ function _loadNotificationBell() { const s = document.createElement('script'); - s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkfb.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=2'; + s.src = (location.hostname.includes('technicalkorea.net') ? 'https://tkds.technicalkorea.net' : location.protocol + '//' + location.hostname + ':30000') + '/shared/notification-bell.js?v=3'; document.head.appendChild(s); }