Skip to content

Commit

Permalink
v1.1.0 (#618)
Browse files Browse the repository at this point in the history
v1.1.0
  • Loading branch information
Leejin-Yang authored Sep 13, 2023
2 parents 483d6db + 8d6be61 commit da5a060
Show file tree
Hide file tree
Showing 120 changed files with 2,771 additions and 1,990 deletions.
4 changes: 2 additions & 2 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ dependencies {
testRuntimeOnly 'com.h2database:h2'

implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
implementation 'commons-fileupload:commons-fileupload:1.5'
implementation 'commons-io:commons-io:2.11.0'
implementation 'com.github.maricn:logback-slack-appender:1.4.0'

implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'

implementation 'com.amazonaws:aws-java-sdk-s3:1.12.547'
}

tasks.named('test') {
Expand Down
11 changes: 11 additions & 0 deletions backend/src/main/java/com/funeat/auth/application/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
import com.funeat.auth.dto.UserInfoDto;
import com.funeat.auth.util.PlatformUserProvider;
import com.funeat.member.application.MemberService;
import javax.servlet.http.Cookie;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
public class AuthService {

private static final String COOKIE_NAME = "FUNEAT";

private final MemberService memberService;
private final PlatformUserProvider platformUserProvider;

Expand All @@ -32,4 +35,12 @@ public void logoutWithKakao(final Long memberId) {
final String platformId = memberService.findPlatformId(memberId);
platformUserProvider.logout(platformId);
}

public Cookie expireCookie() {
final Cookie cookie = new Cookie(COOKIE_NAME, null);
cookie.setMaxAge(0);
cookie.setPath("/");

return cookie;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import com.funeat.auth.dto.SignUserDto;
import com.funeat.auth.util.AuthenticationPrincipal;
import java.net.URI;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -16,6 +18,7 @@
@RestController
public class AuthApiController implements AuthController {

private static final String SESSION_ATTRIBUTE_NAME = "member";
private static final String HOME = "/";
private static final String MY_PAGE = "/members";

Expand All @@ -37,7 +40,7 @@ public ResponseEntity<Void> loginAuthorizeUser(@RequestParam("code") final Strin
final HttpServletRequest request) {
final SignUserDto signUserDto = authService.loginWithKakao(code);
final Long memberId = signUserDto.getMember().getId();
request.getSession().setAttribute("member", memberId);
request.getSession().setAttribute(SESSION_ATTRIBUTE_NAME, memberId);

if (signUserDto.isSignUp()) {
return ResponseEntity.ok()
Expand All @@ -51,10 +54,14 @@ public ResponseEntity<Void> loginAuthorizeUser(@RequestParam("code") final Strin

@PostMapping("/api/logout")
public ResponseEntity<Void> logout(@AuthenticationPrincipal final LoginInfo loginInfo,
final HttpServletRequest request) {
request.getSession().removeAttribute("member");
final HttpServletRequest request, final HttpServletResponse response) {
authService.logoutWithKakao(loginInfo.getId());

request.getSession().removeAttribute(SESSION_ATTRIBUTE_NAME);

final Cookie cookie = authService.expireCookie();
response.addCookie(cookie);

return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create(HOME))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
Expand Down Expand Up @@ -36,5 +37,6 @@ public interface AuthController {
description = "로그아웃 성공."
)
@PostMapping
ResponseEntity<Void> logout(@AuthenticationPrincipal LoginInfo loginInfo, HttpServletRequest request);
ResponseEntity<Void> logout(@AuthenticationPrincipal LoginInfo loginInfo, HttpServletRequest request,
HttpServletResponse response);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
public class AuthArgumentResolver implements HandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter parameter) {
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer,
final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) {
final HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
final HttpSession session = Objects.requireNonNull(request).getSession();
final HttpSession session = Objects.requireNonNull(request).getSession(false);
final String id = String.valueOf(session.getAttribute("member"));

return new LoginInfo(Long.valueOf(id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.funeat.auth.exception.AuthErrorCode;
import com.funeat.auth.exception.AuthException.NotLoggedInException;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
Expand All @@ -12,11 +13,14 @@
public class AuthHandlerInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
final HttpSession session = request.getSession();
if (session.getAttribute("member") == null) {
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response,
final Object handler) {
final HttpSession session = request.getSession(false);

if (Objects.isNull(session)) {
throw new NotLoggedInException(AuthErrorCode.LOGIN_MEMBER_NOT_FOUND);
}

return true;
}
}
10 changes: 0 additions & 10 deletions backend/src/main/java/com/funeat/common/ImageService.java

This file was deleted.

30 changes: 2 additions & 28 deletions backend/src/main/java/com/funeat/common/ImageUploader.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
package com.funeat.common;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
@Profile("!test")
public class ImageUploader implements ImageService {
public interface ImageUploader {

@Value("${image.path}")
private String imagePath;

@Override
public void upload(final MultipartFile image, final String newFileName) {
final Path path = Paths.get(imagePath + newFileName);
try {
Files.write(path, image.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public String getRandomImageName(final MultipartFile image) {
return UUID.randomUUID() + image.getOriginalFilename();
}
String upload(final MultipartFile image);
}
4 changes: 4 additions & 0 deletions backend/src/main/java/com/funeat/common/OpenApiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
@Tag(name = "01.Product", description = "상품 기능"),
@Tag(name = "02.Category", description = "카테고리 기능"),
@Tag(name = "03.Review", description = "리뷰 기능"),
@Tag(name = "04.Tag", description = "태그 기능"),
@Tag(name = "05.Member", description = "사용자 기능"),
@Tag(name = "06.Login", description = "로그인 기능"),
@Tag(name = "07.Recipe", description = "꿀조합 기능"),
}
)
@Configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.funeat.common.exception;

import com.funeat.exception.CommonErrorCode;
import com.funeat.exception.ErrorCode;
import com.funeat.exception.GlobalException;
import org.springframework.http.HttpStatus;

public class CommonException extends GlobalException {

public CommonException(final HttpStatus status, final ErrorCode errorCode) {
super(status, errorCode);
}

public static class NotAllowedFileExtensionException extends CommonException {
public NotAllowedFileExtensionException(final CommonErrorCode errorCode, final String extension) {
super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), extension));
}
}

public static class S3UploadFailException extends CommonException {
public S3UploadFailException(final CommonErrorCode errorCode) {
super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage()));
}
}
}
28 changes: 28 additions & 0 deletions backend/src/main/java/com/funeat/common/s3/AwsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.funeat.common.s3;

import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AwsConfig {

@Value("${cloud.aws.region.static}")
private String region;

@Bean
public InstanceProfileCredentialsProvider awsCredentialsProvider() {
return InstanceProfileCredentialsProvider.getInstance();
}

@Bean
public AmazonS3 amazonS3Client() {
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(awsCredentialsProvider())
.build();
}
}
82 changes: 82 additions & 0 deletions backend/src/main/java/com/funeat/common/s3/S3Uploader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.funeat.common.s3;

import static com.funeat.exception.CommonErrorCode.IMAGE_EXTENSION_ERROR_CODE;
import static com.funeat.exception.CommonErrorCode.UNKNOWN_SERVER_ERROR_CODE;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.funeat.common.ImageUploader;
import com.funeat.common.exception.CommonException.NotAllowedFileExtensionException;
import com.funeat.common.exception.CommonException.S3UploadFailException;
import java.io.IOException;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
@Profile("!test")
public class S3Uploader implements ImageUploader {

public static final String JPEG = "image/jpeg";
public static final String PNG = "image/png";

@Value("${cloud.aws.s3.bucket}")
private String bucket;

@Value("${cloud.aws.s3.folder}")
private String folder;

@Value("${cloud.aws.s3.cloudfrontPath}")
private String cloudfrontPath;

private final AmazonS3 amazonS3;

public S3Uploader(final AmazonS3 amazonS3) {
this.amazonS3 = amazonS3;
}

@Override
public String upload(final MultipartFile image) {
validateExtension(image);
final String randomImageName = getRandomImageName(image);
final ObjectMetadata metadata = getMetadata(image);
try {
final String key = folder + randomImageName;
amazonS3.putObject(getPutObjectRequest(image, key, metadata));

return getCloudfrontImagePath(randomImageName);
} catch (IOException e) {
throw new S3UploadFailException(UNKNOWN_SERVER_ERROR_CODE);
}
}

private void validateExtension(final MultipartFile image) {
final String contentType = image.getContentType();
if (!contentType.equals(JPEG) && !contentType.equals(PNG)) {
throw new NotAllowedFileExtensionException(IMAGE_EXTENSION_ERROR_CODE, contentType);
}
}

private String getRandomImageName(final MultipartFile image) {
return UUID.randomUUID() + image.getOriginalFilename();
}

private ObjectMetadata getMetadata(final MultipartFile image) {
final ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(image.getContentType());
metadata.setContentLength(image.getSize());
return metadata;
}

private PutObjectRequest getPutObjectRequest(final MultipartFile image, final String key,
final ObjectMetadata metadata) throws IOException {
return new PutObjectRequest(bucket, key, image.getInputStream(), metadata);
}

private String getCloudfrontImagePath(final String imageName) {
return cloudfrontPath + imageName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public enum CommonErrorCode {

UNKNOWN_SERVER_ERROR_CODE(HttpStatus.INTERNAL_SERVER_ERROR, "알 수 없는 에러입니다.", "0000"),
REQUEST_VALID_ERROR_CODE(HttpStatus.BAD_REQUEST, "요청을 다시 확인해주세요.", "0001"),
IMAGE_EXTENSION_ERROR_CODE(HttpStatus.BAD_REQUEST, "파일 확장자를 확인해주세요.", "0002"),
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import com.funeat.auth.dto.SignUserDto;
import com.funeat.auth.dto.UserInfoDto;
import com.funeat.common.ImageService;
import com.funeat.common.ImageUploader;
import com.funeat.member.domain.Member;
import com.funeat.member.dto.MemberProfileResponse;
import com.funeat.member.dto.MemberRequest;
Expand All @@ -22,11 +22,11 @@
public class MemberService {

private final MemberRepository memberRepository;
private final ImageService imageService;
private final ImageUploader imageUploader;

public MemberService(final MemberRepository memberRepository, final ImageService imageService) {
public MemberService(final MemberRepository memberRepository, final ImageUploader imageUploader) {
this.memberRepository = memberRepository;
this.imageService = imageService;
this.imageUploader = imageUploader;
}

@Transactional(propagation = REQUIRES_NEW)
Expand Down Expand Up @@ -60,13 +60,11 @@ public void modify(final Long memberId, final MultipartFile image, final MemberR
final String nickname = request.getNickname();

if (Objects.isNull(image)) {
findMember.modifyName(nickname);
findMember.modifyNickname(nickname);
return;
}

final String newImageName = imageService.getRandomImageName(image);
findMember.modifyProfile(nickname, newImageName);
imageService.upload(image, newImageName);
final String imageUrl = imageUploader.upload(image);
findMember.modifyProfile(nickname, imageUrl);
}

public String findPlatformId(final Long memberId) {
Expand Down
Loading

0 comments on commit da5a060

Please sign in to comment.