diff --git a/.gitignore b/.gitignore index 69fa948..fd837ac 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ out/ ### VS Code ### .vscode/ application.properties -application-*.yml \ No newline at end of file +application*.yml +application.yml \ No newline at end of file diff --git a/build.gradle b/build.gradle index b53c962..2dcabb1 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,9 @@ dependencies { // AWS S3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + implementation group: 'ch.simas.qlrm', name: 'qlrm', version: '1.7.1' + + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' } tasks.named('test') { diff --git a/src/main/java/com/example/blendish/controller/CommentController.java b/src/main/java/com/example/blendish/controller/CommentController.java new file mode 100644 index 0000000..08982b4 --- /dev/null +++ b/src/main/java/com/example/blendish/controller/CommentController.java @@ -0,0 +1,40 @@ +package com.example.blendish.controller; + +import com.example.blendish.domain.comments.dto.CommentAllDTO; +import com.example.blendish.domain.comments.dto.CommentDTO; +import com.example.blendish.domain.comments.service.CommentService; +import com.example.blendish.global.dto.ApiResponseTemplate; +import com.example.blendish.global.response.SuccessCode; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "Comment Controller", description = "댓글 API") +@RestController +@RequestMapping("/api/Comment") +@AllArgsConstructor +public class CommentController { + private final CommentService commentService; + + //부모댓글 전체 띄우기 + @GetMapping("/ParentsComment") + public ResponseEntity>> getParentsComment(@RequestParam(name = "recipeId") Long recipeId) { + + List commentDTOList = commentService.getParentCommnet(recipeId); + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, commentDTOList)); + } + + // 전체 댓글 띄우기 + @GetMapping("/AllComment") + public ResponseEntity>> getAllComment(@RequestParam(name = "recipeId") Long recipeId) { + + List commentAllDTOList = commentService.getAllComment(recipeId); + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, commentAllDTOList)); + } +} diff --git a/src/main/java/com/example/blendish/controller/CommunityController.java b/src/main/java/com/example/blendish/controller/CommunityController.java new file mode 100644 index 0000000..1b1f713 --- /dev/null +++ b/src/main/java/com/example/blendish/controller/CommunityController.java @@ -0,0 +1,89 @@ +package com.example.blendish.controller; + +import com.example.blendish.domain.recipe.dto.CommunityDetailDTO; +import com.example.blendish.domain.recipe.dto.CommunityHotRecipeDTO; +import com.example.blendish.domain.recipe.dto.CommunityTodayRecipeDTO; +import com.example.blendish.domain.recipe.dto.RecipeDetailDTO; +import com.example.blendish.domain.recipe.service.CommunityService; +import com.example.blendish.global.dto.ApiResponseTemplate; +import com.example.blendish.global.response.SuccessCode; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "Community Controller", description = "커뮤니티 관련 API") +@RestController +@RequestMapping("/api/community") +@AllArgsConstructor +public class CommunityController { + + private final CommunityService communityService; + + //hot 레시피 + @GetMapping("/HotRecipe") + public ResponseEntity>> getHot() { + + List hotRecipyDTOS = communityService.getTopLikedRecipes(); + + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, hotRecipyDTOS)); + } + //오늘의 레시피 + @GetMapping("/TodayRecipe") + public ResponseEntity>> getToday() { + + List todayRecipe = communityService.getTodayRecipe(); + + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, todayRecipe)); + } + //레시피 디테일 + @GetMapping("/DetailRecipe") + public ResponseEntity> getDetail(@RequestParam(name = "recipeId") Long recipeId) { + + CommunityDetailDTO detailDTOS = communityService.getDetail(recipeId); + + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, detailDTOS)); + } + + // 좋아요 클릭시 +// @PostMapping("/updateLike") +// public ResponseEntity> updateLike(@RequestBody Long recipeId) { +// +// communityService.insertLike(recipeId); +// +// return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, null )); +// } +// +// // 좋아요 삭제시 +// @PostMapping("/deleteLike") +// public ResponseEntity> deleteLike(@RequestBody Long recipeId) { +// +// communityService.removeLike(recipeId); +// +// return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, null )); +// } +// +// // 스크랩 클릭시 +// @PostMapping("/updateScrap") +// public ResponseEntity> updatScrap(@RequestBody Long recipeId) { +// +// communityService.insertScrap(recipeId); +// +// return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, null )); +// } + + //레시피 전체 디테일 + @GetMapping("/AllDetailRecipe") + public ResponseEntity> getAllDetail(@RequestParam(name = "recipeId") Long recipeId) { + + RecipeDetailDTO recipeDetailDTO = communityService.getAllDetail(recipeId); + + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, recipeDetailDTO)); + } + + + + +} diff --git a/src/main/java/com/example/blendish/controller/GPTController.java b/src/main/java/com/example/blendish/controller/GPTController.java new file mode 100644 index 0000000..5c042eb --- /dev/null +++ b/src/main/java/com/example/blendish/controller/GPTController.java @@ -0,0 +1,49 @@ +package com.example.blendish.controller; + +import com.example.blendish.domain.gpt.dto.CustomRecipeReqDTO; +import com.example.blendish.domain.gpt.service.GPTRecipeService; +import com.example.blendish.domain.gpt.service.OpenAIService; +import com.example.blendish.domain.recipe.dto.AddRecipeDTO; +import com.example.blendish.domain.recipe.service.RecipeService; +import com.example.blendish.global.dto.ApiResponseTemplate; +import com.example.blendish.global.response.SuccessCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/gpt") +public class GPTController implements GPTSwagger{ + + private final OpenAIService openAIService; + private final GPTRecipeService gptRecipeService; + private final RecipeService recipeService; + + @GetMapping("/chat") + public ResponseEntity chatWithGpt(@RequestParam String message) { + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, openAIService.getGptResponse(message))); + + } + + @PostMapping("/recipe") + public ResponseEntity> generateCustomRecipe( + @RequestBody CustomRecipeReqDTO request, + @AuthenticationPrincipal UserDetails userDetails) { + + String result = gptRecipeService.getAiGeneratedRecipe(request, userDetails); + + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.CREATED, result)); + } + + @PostMapping("/recipe/save") + public ResponseEntity> saveRecipe(AddRecipeDTO addRecipeDTO, UserDetails userDetails) { + recipeService.createAiRecipe(addRecipeDTO, userDetails.getUsername()); + + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.CREATED, "레시피가 등록되었습니다.")); + } + +} diff --git a/src/main/java/com/example/blendish/controller/GPTSwagger.java b/src/main/java/com/example/blendish/controller/GPTSwagger.java new file mode 100644 index 0000000..6baf51a --- /dev/null +++ b/src/main/java/com/example/blendish/controller/GPTSwagger.java @@ -0,0 +1,30 @@ +package com.example.blendish.controller; + +import com.example.blendish.domain.gpt.dto.CustomRecipeReqDTO; +import com.example.blendish.domain.recipe.dto.AddRecipeDTO; +import com.example.blendish.global.dto.ApiResponseTemplate; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@Tag(name = "GPT API", description = "GPT 관련 API") +public interface GPTSwagger { + + @Operation(summary = "GPT 채팅", description = "GPT에게 메시지를 보내고 응답을 받습니다.") + ResponseEntity chatWithGpt(@RequestParam String message); + + @Operation(summary = "사용자 맞춤 GPT 레시피 생성", description = "사용자 입력을 기반으로 GPT가 맞춤형 레시피를 생성합니다.") + ResponseEntity> generateCustomRecipe( + @RequestBody CustomRecipeReqDTO request, + UserDetails userDetails + ); + + @Operation(summary = "AI 레시피 저장", description = "AI가 생성한 레시피를 데이터베이스에 저장합니다.") + ResponseEntity> saveRecipe( + @RequestBody AddRecipeDTO addRecipeDTO, + UserDetails userDetails + ); +} diff --git a/src/main/java/com/example/blendish/controller/RecipeController.java b/src/main/java/com/example/blendish/controller/RecipeController.java index 23183b9..aec4ab5 100644 --- a/src/main/java/com/example/blendish/controller/RecipeController.java +++ b/src/main/java/com/example/blendish/controller/RecipeController.java @@ -1,15 +1,17 @@ package com.example.blendish.controller; import com.example.blendish.domain.recipe.dto.AddRecipeDTO; -import com.example.blendish.domain.recipe.entity.Recipe; import com.example.blendish.domain.recipe.service.RecipeService; import com.example.blendish.global.dto.ApiResponseTemplate; import com.example.blendish.global.response.SuccessCode; +import com.example.blendish.global.s3.S3UploadService; +import java.io.IOException; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; @RestController @RequiredArgsConstructor @@ -17,11 +19,14 @@ public class RecipeController implements RecipeSwagger{ private final RecipeService recipeService; + private final S3UploadService s3UploadService; - @PostMapping + @PostMapping(consumes = {"multipart/form-data"}) public ResponseEntity> addRecipe(@RequestBody AddRecipeDTO addRecipeDTO, - @AuthenticationPrincipal UserDetails userDetails) { - recipeService.createRecipe(addRecipeDTO, userDetails.getUsername()); + @RequestPart(value = "image", required = false) MultipartFile image, + @AuthenticationPrincipal UserDetails userDetails) throws IOException { + String imageUrl = (image != null && !image.isEmpty()) ? s3UploadService.saveFile(image) : null; + recipeService.createUserRecipe(addRecipeDTO, userDetails.getUsername(), imageUrl); return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, "레시피가 등록되었습니다.")); } } diff --git a/src/main/java/com/example/blendish/controller/RecipeSwagger.java b/src/main/java/com/example/blendish/controller/RecipeSwagger.java index c50c359..562eb33 100644 --- a/src/main/java/com/example/blendish/controller/RecipeSwagger.java +++ b/src/main/java/com/example/blendish/controller/RecipeSwagger.java @@ -2,17 +2,19 @@ import com.example.blendish.domain.recipe.dto.AddRecipeDTO; import com.example.blendish.global.dto.ApiResponseTemplate; -import com.example.blendish.global.response.SuccessCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import java.io.IOException; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; public interface RecipeSwagger { @@ -29,5 +31,6 @@ public interface RecipeSwagger { ) @PostMapping("/api/recipe") ResponseEntity> addRecipe(@RequestBody AddRecipeDTO addRecipeDTO, - @AuthenticationPrincipal UserDetails userDetails); + @RequestPart(value = "image", required = false) MultipartFile image, + @AuthenticationPrincipal UserDetails userDetails) throws IOException; } diff --git a/src/main/java/com/example/blendish/controller/UserController.java b/src/main/java/com/example/blendish/controller/UserController.java index 64690bd..4d76e16 100644 --- a/src/main/java/com/example/blendish/controller/UserController.java +++ b/src/main/java/com/example/blendish/controller/UserController.java @@ -1,17 +1,22 @@ package com.example.blendish.controller; import com.example.blendish.domain.user.dto.UserDTO; +import com.example.blendish.domain.user.dto.check.CheckUserPwDTO; import com.example.blendish.domain.user.service.UserService; import com.example.blendish.global.dto.ApiResponseTemplate; import com.example.blendish.global.response.SuccessCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.List; + @Tag(name = "User Controller", description = "사용자 관리 관련 API") @Controller @RequestMapping("/api/user") @@ -32,4 +37,46 @@ public ResponseEntity>> getAllUsers() { List users = userService.getAllUsers(); return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, users)); } + + @Operation( + summary = "사용자 정보 업데이트", + description = "전체 사용자 정보를 전달받아 유저 정보를 업데이트 한다." + ) + @PutMapping(value = "/update", consumes = {"multipart/form-data"}) + public ResponseEntity> updateUser( + @RequestPart("user") UserDTO userDTO, + @RequestPart(value = "profilePic", required = false) MultipartFile profilePic + ) throws IOException { + UserDTO updatedUser = userService.updateUser(userDTO, profilePic); + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, updatedUser)); + } + + @Operation( + summary = "비밀번호 확인", + description = "입력받은 비밀번호가 저장된 비밀번호와 동일한지 확인한다." + ) + @PostMapping("/check") + public ResponseEntity> checkPassword(@RequestBody CheckUserPwDTO dto) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userId = authentication.getName(); + System.out.println(userId); + + boolean result = userService.checkPassword(userId, dto.getPassword()); + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, result)); + } + + @Operation( + summary = "현재 로그인된 사용자 조회", + description = "토큰에 포함된 사용자 ID를 기반으로 전체 사용자 데이터를 반환한다." + ) + @GetMapping("/me") + public ResponseEntity> getUserById() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userId = authentication.getName(); + System.out.println(userId); + UserDTO userDTO = userService.getUserById(userId); + return ResponseEntity.ok(ApiResponseTemplate.success(SuccessCode.OK, userDTO)); + } + } diff --git a/src/main/java/com/example/blendish/domain/comments/dto/.gitkeep b/src/main/java/com/example/blendish/domain/comments/dto/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/comments/dto/CommentAllDTO.java b/src/main/java/com/example/blendish/domain/comments/dto/CommentAllDTO.java new file mode 100644 index 0000000..84a750e --- /dev/null +++ b/src/main/java/com/example/blendish/domain/comments/dto/CommentAllDTO.java @@ -0,0 +1,20 @@ +package com.example.blendish.domain.comments.dto; + +import lombok.*; + +import java.util.Date; +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CommentAllDTO { + private Long commentId; + private String userId; + private String profilePic; + private String content; + private Date createdAt; + private List ReplyList; +} diff --git a/src/main/java/com/example/blendish/domain/comments/dto/CommentDTO.java b/src/main/java/com/example/blendish/domain/comments/dto/CommentDTO.java new file mode 100644 index 0000000..4617801 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/comments/dto/CommentDTO.java @@ -0,0 +1,19 @@ +package com.example.blendish.domain.comments.dto; + +import lombok.*; + +import java.util.Date; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CommentDTO { + private Long commentId; + private String userId; + private String profilePic; + private String content; + private Date createdAt; + private Long numOfReply; +} diff --git a/src/main/java/com/example/blendish/domain/comments/entity/.gitkeep b/src/main/java/com/example/blendish/domain/comments/entity/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/comments/entity/Comment.java b/src/main/java/com/example/blendish/domain/comments/entity/Comment.java new file mode 100644 index 0000000..478d3d7 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/comments/entity/Comment.java @@ -0,0 +1,45 @@ +package com.example.blendish.domain.comments.entity; + +import com.example.blendish.domain.recipe.entity.Recipe; +import com.example.blendish.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.List; + +@Entity +@Getter +@NoArgsConstructor +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long commentId; + + @Column(nullable = false, length = 100) + private String content; + + @Temporal(TemporalType.DATE) + private Date createdAt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "userId", nullable = false) + private User user; + + @OneToMany(mappedBy = "parentComment", cascade = CascadeType.ALL, orphanRemoval = true) + private List replies; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parentCommentId", nullable = true, insertable = false, updatable = false) + private Comment parentComment; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipeId", nullable = false) + private Recipe recipe; + + @PrePersist + public void prePersist() { + this.createdAt = new Date(); + } +} diff --git a/src/main/java/com/example/blendish/domain/comments/repository/.gitkeep b/src/main/java/com/example/blendish/domain/comments/repository/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/comments/repository/CommentsRepository.java b/src/main/java/com/example/blendish/domain/comments/repository/CommentsRepository.java new file mode 100644 index 0000000..3a3e070 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/comments/repository/CommentsRepository.java @@ -0,0 +1,31 @@ +package com.example.blendish.domain.comments.repository; + +import com.example.blendish.domain.comments.dto.CommentDTO; +import com.example.blendish.domain.comments.entity.Comment; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import org.springframework.data.domain.Pageable; +import java.util.List; + +public interface CommentsRepository extends JpaRepository { + // 해당 recipeId에 대한 댓글 개수 카운트 + @Query("SELECT COUNT(c) FROM Comment c WHERE c.recipe.recipeId = :recipeId") + int countByRecipeRecipeId(@Param("recipeId") Long recipeId); + + // 해당 게시글에 대한 댓글 + @Query(value = "SELECT new com.example.blendish.domain.comments.dto.CommentDTO(c.commentId, c.user.userId, c.user.profilePic, c.content, c.createdAt, " + + "(SELECT COUNT(r) as numOfReply FROM Comment r WHERE r.parentComment.commentId = c.commentId)) " + + "FROM Comment c WHERE c.recipe.recipeId = :recipeId AND c.parentComment IS NULL " + + "ORDER BY c.createdAt DESC ") + List getCommentLately(@Param("recipeId") Long recipeId); + + // 전체 댓글 띄우기 + @Query("SELECT c FROM Comment c LEFT JOIN FETCH c.replies WHERE c.recipe.recipeId = :recipeId AND c.parentComment IS NULL") + List getCommentsByRecipeRecipeId(@Param("recipeId") Long recipeId); + + + +} diff --git a/src/main/java/com/example/blendish/domain/comments/service/.gitkeep b/src/main/java/com/example/blendish/domain/comments/service/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/comments/service/CommentService.java b/src/main/java/com/example/blendish/domain/comments/service/CommentService.java new file mode 100644 index 0000000..43dc0de --- /dev/null +++ b/src/main/java/com/example/blendish/domain/comments/service/CommentService.java @@ -0,0 +1,64 @@ +package com.example.blendish.domain.comments.service; + +import com.example.blendish.domain.comments.dto.CommentAllDTO; +import com.example.blendish.domain.comments.dto.CommentDTO; +import com.example.blendish.domain.comments.entity.Comment; +import com.example.blendish.domain.comments.repository.CommentsRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@AllArgsConstructor +public class CommentService { + private final CommentsRepository commentsRepository; + + // 부모 댓글띄우기 + public List getParentCommnet(Long recipeId){ + return commentsRepository.getCommentLately(recipeId); + } + + public List getAllComment(Long recipeId) { + List comments = commentsRepository.getCommentsByRecipeRecipeId(recipeId); + + List commentDTOList = new ArrayList<>(); + + for (Comment comment : comments) { + // 부모 댓글을 DTO로 변환 + CommentAllDTO commentDTO = CommentAllDTO.builder() + .commentId(comment.getCommentId()) + .createdAt(comment.getCreatedAt()) + .content(comment.getContent()) + .profilePic(comment.getUser().getProfilePic()) + .userId(comment.getUser().getUserId()) + .ReplyList(getChildComments(comment.getReplies())) + .build(); + + commentDTOList.add(commentDTO); + } + + return commentDTOList; + } + + // 대댓글을 재귀적으로 가져오는 메서드 + private List getChildComments(List childs) { + List childDTOList = new ArrayList<>(); + + for (int i=0; i { + request.getHeaders().add("Authorization", "Bearer " + openAiKey); + return execution.execute(request, body); + }); + return restTemplate; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/blendish/domain/gpt/dto/CustomRecipeReqDTO.java b/src/main/java/com/example/blendish/domain/gpt/dto/CustomRecipeReqDTO.java new file mode 100644 index 0000000..911cf50 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/gpt/dto/CustomRecipeReqDTO.java @@ -0,0 +1,12 @@ +package com.example.blendish.domain.gpt.dto; + +import java.util.List; + +public record CustomRecipeReqDTO( + String category, + int cookingTime, + String difficulty, + List tastes, + int spiceLevel +) { +} diff --git a/src/main/java/com/example/blendish/domain/gpt/service/GPTRecipeService.java b/src/main/java/com/example/blendish/domain/gpt/service/GPTRecipeService.java new file mode 100644 index 0000000..126d6ca --- /dev/null +++ b/src/main/java/com/example/blendish/domain/gpt/service/GPTRecipeService.java @@ -0,0 +1,109 @@ +package com.example.blendish.domain.gpt.service; + +import com.example.blendish.domain.gpt.dto.CustomRecipeReqDTO; +import com.example.blendish.domain.user.entity.User; +import com.example.blendish.domain.user.repository.UserRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class GPTRecipeService { + + private static final Logger logger = LoggerFactory.getLogger(GPTRecipeService.class); + + private final RestTemplate restTemplate; + private final UserRepository userRepository; + private final ObjectMapper objectMapper; + + @Value("${openai.api.key}") + private String openAiApiKey; + + public String getAiGeneratedRecipe(CustomRecipeReqDTO request, @AuthenticationPrincipal UserDetails userDetails) { + String userId = userDetails.getUsername(); + User user = userRepository.findByUserId(userId); + + if (user == null) { + throw new IllegalArgumentException("유저를 찾을 수 없습니다."); + } + + String prompt = generateRecipePrompt(request, user.getCountry()); + + Map requestBody = Map.of( + "model", "gpt-4o", + "messages", List.of( + Map.of("role", "system", "content", "너는 요리 레시피 추천 AI야. 요청에 따라 요리를 추천해줘."), + Map.of("role", "user", "content", prompt.replaceAll("\\[|\\]", "")) + ), + "temperature", 0.7 + ); + + try { + String requestJson = objectMapper.writeValueAsString(requestBody); + logger.info("Sending request to OpenAI: {}", requestJson); + } catch (Exception e) { + logger.error("Error converting request to JSON", e); + } + + String url = "https://api.openai.com/v1/chat/completions"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(openAiApiKey); + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity responseEntity = restTemplate.exchange( + url, HttpMethod.POST, requestEntity, Map.class + ); + + Map responseBody = responseEntity.getBody(); + if (responseBody != null && responseBody.containsKey("choices")) { + List> choices = (List>) responseBody.get("choices"); + if (!choices.isEmpty()) { + Map firstChoice = choices.get(0); + Map message = (Map) firstChoice.get("message"); + return message.getOrDefault("content", "레시피를 생성하는 데 실패했습니다."); + } + } + return "레시피를 생성하는 데 실패했습니다."; + + } catch (HttpClientErrorException e) { + logger.error("OpenAI API Error: StatusCode = {}, ResponseBody = {}", e.getStatusCode(), e.getResponseBodyAsString()); + return "OpenAI API 요청 중 오류가 발생했습니다."; + } catch (Exception e) { + logger.error("Unexpected error calling OpenAI API", e); + return "레시피를 생성하는 중 오류가 발생했습니다."; + } + } + + private String generateRecipePrompt(CustomRecipeReqDTO request, String country) { + return String.format(""" + %s 국가에서 쉽게 구할 수 있는 재료를 활용하여 %s, %s 맛의 %d분 내 조리 가능한 %s 요리를 추천해줘. + 맵기 레벨 %d의 4가지 레시피를 제공해줘. + 각 레시피는 다음 형식으로: + 1. [레시피 이름] + - 조리 시간: [시간]분 + - 난이도: [쉬움/보통/어려움] + - 특징: [레시피의 특징] + - 재료: [재료 목록 및 계량] + - 재료 팁: [%s에서 재료를 구할 수 있는 팁] + - 조리 순서 (단계별로 설명): + """, + country, request.tastes(), request.difficulty(), request.cookingTime(), request.category(), request.spiceLevel(), country + ); + } +} diff --git a/src/main/java/com/example/blendish/domain/gpt/service/OpenAIService.java b/src/main/java/com/example/blendish/domain/gpt/service/OpenAIService.java new file mode 100644 index 0000000..a1295a4 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/gpt/service/OpenAIService.java @@ -0,0 +1,60 @@ +package com.example.blendish.domain.gpt.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +@Service +public class OpenAIService { + + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; // JSON 파싱을 위한 ObjectMapper + + public OpenAIService(RestTemplate restTemplate, ObjectMapper objectMapper) { + this.restTemplate = restTemplate; + this.objectMapper = objectMapper; + } + + public String getGptResponse(String userMessage) { + String url = "https://api.openai.com/v1/chat/completions"; // OpenAI API 엔드포인트 + + // 요청 본문 (JSON) + Map requestBody = Map.of( + "model", "gpt-4", + "messages", List.of( + Map.of("role", "system", "content", "You are a helpful assistant."), + Map.of("role", "user", "content", userMessage) + ), + "max_tokens", 100 + ); + + // HTTP 헤더 설정 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth("YOUR_OPENAI_API_KEY"); // 🔴 OpenAI API 키 설정 + + // HTTP 요청 객체 생성 + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // API 호출 및 응답 처리 + ResponseEntity responseEntity = restTemplate.exchange( + url, HttpMethod.POST, requestEntity, Map.class + ); + + // 응답에서 메시지 추출 + Map responseBody = responseEntity.getBody(); + if (responseBody != null && responseBody.containsKey("choices")) { + List> choices = (List>) responseBody.get("choices"); + if (!choices.isEmpty()) { + Map firstChoice = choices.get(0); + Map message = (Map) firstChoice.get("message"); + return message.getOrDefault("content", "No response from GPT."); + } + } + return "No response from GPT."; + } +} diff --git a/src/main/java/com/example/blendish/domain/recipe/dto/.gitkeep b/src/main/java/com/example/blendish/domain/recipe/dto/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/recipe/dto/AddRecipeDTO.java b/src/main/java/com/example/blendish/domain/recipe/dto/AddRecipeDTO.java index d5619b4..8a53b7e 100644 --- a/src/main/java/com/example/blendish/domain/recipe/dto/AddRecipeDTO.java +++ b/src/main/java/com/example/blendish/domain/recipe/dto/AddRecipeDTO.java @@ -1,13 +1,12 @@ package com.example.blendish.domain.recipe.dto; import com.example.blendish.domain.recipe.entity.Ingredient; -import com.example.blendish.domain.recipe.entity.RecipeSteps; import java.util.List; public record AddRecipeDTO(String name, String level, List ingredients, String information, - String foodImage, + boolean isAiGenerated, List steps) { } diff --git a/src/main/java/com/example/blendish/domain/recipe/dto/CommunityDetailDTO.java b/src/main/java/com/example/blendish/domain/recipe/dto/CommunityDetailDTO.java new file mode 100644 index 0000000..cb56a6f --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/dto/CommunityDetailDTO.java @@ -0,0 +1,32 @@ +package com.example.blendish.domain.recipe.dto; + +import com.example.blendish.domain.comments.dto.CommentDTO; +import lombok.*; + +import java.util.Date; +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CommunityDetailDTO { + private Long recipeId; + private String foodImage; + private String level; + private String time; + private Date postDate; + private String userId; + private String profilePic; + private String name; + private int spicyLevel; + private boolean isHart; + private boolean isScrap; + private int crapCount; + private int likeCount; + private int commentCount; + private String information; + private List commentDTOList; + private List flavor; +} diff --git a/src/main/java/com/example/blendish/domain/recipe/dto/CommunityHotRecipeDTO.java b/src/main/java/com/example/blendish/domain/recipe/dto/CommunityHotRecipeDTO.java new file mode 100644 index 0000000..e11352c --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/dto/CommunityHotRecipeDTO.java @@ -0,0 +1,18 @@ +package com.example.blendish.domain.recipe.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class CommunityHotRecipeDTO { + private Long recipeId; + private String foodImage; + private int likeCount; + private int commentCount; + private String name; +} diff --git a/src/main/java/com/example/blendish/domain/recipe/dto/CommunityTodayRecipeDTO.java b/src/main/java/com/example/blendish/domain/recipe/dto/CommunityTodayRecipeDTO.java new file mode 100644 index 0000000..c5a36f5 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/dto/CommunityTodayRecipeDTO.java @@ -0,0 +1,16 @@ +package com.example.blendish.domain.recipe.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class CommunityTodayRecipeDTO { + private Long recipeId; + private String foodImage; + private String name; +} diff --git a/src/main/java/com/example/blendish/domain/recipe/dto/RecipeDetailDTO.java b/src/main/java/com/example/blendish/domain/recipe/dto/RecipeDetailDTO.java new file mode 100644 index 0000000..9907d0e --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/dto/RecipeDetailDTO.java @@ -0,0 +1,24 @@ +package com.example.blendish.domain.recipe.dto; + +import com.example.blendish.domain.comments.dto.CommentDTO; +import com.example.blendish.domain.recipe.entity.Ingredient; +import com.example.blendish.domain.recipe.entity.RecipeSteps; +import com.example.blendish.domain.recipesteps.dto.RecipeStepsDTO; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class RecipeDetailDTO { + private Long recipeId; + private String level; + private String time; + private String name; + private List ingredients; + private RecipeStepsDTO recipeSteps; + +} diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/.gitkeep b/src/main/java/com/example/blendish/domain/recipe/entity/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/AiIngredient.java b/src/main/java/com/example/blendish/domain/recipe/entity/AiIngredient.java new file mode 100644 index 0000000..740fd62 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/entity/AiIngredient.java @@ -0,0 +1,27 @@ +package com.example.blendish.domain.recipe.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Table(name = "ai_ingredient") +public class AiIngredient { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 100) + private String name; + + @Column(nullable = false, length = 50) + private String amount; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private Recipe recipe; +} diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/AiRecipeSteps.java b/src/main/java/com/example/blendish/domain/recipe/entity/AiRecipeSteps.java new file mode 100644 index 0000000..70084dc --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/entity/AiRecipeSteps.java @@ -0,0 +1,36 @@ +package com.example.blendish.domain.recipe.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiRecipeSteps { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long rsId; + + @Column(nullable = false) + private int stepNum; + + @Column(length = 200) + private String details; + + @Column(length = 255) + private String stepImage; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipeId", nullable = false) + private Recipe recipe; + + public void updateRecipe(Recipe recipe) { + this.recipe = recipe; + } +} diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/Ingredient.java b/src/main/java/com/example/blendish/domain/recipe/entity/Ingredient.java index 8acc197..30d99a7 100644 --- a/src/main/java/com/example/blendish/domain/recipe/entity/Ingredient.java +++ b/src/main/java/com/example/blendish/domain/recipe/entity/Ingredient.java @@ -1,7 +1,27 @@ package com.example.blendish.domain.recipe.entity; -import jakarta.persistence.Embeddable; +import jakarta.persistence.*; +import lombok.*; -@Embeddable -public record Ingredient(String name, String amount) { +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Table(name = "ingredient") +public class Ingredient { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 100) + private String name; + + @Column(nullable = false, length = 50) + private String amount; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private Recipe recipe; } diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/Likes.java b/src/main/java/com/example/blendish/domain/recipe/entity/Likes.java new file mode 100644 index 0000000..a9ee496 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/entity/Likes.java @@ -0,0 +1,28 @@ +package com.example.blendish.domain.recipe.entity; + +import com.example.blendish.domain.recipe.entity.Recipe; +import com.example.blendish.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@NoArgsConstructor +@Entity +@Setter +public class Likes { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long likeId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "userId", nullable = false) + private User user; + + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipeId", nullable = false) + private Recipe recipe; + +} diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/Recipe.java b/src/main/java/com/example/blendish/domain/recipe/entity/Recipe.java index 5d577f1..97f1673 100644 --- a/src/main/java/com/example/blendish/domain/recipe/entity/Recipe.java +++ b/src/main/java/com/example/blendish/domain/recipe/entity/Recipe.java @@ -1,22 +1,20 @@ package com.example.blendish.domain.recipe.entity; +import com.example.blendish.domain.comments.entity.Comment; +import com.example.blendish.domain.foodflavor.entity.FoodFlavor; import com.example.blendish.domain.user.entity.User; import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.util.Date; import java.util.List; -import org.springframework.data.relational.core.sql.In; @Entity @Getter @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor +@Table(name = "recipe") public class Recipe { @Id @@ -29,12 +27,18 @@ public class Recipe { @Column(nullable = false, length = 1) private String level; - @ElementCollection - @CollectionTable(name = "recipe_ingredients", joinColumns = @JoinColumn(name = "recipe_id")) + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) private List ingredients; private int likeCount; + @Column(nullable = false, length = 100) + private String time; + + private int scrapCount; + + private int spicyLevel; + @Column(length = 200) private String information; @@ -54,6 +58,27 @@ public class Recipe { @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) private List steps; + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List aiSteps; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List comment; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List likes; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List scraps; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List foodFlavors; + + @Column(nullable = false) + private boolean isAiGenerated; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true) + private List aiIngredients; + @PrePersist protected void onCreate() { this.postDate = new Date(); @@ -64,5 +89,4 @@ protected void onCreate() { protected void onUpdate() { this.updatedDate = new Date(); } - } diff --git a/src/main/java/com/example/blendish/domain/recipe/entity/Scrap.java b/src/main/java/com/example/blendish/domain/recipe/entity/Scrap.java new file mode 100644 index 0000000..455846b --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/entity/Scrap.java @@ -0,0 +1,27 @@ +package com.example.blendish.domain.recipe.entity; + +import com.example.blendish.domain.recipe.entity.Recipe; +import com.example.blendish.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@NoArgsConstructor +@Entity +@Setter +public class Scrap { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long scrapId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "userId", nullable = false) + private User user; + + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipeId", nullable = false) + private Recipe recipe; +} diff --git a/src/main/java/com/example/blendish/domain/recipe/repository/.gitkeep b/src/main/java/com/example/blendish/domain/recipe/repository/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/recipe/repository/LikeRepository.java b/src/main/java/com/example/blendish/domain/recipe/repository/LikeRepository.java new file mode 100644 index 0000000..9e12dd7 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/repository/LikeRepository.java @@ -0,0 +1,16 @@ +package com.example.blendish.domain.recipe.repository; + +import com.example.blendish.domain.recipe.entity.Likes; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface LikeRepository extends JpaRepository { + @Query("SELECT l.recipe.recipeId FROM Likes l GROUP BY l.recipe.recipeId ORDER BY COUNT(l) DESC") + List findTopLikedRecipeIds(); + + void deleteByRecipeRecipeIdAndUserId(Long recipeId,Long userId); +} diff --git a/src/main/java/com/example/blendish/domain/recipe/repository/RecipeRepository.java b/src/main/java/com/example/blendish/domain/recipe/repository/RecipeRepository.java index 42f20da..3337331 100644 --- a/src/main/java/com/example/blendish/domain/recipe/repository/RecipeRepository.java +++ b/src/main/java/com/example/blendish/domain/recipe/repository/RecipeRepository.java @@ -1,10 +1,41 @@ package com.example.blendish.domain.recipe.repository; +import com.example.blendish.domain.recipe.entity.Ingredient; import com.example.blendish.domain.recipe.entity.Recipe; import com.example.blendish.domain.user.entity.User; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; public interface RecipeRepository extends JpaRepository { + Recipe findByRecipeId(Long recipiId); + + @Query("SELECT r FROM Recipe r ORDER BY FUNCTION('RAND')") // 랜덤으로 레시피 정렬 + List findRandomRecipes(); + List findByUser(User user); + + // like 증가 +// @Modifying +// @Query("UPDATE Recipe r SET r.likeCount = r.likeCount + 1 WHERE r.recipeId = :recipeId") +// void incrementLikeCount(Long recipeId); +// +// // like 감소 +// @Modifying +// @Query("UPDATE Recipe r SET r.likeCount = r.likeCount - 1 WHERE r.recipeId = :recipeId") +// void decrementLikeCount(Long recipeId); +// +// // 스크랩 증가 +// @Modifying +// @Query("UPDATE Recipe r SET r.scrapCount = r.scrapCount + 1 WHERE r.recipeId = :recipeId") +// void incrementScrapCount(Long recipeId); +// +// @Modifying +// @Query("UPDATE Recipe r SET r.scrapCount = r.likeCount - 1 WHERE r.recipeId = :recipeId") +// void decrementScrapCount(Long recipeId); + + } diff --git a/src/main/java/com/example/blendish/domain/recipe/repository/ScrapRepository.java b/src/main/java/com/example/blendish/domain/recipe/repository/ScrapRepository.java new file mode 100644 index 0000000..252d2a4 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/repository/ScrapRepository.java @@ -0,0 +1,9 @@ +package com.example.blendish.domain.recipe.repository; + +import com.example.blendish.domain.recipe.entity.Scrap; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ScrapRepository extends JpaRepository { + +// void deleteByRecipeRecipeIdAndUserId(Long recipeId,Long userId); +} diff --git a/src/main/java/com/example/blendish/domain/recipe/service/.gitkeep b/src/main/java/com/example/blendish/domain/recipe/service/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/recipe/service/CommunityService.java b/src/main/java/com/example/blendish/domain/recipe/service/CommunityService.java new file mode 100644 index 0000000..bf7c1c3 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipe/service/CommunityService.java @@ -0,0 +1,227 @@ +package com.example.blendish.domain.recipe.service; + +import com.example.blendish.domain.comments.dto.CommentDTO; +import com.example.blendish.domain.comments.repository.CommentsRepository; +import com.example.blendish.domain.recipe.dto.CommunityDetailDTO; +import com.example.blendish.domain.recipe.dto.CommunityHotRecipeDTO; +import com.example.blendish.domain.recipe.dto.CommunityTodayRecipeDTO; +import com.example.blendish.domain.recipe.dto.RecipeDetailDTO; +import com.example.blendish.domain.recipe.entity.Recipe; +import com.example.blendish.domain.recipe.entity.RecipeSteps; +import com.example.blendish.domain.recipe.repository.LikeRepository; +import com.example.blendish.domain.recipe.repository.RecipeRepository; +import com.example.blendish.domain.recipe.repository.ScrapRepository; +import com.example.blendish.domain.recipesteps.dto.RecipeStepsDTO; +import com.example.blendish.domain.recipesteps.repository.RecipestepsRepository; +import io.jsonwebtoken.lang.Collections; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@AllArgsConstructor +@Slf4j +public class CommunityService { + private final RecipeRepository recipeRepository; + private final LikeRepository likeRepository; + private final CommentsRepository commentsRepository; + private final ScrapRepository scrapRepository; + private final RecipestepsRepository recipestepsRepository; + + // 인기 레시피 가져오는 서비스 + public List getTopLikedRecipes() { + + List topLikedRecipi = new ArrayList<>(); + + // 좋아요 많은 id 가져오기 + List topLikedRecipeIds = likeRepository.findTopLikedRecipeIds(); + + + if (topLikedRecipeIds.isEmpty()){ + return Collections.emptyList(); + } + + // 가져온거 4번 돌리면서 dto 에 집어넣기 + for (int i = 0; i < Math.min(topLikedRecipeIds.size(), 4); i++) { + Long recipeId = topLikedRecipeIds.get(i); + // 레시피 객체 가져오기 + Recipe recipe = recipeRepository.findByRecipeId(recipeId); + // 코멘트 개수 가져오기 + int CommentNum = commentsRepository.countByRecipeRecipeId(recipeId); + + CommunityHotRecipeDTO topRecipes = new CommunityHotRecipeDTO(recipeId,recipe.getFoodImage(),recipe.getLikeCount(),CommentNum,recipe.getName()); + + + topLikedRecipi.add(topRecipes); + } + + return topLikedRecipi; + } + + // 오늘의 레시피 랜덤 띄우기 + public List getTodayRecipe(){ + List RandomRecipe = new ArrayList<>(); + List rendomRecipeRepo = recipeRepository.findRandomRecipes(); + + for(int i=0; i comments = commentsRepository.getCommentLately(recipeId); + + // 리스트에서 최신 2개만 가져오기 + List latestComments = comments.stream() + .limit(2) + .collect(Collectors.toList()); + + + // 맛 List 뽑기 + List flavorList = new ArrayList<>(); + for(int i=0; i steps = addRecipeDTO.steps().stream() + .map(stepDTO -> AiRecipeSteps.builder() + .stepNum(stepDTO.stepNumber()) + .details(stepDTO.details()) + .build()) + .collect(Collectors.toList()); + + List ingredients = addRecipeDTO.ingredients(); + + Recipe recipe = Recipe.builder() + .name(addRecipeDTO.name()) + .level(addRecipeDTO.level()) + .ingredients(ingredients) + .information(addRecipeDTO.information()) + .foodImage(null) + .user(user) + .isAiGenerated(true) + .aiSteps(steps) + .build(); + + steps.forEach(step -> step.updateRecipe(recipe)); + + recipeRepository.save(recipe); + } + } diff --git a/src/main/java/com/example/blendish/domain/recipesteps/dto/.gitkeep b/src/main/java/com/example/blendish/domain/recipesteps/dto/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/recipesteps/dto/RecipeStepsDTO.java b/src/main/java/com/example/blendish/domain/recipesteps/dto/RecipeStepsDTO.java new file mode 100644 index 0000000..e8dcffa --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipesteps/dto/RecipeStepsDTO.java @@ -0,0 +1,17 @@ +package com.example.blendish.domain.recipesteps.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.stereotype.Service; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class RecipeStepsDTO { + private String details; + private String stepImage; + private int stepNum; +} diff --git a/src/main/java/com/example/blendish/domain/recipesteps/repository/.gitkeep b/src/main/java/com/example/blendish/domain/recipesteps/repository/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/example/blendish/domain/recipesteps/repository/RecipestepsRepository.java b/src/main/java/com/example/blendish/domain/recipesteps/repository/RecipestepsRepository.java new file mode 100644 index 0000000..b2a467b --- /dev/null +++ b/src/main/java/com/example/blendish/domain/recipesteps/repository/RecipestepsRepository.java @@ -0,0 +1,9 @@ +package com.example.blendish.domain.recipesteps.repository; + +import com.example.blendish.domain.recipe.dto.RecipeStepDTO; +import com.example.blendish.domain.recipe.entity.RecipeSteps; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipestepsRepository extends JpaRepository { + RecipeSteps findByRecipeRecipeId(Long recipeId); +} diff --git a/src/main/java/com/example/blendish/domain/user/config/SecurityConfig.java b/src/main/java/com/example/blendish/domain/user/config/SecurityConfig.java index b3b0a4b..e172fc9 100644 --- a/src/main/java/com/example/blendish/domain/user/config/SecurityConfig.java +++ b/src/main/java/com/example/blendish/domain/user/config/SecurityConfig.java @@ -54,27 +54,6 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - - http - .cors((corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { - - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - - CorsConfiguration configuration = new CorsConfiguration(); - - configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); - configuration.setAllowedMethods(Collections.singletonList("*")); - configuration.setAllowCredentials(true); - configuration.setAllowedHeaders(Collections.singletonList("*")); - configuration.setMaxAge(3600L); - - configuration.setExposedHeaders(Collections.singletonList("Authorization")); - - return configuration; - } - }))); - http .csrf((auth) -> auth.disable()); diff --git a/src/main/java/com/example/blendish/domain/user/config/UserMapper.java b/src/main/java/com/example/blendish/domain/user/config/UserMapper.java index 3f413be..50ade98 100644 --- a/src/main/java/com/example/blendish/domain/user/config/UserMapper.java +++ b/src/main/java/com/example/blendish/domain/user/config/UserMapper.java @@ -32,6 +32,34 @@ public static UserDTO toDTO(User user) { return dto; } + public static User toEntity(UserDTO dto) { + if (dto == null) return null; + + User user = new User(); + user.setUserId(dto.getUserId()); + user.setUserPw(dto.getUserPw()); + user.setEmail(dto.getEmail()); + user.setHometown(dto.getHometown()); + user.setCountry(dto.getCountry()); + user.setProfilePic(dto.getProfilePic()); + user.setRole(dto.getRole()); + + if (dto.getTastePreference() != null) { + List tastePreferences = dto.getTastePreference().stream() + .map(tpDTO -> { + TastePreference tastePreference = new TastePreference(); + tastePreference.setTaste(tpDTO.getTaste()); + tastePreference.setSpicyLevel(tpDTO.getSpicyLevel()); + tastePreference.setUser(user); + return tastePreference; + }) + .collect(Collectors.toList()); + user.setTastePreferences(tastePreferences); + } + + return user; + } + private static TastePreferenceDTO mapTastePreference(TastePreference tastePreference) { TastePreferenceDTO dto = new TastePreferenceDTO(); dto.setTaste(tastePreference.getTaste()); diff --git a/src/main/java/com/example/blendish/domain/user/controller/JoinController.java b/src/main/java/com/example/blendish/domain/user/controller/JoinController.java index ca16f16..7fbb8ba 100644 --- a/src/main/java/com/example/blendish/domain/user/controller/JoinController.java +++ b/src/main/java/com/example/blendish/domain/user/controller/JoinController.java @@ -1,6 +1,6 @@ package com.example.blendish.domain.user.controller; -import com.example.blendish.domain.user.dto.CheckUserIdDTO; +import com.example.blendish.domain.user.dto.check.CheckUserIdDTO; import com.example.blendish.domain.user.dto.JoinDTO; import com.example.blendish.domain.user.dto.UserDTO; import com.example.blendish.domain.user.dto.preference.UserPreferencesDTO; diff --git a/src/main/java/com/example/blendish/domain/user/dto/CustomUserDetails.java b/src/main/java/com/example/blendish/domain/user/dto/CustomUserDetails.java index 7a9469c..bfe0830 100644 --- a/src/main/java/com/example/blendish/domain/user/dto/CustomUserDetails.java +++ b/src/main/java/com/example/blendish/domain/user/dto/CustomUserDetails.java @@ -17,6 +17,9 @@ public CustomUserDetails(User user) { this.user = user; } + public User getUser() { + return user; + } //롤값 반환 @Override public Collection getAuthorities() { diff --git a/src/main/java/com/example/blendish/domain/user/dto/CheckUserIdDTO.java b/src/main/java/com/example/blendish/domain/user/dto/check/CheckUserIdDTO.java similarity index 62% rename from src/main/java/com/example/blendish/domain/user/dto/CheckUserIdDTO.java rename to src/main/java/com/example/blendish/domain/user/dto/check/CheckUserIdDTO.java index e877769..42b2210 100644 --- a/src/main/java/com/example/blendish/domain/user/dto/CheckUserIdDTO.java +++ b/src/main/java/com/example/blendish/domain/user/dto/check/CheckUserIdDTO.java @@ -1,4 +1,4 @@ -package com.example.blendish.domain.user.dto; +package com.example.blendish.domain.user.dto.check; import lombok.Data; diff --git a/src/main/java/com/example/blendish/domain/user/dto/check/CheckUserPwDTO.java b/src/main/java/com/example/blendish/domain/user/dto/check/CheckUserPwDTO.java new file mode 100644 index 0000000..ae98325 --- /dev/null +++ b/src/main/java/com/example/blendish/domain/user/dto/check/CheckUserPwDTO.java @@ -0,0 +1,8 @@ +package com.example.blendish.domain.user.dto.check; + +import lombok.Data; + +@Data +public class CheckUserPwDTO { + private String password; +} \ No newline at end of file diff --git a/src/main/java/com/example/blendish/domain/user/entity/User.java b/src/main/java/com/example/blendish/domain/user/entity/User.java index 6518a88..b1aaf0c 100644 --- a/src/main/java/com/example/blendish/domain/user/entity/User.java +++ b/src/main/java/com/example/blendish/domain/user/entity/User.java @@ -1,6 +1,9 @@ package com.example.blendish.domain.user.entity; +import com.example.blendish.domain.comments.entity.Comment; +import com.example.blendish.domain.recipe.entity.Likes; +import com.example.blendish.domain.recipe.entity.Scrap; import jakarta.persistence.*; import lombok.Data; @@ -24,4 +27,15 @@ public class User { @OneToMany(mappedBy = "user",cascade = CascadeType.ALL,orphanRemoval = true) private List tastePreferences; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments ; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List likes ; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List scraps ; + } + diff --git a/src/main/java/com/example/blendish/domain/user/jwt/LoginFilter.java b/src/main/java/com/example/blendish/domain/user/jwt/LoginFilter.java index 6d13743..1429819 100644 --- a/src/main/java/com/example/blendish/domain/user/jwt/LoginFilter.java +++ b/src/main/java/com/example/blendish/domain/user/jwt/LoginFilter.java @@ -13,7 +13,6 @@ import java.util.Collection; import java.util.Iterator; - public class LoginFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; @@ -49,7 +48,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR String role = auth.getAuthority(); //토큰 시간!!! - String token = jwtUtil.createJwt(username, role, 60 * 60 * 1000L); + String token = jwtUtil.createJwt(username, role, 48 * 60 * 60 * 1000L); response.addHeader("Authorization", "Bearer " + token); } diff --git a/src/main/java/com/example/blendish/domain/user/service/JoinService.java b/src/main/java/com/example/blendish/domain/user/service/JoinService.java index 946f603..9bbd696 100644 --- a/src/main/java/com/example/blendish/domain/user/service/JoinService.java +++ b/src/main/java/com/example/blendish/domain/user/service/JoinService.java @@ -67,7 +67,6 @@ public UserDTO updateUserPreferences(UserPreferencesDTO dto) { TastePreference tastePreference = new TastePreference(); tastePreference.setUser(user); tastePreference.setTaste(tasteDTO.getTaste()); - tastePreference.setSpicyLevel(tasteDTO.getSpicyLevel()); return tastePreference; }) .collect(Collectors.toList()); diff --git a/src/main/java/com/example/blendish/domain/user/service/UserService.java b/src/main/java/com/example/blendish/domain/user/service/UserService.java index d5aa343..753500d 100644 --- a/src/main/java/com/example/blendish/domain/user/service/UserService.java +++ b/src/main/java/com/example/blendish/domain/user/service/UserService.java @@ -5,10 +5,14 @@ import com.example.blendish.domain.user.entity.User; import com.example.blendish.domain.user.repository.UserRepository; import com.example.blendish.global.dto.ApiResponseTemplate; +import com.example.blendish.global.s3.S3UploadService; import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.List; import java.util.stream.Collectors; @@ -16,9 +20,13 @@ @Transactional public class UserService { private final UserRepository userRepository; + private final S3UploadService s3UploadService; + private final BCryptPasswordEncoder bCryptPasswordEncoder; - public UserService(UserRepository userRepository) { + public UserService(UserRepository userRepository, S3UploadService s3UploadService, BCryptPasswordEncoder bCryptPasswordEncoder) { this.userRepository = userRepository; + this.s3UploadService = s3UploadService; + this.bCryptPasswordEncoder = bCryptPasswordEncoder; } public List getAllUsers() { @@ -27,4 +35,45 @@ public List getAllUsers() { .map(UserMapper::toDTO) .collect(Collectors.toList()); } + + public UserDTO updateUser(UserDTO userDTO, MultipartFile profilePic) throws IOException { + + if (profilePic != null && !profilePic.isEmpty()) { + String uploadedUrl = s3UploadService.saveFile(profilePic); + userDTO.setProfilePic(uploadedUrl); + } + + userDTO.setRole("ROLE_ADMIN"); + + if (userDTO.getUserPw() != null && !userDTO.getUserPw().isEmpty()) { + userDTO.setUserPw(bCryptPasswordEncoder.encode(userDTO.getUserPw())); + } + + User existingUser = userRepository.findByUserId(userDTO.getUserId()); + if (existingUser != null) { + userRepository.delete(existingUser); + userRepository.flush(); + } + + User newUser = UserMapper.toEntity(userDTO); // 기존 UserMapper를 그대로 사용 + newUser = userRepository.save(newUser); + return UserMapper.toDTO(newUser); + } + + public boolean checkPassword(String userId, String rawPassword) { + User user = userRepository.findByUserId(userId); + if (user == null) { + throw new IllegalArgumentException("해당 유저를 찾을 수 없습니다."); + } + return bCryptPasswordEncoder.matches(rawPassword, user.getUserPw()); + } + + public UserDTO getUserById(String userId) { + User user = userRepository.findByUserId(userId); + if (user == null) { + throw new IllegalArgumentException("해당 유저를 찾을 수 없습니다."); + } + return UserMapper.toDTO(user); + } + } diff --git a/src/main/java/com/example/blendish/global/config/CorsMvcConfig.java b/src/main/java/com/example/blendish/global/config/CorsMvcConfig.java deleted file mode 100644 index f3a55a8..0000000 --- a/src/main/java/com/example/blendish/global/config/CorsMvcConfig.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.blendish.global.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class CorsMvcConfig implements WebMvcConfigurer { - - @Override - public void addCorsMappings(CorsRegistry corsRegistry) { - - corsRegistry.addMapping("/**") - .allowedOrigins("http://localhost:3000"); - } -} \ No newline at end of file diff --git a/src/main/java/com/example/blendish/global/config/RestTemplateConfig.java b/src/main/java/com/example/blendish/global/config/RestTemplateConfig.java new file mode 100644 index 0000000..98200aa --- /dev/null +++ b/src/main/java/com/example/blendish/global/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package com.example.blendish.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/com/example/blendish/global/config/SwaggerConfig.java b/src/main/java/com/example/blendish/global/config/SwaggerConfig.java index 0d04a54..6966847 100644 --- a/src/main/java/com/example/blendish/global/config/SwaggerConfig.java +++ b/src/main/java/com/example/blendish/global/config/SwaggerConfig.java @@ -3,6 +3,10 @@ import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,15 +14,31 @@ public class SwaggerConfig { @Bean public OpenAPI openAPI() { + final String securitySchemeName = "bearerAuth"; + + Server server = new Server(); + server.setUrl("https://junyeongan.store"); + server.setDescription("Blendish Server"); + return new OpenAPI() - .components(new Components()) - .info(apiInfo()); + .components(new Components() + .addSecuritySchemes(securitySchemeName, + new SecurityScheme() + .name(securitySchemeName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + ) + ) + .addSecurityItem(new SecurityRequirement().addList(securitySchemeName)) + .info(apiInfo()) + .servers(List.of(server)); } private Info apiInfo() { return new Info() .title("Blendish") .description("나만의 레시피 만들기") - .version("1.0.0"); // API의 버전 + .version("1.0.0"); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/blendish/global/dto/ApiResponseTemplate.java b/src/main/java/com/example/blendish/global/dto/ApiResponseTemplate.java index 497c3a0..098b9ac 100644 --- a/src/main/java/com/example/blendish/global/dto/ApiResponseTemplate.java +++ b/src/main/java/com/example/blendish/global/dto/ApiResponseTemplate.java @@ -2,6 +2,7 @@ import com.example.blendish.global.response.ErrorCode; import com.example.blendish.global.response.SuccessCode; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.*; import java.time.LocalDateTime; @@ -14,7 +15,10 @@ public class ApiResponseTemplate { private final int status; private final String message; - private final LocalDateTime timestamp; // 추가된 날짜/시간 필드 + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") + private final LocalDateTime timestamp; + private T data; public static ApiResponseTemplate success(SuccessCode successCode, T data) { diff --git a/src/main/java/com/example/blendish/global/s3/S3Config.java b/src/main/java/com/example/blendish/global/s3/S3Config.java new file mode 100644 index 0000000..b22ad3c --- /dev/null +++ b/src/main/java/com/example/blendish/global/s3/S3Config.java @@ -0,0 +1,33 @@ +package com.example.blendish.global.s3; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +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 S3Config { + + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String accessSecret; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 s3Client() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, accessSecret); + return AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region).build(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/blendish/global/s3/S3UploadService.java b/src/main/java/com/example/blendish/global/s3/S3UploadService.java new file mode 100644 index 0000000..214b755 --- /dev/null +++ b/src/main/java/com/example/blendish/global/s3/S3UploadService.java @@ -0,0 +1,40 @@ +package com.example.blendish.global.s3; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +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 java.io.InputStream; + +@Service +@RequiredArgsConstructor +public class S3UploadService { + + private final AmazonS3 amazonS3; + @Value("${cloud.aws.s3.bucket-name}") + private String bucket; + + public String saveFile(MultipartFile multipartFile) throws IOException { + String originalFilename = multipartFile.getOriginalFilename(); + + if (originalFilename == null || originalFilename.isEmpty()) { + throw new IllegalArgumentException("파일 이름이 유효하지 않습니다."); + } + + try (InputStream inputStream = multipartFile.getInputStream()) { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(multipartFile.getSize()); + metadata.setContentType(multipartFile.getContentType()); + + amazonS3.putObject(bucket, originalFilename, inputStream, metadata); + } catch (IOException e) { + throw new IOException("파일 업로드 중 오류가 발생했습니다.", e); + } + + return amazonS3.getUrl(bucket, originalFilename).toString(); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 68373f0..f61151c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,5 +18,39 @@ spring: hibernate: format_sql: false use_sql_comments: true + jwt: - secret: ${JWT_SECRET} \ No newline at end of file + secret: ${JWT_SECRET} + + security: + oauth2: + client: + registration: + google: + client-name: google + client-id: ${GOOGLE_CLIENT_ID} + client-secret: ${GOOGLE_CLIENT_SECRET} + redirect-uri: ${GOOGLE_REDIRECT_URI} + authorization-grant-type: authorization_code + scope: profile, email + +openai: + api: + key: ${OPEN_AI_API_KEY} + +cloud: + aws: + s3: + bucket-name: ${AWS_S3_BUCKET_NAME} + credentials: + access-key: ${AWS_ACCESS_KEY_ID} + secret-key: ${AWS_SECRET_ACCESS_KEY} + region: + static: ${AWS_REGION} + stack: + auto: false + +servlet: + multipart: + max-file-size: 50MB + max-request-size: 50MB