Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/dev-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ jobs:
echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin

cd /home/ubuntu
docker compose -f clokey-api/dev-compose.yml up -d
docker compose -p clokey-dev -f clokey-api/dev-compose.yml up -d

echo "Cleaning up dangling Docker images..."
docker image prune -f
2 changes: 1 addition & 1 deletion .github/workflows/prod-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ jobs:
echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin

cd /home/ubuntu
docker compose -f clokey-api/prod-compose.yml up -d
docker compose -p clokey-prod -f clokey-api/prod-compose.yml up -d

echo "Cleaning up dangling Docker images..."
docker image prune -f
4 changes: 2 additions & 2 deletions clokey-api/dev-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
name: clokey-dev

version: "3.8"

services:
Expand Down Expand Up @@ -46,12 +48,10 @@ services:
# Kakao OIDC
KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID}
KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET}
KAKAO_REDIRECT_URI: ${KAKAO_REDIRECT_URI}

# Apple OIDC
APPLE_CLIENT_ID: ${APPLE_CLIENT_ID}
APPLE_CLIENT_SECRET: ${APPLE_CLIENT_SECRET}
APPLE_REDIRECT_URI: ${APPLE_REDIRECT_URI}

# Firebase
FIREBASE_CREDENTIALS_PATH: /run/secrets/firebase-sa.json
Expand Down
4 changes: 2 additions & 2 deletions clokey-api/prod-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
name: clokey-prod

version: "3.8"

services:
Expand Down Expand Up @@ -46,12 +48,10 @@ services:
# Kakao OIDC
KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID}
KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET}
KAKAO_REDIRECT_URI: ${KAKAO_REDIRECT_URI}

# Apple OIDC
APPLE_CLIENT_ID: ${APPLE_CLIENT_ID}
APPLE_CLIENT_SECRET: ${APPLE_CLIENT_SECRET}
APPLE_REDIRECT_URI: ${APPLE_REDIRECT_URI}

# Firebase
FIREBASE_CREDENTIALS_PATH: /run/secrets/firebase-sa.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import java.util.List;

