From c4348a35bb2da82eed5f72a40248c8c861d43d8e Mon Sep 17 00:00:00 2001 From: mikekks Date: Sat, 24 Aug 2024 15:07:54 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20yml=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/src/main/resources/application-dev.yml | 5 +++++ main/src/main/resources/application-prod.yml | 5 +++++ main/src/main/resources/application-test.yml | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/main/src/main/resources/application-dev.yml b/main/src/main/resources/application-dev.yml index 80bbca3a..b640100a 100644 --- a/main/src/main/resources/application-dev.yml +++ b/main/src/main/resources/application-dev.yml @@ -34,6 +34,11 @@ aws-property: s3-bucket-name: ${AWS_S3_BUCKET_NAME} access-key: ${AWS_ACCESS_KEY_ID} secret-key: ${AWS_SECRET_ACCESS_KEY} + file-min-size: ${AWS_FILE_MIN_SIZE} + file-max-size: ${AWS_FILE_MAX_SIZE} + algorithm: ${AWS_ALGORITHM} + content-type: ${AWS_CONTENT_TYPE} + request-type: ${AWS_REQUEST_TYPE} springdoc: packages-to-scan: org.sopt.makers.crew diff --git a/main/src/main/resources/application-prod.yml b/main/src/main/resources/application-prod.yml index b4316fd5..0e2bfed6 100644 --- a/main/src/main/resources/application-prod.yml +++ b/main/src/main/resources/application-prod.yml @@ -33,6 +33,11 @@ aws-property: s3-bucket-name: ${AWS_S3_BUCKET_NAME} access-key: ${AWS_ACCESS_KEY_ID} secret-key: ${AWS_SECRET_ACCESS_KEY} + file-min-size: ${AWS_FILE_MIN_SIZE} + file-max-size: ${AWS_FILE_MAX_SIZE} + algorithm: ${AWS_ALGORITHM} + content-type: ${AWS_CONTENT_TYPE} + request-type: ${AWS_REQUEST_TYPE} springdoc: packages-to-scan: org.sopt.makers.crew diff --git a/main/src/main/resources/application-test.yml b/main/src/main/resources/application-test.yml index f6d1649a..6b6889db 100644 --- a/main/src/main/resources/application-test.yml +++ b/main/src/main/resources/application-test.yml @@ -37,6 +37,11 @@ aws-property: s3-bucket-name: ${AWS_S3_BUCKET_NAME} access-key: ${AWS_ACCESS_KEY_ID} secret-key: ${AWS_SECRET_ACCESS_KEY} + file-min-size: ${AWS_FILE_MIN_SIZE} + file-max-size: ${AWS_FILE_MAX_SIZE} + algorithm: ${AWS_ALGORITHM} + content-type: ${AWS_CONTENT_TYPE} + request-type: ${AWS_REQUEST_TYPE} springdoc: packages-to-scan: org.sopt.makers.crew From 562472fac1b5ee430b18309804c28d38860faa25 Mon Sep 17 00:00:00 2001 From: mikekks Date: Sat, 24 Aug 2024 15:11:48 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20pre-signed-url=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/build.gradle | 3 + .../main/common/exception/ErrorStatus.java | 1 + .../external/s3/config/AwsProperties.java | 21 ++ .../s3/config/AwsPropertiesConfiguration.java | 9 + .../main/external/s3/config/S3Config.java | 44 ++++ .../main/external/s3/service/S3Service.java | 194 ++++++++++++++++++ .../crew/main/meeting/v2/MeetingV2Api.java | 15 ++ .../main/meeting/v2/MeetingV2Controller.java | 28 +++ .../PreSignedUrlFieldResponseDto.java | 43 ++++ .../dto/response/PreSignedUrlResponseDto.java | 16 ++ 10 files changed, 374 insertions(+) create mode 100644 main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsProperties.java create mode 100644 main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsPropertiesConfiguration.java create mode 100644 main/src/main/java/org/sopt/makers/crew/main/external/s3/config/S3Config.java create mode 100644 main/src/main/java/org/sopt/makers/crew/main/external/s3/service/S3Service.java create mode 100644 main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlFieldResponseDto.java create mode 100644 main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlResponseDto.java diff --git a/main/build.gradle b/main/build.gradle index b2d70c5b..7818e2f2 100644 --- a/main/build.gradle +++ b/main/build.gradle @@ -71,6 +71,9 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + // AWS SDK for S3 + implementation "software.amazon.awssdk:s3:2.27.0" } tasks.named('test') { diff --git a/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java b/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java index 4c67a8cd..541508d1 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java +++ b/main/src/main/java/org/sopt/makers/crew/main/common/exception/ErrorStatus.java @@ -45,6 +45,7 @@ public enum ErrorStatus { * 500 SERVER_ERROR */ NOTIFICATION_SERVER_ERROR("알림 서버에 에러가 발생했습니다."), + S3_STORAGE_ERROR("s3 스토리지에 에러가 발생했습니다."), INTERNAL_SERVER_ERROR("예상치 못한 서버 에러가 발생했습니다."); private final String errorCode; diff --git a/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsProperties.java b/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsProperties.java new file mode 100644 index 00000000..57b640aa --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsProperties.java @@ -0,0 +1,21 @@ +package org.sopt.makers.crew.main.external.s3.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +@ConfigurationProperties("aws-property") +public final class AwsProperties { + private final String awsRegion; + private final String s3BucketName; + private final String accessKey; + private final String secretKey; + private final long fileMinSize; + private final long fileMaxSize; + private final String algorithm; + private final String contentType; + private final String requestType; +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsPropertiesConfiguration.java b/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsPropertiesConfiguration.java new file mode 100644 index 00000000..72805a8f --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/AwsPropertiesConfiguration.java @@ -0,0 +1,9 @@ +package org.sopt.makers.crew.main.external.s3.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(value = {AwsProperties.class}) +public class AwsPropertiesConfiguration { +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/S3Config.java b/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/S3Config.java new file mode 100644 index 00000000..dd06a63f --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/external/s3/config/S3Config.java @@ -0,0 +1,44 @@ +package org.sopt.makers.crew.main.external.s3.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import lombok.RequiredArgsConstructor; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; + +@Configuration +@RequiredArgsConstructor +public class S3Config { + + private final AwsProperties awsProperties; + + @Bean + public S3Client s3Client() { + AwsBasicCredentials awsBasicCredentials = getAwsBasicCredentials(); + + return S3Client.builder() + .region(Region.of(awsProperties.getAwsRegion())) + .credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials)) + .build(); + } + + @Bean + public S3Presigner s3Presigner() { + AwsBasicCredentials awsBasicCredentials = getAwsBasicCredentials(); + + return S3Presigner.builder() + .region(Region.of(awsProperties.getAwsRegion())) + .credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials)) + .build(); + } + + private AwsBasicCredentials getAwsBasicCredentials() { + return AwsBasicCredentials.create( + awsProperties.getAccessKey(), + awsProperties.getSecretKey()); + } +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/external/s3/service/S3Service.java b/main/src/main/java/org/sopt/makers/crew/main/external/s3/service/S3Service.java new file mode 100644 index 00000000..0176ad22 --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/external/s3/service/S3Service.java @@ -0,0 +1,194 @@ +package org.sopt.makers.crew.main.external.s3.service; + +import static org.sopt.makers.crew.main.common.exception.ErrorStatus.*; + +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Base64; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.SimpleTimeZone; +import java.util.UUID; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.sopt.makers.crew.main.common.exception.ServerException; +import org.sopt.makers.crew.main.external.s3.config.AwsProperties; +import org.sopt.makers.crew.main.meeting.v2.dto.response.PreSignedUrlFieldResponseDto; +import org.sopt.makers.crew.main.meeting.v2.dto.response.PreSignedUrlResponseDto; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.RequiredArgsConstructor; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.utils.BinaryUtils; + +@Service +@RequiredArgsConstructor +public class S3Service { + private static final String MEETING_PATH = "meeting"; + private static final String S3_SERVCIE = "s3"; + private static final String PRE_SIGNED_URL_PREFIX = "https://s3.ap-northeast-2.amazonaws.com"; + + private final S3Client s3Client; + + private final AwsProperties awsProperties; + + public PreSignedUrlResponseDto generatePreSignedUrl(String contentType) { + SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); + dateTimeFormat.setTimeZone(new SimpleTimeZone(0, "UTC")); + SimpleDateFormat dateStampFormat = new SimpleDateFormat("yyyyMMdd"); + dateStampFormat.setTimeZone(new SimpleTimeZone(0, "UTC")); + + Date now = new Date(); + String dateTimeStamp = dateTimeFormat.format(now); + String dateStamp = dateStampFormat.format(now); + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(now); + calendar.add(Calendar.MINUTE, 10); + Date expirationDate = calendar.getTime(); + + String objectKey = getObjectKey(contentType); + String encodedPolicy = generateEncodedPolicy(awsProperties.getS3BucketName(), objectKey, dateTimeStamp, + dateStamp, expirationDate); + + String signature = sign(encodedPolicy, dateStamp, S3_SERVCIE); + String credentials = getCredentials(dateStamp); + + PreSignedUrlFieldResponseDto fieldResponseDto = new PreSignedUrlFieldResponseDto(awsProperties.getContentType(), + objectKey, awsProperties.getS3BucketName(), awsProperties.getAlgorithm(), credentials, dateTimeStamp, + encodedPolicy, + signature); + + return PreSignedUrlResponseDto.of(PRE_SIGNED_URL_PREFIX + "/" + awsProperties.getS3BucketName(), + fieldResponseDto); + } + + /** + * 파일의 경로명을 랜덤으로 생성 + * @author @mikekks + * @param contentType 파일의 컨텐츠 타입 + * @returns 파일의 경로명 + */ + private String getObjectKey(String contentType) { + UUID uuid = UUID.randomUUID(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); + String curDate = LocalDateTime.now().format(formatter); + + return MEETING_PATH + "/" + curDate + "/" + uuid + "." + contentType; + } + + /** + * PreSignedUrl 업로드 정책 생성 + * @author @mikekks + * @returns base64로 인코딩된 String + */ + public String generateEncodedPolicy(String bucketName, String objectKey, String dateTimeStamp, String dateStamp, + Date expirationDate) { + List conditions = generateConditions(bucketName, objectKey, dateTimeStamp, + dateStamp); + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + simpleDateFormat.setTimeZone(new SimpleTimeZone(0, "UTC")); + + Map policy = Map.of( + "expiration", simpleDateFormat.format(expirationDate), + "conditions", conditions + ); + + return encodePolicy(policy); + } + + private List generateConditions(String bucketName, String objectKey, String dateTimeStamp, String dateStamp) { + List contentLengthRange = List.of("content-length-range", awsProperties.getFileMinSize(), + awsProperties.getFileMaxSize()); + + List conditions = new LinkedList<>(); + conditions.add(contentLengthRange); + + Map params = new HashMap<>(); + String credentials = getCredentials(dateStamp); + params.put("Content-Type", awsProperties.getContentType()); + params.put("key", objectKey); + params.put("bucket", bucketName); + params.put("X-Amz-Algorithm", awsProperties.getAlgorithm()); + params.put("X-Amz-Credential", credentials); + params.put("X-Amz-Date", dateTimeStamp); + + Map awsDefaults = generateAwsDefaultParams(bucketName, objectKey, dateTimeStamp, dateStamp); + + conditions.addAll(awsDefaults.entrySet()); + + return conditions; + } + + private Map generateAwsDefaultParams(String bucketName, String objectKey, String dateTimeStamp, + String dateStamp) { + + String credentials = getCredentials(dateStamp); + Map params = new HashMap<>(); + params.put("Content-Type", awsProperties.getContentType()); + params.put("key", objectKey); + params.put("bucket", bucketName); + params.put("X-Amz-Algorithm", awsProperties.getAlgorithm()); + params.put("X-Amz-Credential", credentials); + params.put("X-Amz-Date", dateTimeStamp); + + return params; + } + + private String getCredentials(String dateStamp) { + return String.format("%s/%s/%s/%s/%s", awsProperties.getAccessKey(), dateStamp, awsProperties.getAwsRegion(), + S3_SERVCIE, + awsProperties.getRequestType()); + } + + private String encodePolicy(Map policy) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonPolicy = objectMapper.writeValueAsString(policy); + + return Base64.getEncoder().encodeToString(jsonPolicy.getBytes()); + } catch (Exception e) { + throw new ServerException(S3_STORAGE_ERROR.getErrorCode()); + } + } + + public String sign(String toSign, String dateStamp, String service) { + try { + String awsSecretKey = awsProperties.getSecretKey(); + + byte[] kSecret = ("AWS4" + awsSecretKey).getBytes(StandardCharsets.UTF_8); + byte[] kDate = hmacSHA256(dateStamp, kSecret); + byte[] kRegion = hmacSHA256("ap-northeast-2", kDate); + byte[] kService = hmacSHA256(service, kRegion); + byte[] kSigning = hmacSHA256("aws4_request", kService); + byte[] signature = hmacSHA256(toSign, kSigning); + + return BinaryUtils.toHex(signature); + } catch (Exception e) { + throw new ServerException(S3_STORAGE_ERROR.getErrorCode()); + } + } + + private byte[] hmacSHA256(String data, byte[] key) { + try { + String algorithm = "HmacSHA256"; + Mac mac = Mac.getInstance(algorithm); + mac.init(new SecretKeySpec(key, algorithm)); + return mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new ServerException(S3_STORAGE_ERROR.getErrorCode()); + } + } + +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java index 328ad758..3075b694 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Api.java @@ -10,6 +10,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import jakarta.websocket.server.PathParam; + import java.security.Principal; import java.util.List; @@ -20,12 +22,14 @@ import org.sopt.makers.crew.main.meeting.v2.dto.request.ApplyV2UpdateStatusBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2ApplyMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; +import org.sopt.makers.crew.main.meeting.v2.dto.response.AppliesCsvFileUrlResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingGetApplyListResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2ApplyMeetingResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2CreateMeetingResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingByOrgUserDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseDto; +import org.sopt.makers.crew.main.meeting.v2.dto.response.PreSignedUrlResponseDto; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; @@ -114,4 +118,15 @@ ResponseEntity> getMeetingsTemp(@Mode @Operation(summary = "모임 지원자 상태 변경", description = "모임 지원자의 지원 상태를 변경합니다.") ResponseEntity updateApplyStatus(@PathVariable Integer meetingId, @RequestBody @Valid ApplyV2UpdateStatusBodyDto requestBody ,Principal principal); + + @Operation(summary = "Meeting 썸네일 업로드용 Pre-Signed URL 발급", description = "Meeting 썸네일 업로드용 Pre-Signed URL 발급합니다.") + ResponseEntity createPreSignedUrl(@PathParam("contentType") String contentType ,Principal principal); + + @Operation(summary = "모임 지원자 목록 csv 파일 다운로드", description = "모임 지원자 목록 csv 파일 다운로드") + ResponseEntity getAppliesCsvFileUrl( + @PathVariable Integer meetingId, + @PathParam("status") Integer status, + @PathParam("type") Integer type, + @PathParam("order") String order, + Principal principal); } diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java index 35700230..6c8ed187 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/MeetingV2Controller.java @@ -6,22 +6,26 @@ import java.security.Principal; import java.util.List; +import jakarta.websocket.server.PathParam; import lombok.RequiredArgsConstructor; import org.sopt.makers.crew.main.common.dto.TempResponseDto; import org.sopt.makers.crew.main.common.util.UserUtil; +import org.sopt.makers.crew.main.external.s3.service.S3Service; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingGetAppliesQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingByOrgUserQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingQueryDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.ApplyV2UpdateStatusBodyDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2ApplyMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.request.MeetingV2CreateMeetingBodyDto; +import org.sopt.makers.crew.main.meeting.v2.dto.response.AppliesCsvFileUrlResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingGetApplyListResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2ApplyMeetingResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2CreateMeetingResponseDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingByOrgUserDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingDto; import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseDto; +import org.sopt.makers.crew.main.meeting.v2.dto.response.PreSignedUrlResponseDto; import org.sopt.makers.crew.main.meeting.v2.service.MeetingV2Service; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -43,6 +47,8 @@ public class MeetingV2Controller implements MeetingV2Api { private final MeetingV2Service meetingV2Service; + private final S3Service s3Service; + @Override @GetMapping("/org-user") @ResponseStatus(HttpStatus.OK) @@ -152,4 +158,26 @@ public ResponseEntity updateApplyStatus( return ResponseEntity.ok().build(); } + + @Override + @GetMapping("/presigned-url") + public ResponseEntity createPreSignedUrl( + @PathParam("contentType") String contentType, Principal principal) { + PreSignedUrlResponseDto responseDto = s3Service.generatePreSignedUrl(contentType); + + return ResponseEntity.ok(responseDto); + } + + @Override + @GetMapping("/{meetingId}/list/csv") + public ResponseEntity getAppliesCsvFileUrl( + @PathVariable Integer meetingId, + @PathParam("status") Integer status, + @PathParam("type") Integer type, + @PathParam("order") String order, + Principal principal) { + + + return null; + } } diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlFieldResponseDto.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlFieldResponseDto.java new file mode 100644 index 00000000..6c016a8d --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlFieldResponseDto.java @@ -0,0 +1,43 @@ +package org.sopt.makers.crew.main.meeting.v2.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class PreSignedUrlFieldResponseDto { + + @JsonProperty("Content-Type") + private final String contentType; + + @JsonProperty("key") + private final String key; + + @JsonProperty("bucket") + private final String bucket; + + @JsonProperty("X-Amz-Algorithm") + private final String algorithm; + + @JsonProperty("X-Amz-Credential") + private final String credential; + + @JsonProperty("X-Amz-Date") + private final String date; + + @JsonProperty("Policy") + private final String policy; + + @JsonProperty("X-Amz-Signature") + private final String signature; + + public static PreSignedUrlFieldResponseDto of(String contentType, String key, String bucket, String algorithm, + String credential, + String date, String policy, String signature) { + + return new PreSignedUrlFieldResponseDto(contentType, key, bucket, algorithm, credential, date, policy, + signature); + } +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlResponseDto.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlResponseDto.java new file mode 100644 index 00000000..19ddad50 --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/PreSignedUrlResponseDto.java @@ -0,0 +1,16 @@ +package org.sopt.makers.crew.main.meeting.v2.dto.response; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class PreSignedUrlResponseDto { + private final String url; + private final PreSignedUrlFieldResponseDto fields; + + public static PreSignedUrlResponseDto of(String url, PreSignedUrlFieldResponseDto fields){ + + return new PreSignedUrlResponseDto(url, fields); + } +} From b59360d1d262428d4e582eb548e3d526988aeafc Mon Sep 17 00:00:00 2001 From: mikekks Date: Sat, 24 Aug 2024 15:12:00 +0900 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20deprecated=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/meeting/v1/meeting-v1.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/meeting/v1/meeting-v1.controller.ts b/server/src/meeting/v1/meeting-v1.controller.ts index dbb41ffc..ac81c7c5 100644 --- a/server/src/meeting/v1/meeting-v1.controller.ts +++ b/server/src/meeting/v1/meeting-v1.controller.ts @@ -65,6 +65,7 @@ export class MeetingV1Controller { @ApiOperation({ summary: 'Meeting 썸네일 업로드용 Pre-Signed URL 발급', + deprecated: true, }) @ApiOkResponseCommon(MeetingV1GetPresignedUrlResponseDto) @ApiResponse({ From 8d468fa5bdcf4f370ec5b3d52782ba97b7620785 Mon Sep 17 00:00:00 2001 From: mikekks Date: Sat, 24 Aug 2024 15:21:33 +0900 Subject: [PATCH 4/4] =?UTF-8?q?add:=20csv=20=EA=B4=80=EB=A0=A8=20dto=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../v2/dto/response/AppliesCsvFileUrlResponseDto.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/AppliesCsvFileUrlResponseDto.java diff --git a/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/AppliesCsvFileUrlResponseDto.java b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/AppliesCsvFileUrlResponseDto.java new file mode 100644 index 00000000..7a8ba882 --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/meeting/v2/dto/response/AppliesCsvFileUrlResponseDto.java @@ -0,0 +1,10 @@ +package org.sopt.makers.crew.main.meeting.v2.dto.response; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class AppliesCsvFileUrlResponseDto { + private final String url; +}