Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ out/
/newrelic
.secrets
.env
.env.local
.env.dev
.env.prod
.env.redis
.env.redis.dev
/dockercomposes/monitoring/newrelic-infra/newrelic-infra.yml
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/newzet/api/article/domain/Article.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,9 @@ public Article share() {
this.deletedAt
);
}

public boolean isSaveInStorage() {
return contentUrl.endsWith(".html");
}
Comment on lines +105 to +107
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for determining storage type based on file extension '.html' seems fragile. Consider using a more explicit field or enum to indicate storage type rather than inferring from file extension.

Copilot uses AI. Check for mistakes.
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import com.newzet.api.article.business.service.ArticleService;
import com.newzet.api.article.controller.dto.ArticleContentResponse;
import com.newzet.api.article.domain.Article;
import com.newzet.api.common.s3.S3Service;
import com.newzet.api.common.storage.StorageService;

import lombok.RequiredArgsConstructor;

Expand All @@ -18,19 +18,19 @@
public class ArticleOrchestrator {

private final ArticleService articleService;
private final S3Service s3Service;
private final StorageService storageService;

public ArticleContentResponse getSharedArticle(UUID articleId) {
Article sharedArticle = articleService.getSharedArticle(articleId);
String content = s3Service.getContentAsString(sharedArticle.getContentUrl());
String content = storageService.getContent(sharedArticle.getContentUrl(), sharedArticle.isSaveInStorage());
return new ArticleContentResponse(sharedArticle.getTitle(), content,
sharedArticle.isLike());
}

@Transactional
public ArticleContentResponse getArticle(UUID articleId) {
Article article = articleService.getArticle(articleId);
String content = s3Service.getContentAsString(article.getContentUrl());
String content = storageService.getContent(article.getContentUrl(), article.isSaveInStorage());
return ArticleContentResponse.of(article.getTitle(), content,
article.isLike());
}
Expand Down
43 changes: 0 additions & 43 deletions src/main/java/com/newzet/api/common/s3/S3Service.java

This file was deleted.

63 changes: 63 additions & 0 deletions src/main/java/com/newzet/api/common/storage/StorageService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.newzet.api.common.storage;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.newzet.api.common.exception.InternalErrorException;
import com.newzet.api.config.storage.StorageConfig;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class StorageService {

private final AmazonS3 awsS3Client;
private final AmazonS3 supabaseS3Client;
private final StorageConfig storageConfig;

public StorageService(@Qualifier("awsS3Client") AmazonS3 awsS3Client,
@Qualifier("supabaseS3Client") AmazonS3 supabaseS3Client,
StorageConfig storageConfig) {
this.awsS3Client = awsS3Client;
this.supabaseS3Client = supabaseS3Client;
this.storageConfig = storageConfig;
}

public String getContent(String key, boolean isSaveInStorage) {
AmazonS3 client;
String bucketName;

if (isSaveInStorage) {
client = supabaseS3Client;
bucketName = storageConfig.getSupabaseBucketName();
} else {
client = awsS3Client;
bucketName = storageConfig.getAwsBucketName();
}

GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);

try (S3Object s3Object = client.getObject(getObjectRequest);
S3ObjectInputStream inputStream = s3Object.getObjectContent()) {

byte[] contentBytes = inputStream.readAllBytes();
return new String(contentBytes, StandardCharsets.UTF_8);

} catch (AmazonS3Exception e) {
log.error("Storage에서 객체를 가져오는 중 오류가 발생했습니다. Key: {}", key, e);
throw new InternalErrorException("아티클을 불러오는 과정에서 에러가 발생하였습니다.");
} catch (IOException e) {
log.error("Storage 파일 내용을 읽는 중 I/O 오류가 발생했습니다.", e);
throw new InternalErrorException("아티클을 불러오는 과정에서 에러가 발생하였습니다.");
}
}
}
38 changes: 0 additions & 38 deletions src/main/java/com/newzet/api/config/s3/S3Config.java

This file was deleted.

65 changes: 65 additions & 0 deletions src/main/java/com/newzet/api/config/storage/StorageConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.newzet.api.config.storage;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

import lombok.Getter;