public record ClothImagesPresignedUrlResponse(
@Schema(description = "생성된 presigned url 리스트") List<String> urls) {
public static ClothImagesPresignedUrlResponse of(List<String> urls) {
return new ClothImagesPresignedUrlResponse(urls);
@Schema(description = "업로드용 presigned url 리스트") List<String> urls,
@Schema(description = "저장/조회용 공개 객체 url 리스트") List<String> objectUrls) {
public static ClothImagesPresignedUrlResponse of(List<String> urls, List<String> objectUrls) {
return new ClothImagesPresignedUrlResponse(urls, objectUrls);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.clokey.global.util.MemberUtil;
import org.clokey.member.entity.Member;
import org.clokey.properties.WebClientProperties;
import org.clokey.util.PresignedUrlResult;
import org.clokey.util.StorageUtil;
import org.clokey.util.WebClientUtil;
import org.springframework.stereotype.Service;
Expand All @@ -61,19 +62,19 @@ public ClothImagesPresignedUrlResponse getClothUploadPresignedUrls(
ClothImagesUploadRequest request) {
final Member currentMember = memberUtil.getCurrentMember();

// 중요 : md5 해시로 변조 확인을 하기 때문에 들어온 순서대로 반환해야함!!
List<String> presignedUrls =
List<PresignedUrlResult> presignedUrlResults =
request.payloads().stream()
.map(
req ->
storageUtil.createPresignedUrl(
ImageType.CLOTH_IMAGE,
currentMember.getId(),
req.fileExtension(),
req.md5Hashes()))
req.fileExtension()))
.toList();

return ClothImagesPresignedUrlResponse.of(presignedUrls);
return ClothImagesPresignedUrlResponse.of(
presignedUrlResults.stream().map(PresignedUrlResult::uploadUrl).toList(),
presignedUrlResults.stream().map(PresignedUrlResult::objectUrl).toList());
}

@Override
Expand Down Expand Up @@ -150,7 +151,7 @@ public ClothInfoExtractResponse extractClothInfo(ClothInfoExtractRequest request

for (int i = 0; i < resultItems.size(); i++) {
ClothInfoExtractAiResponseDTO.ResultItem resultItem = resultItems.get(i);
String clothImageUrl = resultItem.uploadedUrl();
String clothImageUrl = storageUtil.toPublicObjectUrl(resultItem.uploadedUrl());

List<ClothInfoExtractAiResponseDTO.CategoryItem> categories =
resultItem.categories();
Expand Down Expand Up @@ -346,7 +347,10 @@ public ClothDetectResponse detectClothes(ClothDetectRequest request) {
phaseStartedAtNs = System.nanoTime();
List<ClothDetectResponse.Payload> payloads =
aiResponse.result().uploadedUrls().stream()
.map(ClothDetectResponse.Payload::new)
.map(
url ->
new ClothDetectResponse.Payload(
storageUtil.toPublicObjectUrl(url)))
.toList();
postProcessMs = elapsedMillis(phaseStartedAtNs);

Expand Down Expand Up @@ -403,8 +407,10 @@ private List<String> createPresignedUrls(Long memberId, int count) {
return java.util.stream.IntStream.range(0, count)
.mapToObj(
i ->
storageUtil.createPresignedUrlWithoutMd5(
ImageType.CLOTH_IMAGE, memberId, FileExtension.JPEG))
storageUtil
.createPresignedUrl(
ImageType.CLOTH_IMAGE, memberId, FileExtension.JPEG)
.uploadUrl())
.toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class ClothServiceImpl implements ClothService {
private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final MemberUtil memberUtil;
private final StorageUtil storageUtil;

private final ClothRepository clothRepository;
private final CategoryRepository categoryRepository;
Expand All @@ -52,7 +53,6 @@ public class ClothServiceImpl implements ClothService {

private final ApplicationEventPublisher eventPublisher;
private final CoordinateClothRepository coordinateClothRepository;
private final StorageUtil storageUtil;

@Override
@Transactional
Expand All @@ -73,7 +73,7 @@ public ClothCreateResponse createClothes(ClothCreateRequests request) {
cr -> {
Category category = categoryMap.get(cr.categoryId());
return Cloth.createCloth(
cr.clothImageUrl(),
storageUtil.toPublicObjectUrl(cr.clothImageUrl()),
cr.clothUrl(),
cr.name(),
cr.brand(),
Expand Down Expand Up @@ -168,15 +168,16 @@ public void updateCloth(Long clothId, ClothUpdateRequest request) {
validateChildCategory(category);

// 사진이 바뀌는 경우 기존 imageUrl을 기반으로 S3에서 삭제합니다.
if (!cloth.getClothImageUrl().equals(request.clothImageUrl())) {
String normalizedImageUrl = storageUtil.toPublicObjectUrl(request.clothImageUrl());
if (!storageUtil.toPublicObjectUrl(cloth.getClothImageUrl()).equals(normalizedImageUrl)) {
eventPublisher.publishEvent(ImageDeleteEvent.of(cloth.getClothImageUrl()));
}

// Category 변경 여부 확인
boolean categoryChanged = !cloth.getCategory().getId().equals(category.getId());

cloth.updateCloth(
request.clothImageUrl(),
normalizedImageUrl,
request.clothUrl(),
request.name(),
request.brand(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.clokey.member.entity.Member;
import org.clokey.member.enums.Visibility;
import org.clokey.response.SliceResponse;
import org.clokey.util.StorageUtil;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Slice;
import org.springframework.data.redis.core.RedisTemplate;
Expand All @@ -61,6 +62,7 @@ public class CoordinateServiceImpl implements CoordinateService {
private final RedisTemplate<String, String> redisTemplate;

private final ApplicationEventPublisher eventPublisher;
private final StorageUtil storageUtil;

@Override
@Transactional
Expand Down Expand Up @@ -97,7 +99,8 @@ public CoordinateCreateResponse createDailyCoordinate(DailyCoordinateCreateReque
validateDailyCoordinateExist(currentMember.getId(), LocalDate.now(KST));

Coordinate coordinate =
Coordinate.createDailyCoordinate(request.coordinateImageUrl(), currentMember);
Coordinate.createDailyCoordinate(
storageUtil.toPublicObjectUrl(request.coordinateImageUrl()), currentMember);
coordinateRepository.save(coordinate);

List<CoordinateCloth> coordinateClothes =
Expand Down Expand Up @@ -160,7 +163,7 @@ public CoordinateCreateResponse createCoordinateManual(CoordinateManualCreateReq
Coordinate.createCoordinateManual(
request.name(),
request.memo(),
request.coordinateImageUrl(),
storageUtil.toPublicObjectUrl(request.coordinateImageUrl()),
currentMember,
lookBook);
coordinateRepository.save(coordinate);
Expand Down Expand Up @@ -245,10 +248,11 @@ public void updateCoordinate(Long coordinateId, CoordinateUpdateRequest request)
validateAllClothesOwnership(currentMember, clothes);

/** Coordinate 업데이트 로직 */
if (!Objects.equals(coordinate.getImageUrl(), request.coordinateImageUrl())) {
String normalizedImageUrl = storageUtil.toPublicObjectUrl(request.coordinateImageUrl());
if (!storageUtil.toPublicObjectUrl(coordinate.getImageUrl()).equals(normalizedImageUrl)) {
eventPublisher.publishEvent(ImageDeleteEvent.of(coordinate.getImageUrl()));
}
coordinate.updateCoordinate(request.name(), request.memo(), request.coordinateImageUrl());
coordinate.updateCoordinate(request.name(), request.memo(), normalizedImageUrl);

/** CoordinateCloth 업데이트 로직 */
List<CoordinateCloth> coordinateCloths = coordinate.getCoordinateClothes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import java.util.List;

public record HistoryImagesPresignedUrlResponse(
@Schema(description = "생성된 presigned url 리스트") List<String> urls) {
public static HistoryImagesPresignedUrlResponse of(List<String> urls) {
return new HistoryImagesPresignedUrlResponse(urls);
@Schema(description = "업로드용 presigned url 리스트") List<String> urls,
@Schema(description = "저장/조회용 공개 객체 url 리스트") List<String> objectUrls) {
public static HistoryImagesPresignedUrlResponse of(List<String> urls, List<String> objectUrls) {
return new HistoryImagesPresignedUrlResponse(urls, objectUrls);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.clokey.member.entity.Member;
import org.clokey.member.enums.Visibility;
import org.clokey.report.enums.TargetType;
import org.clokey.util.PresignedUrlResult;
import org.clokey.util.StorageUtil;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -105,7 +106,8 @@ public HistoryCreateResponse createHistory(HistoryCreateRequest request) {

for (HistoryCreateRequest.Payload payload : request.payloads()) {
final HistoryImage historyImage =
HistoryImage.createHistoryImage(payload.imageUrl(), history);
HistoryImage.createHistoryImage(
storageUtil.toPublicObjectUrl(payload.imageUrl()), history);
images.add(historyImage);

if (payload.clothTags() != null && !payload.clothTags().isEmpty()) {
Expand Down Expand Up @@ -164,18 +166,21 @@ public void updateHistory(Long historyId, HistoryUpdateRequest request) {
existingImages.stream()
.collect(
Collectors.toMap(
HistoryImage::getImageUrl,
image -> storageUtil.toPublicObjectUrl(image.getImageUrl()),
Function.identity(),
(left, right) -> left));

Set<String> requestedImageUrls =
request.payloads().stream()
.map(HistoryUpdateRequest.Payload::imageUrl)
.map(payload -> storageUtil.toPublicObjectUrl(payload.imageUrl()))
.collect(Collectors.toSet());

List<HistoryImage> imagesToDelete =
existingImages.stream()
.filter(image -> !requestedImageUrls.contains(image.getImageUrl()))
.filter(
image ->
!requestedImageUrls.contains(
storageUtil.toPublicObjectUrl(image.getImageUrl())))
.toList();

if (!imagesToDelete.isEmpty()) {
Expand All @@ -186,7 +191,10 @@ public void updateHistory(Long historyId, HistoryUpdateRequest request) {

Set<Long> keptImageIds =
existingImages.stream()
.filter(image -> requestedImageUrls.contains(image.getImageUrl()))
.filter(
image ->
requestedImageUrls.contains(
storageUtil.toPublicObjectUrl(image.getImageUrl())))
.map(HistoryImage::getId)
.collect(Collectors.toSet());

Expand All @@ -198,12 +206,13 @@ public void updateHistory(Long historyId, HistoryUpdateRequest request) {
List<HistoryClothTag> clothTags = new ArrayList<>();

for (HistoryUpdateRequest.Payload payload : request.payloads()) {
String normalizedImageUrl = storageUtil.toPublicObjectUrl(payload.imageUrl());
final HistoryImage historyImage =
existingImageMap.getOrDefault(
payload.imageUrl(),
HistoryImage.createHistoryImage(payload.imageUrl(), history));
normalizedImageUrl,
HistoryImage.createHistoryImage(normalizedImageUrl, history));

if (!existingImageMap.containsKey(payload.imageUrl())) {
if (!existingImageMap.containsKey(normalizedImageUrl)) {
newImages.add(historyImage);
}

Expand Down Expand Up @@ -402,18 +411,19 @@ public HistoryImagesPresignedUrlResponse getHistoryUploadPresignedUrls(
HistoryImagesUploadRequest request) {
final Member currentMember = memberUtil.getCurrentMember();

List<String> presignedUrls =
List<PresignedUrlResult> presignedUrlResults =
request.payloads().stream()
.map(
payload ->
storageUtil.createPresignedUrl(
ImageType.HISTORY_IMAGE,
currentMember.getId(),
payload.fileExtension(),
payload.md5Hashes()))
payload.fileExtension()))
.toList();

return HistoryImagesPresignedUrlResponse.of(presignedUrls);
return HistoryImagesPresignedUrlResponse.of(
presignedUrlResults.stream().map(PresignedUrlResult::uploadUrl).toList(),
presignedUrlResults.stream().map(PresignedUrlResult::objectUrl).toList());
}

private HistoryImage getHistoryImageById(Long historyImageId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.clokey.member.enums.MemberStatus;
import org.clokey.member.enums.Visibility;
import org.clokey.response.SliceResponse;
import org.clokey.util.StorageUtil;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -44,6 +45,7 @@ public class MemberServiceImpl implements MemberService {
private final MemberLikeRepository memberLikeRepository;

private final ApplicationEventPublisher eventPublisher;
private final StorageUtil storageUtil;

@Override
@Transactional
Expand All @@ -55,8 +57,10 @@ public void updateProfile(ProfileUpdateRequest request) {

// s3 삭제 로직 구현 이후에 반영 필요 -> 배경 및 프로필 이미지를 없애버리는 경우

String normalizedProfileImageUrl = storageUtil.toPublicObjectUrl(request.profileImageUrl());

currentMember.updateProfile(
request.nickname(), request.profileImageUrl(), request.bio(), request.visibility());
request.nickname(), normalizedProfileImageUrl, request.bio(), request.visibility());

// Member 동기화
eventPublisher.publishEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class CodiveNotificationServiceImpl implements CodiveNotificationService
private final ApplicationEventPublisher eventPublisher;

private final MemberUtil memberUtil;
private final StorageUtil storageUtil;

private static final String NEW_FOLLOWER_NOTIFICATION = "%s님이 회원님의 옷장을 팔로우하기 시작했습니다.";
private static final String NEW_PENDING_FOLLOWER_NOTIFICATION = "%s님이 회원님의 옷장에 팔로우를 요청했습니다.";
Expand Down Expand Up @@ -256,6 +257,7 @@ public void sendNewTemperatureNotification(TemperatureNotificationRequest reques

if (isAbleToSendNotification(receiver)) {
Notification notification = createPushNotification(content, temperatureImageUrl);

Message message =
Message.builder()
.setToken(receiver.getDeviceToken())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public class TestObjectStorageConfig {
@Bean
@ConditionalOnMissingBean(ObjectStorageClient.class)
public ObjectStorageClient objectStorageClient() {
return org.mockito.Mockito.mock(ObjectStorageClient.class);
ObjectStorageClient client = org.mockito.Mockito.mock(ObjectStorageClient.class);
org.mockito.Mockito.when(client.getEndpoint())
.thenReturn("https://objectstorage.test.oraclecloud.com");
return client;
}
}
Loading
Loading