From 7cde0c57d8e6c30975d9084b07af3313d8979452 Mon Sep 17 00:00:00 2001 From: htlee Date: Tue, 17 Mar 2026 15:50:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat(frontend):=20UI=20=EB=B8=8C=EB=9E=9C?= =?UTF-8?q?=EB=94=A9=20=EA=B0=9C=EC=84=A0=20+=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인 화면: kcg.svg 로고 적용 (이모지 교체) - 헤더 우측: 사용자 프로필/이름 + 로그아웃 버튼 추가 - 브라우저 탭: favicon → kcg.svg, 제목 → kcg-dashboard-demo - 프로덕션 빌드: console/debugger 자동 제거 - CORS: CorsFilter 최우선 순위 등록 (AuthFilter 이전) - deploy.yml: secrets → .env 파일로 배포 - systemd/nginx: 경로 /devdata/services/kcg/ 반영 --- .gitea/workflows/deploy.yml | 9 + .../java/gc/mda/kcg/config/WebConfig.java | 38 +- deploy/kcg-backend.service | 7 +- deploy/nginx-kcg.conf | 2 +- frontend/index.html | 4 +- frontend/public/kcg.svg | 1004 +++++++++++++++++ frontend/src/App.css | 23 + frontend/src/App.tsx | 13 +- frontend/src/components/auth/LoginPage.tsx | 2 +- frontend/vite.config.ts | 7 +- 10 files changed, 1088 insertions(+), 21 deletions(-) create mode 100644 frontend/public/kcg.svg diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 06fa3e9..1f34937 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -43,6 +43,9 @@ jobs: run: mvn -B clean package -DskipTests - name: Deploy backend + env: + GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} run: | DEPLOY_DIR=/deploy/kcg-backend mkdir -p $DEPLOY_DIR/backup @@ -53,6 +56,12 @@ jobs: ls -t $DEPLOY_DIR/backup/*.jar | tail -n +6 | xargs -r rm fi + # Secrets → 환경변수 파일 (systemd EnvironmentFile) + cat > $DEPLOY_DIR/.env << ENVEOF + GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} + JWT_SECRET=${JWT_SECRET} + ENVEOF + # JAR 교체 + 재시작 트리거 cp backend/target/kcg.jar $DEPLOY_DIR/kcg.jar date '+%s' > $DEPLOY_DIR/.deploy-trigger diff --git a/backend/src/main/java/gc/mda/kcg/config/WebConfig.java b/backend/src/main/java/gc/mda/kcg/config/WebConfig.java index f18492d..b9afae0 100644 --- a/backend/src/main/java/gc/mda/kcg/config/WebConfig.java +++ b/backend/src/main/java/gc/mda/kcg/config/WebConfig.java @@ -1,18 +1,36 @@ package gc.mda.kcg.config; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.core.Ordered; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.util.List; @Configuration -public class WebConfig implements WebMvcConfigurer { +public class WebConfig { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/api/**") - .allowedOrigins("http://localhost:5173") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .allowCredentials(true); + @Value("${app.cors.allowed-origins:http://localhost:5173}") + private List allowedOrigins; + + @Bean + public FilterRegistrationBean corsFilterRegistration() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(allowedOrigins); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + config.setAllowedHeaders(List.of("*")); + config.setAllowCredentials(true); + config.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/api/**", config); + + FilterRegistrationBean bean = new FilterRegistrationBean<>(new CorsFilter(source)); + bean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return bean; } } diff --git a/deploy/kcg-backend.service b/deploy/kcg-backend.service index 1932377..36f471e 100644 --- a/deploy/kcg-backend.service +++ b/deploy/kcg-backend.service @@ -6,12 +6,13 @@ After=network.target Type=simple User=root Group=root -WorkingDirectory=/deploy/kcg-backend +WorkingDirectory=/devdata/services/kcg/backend +EnvironmentFile=-/devdata/services/kcg/backend/.env ExecStart=/usr/lib/jvm/java-17-openjdk-17.0.18.0.8-1.el9.x86_64/bin/java \ -Xms2g -Xmx4g \ -Dspring.profiles.active=prod \ - -Dspring.config.additional-location=file:/deploy/kcg-backend/ \ - -jar /deploy/kcg-backend/kcg.jar + -Dspring.config.additional-location=file:/devdata/services/kcg/backend/ \ + -jar /devdata/services/kcg/backend/kcg.jar Restart=on-failure RestartSec=10 diff --git a/deploy/nginx-kcg.conf b/deploy/nginx-kcg.conf index b86d694..4820fea 100644 --- a/deploy/nginx-kcg.conf +++ b/deploy/nginx-kcg.conf @@ -8,7 +8,7 @@ server { ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # ── Frontend SPA ── - root /deploy/kcg; + root /devdata/services/kcg/dist; # Static cache location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 1y; diff --git a/frontend/index.html b/frontend/index.html index bcd194d..462491d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - iran-airstrike-replay + kcg-dashboard-demo
diff --git a/frontend/public/kcg.svg b/frontend/public/kcg.svg new file mode 100644 index 0000000..fb0960a --- /dev/null +++ b/frontend/public/kcg.svg @@ -0,0 +1,1004 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.css b/frontend/src/App.css index 46461bf..a466923 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -146,6 +146,29 @@ 50% { opacity: 0.4; } } +.header-user { + display: flex; + align-items: center; + gap: 6px; + padding-left: 8px; + border-left: 1px solid var(--kcg-border); +} + +.header-user-avatar { + width: 22px; + height: 22px; + border-radius: 50%; +} + +.header-user-name { + font-size: 11px; + color: var(--kcg-text); + max-width: 80px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + /* Cheonghae Unit pulsing beacon */ @keyframes cheonghae-pulse { 0% { transform: scale(1); opacity: 0.8; } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7249338..22eb2bb 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -103,7 +103,7 @@ interface AuthenticatedAppProps { onLogout: () => Promise; } -function AuthenticatedApp(_props: AuthenticatedAppProps) { +function AuthenticatedApp({ user, onLogout }: AuthenticatedAppProps) { const [appMode, setAppMode] = useState('live'); const [events, setEvents] = useState([]); const [sensorData, setSensorData] = useState([]); @@ -965,6 +965,17 @@ function AuthenticatedApp(_props: AuthenticatedAppProps) { {isLive ? t('header.live') : replay.state.isPlaying ? t('header.replaying') : t('header.paused')} + {user && ( +
+ {user.picture && ( + + )} + {user.name} + +
+ )} diff --git a/frontend/src/components/auth/LoginPage.tsx b/frontend/src/components/auth/LoginPage.tsx index 3a34ae7..9742506 100644 --- a/frontend/src/components/auth/LoginPage.tsx +++ b/frontend/src/components/auth/LoginPage.tsx @@ -104,7 +104,7 @@ const LoginPage = ({ onGoogleLogin, onDevLogin }: LoginPageProps) => { > {/* Title */}
-
🛡️
+ KCG

({ plugins: [tailwindcss(), react()], + esbuild: mode === 'production' ? { drop: ['console', 'debugger'] } : {}, server: { proxy: { '/api/ais': { @@ -117,4 +118,4 @@ export default defineConfig({ }, }, }, -}) +})) -- 2.45.2 From 5abbe1ded7c9e28ed86f3aa2e890d48ef65c01b4 Mon Sep 17 00:00:00 2001 From: htlee Date: Tue, 17 Mar 2026 15:51:36 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix(frontend):=20=ED=95=AD=EA=B3=B5?= =?UTF-8?q?=EA=B8=B0=20API=20=ED=8F=B4=EB=A7=81=20=EC=A3=BC=EA=B8=B0=2015?= =?UTF-8?q?=EC=B4=88/25=EC=B4=88=20=E2=86=92=2060=EC=B4=88=20(Rate=20Limit?= =?UTF-8?q?=20=EB=8C=80=EC=9D=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 22eb2bb..7e5c671 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -266,7 +266,7 @@ function AuthenticatedApp({ user, onLogout }: AuthenticatedAppProps) { } }; load(); - const interval = setInterval(load, 15_000); + const interval = setInterval(load, 60_000); return () => clearInterval(interval); }, [appMode, refreshKey]); @@ -325,7 +325,7 @@ function AuthenticatedApp({ user, onLogout }: AuthenticatedAppProps) { if (result.length > 0) setBaseAircraftKorea(result); }; load(); - const interval = setInterval(load, 25_000); + const interval = setInterval(load, 60_000); return () => clearInterval(interval); }, [refreshKey]); -- 2.45.2 From fb5b0fa93538d686c480fa82b1e5dfeab25cb1a0 Mon Sep 17 00:00:00 2001 From: htlee Date: Tue, 17 Mar 2026 15:58:38 +0900 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?=EB=85=B8=ED=8A=B8=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/RELEASE-NOTES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/RELEASE-NOTES.md b/docs/RELEASE-NOTES.md index b029811..bf46f4e 100644 --- a/docs/RELEASE-NOTES.md +++ b/docs/RELEASE-NOTES.md @@ -4,6 +4,20 @@ ## [Unreleased] +### 추가 +- 헤더 우측 사용자 프로필/이름 + 로그아웃 버튼 +- 로그인 화면 KCG 로고 적용 (kcg.svg) +- 브라우저 탭 favicon/제목 변경 (kcg-dashboard-demo) + +### 수정 +- 항공기 API 폴링 주기 15초/25초 → 60초 (Rate Limit 대응) +- CORS: CorsFilter 최우선 순위 등록 (프로덕션 도메인 허용) +- 프로덕션 빌드 시 console/debugger 자동 제거 + +### 변경 +- deploy.yml: Gitea secrets → .env 파일로 백엔드 환경변수 배포 +- systemd/nginx: 배포 경로 /devdata/services/kcg/ 반영 + ## [2026-03-17.3] ### 수정 -- 2.45.2 From a2ece61f143fb0ad43fb4125cd580c014f4e9461 Mon Sep 17 00:00:00 2001 From: htlee Date: Tue, 17 Mar 2026 15:59:41 +0900 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?=EB=85=B8=ED=8A=B8=20=EC=A0=95=EB=A6=AC=20(2026-03-17.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/RELEASE-NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/RELEASE-NOTES.md b/docs/RELEASE-NOTES.md index bf46f4e..ad37ced 100644 --- a/docs/RELEASE-NOTES.md +++ b/docs/RELEASE-NOTES.md @@ -4,6 +4,8 @@ ## [Unreleased] +## [2026-03-17.4] + ### 추가 - 헤더 우측 사용자 프로필/이름 + 로그아웃 버튼 - 로그인 화면 KCG 로고 적용 (kcg.svg) -- 2.45.2