From c63af7abe012677dfc5acbcc8de27be4fd46d1d8 Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:21:45 +0900 Subject: [PATCH 1/7] =?UTF-8?q?fix(frontend):=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20import=20=EA=B2=BD=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(vite=20build=20=EC=8B=A4=ED=8C=A8=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0)=20(#43)=20Co-authored-by:=20htlee=20=20Co-committed-by:=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/CollectorMonitor.tsx | 4 ++-- frontend/src/components/common/EventLog.tsx | 4 ++-- frontend/src/components/common/EventStrip.tsx | 2 +- frontend/src/components/common/SensorChart.tsx | 2 +- frontend/src/components/common/TimelineSlider.tsx | 2 +- frontend/src/components/iran/AirportLayer.tsx | 2 +- frontend/src/components/iran/GlobeMap.tsx | 4 ++-- frontend/src/components/iran/OilFacilityLayer.tsx | 2 +- frontend/src/components/iran/ReplayMap.tsx | 8 ++++---- frontend/src/components/iran/SatelliteMap.tsx | 8 ++++---- frontend/src/components/korea/CctvLayer.tsx | 4 ++-- frontend/src/components/korea/CoastGuardLayer.tsx | 4 ++-- frontend/src/components/korea/EezLayer.tsx | 2 +- frontend/src/components/korea/InfraLayer.tsx | 2 +- frontend/src/components/korea/KoreaAirportLayer.tsx | 4 ++-- frontend/src/components/korea/KoreaMap.tsx | 10 +++++----- frontend/src/components/korea/NavWarningLayer.tsx | 4 ++-- frontend/src/components/korea/OsintMapLayer.tsx | 2 +- frontend/src/components/korea/PiracyLayer.tsx | 4 ++-- frontend/src/components/korea/SubmarineCableLayer.tsx | 4 ++-- frontend/src/components/layers/AircraftLayer.tsx | 2 +- frontend/src/components/layers/DamagedShipLayer.tsx | 4 ++-- frontend/src/components/layers/SatelliteLayer.tsx | 2 +- frontend/src/components/layers/ShipLayer.tsx | 2 +- 24 files changed, 44 insertions(+), 44 deletions(-) diff --git a/frontend/src/components/common/CollectorMonitor.tsx b/frontend/src/components/common/CollectorMonitor.tsx index 5b86731..e1d8ee7 100644 --- a/frontend/src/components/common/CollectorMonitor.tsx +++ b/frontend/src/components/common/CollectorMonitor.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; -import { fetchCollectorStatus } from '../services/collectorStatus'; -import type { CollectorInfo } from '../services/collectorStatus'; +import { fetchCollectorStatus } from '../../services/collectorStatus'; +import type { CollectorInfo } from '../../services/collectorStatus'; interface CollectorMonitorProps { onClose: () => void; diff --git a/frontend/src/components/common/EventLog.tsx b/frontend/src/components/common/EventLog.tsx index 01e6b70..e098280 100644 --- a/frontend/src/components/common/EventLog.tsx +++ b/frontend/src/components/common/EventLog.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import type { GeoEvent, Ship } from '../types'; -import type { OsintItem } from '../services/osint'; +import type { GeoEvent, Ship } from '../../types'; +import type { OsintItem } from '../../services/osint'; type DashboardTab = 'iran' | 'korea'; diff --git a/frontend/src/components/common/EventStrip.tsx b/frontend/src/components/common/EventStrip.tsx index 8a72c87..922a14a 100644 --- a/frontend/src/components/common/EventStrip.tsx +++ b/frontend/src/components/common/EventStrip.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { GeoEvent } from '../types'; +import type { GeoEvent } from '../../types'; interface Props { events: GeoEvent[]; diff --git a/frontend/src/components/common/SensorChart.tsx b/frontend/src/components/common/SensorChart.tsx index 288a22d..9aa24d1 100644 --- a/frontend/src/components/common/SensorChart.tsx +++ b/frontend/src/components/common/SensorChart.tsx @@ -10,7 +10,7 @@ import { ResponsiveContainer, ReferenceLine, } from 'recharts'; -import type { SensorLog } from '../types'; +import type { SensorLog } from '../../types'; interface Props { data: SensorLog[]; diff --git a/frontend/src/components/common/TimelineSlider.tsx b/frontend/src/components/common/TimelineSlider.tsx index 23e4e23..27cc837 100644 --- a/frontend/src/components/common/TimelineSlider.tsx +++ b/frontend/src/components/common/TimelineSlider.tsx @@ -1,6 +1,6 @@ import { useMemo, useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import type { GeoEvent } from '../types'; +import type { GeoEvent } from '../../types'; interface Props { currentTime: number; diff --git a/frontend/src/components/iran/AirportLayer.tsx b/frontend/src/components/iran/AirportLayer.tsx index 1b3b1ab..bc0cc96 100644 --- a/frontend/src/components/iran/AirportLayer.tsx +++ b/frontend/src/components/iran/AirportLayer.tsx @@ -1,6 +1,6 @@ import { memo, useMemo, useState } from 'react'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import type { Airport } from '../data/airports'; +import type { Airport } from '../../data/airports'; const US_BASE_ICAOS = new Set([ 'OMAD', 'OTBH', 'OKAJ', 'LTAG', 'OEPS', 'ORAA', 'ORBD', 'OBBS', 'OMTH', 'HDCL', diff --git a/frontend/src/components/iran/GlobeMap.tsx b/frontend/src/components/iran/GlobeMap.tsx index 109bdc5..682f9cd 100644 --- a/frontend/src/components/iran/GlobeMap.tsx +++ b/frontend/src/components/iran/GlobeMap.tsx @@ -1,8 +1,8 @@ import { useRef, useEffect } from 'react'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; -import { countryLabelsGeoJSON } from '../data/countryLabels'; -import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../types'; +import { countryLabelsGeoJSON } from '../../data/countryLabels'; +import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../../types'; interface Props { events: GeoEvent[]; diff --git a/frontend/src/components/iran/OilFacilityLayer.tsx b/frontend/src/components/iran/OilFacilityLayer.tsx index 8e95089..e1248ce 100644 --- a/frontend/src/components/iran/OilFacilityLayer.tsx +++ b/frontend/src/components/iran/OilFacilityLayer.tsx @@ -1,7 +1,7 @@ import { memo, useState } from 'react'; import { Marker, Popup } from 'react-map-gl/maplibre'; import { useTranslation } from 'react-i18next'; -import type { OilFacility, OilFacilityType } from '../types'; +import type { OilFacility, OilFacilityType } from '../../types'; interface Props { facilities: OilFacility[]; diff --git a/frontend/src/components/iran/ReplayMap.tsx b/frontend/src/components/iran/ReplayMap.tsx index 388b922..f6be33c 100644 --- a/frontend/src/components/iran/ReplayMap.tsx +++ b/frontend/src/components/iran/ReplayMap.tsx @@ -8,10 +8,10 @@ import { ShipLayer } from '../layers/ShipLayer'; import { DamagedShipLayer } from '../layers/DamagedShipLayer'; import { OilFacilityLayer } from './OilFacilityLayer'; import { AirportLayer } from './AirportLayer'; -import { iranOilFacilities } from '../data/oilFacilities'; -import { middleEastAirports } from '../data/airports'; -import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../types'; -import { countryLabelsGeoJSON } from '../data/countryLabels'; +import { iranOilFacilities } from '../../data/oilFacilities'; +import { middleEastAirports } from '../../data/airports'; +import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../../types'; +import { countryLabelsGeoJSON } from '../../data/countryLabels'; import 'maplibre-gl/dist/maplibre-gl.css'; export interface FlyToTarget { diff --git a/frontend/src/components/iran/SatelliteMap.tsx b/frontend/src/components/iran/SatelliteMap.tsx index 8ac21f6..9180ee8 100644 --- a/frontend/src/components/iran/SatelliteMap.tsx +++ b/frontend/src/components/iran/SatelliteMap.tsx @@ -8,10 +8,10 @@ import { ShipLayer } from '../layers/ShipLayer'; import { DamagedShipLayer } from '../layers/DamagedShipLayer'; import { OilFacilityLayer } from './OilFacilityLayer'; import { AirportLayer } from './AirportLayer'; -import { iranOilFacilities } from '../data/oilFacilities'; -import { middleEastAirports } from '../data/airports'; -import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../types'; -import { countryLabelsGeoJSON } from '../data/countryLabels'; +import { iranOilFacilities } from '../../data/oilFacilities'; +import { middleEastAirports } from '../../data/airports'; +import type { GeoEvent, Aircraft, SatellitePosition, Ship, LayerVisibility } from '../../types'; +import { countryLabelsGeoJSON } from '../../data/countryLabels'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; diff --git a/frontend/src/components/korea/CctvLayer.tsx b/frontend/src/components/korea/CctvLayer.tsx index 53c3a65..30604e1 100644 --- a/frontend/src/components/korea/CctvLayer.tsx +++ b/frontend/src/components/korea/CctvLayer.tsx @@ -2,8 +2,8 @@ import { useState, useRef, useEffect, useCallback } from 'react'; import { Marker, Popup } from 'react-map-gl/maplibre'; import { useTranslation } from 'react-i18next'; import Hls from 'hls.js'; -import { KOREA_CCTV_CAMERAS } from '../services/cctv'; -import type { CctvCamera } from '../services/cctv'; +import { KOREA_CCTV_CAMERAS } from '../../services/cctv'; +import type { CctvCamera } from '../../services/cctv'; const REGION_COLOR: Record = { '제주': '#ff6b6b', diff --git a/frontend/src/components/korea/CoastGuardLayer.tsx b/frontend/src/components/korea/CoastGuardLayer.tsx index 87a674f..df10aef 100644 --- a/frontend/src/components/korea/CoastGuardLayer.tsx +++ b/frontend/src/components/korea/CoastGuardLayer.tsx @@ -1,8 +1,8 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import { COAST_GUARD_FACILITIES, CG_TYPE_LABEL } from '../services/coastGuard'; -import type { CoastGuardFacility, CoastGuardType } from '../services/coastGuard'; +import { COAST_GUARD_FACILITIES, CG_TYPE_LABEL } from '../../services/coastGuard'; +import type { CoastGuardFacility, CoastGuardType } from '../../services/coastGuard'; const TYPE_COLOR: Record = { hq: '#ff6b6b', diff --git a/frontend/src/components/korea/EezLayer.tsx b/frontend/src/components/korea/EezLayer.tsx index 00d2cb6..a97f2ee 100644 --- a/frontend/src/components/korea/EezLayer.tsx +++ b/frontend/src/components/korea/EezLayer.tsx @@ -1,5 +1,5 @@ import { Source, Layer } from 'react-map-gl/maplibre'; -import { KOREA_EEZ_BOUNDARY, KOREA_CHINA_PMZ, NLL_WEST_SEA, NLL_EAST_SEA } from '../services/koreaEez'; +import { KOREA_EEZ_BOUNDARY, KOREA_CHINA_PMZ, NLL_WEST_SEA, NLL_EAST_SEA } from '../../services/koreaEez'; import type { FillLayerSpecification, LineLayerSpecification } from 'maplibre-gl'; // Convert [lat, lng][] to GeoJSON [lng, lat][] ring diff --git a/frontend/src/components/korea/InfraLayer.tsx b/frontend/src/components/korea/InfraLayer.tsx index 5681159..f4795b9 100644 --- a/frontend/src/components/korea/InfraLayer.tsx +++ b/frontend/src/components/korea/InfraLayer.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import type { PowerFacility } from '../services/infra'; +import type { PowerFacility } from '../../services/infra'; // SVG Wind Turbine Icon function WindTurbineIcon({ color, size = 14 }: { color: string; size?: number }) { diff --git a/frontend/src/components/korea/KoreaAirportLayer.tsx b/frontend/src/components/korea/KoreaAirportLayer.tsx index a95a18d..5c95573 100644 --- a/frontend/src/components/korea/KoreaAirportLayer.tsx +++ b/frontend/src/components/korea/KoreaAirportLayer.tsx @@ -1,8 +1,8 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import { KOREAN_AIRPORTS } from '../services/airports'; -import type { KoreanAirport } from '../services/airports'; +import { KOREAN_AIRPORTS } from '../../services/airports'; +import type { KoreanAirport } from '../../services/airports'; export function KoreaAirportLayer() { const [selected, setSelected] = useState(null); diff --git a/frontend/src/components/korea/KoreaMap.tsx b/frontend/src/components/korea/KoreaMap.tsx index 2443e36..3a5d0c3 100644 --- a/frontend/src/components/korea/KoreaMap.tsx +++ b/frontend/src/components/korea/KoreaMap.tsx @@ -14,11 +14,11 @@ import { NavWarningLayer } from './NavWarningLayer'; import { OsintMapLayer } from './OsintMapLayer'; import { EezLayer } from './EezLayer'; import { PiracyLayer } from './PiracyLayer'; -import { fetchKoreaInfra } from '../services/infra'; -import type { PowerFacility } from '../services/infra'; -import type { Ship, Aircraft, SatellitePosition } from '../types'; -import type { OsintItem } from '../services/osint'; -import { countryLabelsGeoJSON } from '../data/countryLabels'; +import { fetchKoreaInfra } from '../../services/infra'; +import type { PowerFacility } from '../../services/infra'; +import type { Ship, Aircraft, SatellitePosition } from '../../types'; +import type { OsintItem } from '../../services/osint'; +import { countryLabelsGeoJSON } from '../../data/countryLabels'; import 'maplibre-gl/dist/maplibre-gl.css'; export interface KoreaFiltersState { diff --git a/frontend/src/components/korea/NavWarningLayer.tsx b/frontend/src/components/korea/NavWarningLayer.tsx index f8bdf2b..4015b62 100644 --- a/frontend/src/components/korea/NavWarningLayer.tsx +++ b/frontend/src/components/korea/NavWarningLayer.tsx @@ -1,8 +1,8 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import { NAV_WARNINGS, NW_LEVEL_LABEL, NW_ORG_LABEL } from '../services/navWarning'; -import type { NavWarning, NavWarningLevel, TrainingOrg } from '../services/navWarning'; +import { NAV_WARNINGS, NW_LEVEL_LABEL, NW_ORG_LABEL } from '../../services/navWarning'; +import type { NavWarning, NavWarningLevel, TrainingOrg } from '../../services/navWarning'; const LEVEL_COLOR: Record = { danger: '#ef4444', diff --git a/frontend/src/components/korea/OsintMapLayer.tsx b/frontend/src/components/korea/OsintMapLayer.tsx index 2fa3d6d..bf2c616 100644 --- a/frontend/src/components/korea/OsintMapLayer.tsx +++ b/frontend/src/components/korea/OsintMapLayer.tsx @@ -1,7 +1,7 @@ import { useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import type { OsintItem } from '../services/osint'; +import type { OsintItem } from '../../services/osint'; const CAT_COLOR: Record = { maritime_accident: '#ef4444', diff --git a/frontend/src/components/korea/PiracyLayer.tsx b/frontend/src/components/korea/PiracyLayer.tsx index 89bc2e8..a575c53 100644 --- a/frontend/src/components/korea/PiracyLayer.tsx +++ b/frontend/src/components/korea/PiracyLayer.tsx @@ -1,8 +1,8 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import { PIRACY_ZONES, PIRACY_LEVEL_COLOR, PIRACY_LEVEL_LABEL } from '../services/piracy'; -import type { PiracyZone } from '../services/piracy'; +import { PIRACY_ZONES, PIRACY_LEVEL_COLOR, PIRACY_LEVEL_LABEL } from '../../services/piracy'; +import type { PiracyZone } from '../../services/piracy'; function SkullIcon({ color, size }: { color: string; size: number }) { return ( diff --git a/frontend/src/components/korea/SubmarineCableLayer.tsx b/frontend/src/components/korea/SubmarineCableLayer.tsx index f832d3d..5be99a8 100644 --- a/frontend/src/components/korea/SubmarineCableLayer.tsx +++ b/frontend/src/components/korea/SubmarineCableLayer.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Marker, Popup, Source, Layer } from 'react-map-gl/maplibre'; -import { KOREA_SUBMARINE_CABLES, KOREA_LANDING_POINTS } from '../services/submarineCable'; -import type { SubmarineCable } from '../services/submarineCable'; +import { KOREA_SUBMARINE_CABLES, KOREA_LANDING_POINTS } from '../../services/submarineCable'; +import type { SubmarineCable } from '../../services/submarineCable'; export function SubmarineCableLayer() { const [selectedCable, setSelectedCable] = useState(null); diff --git a/frontend/src/components/layers/AircraftLayer.tsx b/frontend/src/components/layers/AircraftLayer.tsx index 173157d..874852d 100644 --- a/frontend/src/components/layers/AircraftLayer.tsx +++ b/frontend/src/components/layers/AircraftLayer.tsx @@ -1,7 +1,7 @@ import { memo, useMemo, useState, useEffect } from 'react'; import { Marker, Popup, Source, Layer } from 'react-map-gl/maplibre'; import { useTranslation } from 'react-i18next'; -import type { Aircraft, AircraftCategory } from '../types'; +import type { Aircraft, AircraftCategory } from '../../types'; interface Props { aircraft: Aircraft[]; diff --git a/frontend/src/components/layers/DamagedShipLayer.tsx b/frontend/src/components/layers/DamagedShipLayer.tsx index faa577f..6b79ec4 100644 --- a/frontend/src/components/layers/DamagedShipLayer.tsx +++ b/frontend/src/components/layers/DamagedShipLayer.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react'; import { Marker, Popup } from 'react-map-gl/maplibre'; -import { damagedShips } from '../data/damagedShips'; -import type { DamagedShip } from '../data/damagedShips'; +import { damagedShips } from '../../data/damagedShips'; +import type { DamagedShip } from '../../data/damagedShips'; interface Props { currentTime: number; diff --git a/frontend/src/components/layers/SatelliteLayer.tsx b/frontend/src/components/layers/SatelliteLayer.tsx index 0f247b3..b5c1dde 100644 --- a/frontend/src/components/layers/SatelliteLayer.tsx +++ b/frontend/src/components/layers/SatelliteLayer.tsx @@ -1,7 +1,7 @@ import { memo, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup, Source, Layer } from 'react-map-gl/maplibre'; -import type { SatellitePosition } from '../types'; +import type { SatellitePosition } from '../../types'; interface Props { satellites: SatellitePosition[]; diff --git a/frontend/src/components/layers/ShipLayer.tsx b/frontend/src/components/layers/ShipLayer.tsx index 5bd7292..addd5fe 100644 --- a/frontend/src/components/layers/ShipLayer.tsx +++ b/frontend/src/components/layers/ShipLayer.tsx @@ -1,7 +1,7 @@ import { memo, useMemo, useState, useEffect } from 'react'; import { Marker, Popup, Source, Layer, useMap } from 'react-map-gl/maplibre'; import { useTranslation } from 'react-i18next'; -import type { Ship, ShipCategory } from '../types'; +import type { Ship, ShipCategory } from '../../types'; import maplibregl from 'maplibre-gl'; interface Props { From 1549adafc76b227753d98faac270de1b9338a3f0 Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:25:52 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix(deploy):=20health=20check=20+=20Pressur?= =?UTF-8?q?eCollector=20=EC=A4=91=EB=B3=B5=20=EB=B0=A9=EC=A7=80=20(#44)=20?= =?UTF-8?q?Co-authored-by:=20htlee=20=20Co-committed-by:?= =?UTF-8?q?=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 9 ++++---- .../collector/sensor/PressureCollector.java | 23 ++++++++----------- .../sensor/PressureReadingRepository.java | 1 + 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 089ed48..a8ee76e 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -112,10 +112,11 @@ jobs: echo "--- Restarting kcg-backend ---" systemctl restart kcg-backend - # 기동 확인 (최대 30초) - for i in $(seq 1 30); do - if curl -sf http://localhost:8080/api/aircraft > /dev/null 2>&1; then - echo "Backend started successfully (${i}s)" + # 기동 확인 (최대 60초, 401=인증필요=정상 기동) + for i in $(seq 1 60); do + HTTP=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/api/aircraft 2>/dev/null) + if [ "$HTTP" = "200" ] || [ "$HTTP" = "401" ] || [ "$HTTP" = "403" ]; then + echo "Backend started successfully (${i}s, HTTP $HTTP)" exit 0 fi sleep 1 diff --git a/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java b/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java index cb27cff..a95e121 100644 --- a/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java +++ b/backend/src/main/java/gc/mda/kcg/collector/sensor/PressureCollector.java @@ -91,19 +91,16 @@ public class PressureCollector { try { // Open-Meteo 시간 형식: "2026-03-18T07:00" (Z 없음) → Z 추가 Instant readingTime = OffsetDateTime.parse(timeStr + "Z").toInstant(); - try { - repository.save(PressureReading.builder() - .station(station) - .lat(lat) - .lng(lng) - .pressureHpa(pressure) - .readingTime(readingTime) - .collectedAt(now) - .build()); - saved++; - } catch (Exception ignored) { - // unique constraint violation = 이미 존재, 무시 - } + if (repository.existsByStationAndReadingTime(station, readingTime)) continue; + repository.save(PressureReading.builder() + .station(station) + .lat(lat) + .lng(lng) + .pressureHpa(pressure) + .readingTime(readingTime) + .collectedAt(now) + .build()); + saved++; } catch (DateTimeParseException e) { log.debug("기압 시간 파싱 실패: {}", timeStr); } diff --git a/backend/src/main/java/gc/mda/kcg/domain/sensor/PressureReadingRepository.java b/backend/src/main/java/gc/mda/kcg/domain/sensor/PressureReadingRepository.java index 3c418c0..d6057d4 100644 --- a/backend/src/main/java/gc/mda/kcg/domain/sensor/PressureReadingRepository.java +++ b/backend/src/main/java/gc/mda/kcg/domain/sensor/PressureReadingRepository.java @@ -6,6 +6,7 @@ import java.time.Instant; import java.util.List; public interface PressureReadingRepository extends JpaRepository { + boolean existsByStationAndReadingTime(String station, Instant readingTime); List findByStationAndReadingTimeAfterOrderByReadingTimeAsc(String station, Instant since); List findByReadingTimeAfterOrderByReadingTimeAsc(Instant since); } From db8334b08ee59648268325db62c8e40ea742c2ef Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:31:34 +0900 Subject: [PATCH 3/7] =?UTF-8?q?fix(deploy):=20SSH=20=ED=98=B8=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20IP=20=EB=8F=99=EC=A0=81=20=EA=B0=90=EC=A7=80=20+=20?= =?UTF-8?q?health=20check=20=EA=B0=9C=EC=84=A0=20(#45)=20Co-authored-by:?= =?UTF-8?q?=20htlee=20=20Co-committed-by:=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index a8ee76e..cfe576f 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -89,8 +89,8 @@ jobs: mkdir -p ~/.ssh echo "$DEPLOY_KEY" > ~/.ssh/id_deploy chmod 600 ~/.ssh/id_deploy - # Docker 컨테이너 → 호스트: bridge gateway(172.17.0.1) 경유 - DOCKER_HOST_IP=172.17.0.1 + # Docker 컨테이너 → 호스트: services_devnet gateway 경유 + DOCKER_HOST_IP=$(ip route | awk '/default/ {print $3}') ssh-keyscan $DOCKER_HOST_IP >> ~/.ssh/known_hosts 2>/dev/null || true SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no root@$DOCKER_HOST_IP" From 5d01b0e6e5a601ef12a35944b1ec96bc6d512fa9 Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:35:17 +0900 Subject: [PATCH 4/7] =?UTF-8?q?fix(deploy):=20gateway=20=EA=B0=90=EC=A7=80?= =?UTF-8?q?=20ip=20=E2=86=92=20/proc/net/route=20(#46)=20Co-authored-by:?= =?UTF-8?q?=20htlee=20=20Co-committed-by:=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index cfe576f..4aacc0b 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -89,8 +89,8 @@ jobs: mkdir -p ~/.ssh echo "$DEPLOY_KEY" > ~/.ssh/id_deploy chmod 600 ~/.ssh/id_deploy - # Docker 컨테이너 → 호스트: services_devnet gateway 경유 - DOCKER_HOST_IP=$(ip route | awk '/default/ {print $3}') + # Docker 컨테이너 → 호스트: default gateway (최소 의존) + DOCKER_HOST_IP=$(cat /proc/net/route | awk '$2 == "00000000" {printf "%d.%d.%d.%d", "0x"substr($3,7,2), "0x"substr($3,5,2), "0x"substr($3,3,2), "0x"substr($3,1,2); exit}') ssh-keyscan $DOCKER_HOST_IP >> ~/.ssh/known_hosts 2>/dev/null || true SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no root@$DOCKER_HOST_IP" From 6182a79ae42a2a3150966d3ce1abfbe143af83aa Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:38:49 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix(deploy):=20SSH=20=ED=98=B8=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=82=B4=EB=B6=80=20IP=20=EA=B3=A0=EC=A0=95=20(sig?= =?UTF-8?q?nal-batch=20=ED=8C=A8=ED=84=B4)=20(#47)=20Co-authored-by:=20htl?= =?UTF-8?q?ee=20=20Co-committed-by:=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 4aacc0b..da1fc58 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -85,15 +85,14 @@ jobs: - name: Restart backend via SSH env: DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + DEPLOY_HOST: 192.168.1.20 run: | mkdir -p ~/.ssh echo "$DEPLOY_KEY" > ~/.ssh/id_deploy chmod 600 ~/.ssh/id_deploy - # Docker 컨테이너 → 호스트: default gateway (최소 의존) - DOCKER_HOST_IP=$(cat /proc/net/route | awk '$2 == "00000000" {printf "%d.%d.%d.%d", "0x"substr($3,7,2), "0x"substr($3,5,2), "0x"substr($3,3,2), "0x"substr($3,1,2); exit}') - ssh-keyscan $DOCKER_HOST_IP >> ~/.ssh/known_hosts 2>/dev/null || true + ssh-keyscan $DEPLOY_HOST >> ~/.ssh/known_hosts 2>/dev/null || true - SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no root@$DOCKER_HOST_IP" + SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no root@$DEPLOY_HOST" $SSH_CMD bash -s << 'RESTART' set -e From 7b055987149f76434bc37dd3c2bf469ddd53a1a2 Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:42:19 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix(deploy):=20SSH=20=EB=94=94=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20+=20printf=20=ED=82=A4=20=EC=A0=80=EC=9E=A5=20(#48)?= =?UTF-8?q?=20Co-authored-by:=20htlee=20=20Co-committed-?= =?UTF-8?q?by:=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index da1fc58..552118c 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -88,11 +88,14 @@ jobs: DEPLOY_HOST: 192.168.1.20 run: | mkdir -p ~/.ssh - echo "$DEPLOY_KEY" > ~/.ssh/id_deploy + printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_deploy chmod 600 ~/.ssh/id_deploy - ssh-keyscan $DEPLOY_HOST >> ~/.ssh/known_hosts 2>/dev/null || true + ssh-keyscan -T 5 $DEPLOY_HOST >> ~/.ssh/known_hosts 2>/dev/null || true - SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no root@$DEPLOY_HOST" + echo "=== SSH debug: key=$(wc -c < ~/.ssh/id_deploy) bytes, host=$DEPLOY_HOST ===" + ssh -v -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@$DEPLOY_HOST 'echo SSH_OK' 2>&1 | grep -E 'Authenticated|Permission|connect|SSH_OK|exit' || true + + SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@$DEPLOY_HOST" $SSH_CMD bash -s << 'RESTART' set -e From c9f295288c0a018ada38539e97018bc9740f2d20 Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 18 Mar 2026 08:45:33 +0900 Subject: [PATCH 7/7] =?UTF-8?q?fix(deploy):=20SSH=20set=20-e=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20+=20curl=20fallback=20(#49)=20Co-authored-by:=20htl?= =?UTF-8?q?ee=20=20Co-committed-by:=20htlee=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 552118c..c2eb9cd 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -92,39 +92,33 @@ jobs: chmod 600 ~/.ssh/id_deploy ssh-keyscan -T 5 $DEPLOY_HOST >> ~/.ssh/known_hosts 2>/dev/null || true - echo "=== SSH debug: key=$(wc -c < ~/.ssh/id_deploy) bytes, host=$DEPLOY_HOST ===" - ssh -v -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@$DEPLOY_HOST 'echo SSH_OK' 2>&1 | grep -E 'Authenticated|Permission|connect|SSH_OK|exit' || true - SSH_CMD="ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@$DEPLOY_HOST" $SSH_CMD bash -s << 'RESTART' - set -e DEPLOY_DIR=/devdata/services/kcg/backend SYSTEMD_DIR=/etc/systemd/system # systemd 서비스 파일 갱신 - CHANGED=0 if [ -f "$DEPLOY_DIR/kcg-backend.service" ] && ! diff -q "$DEPLOY_DIR/kcg-backend.service" "$SYSTEMD_DIR/kcg-backend.service" >/dev/null 2>&1; then cp "$DEPLOY_DIR/kcg-backend.service" "$SYSTEMD_DIR/kcg-backend.service" - CHANGED=1 + systemctl daemon-reload fi - [ "$CHANGED" = "1" ] && systemctl daemon-reload # 백엔드 재시작 echo "--- Restarting kcg-backend ---" systemctl restart kcg-backend - # 기동 확인 (최대 60초, 401=인증필요=정상 기동) + # 기동 확인 (최대 60초) for i in $(seq 1 60); do - HTTP=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/api/aircraft 2>/dev/null) + HTTP=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/api/aircraft 2>/dev/null || echo "000") if [ "$HTTP" = "200" ] || [ "$HTTP" = "401" ] || [ "$HTTP" = "403" ]; then echo "Backend started successfully (${i}s, HTTP $HTTP)" exit 0 fi sleep 1 done - echo "WARNING: Startup timeout. Recent logs:" - journalctl -u kcg-backend --no-pager -n 20 + echo "WARNING: Startup timeout" + journalctl -u kcg-backend --no-pager -n 10 exit 1 RESTART