gc-guide-api/src/main/java/com/gcsc/guide/controller/WingDataController.java
htlee 69de3f9ae7 feat(wing): Wing 데모 사이트 프록시 API + 복수 Google Client ID 지원
- WingAisController: AIS 선박 위치 조회 프록시 (bbox 필터링 포함)
- WingDataController: 해역/케이블 정적 GeoJSON 데이터 서빙
- GoogleTokenVerifier: app.google.client-ids 복수 audience 지원
- wing-data/: zones, chinese-permitted GeoJSON 데이터 파일

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:34:58 +09:00

58 lines
2.3 KiB
Java

package com.gcsc.guide.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.util.concurrent.TimeUnit;
import static org.springframework.http.HttpStatus.NOT_FOUND;
@RestController
@RequestMapping("/api/wing/data")
@RequiredArgsConstructor
@Tag(name = "WING · Data", description = "WING embedded datasets (JWT required)")
public class WingDataController {
@GetMapping(value = "/zones", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Resource> zones() {
return serveJson("wing-data/zones.wgs84.geojson");
}
@GetMapping(value = "/legacy", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Resource> legacyChinesePermitted() {
return serveJson("wing-data/chinese-permitted.v1.json");
}
@GetMapping(value = "/subcables/geo", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Resource> subcablesGeo() {
return serveJson("wing-data/subcables/cable-geo.json");
}
@GetMapping(value = "/subcables/details", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Resource> subcablesDetails() {
return serveJson("wing-data/subcables/cable-details.min.json");
}
private ResponseEntity<Resource> serveJson(String classpathLocation) {
Resource resource = new ClassPathResource(classpathLocation);
if (!resource.exists()) {
throw new ResponseStatusException(NOT_FOUND, "Resource not found: " + classpathLocation);
}
return ResponseEntity.ok()
// Authenticated endpoint: allow browser caching but keep it private.
.cacheControl(CacheControl.maxAge(6, TimeUnit.HOURS).cachePrivate())
.contentType(MediaType.APPLICATION_JSON)
.body(resource);
}
}