Skip to content
Merged

Dev #80

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,29 @@ jobs:
key: ${{ secrets.EC2_PRIVATE_KEY }}
port: 22
script: |
set -eux
set -euo pipefail
# λŒ€μƒ 디렉터리 보μž₯
mkdir -p ${{ secrets.EC2_TARGET_PATH }}

# 멀티라인 .env μ•ˆμ „ μ €μž₯ - μ˜΅μ…˜ A(Heredoc) μ˜ˆμ‹œ
cat > ${{ secrets.EC2_TARGET_PATH }}/.env <<'ENV_EOF'${{ secrets.ENV_FILE }}ENV_EOF
chmod 600 ${{ secrets.EC2_TARGET_PATH }}/.env

# Docker Hub 둜그인
echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
# μ΅œμ‹  이미지 κ°€μ Έμ˜€κΈ°

# λ„€νŠΈμ›Œν¬ μ€€λΉ„
docker network create gitdeun-network || true

# μ΅œμ‹  컀밋 SHA둜 배포(λΆˆλ³€ νƒœκ·Έ)
IMAGE="${{ secrets.DOCKER_USERNAME }}/gitdeun:${{ github.sha }}"
docker pull "$IMAGE"

# κΈ°μ‘΄ μ»¨ν…Œμ΄λ„ˆ 쀑지/μ‚­μ œ
docker stop gitdeun || true
docker rm gitdeun || true

# μ‹€ν–‰
docker run -d \
--name gitdeun \
--restart unless-stopped \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import lombok.*;

import java.util.List;

