feat(comment-cards): 댓글 fetch + 프로필 프록시 API 추가
POST /api/comment-cards/fetch, GET /api/comment-cards/avatar(구글 도메인 화이트리스트) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
15a655d7f9
commit
0e84f5de08
@ -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<List<CommentCardDto>> fetch(@RequestBody FetchRequest req) {
|
||||
return ApiResponse.ok(commentService.fetchComments(req.getUrl()));
|
||||
}
|
||||
|
||||
/** 프로필 이미지를 동일 출처로 프록시(canvas taint 회피). 구글 도메인만 허용(SSRF 방지). */
|
||||
@GetMapping("/avatar")
|
||||
public ResponseEntity<byte[]> avatar(@RequestParam("url") String url) {
|
||||
if (!isAllowed(url)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
ResponseEntity<byte[]> 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user