From 468abe85ecd18ab701e7c45c97384a925bf39919 Mon Sep 17 00:00:00 2001 From: Lucy Oh <56earls@gmail.com> Date: Tue, 21 Jan 2025 22:54:54 +0900 Subject: [PATCH 1/3] Add: blob storage settings #28 --- .gitignore | 3 +++ build.gradle | 4 ++++ src/main/resources/application.yml | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index ef3c3e6..79cde7c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +### data ### +docker/redis + ### STS ### .apt_generated .classpath diff --git a/build.gradle b/build.gradle index 044118a..7d6e7e7 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.security:spring-security-test' + // azure blob storage + implementation 'com.azure.spring:spring-cloud-azure-dependencies:5.19.0' + implementation 'com.azure.spring:spring-cloud-azure-starter-storage-blob:5.19.0' + // jasypt encryption implementation "com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4e6fd28..2714043 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,6 +24,13 @@ spring: url: ENC(YbE5zqnIUB6ekwgzEtUY9+vfU13OSev9N00+dIaSK0JpMQ7ajpK8EcIBlUFg+jdNd0Q3r9ow6e8io/hEtii5z3YN4vpV6jQrKcm4i3a6eqE/FtwTUT0zD/BpWNGA5MG4h3SWmWzWCzG9eLEoUw2ANbxksDXsxJ3xx7mJ+6kAHt0=) username: ENC(1a0kqqs8WICexcTbsqfqmg==) password: ENC(BvKDYlNIWK7fxHjiuEEQpGc+oDqD9XS3vz2d/EWwqok=) + cloud: + azure: + storage: + blob: + account-name: coaster + container-name: coaster-image + account-key: ENC(sSEeJWspfQCXTqUPvyI5Nsz2cPuVUl9aAVUJgDjccwU0WpwojTB6EZknus7474nCgWPxtY7T4lyavN4UUxUaSsesjLQgn6o7xSCv+rdVpbkJaK1r56lq+MJqPucJ+68c2Wx8MEqGo7w=) jwt: secretKey: ENC(bekmp+cZY7Qvbii3ldKJxdKF1Qx+O1J0wmlrxf9GbygY/aIGNzIfIwo0g56qzJ5WqM6FWbg/UjM3+BLjQJ/NHD1G0T1OcUW36rZ2xAI81AfN23y4PSV1J+5ybf/s98D975u/nWr+f3JRbPrpowqkj2R6XW6Vrm4KXqLeDXfPetAaZFYA0rK6XMbit6J/nya7) jasypt: From 5176c83252489fb0e5da31c70c1e1f77b6f91cfc Mon Sep 17 00:00:00 2001 From: Lucy Oh <56earls@gmail.com> Date: Tue, 21 Jan 2025 22:55:14 +0900 Subject: [PATCH 2/3] Feat: image upload/delete #28 --- .../coastee/server/image/domain/DirName.java | 27 ++++++++ .../image/service/BlobStorageService.java | 67 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/main/java/com/coastee/server/image/domain/DirName.java create mode 100644 src/main/java/com/coastee/server/image/service/BlobStorageService.java diff --git a/src/main/java/com/coastee/server/image/domain/DirName.java b/src/main/java/com/coastee/server/image/domain/DirName.java new file mode 100644 index 0000000..b5f5811 --- /dev/null +++ b/src/main/java/com/coastee/server/image/domain/DirName.java @@ -0,0 +1,27 @@ +package com.coastee.server.image.domain; + +import com.coastee.server.auth.domain.Authority; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +@Getter +@RequiredArgsConstructor +public enum DirName { + USER("user"), POST("post"), CHAT("chat"); + + private final String code; + + @Override + public String toString() { + return this.code; + } + + public static Authority of(final String code) { + return Arrays.stream(Authority.values()) + .filter(r -> r.getCode().equals(code.toUpperCase())) + .findAny() + .orElse(null); + } +} diff --git a/src/main/java/com/coastee/server/image/service/BlobStorageService.java b/src/main/java/com/coastee/server/image/service/BlobStorageService.java new file mode 100644 index 0000000..421e3f4 --- /dev/null +++ b/src/main/java/com/coastee/server/image/service/BlobStorageService.java @@ -0,0 +1,67 @@ +package com.coastee.server.image.service; + +import com.azure.core.util.BinaryData; +import com.azure.core.util.Context; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.models.BlobHttpHeaders; +import com.azure.storage.blob.models.BlobRequestConditions; +import com.azure.storage.blob.options.BlobParallelUploadOptions; +import com.coastee.server.global.apipayload.exception.GeneralException; +import com.coastee.server.image.domain.DirName; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +import static com.coastee.server.global.apipayload.code.status.ErrorStatus.IO_EXCEPTION; + +@Service +@RequiredArgsConstructor +public class BlobStorageService { + private final BlobContainerClient blobContainerClient; + + @Value("${spring.cloud.azure.storage.blob.account-name}") + private String account; + + @Value("${spring.cloud.azure.storage.blob.container-name}") + private String container; + + public String upload( + final MultipartFile file, + final DirName imageDir, + final Long id + ) { + String fileName = imageDir + "/" + id; + + BlobClient blobClient = blobContainerClient.getBlobClient(fileName); + BlobParallelUploadOptions uploadOptions = getUploadOptions(file); + blobClient.uploadWithResponse(uploadOptions, null, Context.NONE); + + return String.format("https://%s.blob.core.windows.net/%s/%s/%s", account, container, imageDir, id); + } + + public void delete( + final DirName imageDir, + final Long id + ) { + String fileName = imageDir + "/" + id; + BlobClient blobClient = blobContainerClient.getBlobClient(fileName); + blobClient.delete(); + } + + private BlobParallelUploadOptions getUploadOptions(final MultipartFile file) { + try { + BlobHttpHeaders jsonHeaders = new BlobHttpHeaders() + .setContentType(file.getContentType()); + BinaryData data = BinaryData.fromStream(file.getInputStream(), file.getSize()); + return new BlobParallelUploadOptions(data) + .setRequestConditions(new BlobRequestConditions()) + .setHeaders(jsonHeaders); + } catch (IOException e) { + throw new GeneralException(IO_EXCEPTION); + } + } +} From 0f974a567cd4f84ae47a5a46e3976bf792a08e25 Mon Sep 17 00:00:00 2001 From: Lucy Oh <56earls@gmail.com> Date: Tue, 21 Jan 2025 22:56:07 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Fix:=20Experience=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/user/domain/Experience.java | 22 +++++++++++++++++-- .../com/coastee/server/user/domain/User.java | 5 ----- .../user/dto/response/ProfileResponse.java | 4 +--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/coastee/server/user/domain/Experience.java b/src/main/java/com/coastee/server/user/domain/Experience.java index a51860d..b012aa2 100644 --- a/src/main/java/com/coastee/server/user/domain/Experience.java +++ b/src/main/java/com/coastee/server/user/domain/Experience.java @@ -1,16 +1,34 @@ package com.coastee.server.user.domain; +import com.coastee.server.global.BaseEntity; +import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; -import java.io.Serializable; import java.time.LocalDateTime; +import static jakarta.persistence.FetchType.LAZY; +import static lombok.AccessLevel.PROTECTED; + @Getter -public class Experience implements Serializable { +@Entity +@NoArgsConstructor(access = PROTECTED) +public class Experience extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = LAZY) + @JoinColumn(name = "user_id") + private User user; + private String title; + private LocalDateTime startDate; + private LocalDateTime endDate; + private String content; @Builder(builderMethodName = "of") diff --git a/src/main/java/com/coastee/server/user/domain/User.java b/src/main/java/com/coastee/server/user/domain/User.java index 267364e..cabc4cb 100644 --- a/src/main/java/com/coastee/server/user/domain/User.java +++ b/src/main/java/com/coastee/server/user/domain/User.java @@ -36,9 +36,6 @@ public class User extends BaseEntity { @JdbcTypeCode(SqlTypes.ARRAY) private List urlList = new ArrayList<>(); - @JdbcTypeCode(SqlTypes.JSON) - private List experienceList = new ArrayList<>(); - @Enumerated(STRING) private SocialType socialType; @@ -52,7 +49,6 @@ public User( final String profileImage, final String refreshToken, final List urlList, - final List experienceList, final SocialType socialType, final String socialId ) { @@ -62,7 +58,6 @@ public User( this.profileImage = profileImage; this.refreshToken = refreshToken; this.urlList = urlList; - this.experienceList = experienceList; this.socialType = socialType; this.socialId = socialId; } diff --git a/src/main/java/com/coastee/server/user/dto/response/ProfileResponse.java b/src/main/java/com/coastee/server/user/dto/response/ProfileResponse.java index b886ce9..8be94f3 100644 --- a/src/main/java/com/coastee/server/user/dto/response/ProfileResponse.java +++ b/src/main/java/com/coastee/server/user/dto/response/ProfileResponse.java @@ -20,7 +20,6 @@ public class ProfileResponse { private String bio; private String profileImage; private List urlList; - private List experienceList; public static ProfileResponse from(final User user) { return new ProfileResponse( @@ -29,8 +28,7 @@ public static ProfileResponse from(final User user) { user.getHeadline(), user.getBio(), user.getProfileImage(), - user.getUrlList(), - user.getExperienceList() + user.getUrlList() ); } }