package kr.itn.itnhub.feed; import kr.itn.itnhub.mattermost.MattermostClient; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; /** * 기관관리 상세의 게시물/자료 탭이 부르는 읽기 전용 엔드포인트. * 브라우저는 Mattermost를 절대 직접 호출하지 않고 전부 이 컨트롤러를 거친다. */ @RestController public class ChannelFeedController { private final ChannelFeedService feedService; private final MattermostClient mattermost; public ChannelFeedController(ChannelFeedService feedService, MattermostClient mattermost) { this.feedService = feedService; this.mattermost = mattermost; } @GetMapping("/api/orgs/{id}/posts") public List posts(@PathVariable Long id, @RequestParam String channel) { return feedService.posts(id, channel); } @GetMapping("/api/orgs/{id}/files") public List files(@PathVariable Long id, @RequestParam String channel) { return feedService.files(id, channel); } /** * 한글 파일명이 대부분이라 {@code Content-Disposition}은 반드시 RFC 5987의 * {@code filename*=UTF-8''...} 형식으로 인코딩해야 한다. 일반 {@code filename="..."} * 만 쓰면 브라우저마다 한글이 깨진다. */ @GetMapping("/api/files/{fileId}") public ResponseEntity download(@PathVariable String fileId) { FileRef info = mattermost.fileInfo(fileId); byte[] content = mattermost.downloadFile(fileId); String encodedName = URLEncoder.encode(info.name(), StandardCharsets.UTF_8) .replace("+", "%20"); MediaType contentType = resolveContentType(info.mimeType()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedName) .contentType(contentType) .body(content); } private MediaType resolveContentType(String mimeType) { if (mimeType == null || mimeType.isBlank()) { return MediaType.APPLICATION_OCTET_STREAM; } try { return MediaType.parseMediaType(mimeType); } catch (org.springframework.util.InvalidMimeTypeException e) { return MediaType.APPLICATION_OCTET_STREAM; } } }