@Configuration
@Getter
public class StorageConfig {

// AWS S3 Credentials
@Value("${cloud.aws.s3.bucket}")
private String awsBucketName;
@Value("${cloud.aws.region.static}")
private String awsRegion;
@Value("${cloud.aws.credentials.access-key}")
private String awsAccessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String awsSecretKey;

// Supabase S3-compatible Storage Credentials
@Value("${cloud.supabase.storage.bucket}")
private String supabaseBucketName;
@Value("${cloud.supabase.storage.region}")
private String supabaseRegion;
@Value("${cloud.supabase.storage.endpoint-url}")
private String supabaseEndpointUrl;
@Value("${cloud.supabase.storage.credentials.access-key}")
private String supabaseAccessKey;
@Value("${cloud.supabase.storage.credentials.secret-key}")
private String supabaseSecretKey;

@Bean
@Qualifier("awsS3Client")
public AmazonS3 awsS3Client() {
AWSCredentials credentials = new BasicAWSCredentials(awsAccessKey, awsSecretKey);
return AmazonS3ClientBuilder.standard()
.withRegion(awsRegion)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.build();
}

@Bean
@Qualifier("supabaseS3Client")
public AmazonS3 supabaseS3Client() {
AWSCredentials credentials = new BasicAWSCredentials(supabaseAccessKey, supabaseSecretKey);
AwsClientBuilder.EndpointConfiguration endpointConfig =
new AwsClientBuilder.EndpointConfiguration(supabaseEndpointUrl, supabaseRegion);

return AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(endpointConfig)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.build();
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/newzet/api/welcome/api/WelcomeApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.web.bind.annotation.RequestMapping;

import com.newzet.api.common.auth.annotation.Login;
import com.newzet.api.common.auth.annotation.RequireAuth;
import com.newzet.api.common.auth.domain.AuthUser;
import com.newzet.api.common.response.SuccessResponse;

Expand All @@ -15,6 +16,7 @@
public interface WelcomeApi {

@PostMapping("/welcome")
@RequireAuth
@Operation(summary = "welcome 메일 전송",
description = "welcome 메일을 전송한다.")
SuccessResponse<Object> sendWelcomeMail(@Login AuthUser authUser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.api.client.util.Value;
import com.newzet.api.article.business.service.ArticleService;
import com.newzet.api.article.domain.Article;
import com.newzet.api.fcm.business.service.FcmSenderService;
Expand All @@ -18,21 +19,20 @@
@RequiredArgsConstructor
@Transactional
public class WelcomeOrchestrator {

private static final UUID NEWZET_NEWSLETTER_ID = UUID.fromString("c4922e54-f58a-4270-80da-2dc6d59bc4fa");

private static final String WELCOME_MAIL_TITLE = "💌 뉴젯과 더욱 친해지는 방법 💌";
private static final String WELCOME_MAIL_URL = "newzet_content/welcome_letter";
private static final String WELCOME_MAIL_IMAGE_URL = "https://newzet-lib.s3.ap-northeast-2.amazonaws.com/newsletter-image/trend_issue/nz_logo.webp";

private static final String WELCOME_MAIL_URL = "welcome_letter";
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WELCOME_MAIL_URL was changed from 'newzet_content/welcome_letter' to just 'welcome_letter', removing the directory prefix. Ensure this path change aligns with the actual storage structure.

Suggested change
private static final String WELCOME_MAIL_URL = "welcome_letter";
private static final String WELCOME_MAIL_URL = "newzet_content/welcome_letter";

Copilot uses AI. Check for mistakes.
private final NewsletterService newsletterService;
private final ArticleService articleService;
private final SubscriptionService subscriptionService;
private final FcmSenderService fcmSenderService;
@Value("${newzet.newsletter.id}")
private String newzetNewsletterId;

public void sendWelcomeMail(UUID userId) {
Newsletter newsletter = newsletterService.findNewsLetterById(NEWZET_NEWSLETTER_ID);
Newsletter newsletter = newsletterService.findNewsLetterById(UUID.fromString(newzetNewsletterId));
Article article = articleService.addArticle(userId, newsletter.getName(), newsletter.getDomain(),
WELCOME_MAIL_TITLE, WELCOME_MAIL_URL, WELCOME_MAIL_IMAGE_URL, newsletter.getMailingList());
WELCOME_MAIL_TITLE, WELCOME_MAIL_URL, newsletter.getImageUrl(), newsletter.getMailingList());
subscriptionService.addSubscriptionIfUnsubscribed(userId, newsletter.getName(), newsletter.getDomain(),
newsletter.getMailingList());
fcmSenderService.sendFcmNotBatch(userId, article.getId(), article.getCreatedAt(), article.getTitle(),
Expand Down
15 changes: 14 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,17 @@ cloud:
static: ${S3_REGION}
credentials:
access-key: ${S3_ACCESS_KEY}
secret-key: ${S3_SECRET_KEY}
secret-key: ${S3_SECRET_KEY}

supabase:
storage:
bucket: ${SUPABASE_BUCKET_NAME}
endpoint-url: ${SUPABASE_ENDPOINT_URL}
region: ${SUPABASE_REGION}
credentials:
access-key: ${SUPABASE_ACCESS_KEY}
secret-key: ${SUPABASE_SECRET_KEY}

newzet:
newsletter:
id: ${NEWZET_NEWSLETTER_ID}
Loading
Loading