public class CodeReferenceResponseDtos {

@Getter
Expand All @@ -25,5 +27,6 @@ public static class ReferenceDetailResponse {
private Integer startLine;
private Integer endLine;
private String codeContent; // μ‹€μ œ μ½”λ“œ λ‚΄μš©μ„ 담을 ν•„λ“œ
private List<Long> reviewIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

import com.teamEWSN.gitdeun.codereference.dto.CodeReferenceResponseDtos.*;
import com.teamEWSN.gitdeun.codereference.entity.CodeReference;
import com.teamEWSN.gitdeun.codereview.entity.CodeReview;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.ReportingPolicy;

import java.util.List;
import java.util.stream.Collectors;

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface CodeReferenceMapper {

Expand All @@ -22,6 +26,7 @@ public interface CodeReferenceMapper {
@Mapping(target = "startLine", source = "codeReference.startLine")
@Mapping(target = "endLine", source = "codeReference.endLine")
@Mapping(target = "codeContent", expression = "java(codeContent)")
@Mapping(target = "reviewIds", source = "codeReference.codeReviews", qualifiedByName = "reviewsToIds")
ReferenceDetailResponse toReferenceDetailResponse(CodeReference codeReference, String codeContent);

/**
Expand All @@ -32,4 +37,12 @@ default String extractFileName(String filePath) {
if (filePath == null) return null;
return filePath.substring(filePath.lastIndexOf('/') + 1);
}

@Named("reviewsToIds")
default List<Long> reviewsToIds(List<CodeReview> reviews) {
if (reviews == null) {
return null;
}
return reviews.stream().map(CodeReview::getId).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
import com.teamEWSN.gitdeun.codereference.repository.CodeReferenceRepository;
import com.teamEWSN.gitdeun.common.exception.ErrorCode;
import com.teamEWSN.gitdeun.common.exception.GlobalException;
import com.teamEWSN.gitdeun.common.fastapi.FastApiClient;
import com.teamEWSN.gitdeun.common.fastapi.dto.NodeDto;
import com.teamEWSN.gitdeun.mindmap.dto.MindmapGraphResponseDto;
import com.teamEWSN.gitdeun.mindmap.entity.Mindmap;
import com.teamEWSN.gitdeun.mindmap.repository.MindmapRepository;
import com.teamEWSN.gitdeun.mindmap.util.FileContentCache;
import com.teamEWSN.gitdeun.mindmap.util.MindmapGraphCache;
import com.teamEWSN.gitdeun.mindmapmember.service.MindmapAuthService;
import com.teamEWSN.gitdeun.repo.entity.Repo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -34,7 +34,6 @@ public class CodeReferenceService {
private final CodeReferenceMapper codeReferenceMapper;
private final MindmapGraphCache mindmapGraphCache;
private final FileContentCache fileContentCache;
private final FastApiClient fastApiClient;

@Transactional
public ReferenceResponse createReference(Long mapId, String nodeKey, Long userId, CreateRequest request, String authorizationHeader) throws GlobalException {
Expand Down Expand Up @@ -72,10 +71,18 @@ public ReferenceDetailResponse getReferenceDetail(Long mapId, Long refId, Long u
CodeReference codeReference = codeReferenceRepository.findByMindmapIdAndId(mapId, refId)
.orElseThrow(() -> new GlobalException(ErrorCode.CODE_REFERENCE_NOT_FOUND));

Mindmap mindmap = codeReference.getMindmap();
Repo repo = codeReference.getMindmap().getRepo();
String repoUrl = repo.getGithubRepoUrl();
LocalDateTime lastCommit = repo.getLastCommit();

// FastAPIλ₯Ό 톡해 전체 파일 λ‚΄μš© κ°€μ Έμ˜€κΈ°
String fullContent = fastApiClient.getFileRaw(mindmap.getRepo().getGithubRepoUrl(), codeReference.getFilePath(), authorizationHeader);
// FastAPIλ₯Ό 톡해 전체 파일 λ‚΄μš© κ°€μ Έμ˜€κΈ° (μˆ˜μ •λœ λ©”μ„œλ“œ 호좜)
String fullContent = fileContentCache.getFileContentWithCacheFromNode(
repoUrl,
codeReference.getNodeKey(),
codeReference.getFilePath(),
lastCommit,
authorizationHeader
);

// νŠΉμ • 라인만 μΆ”μΆœ (snippet)
String snippet = extractLines(fullContent, codeReference.getStartLine(), codeReference.getEndLine());
Expand Down Expand Up @@ -166,7 +173,13 @@ private void validateCodeReferenceRequest(Mindmap mindmap, String nodeKey, Creat
}

// 파일의 전체 λ‚΄μš©μ„ 가져와 총 라인 수 계산
String fileContent = fileContentCache.getFileContentWithCache(repoUrl, request.getFilePath(), lastCommit, authorizationHeader);
String fileContent = fileContentCache.getFileContentWithCacheFromNode(
repoUrl,
nodeKey,
request.getFilePath(),
lastCommit,
authorizationHeader
);
int totalLines = fileContent.split("\\r?\\n").length;

// μš”μ²­λœ 라인 λ²”μœ„(startLine, endLine)κ°€ μœ νš¨ν•œμ§€ 확인
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,53 @@ public String getFileRaw(String repoUrl, String filePath, Integer startLine, Int
}
}

public String getFileRawFromNode(String nodeKey, String filePath, String authHeader) {
try {
log.debug("FastAPI 파일 λ‚΄μš© 쑰회 μ‹œμž‘ - nodeKey: {}, filePath: {}", nodeKey, filePath);

// URI 생성 (쿼리 νŒŒλΌλ―Έν„° 포함)
UriComponentsBuilder uriBuilder = UriComponentsBuilder
.fromPath("/content/file/nodes")
.queryParam("node_key", nodeKey)
.queryParam("file_path", filePath);

String uri = uriBuilder.build().toUriString();

String response = webClient.get()
.uri(uri)
.headers(headers -> {
if (authHeader != null && !authHeader.trim().isEmpty()) {
headers.set("Authorization", authHeader);
}
headers.set("Accept", "text/plain");
})
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, clientResponse -> {
log.warn("FastAPI 파일 쑰회 4xx 였λ₯˜ - nodeKey: {}, filePath: {}, status: {}",
nodeKey, filePath, clientResponse.statusCode());
return clientResponse.bodyToMono(String.class)
.map(errorBody -> new RuntimeException("νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: " + errorBody));
})
.onStatus(HttpStatusCode::is5xxServerError, serverResponse -> {
log.error("FastAPI 파일 쑰회 5xx 였λ₯˜ - nodeKey: {}, filePath: {}, status: {}",
nodeKey, filePath, serverResponse.statusCode());
return Mono.error(new RuntimeException("FastAPI μ„œλ²„ 였λ₯˜"));
})
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(30))
.block();

log.debug("FastAPI 파일 λ‚΄μš© 쑰회 μ™„λ£Œ - nodeKey: {}, filePath: {}, 길이: {}",
nodeKey, filePath, response != null ? response.length() : 0);

return response != null ? response : "";

} catch (Exception e) {
log.error("FastAPI 파일 λ‚΄μš© 쑰회 μ‹€νŒ¨ - nodeKey: {}, filePath: {}", nodeKey, filePath, e);
return ""; // 빈 λ¬Έμžμ—΄ λ°˜ν™˜ (null λŒ€μ‹ )
}
}

// === Helper Methods ===

// μ €μž₯μ†Œλͺ… μΆ”μΆœ
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ public void promptMindmap(Long mapId, String prompt, Long userId, String authHea

String repoUrl = mindmap.getRepo().getGithubRepoUrl();

// FastAPI 뢄석 μš”μ²­ μ „, κ΄€λ ¨λœ λͺ¨λ“  파일 μΊμ‹œλ₯Ό λ¬΄νš¨ν™”
fileContentCache.evictFileCacheForRepo(repoUrl);

// FastAPI에 μžλ™ 뢄석 μš”μ²­
SuggestionAutoResponse suggestionResponse = fastApiClient.createAutoSuggestions(repoUrl, prompt, authHeader);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public NodeCodeResponseDto getNodeDetailsWithCode(Long mapId, String nodeKey, Lo
Map<RelatedFileDto, String> codeContentsMap = filePaths.parallelStream()
.collect(Collectors.toConcurrentMap(
filePath -> filePath,
filePath -> fileContentCache.getFileContentWithCache(repoUrl, filePath.getFilePath(), lastCommit, authorizationHeader)
filePath -> fileContentCache.getFileContentWithCacheFromNode(repoUrl, nodeKey, filePath.getFilePath(), lastCommit, authorizationHeader)
));

// 4. Map을 List<FileWithCodeDto> ν˜•νƒœλ‘œ λ³€ν™˜
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ public void evictL1Cache() {
}
}

public String getFileContentWithCache(String repoUrl, String filePath, LocalDateTime lastCommit, String authHeader) {
String cacheKey = "file-content:" + repoUrl + ":" + filePath + ":" + lastCommit.toString();

public String getFileContentWithCacheFromNode(String repoUrl, String nodeKey, String filePath, LocalDateTime lastCommit, String authHeader) {
// μΊμ‹œ 킀에 repoUrl을 ν¬ν•¨μ‹œμΌœ μ–΄λ–€ λ¦¬ν¬μ§€ν† λ¦¬μ˜ μΊμ‹œμΈμ§€ λͺ…ν™•νžˆ ν•©λ‹ˆλ‹€.
String cacheKey = "file-content:" + repoUrl + ":node:" + nodeKey + ":" + filePath + ":" + lastCommit.toString();

// 1. L1 μΊμ‹œ 확인
String content = l1Cache.getFromL1Cache(cacheKey);
Expand All @@ -67,7 +69,7 @@ public String getFileContentWithCache(String repoUrl, String filePath, LocalDate
}

// 3. FastAPI μ‹€μ‹œκ°„ 쑰회
content = fastApiClient.getFileRaw(repoUrl, filePath, authHeader);
content = fastApiClient.getFileRawFromNode(nodeKey, filePath, authHeader);

// 4. L1, L2 μΊμ‹œμ— μ €μž₯
if (content != null) {
Expand All @@ -81,20 +83,19 @@ public String getFileContentWithCache(String repoUrl, String filePath, LocalDate
return content;
}


// μΊμ‹œ λ¬΄νš¨ν™”
public void evictFileCacheForRepo(String repoUrl) {
// L1 μΊμ‹œλŠ” 전체 μ‚­μ œ
l1Cache.evictL1Cache();
deleteRedisKeysByPattern("file-content:repo:" + repoUrl + ":*");
}

// L2 μΊμ‹œλŠ” νŒ¨ν„΄μœΌλ‘œ κ²€μƒ‰ν•˜μ—¬ μ‚­μ œ
// L2 μΊμ‹œ(Redis)λŠ” SCAN을 μ‚¬μš©ν•˜μ—¬ μ•ˆμ „ν•˜κ²Œ μ‚­μ œ
String pattern = "file-content:" + repoUrl + ":*";
private void deleteRedisKeysByPattern(String pattern) {
try {
Set<String> keysToDelete = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keys = new HashSet<>();

try (Cursor<byte[]> cursor = connection.keyCommands().scan(ScanOptions.scanOptions().match(pattern).count(100).build())) {

while (cursor.hasNext()) {
keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
}
Expand Down