diff --git a/src/main/java/com/hlab/yanalyst/web/CommentCardApiController.java b/src/main/java/com/hlab/yanalyst/web/CommentCardApiController.java new file mode 100644 index 0000000..444c1ea --- /dev/null +++ b/src/main/java/com/hlab/yanalyst/web/CommentCardApiController.java @@ -0,0 +1,65 @@ +package com.hlab.yanalyst.web; + +import com.hlab.yanalyst.global.common.ApiResponse; +import com.hlab.yanalyst.service.YoutubeCommentService; +import com.hlab.yanalyst.web.dto.CommentCardDto; +import lombok.Data; +import lombok.RequiredArgsConstructor; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +import java.time.Duration; +import java.util.List; + +@RestController +@RequestMapping("/api/comment-cards") +@RequiredArgsConstructor +public class CommentCardApiController { + + private final YoutubeCommentService commentService; + private final RestTemplate restTemplate; + + @PostMapping("/fetch") + public ApiResponse> fetch(@RequestBody FetchRequest req) { + return ApiResponse.ok(commentService.fetchComments(req.getUrl())); + } + + /** 프로필 이미지를 동일 출처로 프록시(canvas taint 회피). 구글 도메인만 허용(SSRF 방지). */ + @GetMapping("/avatar") + public ResponseEntity avatar(@RequestParam("url") String url) { + if (!isAllowed(url)) { + return ResponseEntity.badRequest().build(); + } + ResponseEntity resp = restTemplate.getForEntity(URI.create(url), byte[].class); + MediaType ct = resp.getHeaders().getContentType(); + return ResponseEntity.ok() + .contentType(ct != null ? ct : MediaType.IMAGE_JPEG) + .cacheControl(CacheControl.maxAge(Duration.ofHours(6)).cachePublic()) + .body(resp.getBody()); + } + + private boolean isAllowed(String url) { + try { + String host = URI.create(url).getHost(); + if (host == null) return false; + host = host.toLowerCase(); + return host.endsWith("ggpht.com") || host.endsWith("googleusercontent.com"); + } catch (Exception e) { + return false; + } + } + + @Data + public static class FetchRequest { + private String url; + } +}