From 566e857666f429c59eece5d12bd6caa8c68db020 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 14 Feb 2025 18:39:36 +0900 Subject: [PATCH 001/114] =?UTF-8?q?Settings:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=EC=99=80=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes build.gradle | 6 +++ .../log/common/config/CorsMvcConfig.java | 19 ++++++++ .../log/common/config/SecurityConfig.java | 46 ++++++++++++++++++ .../log/common/config/SwaggerConfig.java | 12 +++++ 5 files changed, 83 insertions(+) create mode 100644 src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java create mode 100644 src/main/java/com/bamboo/log/common/config/SecurityConfig.java create mode 100644 src/main/java/com/bamboo/log/common/config/SwaggerConfig.java diff --git a/.DS_Store b/.DS_Store index dd46a9923d3511c0ddce67c1cc0fd4f8a0b2c125..5770197c32987a55423285d0d1e1dddc77abfd3b 100644 GIT binary patch delta 71 zcmZoMXfc=|#>B)qu~2NHo+2a1#(>?7j2x4BSc)ezvYy!domGHoW5Wif&Fmcf96)88 Z1v$PmPv#eKB!ku~2NHo+2a9#(>?7iw`g}G4f32VJc>1pWMYH6UAu9z`(HVKNtX6 z3=FIcdJLHiDGaF$iJm$6$w@i+NkCBn1_s7yKpItL5<@9aZ4N^Us!9ekph~03B}|v) z*%>?-Tp4^A0)VFGqN`(kx7mVOo@p~X2R{eUotrl@e`lV|FXG6-$iTn^G=gDsgvc6Z E0A!UiL;wH) diff --git a/build.gradle b/build.gradle index b68e796..1049dc1 100644 --- a/build.gradle +++ b/build.gradle @@ -28,11 +28,17 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' developmentOnly 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' } tasks.named('test') { diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java new file mode 100644 index 0000000..fd813ab --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -0,0 +1,19 @@ +package com.bamboo.log.common.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("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") + .allowedHeaders("*") + .allowedOriginPatterns("/**") //프론트 url 사용 + .allowCredentials(true); + } +} diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java new file mode 100644 index 0000000..66f465f --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -0,0 +1,46 @@ +package com.bamboo.log.common.config; + + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Arrays; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.csrf(csrf -> csrf.disable()) + .formLogin(form -> form.disable()) + .httpBasic(httpBasic -> httpBasic.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.addAllowedOriginPattern("/**");//프론트 url 사용 + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + configuration.setExposedHeaders(Arrays.asList("Authorization")); + configuration.setMaxAge(3600L); + + return configuration; + } + })); + return http.build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java new file mode 100644 index 0000000..dd0a87a --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java @@ -0,0 +1,12 @@ +package com.bamboo.log.common.config; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import org.springframework.context.annotation.Configuration; + +@Configuration +@OpenAPIDefinition(info = @Info(title = "ForRest", version = "v1")) +public class SwaggerConfig { +} \ No newline at end of file From 024bb53eb41948bab2b9faac11f2257d23477421 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Wed, 19 Feb 2025 22:41:51 +0900 Subject: [PATCH 002/114] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 + .../com/bamboo/log/config/CorsMvcConfig.java | 18 +++ .../com/bamboo/log/config/SecurityConfig.java | 76 +++++++++++++ .../user/controller/RefreshController.java | 25 +++++ .../domain/user/jwt/service/JWTFilter.java | 76 +++++++++++++ .../log/domain/user/jwt/service/JWTUtil.java | 57 ++++++++++ .../user/logout/CustomLogoutFilter.java | 94 ++++++++++++++++ .../user/oauth/dto/CustomOAuth2User.java | 49 +++++++++ .../domain/user/oauth/dto/KakaoResponse.java | 40 +++++++ .../domain/user/oauth/dto/OAuth2Response.java | 11 ++ .../log/domain/user/oauth/dto/UserDTO.java | 14 +++ .../user/oauth/entity/RefreshToken.java | 40 +++++++ .../domain/user/oauth/entity/UserEntity.java | 45 ++++++++ .../oauth/repository/RefreshRepository.java | 10 ++ .../user/oauth/repository/UserRepository.java | 8 ++ .../oauth/service/CustomFailureHandler.java | 20 ++++ .../service/CustomOAuth2UserService.java | 67 ++++++++++++ .../oauth/service/CustomSuccessHandler.java | 78 +++++++++++++ .../refresh/service/ProcessTokenReissue.java | 9 ++ .../service/ProcessTokenReissueImpl.java | 103 ++++++++++++++++++ src/main/resources/application.yml | 62 ++++++++++- 21 files changed, 901 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/bamboo/log/config/CorsMvcConfig.java create mode 100644 src/main/java/com/bamboo/log/config/SecurityConfig.java create mode 100644 src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java create mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java create mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java create mode 100644 src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java create mode 100644 src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java create mode 100644 src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java diff --git a/build.gradle b/build.gradle index b68e796..cff809d 100644 --- a/build.gradle +++ b/build.gradle @@ -30,9 +30,13 @@ dependencies { developmentOnly 'org.springframework.boot:spring-boot-devtools' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' } tasks.named('test') { diff --git a/src/main/java/com/bamboo/log/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/config/CorsMvcConfig.java new file mode 100644 index 0000000..553586b --- /dev/null +++ b/src/main/java/com/bamboo/log/config/CorsMvcConfig.java @@ -0,0 +1,18 @@ +package com.bamboo.log.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("/**") + .exposedHeaders("Set-Cookie") + .allowedOrigins("http://localhost:3000"); + } +} + diff --git a/src/main/java/com/bamboo/log/config/SecurityConfig.java b/src/main/java/com/bamboo/log/config/SecurityConfig.java new file mode 100644 index 0000000..b2ddff4 --- /dev/null +++ b/src/main/java/com/bamboo/log/config/SecurityConfig.java @@ -0,0 +1,76 @@ +package com.bamboo.log.config; + +import com.bamboo.log.domain.user.jwt.service.JWTFilter; +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.logout.CustomLogoutFilter; +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; +import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; +import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Collections; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final RefreshRepository refreshRepository; + private final CustomOAuth2UserService customOAuth2UserService; + private final CustomSuccessHandler customSuccessHandler; + private final CustomFailureHandler customFailureHandler; + private final JWTUtil jwtUtil; + + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf((auth) -> auth.disable()); + http.formLogin((auth) -> auth.disable()); + http.httpBasic((auth) -> auth.disable()); + http.oauth2Login((oauth2) -> oauth2 + .userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig + .userService(customOAuth2UserService)) + .successHandler(customSuccessHandler) + .failureHandler(customFailureHandler) + ); + http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); + + //이쪽에다가 각각의 엔드포인트를 넣어야함 + http.authorizeHttpRequests((auth) -> auth + .requestMatchers("/").permitAll() + .anyRequest().authenticated()); + + + http.sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); + 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("Set-Cookie")); + configuration.setExposedHeaders(Collections.singletonList("Authorization")); + return configuration; + } + })); + return http.build(); + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java new file mode 100644 index 0000000..c02de26 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java @@ -0,0 +1,25 @@ +package com.bamboo.log.domain.user.controller; + +import com.bamboo.log.domain.user.refresh.service.ProcessTokenReissue; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class RefreshController { + + private final ProcessTokenReissue processTokenReissue; + + + @PostMapping("/refresh") + public ResponseEntity reissue( + HttpServletRequest request, + HttpServletResponse response) { + return processTokenReissue.reissue(request, response); + } +} + diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java new file mode 100644 index 0000000..36c936f --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java @@ -0,0 +1,76 @@ +package com.bamboo.log.domain.user.jwt.service; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.dto.UserDTO; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.io.PrintWriter; + +@RequiredArgsConstructor +public class JWTFilter extends OncePerRequestFilter { + + private final JWTUtil jwtUtil; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // 헤더에서 Authorization에 담긴 토큰을 꺼냄 + String accessToken = request.getHeader("Authorization"); + + if (accessToken != null && accessToken.startsWith("Bearer ")) { + accessToken = accessToken.substring(7); // "Bearer "의 길이는 7 + } + + // 토큰이 없다면 다음 필터로 넘김 + if (accessToken == null|| accessToken.isEmpty()) { + filterChain.doFilter(request, response); + return; + } + + // 토큰 만료 여부 확인, 만료시 다음 필터로 넘기지 않음 + try { + jwtUtil.isExpired(accessToken); + } catch (ExpiredJwtException e) { + // 만료된 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 + PrintWriter writer = response.getWriter(); + writer.print("access token expired"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + // 토큰이 access인지 확인 (발급시 페이로드에 명시) + String category = jwtUtil.getCategory(accessToken); + if (!category.equals("access")) { + // 유효하지 않은 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 + PrintWriter writer = response.getWriter(); + writer.print("invalid access token"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + + String username = jwtUtil.getUsername(accessToken); + String role = jwtUtil.getRole(accessToken); + String name=jwtUtil.getName(accessToken); + UserDTO userDTO = UserDTO.builder() + .name(name) + .username(username) + .role(role) + .build(); + CustomOAuth2User customOAuth2User = new CustomOAuth2User(userDTO); + Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, customOAuth2User.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java new file mode 100644 index 0000000..d7f3a78 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java @@ -0,0 +1,57 @@ +package com.bamboo.log.domain.user.jwt.service; + +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +@Component +public class JWTUtil { + + private SecretKey secretKey; + + public JWTUtil(@Value("${spring.jwt.secret}")String secret) { + secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); + } + + public String getUsername(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class); + } + + public String getRole(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); + } + + public Boolean isExpired(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + } + + public String getCategory(String token) { + try { + // 토큰에서 카테고리 정보를 추출합니다. + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("category", String.class); + } catch (JwtException e) { + throw new RuntimeException("토큰에서 카테고리를 추출하는 중 오류가 발생했습니다."); + } + } + + public String getName(String token){ + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("name", String.class); + } + public String createJwt(String category,String name,String username, String role, Long expiredMs) { + return Jwts.builder() + .claim("category",category) + .claim("username", username) + .claim("name",name) + .claim("role", role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiredMs)) + .signWith(secretKey) + .compact(); + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java b/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java new file mode 100644 index 0000000..3ff12c5 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java @@ -0,0 +1,94 @@ +package com.bamboo.log.domain.user.logout; + +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.filter.GenericFilterBean; + +import java.io.IOException; + +@RequiredArgsConstructor +@Transactional +@Slf4j +public class CustomLogoutFilter extends GenericFilterBean { + + private final JWTUtil jwtUtil; + private final RefreshRepository refreshRepository; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); + } + + private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + + String requestUri = request.getRequestURI(); + if (!requestUri.matches("^\\/logout$")) { + filterChain.doFilter(request, response); + return; + } + + String requestMethod = request.getMethod(); + if (!requestMethod.equals("POST")) { + filterChain.doFilter(request, response); + return; + } + + String refresh = null; + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + if (cookie.getName().equals("refresh")) { + refresh = cookie.getValue(); + } + } + + if (refresh == null) { + log.error("로그아웃 시 리프레시 토큰이 없습니다."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + try { + jwtUtil.isExpired(refresh); + } catch (ExpiredJwtException e) { + log.error("리프레시 토큰이 만료되었습니다. refresh token: {}", refresh); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + String category = jwtUtil.getCategory(refresh); + if (!category.equals("refresh")) { + log.error("유효하지 않은 리프레시 토큰입니다. refresh token: {}", refresh); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + Boolean isExist = refreshRepository.existsByToken(refresh); + if (!isExist) { + log.error("리프레시 토큰이 데이터베이스에 존재하지 않습니다. refresh token: {}", refresh); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + refreshRepository.deleteByToken(refresh); + log.info("리프레시 토큰이 데이터베이스에서 삭제되었습니다. refresh token: {}", refresh); + + Cookie cookie = new Cookie("refresh", null); + cookie.setMaxAge(0); + cookie.setPath("/"); + + response.addCookie(cookie); + log.info("리프레시 토큰 쿠키가 삭제되었습니다."); + response.setStatus(HttpServletResponse.SC_OK); + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java new file mode 100644 index 0000000..fe0da6f --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java @@ -0,0 +1,49 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +public class CustomOAuth2User implements OAuth2User { + + private final UserDTO userDTO; + + public CustomOAuth2User(UserDTO userDTO) { + + this.userDTO = userDTO; + } + + @Override + public Map getAttributes() { + return null; + } + + @Override + public Collection getAuthorities() { + + Collection collection = new ArrayList<>(); + + collection.add(new GrantedAuthority() { + + @Override + public String getAuthority() { + + return userDTO.getRole(); + } + }); + + return collection; + } + + @Override + public String getName() { + return userDTO.getName(); + } + + public String getUsername() { + return userDTO.getUsername(); + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java new file mode 100644 index 0000000..2e16ed0 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java @@ -0,0 +1,40 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import java.util.Map; + +public class KakaoResponse implements OAuth2Response { + private final Map attributes; + private final Map kakaoAccount; + private final Map profile; + + public KakaoResponse(Map attributes) { + this.attributes = attributes; + this.kakaoAccount = (Map) attributes.get("kakao_account"); + this.profile = (Map) kakaoAccount.get("profile"); + } + + @Override + public String getProvider() { + return "kakao"; + } + + @Override + public String getProviderId() { + return attributes.get("id").toString(); + } + + @Override + public String getEmail() { + return kakaoAccount.get("email") != null ? kakaoAccount.get("email").toString() : "N/A"; + } + + @Override + public String getName() { + return profile.get("nickname").toString(); + } + + @Override + public Map getAttributes() { + return attributes; + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java new file mode 100644 index 0000000..28f415d --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java @@ -0,0 +1,11 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import java.util.Map; + +public interface OAuth2Response { + String getProvider(); + String getProviderId(); + String getEmail(); + String getName(); + Map getAttributes(); +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java new file mode 100644 index 0000000..f76f9a2 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java @@ -0,0 +1,14 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@AllArgsConstructor +@Builder +@Getter +public class UserDTO { + private String name; + private String username; + private String role; +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java new file mode 100644 index 0000000..e02ceb4 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java @@ -0,0 +1,40 @@ +package com.bamboo.log.domain.user.oauth.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Date; +@Entity +@Table(name = "refresh_token") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class RefreshToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String username; + + @Column(nullable = false) + private String token; + + @Column(name = "expires_at", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date expiresAt; + + @Column(name = "created_at", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date createdAt; + +} + diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java new file mode 100644 index 0000000..fcf2836 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java @@ -0,0 +1,45 @@ +package com.bamboo.log.domain.user.oauth.entity; + +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@Entity +@Table(name="users") +public class UserEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true, nullable = false) + private String username; + + + private String name; + private String email; + private String role; + private String profile_img_url; + + @Builder + public UserEntity(String username, String name, String email, String role,String profile_img_url) { + this.username = username; + this.name = name; + this.email = email; + this.role = role; + this.profile_img_url = profile_img_url; + } + + public UserEntity updateEmailAndName(String email, String name) { + return UserEntity.builder() + .username(this.username) + .name(name) + .email(email) + .role(this.role) + .profile_img_url(this.profile_img_url) + .build(); + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java new file mode 100644 index 0000000..8bbe809 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java @@ -0,0 +1,10 @@ +package com.bamboo.log.domain.user.oauth.repository; + +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RefreshRepository extends JpaRepository { + Boolean existsByToken(String token); + void deleteByToken(String token); + RefreshToken findByToken(String token); +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java new file mode 100644 index 0000000..645a385 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java @@ -0,0 +1,8 @@ +package com.bamboo.log.domain.user.oauth.repository; + +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + UserEntity findByUsername(String username); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java new file mode 100644 index 0000000..1dac05d --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java @@ -0,0 +1,20 @@ +package com.bamboo.log.domain.user.oauth.service; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + response.sendRedirect("http://localhost:3000"); + } +} + diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java new file mode 100644 index 0000000..8de41c2 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java @@ -0,0 +1,67 @@ +package com.bamboo.log.domain.user.oauth.service; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.dto.KakaoResponse; +import com.bamboo.log.domain.user.oauth.dto.OAuth2Response; +import com.bamboo.log.domain.user.oauth.dto.UserDTO; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + + +@Service +@RequiredArgsConstructor +public class CustomOAuth2UserService extends DefaultOAuth2UserService { + + private final UserRepository userRepository; + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + OAuth2User oAuth2User = super.loadUser(userRequest); + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + + OAuth2Response oAuth2Response = null; + if (registrationId.equals("kakao")) { + oAuth2Response = new KakaoResponse(oAuth2User.getAttributes()); + } else { + return null; + } + + System.out.println("OAuth2Response: " + oAuth2Response); + String username = oAuth2Response.getProvider() + " " + oAuth2Response.getProviderId(); + UserEntity existData = userRepository.findByUsername(username); + System.out.println("username = " + username); + System.out.println("Existing User: " + existData); + + if (existData == null) { + UserEntity userEntity = UserEntity.builder() + .username(username) + .name(oAuth2Response.getName()) + .email(oAuth2Response.getEmail()) + .role("ROLE_USER") + .build(); + + userRepository.save(userEntity); + + UserDTO userDTO = UserDTO.builder() + .username(username) + .name(oAuth2Response.getName()) + .role("ROLE_USER") + .build(); + + return new CustomOAuth2User(userDTO); + } else { + UserDTO userDTO = UserDTO.builder() + .username(existData.getUsername()) + .name(existData.getName()) + .role(existData.getRole()) + .build(); + return new CustomOAuth2User(userDTO); + } + } +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java new file mode 100644 index 0000000..069d271 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -0,0 +1,78 @@ +package com.bamboo.log.domain.user.oauth.service; + +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; + +@Component +@RequiredArgsConstructor +public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + private final JWTUtil jwtUtil; + private final RefreshRepository refreshRepository; + + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); + String name=customUserDetails.getName(); + String username = customUserDetails.getUsername(); + Collection authorities = authentication.getAuthorities(); + Iterator iterator = authorities.iterator(); + GrantedAuthority auth = iterator.next(); + String role = auth.getAuthority(); + + String refreshToken = jwtUtil.createJwt("refresh",name,username, role, 1800000L); + String accessToken = jwtUtil.createJwt("access",name,username, role, 1209600000L); + addRefreshEntity(name, username, refreshToken, 1209600000L); + response.addCookie(createCookie("refresh", refreshToken)); + response.addCookie(UnScretCreateCookie("access", accessToken)); + response.sendRedirect("http://localhost:3000/"); + } + + private Cookie createCookie(String key, String value) { + + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(24 * 60 * 60 * 14); + cookie.setPath("/"); + cookie.setHttpOnly(true); + + return cookie; + } + private Cookie UnScretCreateCookie(String key, String value) { + + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(60*60); + cookie.setPath("/"); + return cookie; + } + private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { + Date CrDate = new Date(System.currentTimeMillis()); + Date ExDate = new Date(System.currentTimeMillis() + expiredMs); + refreshRepository.save( + RefreshToken.builder() + .name(name) + .username(username) + .token(refresh) + .createdAt(CrDate) + .expiresAt(ExDate) + .build() + ); + } +} + diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java new file mode 100644 index 0000000..73dcdd3 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java @@ -0,0 +1,9 @@ +package com.bamboo.log.domain.user.refresh.service; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.ResponseEntity; + +public interface ProcessTokenReissue { + public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response); +} diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java new file mode 100644 index 0000000..e808c3f --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java @@ -0,0 +1,103 @@ +package com.bamboo.log.domain.user.refresh.service; + +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +@Service +@RequiredArgsConstructor +@Transactional +@Slf4j +public class ProcessTokenReissueImpl implements ProcessTokenReissue { + + private final JWTUtil jwtUtil; + private final RefreshRepository refreshRepository; + + public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response) { + + String refresh = null; + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + if (cookie.getName().equals("refresh")) { + refresh = cookie.getValue(); + } + } + + if (refresh == null) { + log.error("리프레시 토큰이 없습니다."); + return new ResponseEntity<>("refresh token null", HttpStatus.BAD_REQUEST); + } + + try { + jwtUtil.isExpired(refresh); + } catch (ExpiredJwtException e) { + log.error("리프레시 토큰이 만료되었습니다. token: {}", refresh); + return new ResponseEntity<>("refresh token expired", HttpStatus.BAD_REQUEST); + } + + String category = jwtUtil.getCategory(refresh); + if (!category.equals("refresh")) { + log.error("유효하지 않은 리프레시 토큰입니다. token: {}", refresh); + return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); + } + + if (!refreshRepository.existsByToken(refresh)) { + log.error("리프레시 토큰이 존재하지 않습니다. token: {}", refresh); + return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); + } + + String name = jwtUtil.getName(refresh); + String username = jwtUtil.getUsername(refresh); + String role = jwtUtil.getRole(refresh); + + String newAccess = jwtUtil.createJwt("access", name, username , role, 1800000L); + String newRefresh = jwtUtil.createJwt("refresh", name, username, role, 1209600000L); + + refreshRepository.deleteByToken(refresh); + addRefreshEntity(name,username,role,1209600000L); + + log.info("새로운 액세스 토큰과 리프레시 토큰을 발급했습니다. 새로운 리프레시 토큰: {}", newRefresh); + + response.setHeader("Authorization", newAccess); + response.addCookie(createCookie("refresh", newRefresh)); + + log.info("리프레시 토큰 재발급 완료"); + + return new ResponseEntity<>(HttpStatus.OK); + } + + private Cookie createCookie(String key, String value) { + log.info("쿠키 생성 시작"); + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(24 * 60 * 60 * 14); // 14일 유효 + cookie.setHttpOnly(true); // 클라이언트 측에서 접근 불가 + log.info("쿠키 생성 끝"); + return cookie; + } + private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { + Date CrDate = new Date(System.currentTimeMillis()); + Date ExDate = new Date(System.currentTimeMillis() + expiredMs); + refreshRepository.save( + RefreshToken.builder() + .name(name) + .username(username) + .token(refresh) + .createdAt(CrDate) + .expiresAt(ExDate) + .build() + ); + } +} + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 356817f..beb25f4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,13 +1,65 @@ +#spring: +# datasource: +# url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} +# username: ${MYSQL_USERNAME} +# password: ${MYSQL_PASSWORD} +# driver-class-name: com.mysql.cj.jdbc.Driver +# jpa: +# show-sql: true +# open-in-view: false +# hibernate: +# ddl-auto: update +# properties: +# hibernate.dialect: org.hibernate.dialect.MySQL8Dialect +#security: +# oauth2: +# client: +# registration: +# kakao: +# client-authentication-method: client_secret_post +# client-name: kakao +# client-id: 649c1e34aedc1a7db892333d900725f2 +# client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 +# redirect-uri: http://localhost:8080/login/oauth2/code/kakao +# authorization-grant-type: authorization_code +# scope: profile_nickname,account_email +# provider: +# kakao: +# authorization-uri: https://kauth.kakao.com/oauth/authorize +# token-uri: https://kauth.kakao.com/oauth/token +# user-info-uri: https://kapi.kakao.com/v2/user/me +# user-name-attribute: id spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} + url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + username: root + password: heodongun0922 driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true open-in-view: false hibernate: - ddl-auto: update + ddl-auto: none properties: - hibernate.dialect: org.hibernate.dialect.MySQL8Dialect \ No newline at end of file + hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: profile_nickname,account_email + provider: + kakao: + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id + jwt: + secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb From 558a7ba01f057e217644c01cc018978472625b6d Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Wed, 19 Feb 2025 22:42:58 +0900 Subject: [PATCH 003/114] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/config/SecurityConfig.java b/src/main/java/com/bamboo/log/config/SecurityConfig.java index b2ddff4..4add644 100644 --- a/src/main/java/com/bamboo/log/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/config/SecurityConfig.java @@ -50,7 +50,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { //이쪽에다가 각각의 엔드포인트를 넣어야함 http.authorizeHttpRequests((auth) -> auth - .requestMatchers("/").permitAll() + .requestMatchers("/refresh").permitAll() .anyRequest().authenticated()); From 67a66fecd4acb08b471d3629f726ecba51fd68a6 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:10:47 +0900 Subject: [PATCH 004/114] Update auto-branch.yml --- .github/workflows/auto-branch.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/auto-branch.yml b/.github/workflows/auto-branch.yml index 4634d2b..9675686 100644 --- a/.github/workflows/auto-branch.yml +++ b/.github/workflows/auto-branch.yml @@ -15,3 +15,5 @@ jobs: uses: robvanderleek/create-issue-branch@main env: GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + base-branch: dev From fe0f73655efca50ce43503da906dff7c8aa321a0 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:17:45 +0900 Subject: [PATCH 005/114] Update auto-branch.yml --- .github/workflows/auto-branch.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/auto-branch.yml b/.github/workflows/auto-branch.yml index 9675686..4b738cd 100644 --- a/.github/workflows/auto-branch.yml +++ b/.github/workflows/auto-branch.yml @@ -17,3 +17,20 @@ jobs: GITHUB_TOKEN: ${{ secrets.TOKEN }} with: base-branch: dev + + - name: Open Pull Request + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.TOKEN }} + base: dev + branch: "feature-${{ github.event.issue.number }}" + title: "🚀 Pull Request #${{ github.event.issue.number }}" + body: | + 🔥 **이슈 #${{ github.event.issue.number }}에 대한 PR 생성!** 🔥 + + - 이슈 링크: #${{ github.event.issue.number }} + - 자동 생성된 PR입니다. + + 🚀 **확인 후 리뷰 부탁드립니다!** + draft: true + From 657223b190f243b5b0292d1362f865bdc461398b Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:20:19 +0900 Subject: [PATCH 006/114] Update and rename auto-branch.yml to create-branch.yml --- .github/workflows/{auto-branch.yml => create-branch.yml} | 1 - 1 file changed, 1 deletion(-) rename .github/workflows/{auto-branch.yml => create-branch.yml} (99%) diff --git a/.github/workflows/auto-branch.yml b/.github/workflows/create-branch.yml similarity index 99% rename from .github/workflows/auto-branch.yml rename to .github/workflows/create-branch.yml index 4b738cd..49fc1a6 100644 --- a/.github/workflows/auto-branch.yml +++ b/.github/workflows/create-branch.yml @@ -33,4 +33,3 @@ jobs: 🚀 **확인 후 리뷰 부탁드립니다!** draft: true - From b1d899037df3667868cf27f40e1324ea825542ab Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:20:52 +0900 Subject: [PATCH 007/114] Create delete-branch.yml --- .github/workflows/delete-branch.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/delete-branch.yml diff --git a/.github/workflows/delete-branch.yml b/.github/workflows/delete-branch.yml new file mode 100644 index 0000000..5f13dc0 --- /dev/null +++ b/.github/workflows/delete-branch.yml @@ -0,0 +1,14 @@ +name: Delete Merged Branch +on: + pull_request: + types: + - closed + +jobs: + delete_merged_branch: + runs-on: ubuntu-latest + steps: + - name: Delete branch if merged + uses: github-actions/delete-merged-branch@v4 + with: + github_token: ${{ secrets.TOKEN }} From da5b03791168d5bf474a08b1d711a5159601cb78 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:21:15 +0900 Subject: [PATCH 008/114] Create close-issue.yml --- .github/workflows/close-issue.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/close-issue.yml diff --git a/.github/workflows/close-issue.yml b/.github/workflows/close-issue.yml new file mode 100644 index 0000000..cd25d28 --- /dev/null +++ b/.github/workflows/close-issue.yml @@ -0,0 +1,14 @@ +name: Close Issue When PR Merged +on: + pull_request: + types: [closed] + +jobs: + close_issue: + runs-on: ubuntu-latest + steps: + - name: Close linked issue + uses: peter-evans/close-issue@v2 + with: + github_token: ${{ secrets.TOKEN }} + comment: "✅ PR이 머지되어 해당 이슈를 닫습니다." From 76c54cee2f1501af08ccb8f09ef54d4ed5b6d42f Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:38:31 +0900 Subject: [PATCH 009/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index 49fc1a6..e14f789 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -32,4 +32,5 @@ jobs: - 자동 생성된 PR입니다. 🚀 **확인 후 리뷰 부탁드립니다!** + Closes #${{ github.event.issue.number }} draft: true From 77a3bc463991762ec9389d502bfd155a2cda8f60 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:41:02 +0900 Subject: [PATCH 010/114] Update delete-branch.yml --- .github/workflows/delete-branch.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/delete-branch.yml b/.github/workflows/delete-branch.yml index 5f13dc0..ad1416a 100644 --- a/.github/workflows/delete-branch.yml +++ b/.github/workflows/delete-branch.yml @@ -1,12 +1,12 @@ name: Delete Merged Branch on: pull_request: - types: - - closed + types: [closed] jobs: delete_merged_branch: runs-on: ubuntu-latest + if: github.event.pull_request.merged == true steps: - name: Delete branch if merged uses: github-actions/delete-merged-branch@v4 From 1f7548aace898b424d4e11c8863ec5c1848480c8 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:44:55 +0900 Subject: [PATCH 011/114] Create test-issue-template.yml --- .../ISSUE_TEMPLATE/test-issue-template.yml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/test-issue-template.yml diff --git a/.github/ISSUE_TEMPLATE/test-issue-template.yml b/.github/ISSUE_TEMPLATE/test-issue-template.yml new file mode 100644 index 0000000..465c4ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/test-issue-template.yml @@ -0,0 +1,22 @@ +name: "💬 Test" +description: "코드 테스트" +labels: ["test"] +body: + - type: textarea + attributes: + label: 📄 설명 + description: 검증하고자 하는 코드 대한 설명을 작성해 주세요. + placeholder: 검증하고자 하는 코드와 검증 방식에 대해 최대한 자세히 작성해 주세요! + validations: + required: true + - type: textarea + attributes: + label: ✅ 작업할 내용 + description: 할 일을 체크박스 형태로 작성해주세요. + placeholder: 최대한 세분화 해서 적어주세요! + validations: + required: true + - type: textarea + attributes: + label: 🙋🏻 참고 자료 + description: 참고 자료가 있다면 작성해 주세요. From f75b409d1c736aef37aae3562407c289a8482dba Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:07:00 +0900 Subject: [PATCH 012/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 33 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index e14f789..41ef108 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -1,11 +1,11 @@ name: Create Issue Branch on: issues: - types: [ assigned ] + types: [assigned] issue_comment: - types: [ created ] + types: [created] pull_request: - types: [ closed ] + types: [closed] jobs: create_issue_branch_job: @@ -14,23 +14,32 @@ jobs: - name: Create Issue Branch uses: robvanderleek/create-issue-branch@main env: - GITHUB_TOKEN: ${{ secrets.TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: base-branch: dev - + branch-name: "feature-${{ github.event.issue.number }}" + overwrite-existing: true # 기존 브랜치가 있어도 덮어쓰기 + + - name: Check if PR already exists + id: check_pr + run: | + if gh pr list --state open --base dev --head "feature-${{ github.event.issue.number }}" | grep "feature-${{ github.event.issue.number }}"; then + echo "pr_exists=true" >> $GITHUB_ENV + else + echo "pr_exists=false" >> $GITHUB_ENV + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Open Pull Request + if: env.pr_exists == 'false' # PR이 없을 때만 실행 uses: peter-evans/create-pull-request@v5 with: - token: ${{ secrets.TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} base: dev branch: "feature-${{ github.event.issue.number }}" title: "🚀 Pull Request #${{ github.event.issue.number }}" body: | 🔥 **이슈 #${{ github.event.issue.number }}에 대한 PR 생성!** 🔥 - + - 이슈 링크: #${{ github.event.issue.number }} - - 자동 생성된 PR입니다. - - 🚀 **확인 후 리뷰 부탁드립니다!** - Closes #${{ github.event.issue.number }} - draft: true From 81f365fc04bdd39669ea838adeeefd64b5de8b0a Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:11:42 +0900 Subject: [PATCH 013/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index 41ef108..cc14524 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -11,6 +11,9 @@ jobs: create_issue_branch_job: runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Create Issue Branch uses: robvanderleek/create-issue-branch@main env: @@ -18,7 +21,7 @@ jobs: with: base-branch: dev branch-name: "feature-${{ github.event.issue.number }}" - overwrite-existing: true # 기존 브랜치가 있어도 덮어쓰기 + overwrite-existing: true - name: Check if PR already exists id: check_pr @@ -32,7 +35,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Open Pull Request - if: env.pr_exists == 'false' # PR이 없을 때만 실행 + if: env.pr_exists == 'false' uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GITHUB_TOKEN }} From 8301c8e14011b0220cbc41039887a0560820868b Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:14:43 +0900 Subject: [PATCH 014/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index cc14524..82b1b34 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -19,7 +19,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - base-branch: dev + default-branch: dev branch-name: "feature-${{ github.event.issue.number }}" overwrite-existing: true From 10f236fa949f42156942bf6d09a00b19e89daf1f Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:18:40 +0900 Subject: [PATCH 015/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index 82b1b34..b54f723 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -11,9 +11,17 @@ jobs: create_issue_branch_job: runs-on: ubuntu-latest steps: - - name: Checkout repository + - name: Checkout repository # ✅ 저장소 가져오기 (fetch-depth 설정 추가) uses: actions/checkout@v4 - + with: + fetch-depth: 0 # 전체 Git 히스토리 가져오기 + + - name: Debug Git repository # ✅ 원격 저장소 문제 확인 + run: | + git remote -v + git branch + git status + - name: Create Issue Branch uses: robvanderleek/create-issue-branch@main env: @@ -26,7 +34,7 @@ jobs: - name: Check if PR already exists id: check_pr run: | - if gh pr list --state open --base dev --head "feature-${{ github.event.issue.number }}" | grep "feature-${{ github.event.issue.number }}"; then + if gh pr list --state open --base dev --head "feature-${{ github.event.issue.number }}" --repo ${{ github.repository }} | grep "feature-${{ github.event.issue.number }}"; then echo "pr_exists=true" >> $GITHUB_ENV else echo "pr_exists=false" >> $GITHUB_ENV From 1a6deb31a4467377c6d8d618ea22952575e7d628 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:27:44 +0900 Subject: [PATCH 016/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index b54f723..bbceb6f 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -43,7 +43,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Open Pull Request - if: env.pr_exists == 'false' uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -54,3 +53,4 @@ jobs: 🔥 **이슈 #${{ github.event.issue.number }}에 대한 PR 생성!** 🔥 - 이슈 링크: #${{ github.event.issue.number }} + delete-branch: false From c2463fa83fe03a3d98589b31cbcba8eafa1c30e9 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:28:47 +0900 Subject: [PATCH 017/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index bbceb6f..d3c99b5 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -11,12 +11,12 @@ jobs: create_issue_branch_job: runs-on: ubuntu-latest steps: - - name: Checkout repository # ✅ 저장소 가져오기 (fetch-depth 설정 추가) + - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 # 전체 Git 히스토리 가져오기 + fetch-depth: 0 - - name: Debug Git repository # ✅ 원격 저장소 문제 확인 + - name: Debug Git repository run: | git remote -v git branch @@ -54,3 +54,4 @@ jobs: - 이슈 링크: #${{ github.event.issue.number }} delete-branch: false + allow-empty-commits: true From 49573c40fb01780bc0ea89c436afa1e3a4355d54 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:36:48 +0900 Subject: [PATCH 018/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index d3c99b5..d928698 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -22,19 +22,31 @@ jobs: git branch git status + - name: Determine Branch Name Based on Issue Labels + id: detect_branch + run: | + LABEL=$(echo '${{ toJSON(github.event.issue.labels) }}' | grep -oE '"name":"(refactor|release|test|fix|validation)"' | head -n 1 | cut -d':' -f2 | tr -d '"') + if [[ -z "$LABEL" ]]; then + LABEL="feature" # 기본값 설정 + fi + BRANCH_NAME="${LABEL}/issue-${{ github.event.issue.number }}" + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Issue Branch uses: robvanderleek/create-issue-branch@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: default-branch: dev - branch-name: "feature-${{ github.event.issue.number }}" + branch-name: "${{ env.BRANCH_NAME }}" overwrite-existing: true - name: Check if PR already exists id: check_pr run: | - if gh pr list --state open --base dev --head "feature-${{ github.event.issue.number }}" --repo ${{ github.repository }} | grep "feature-${{ github.event.issue.number }}"; then + if gh pr list --state open --base dev --head "${{ env.BRANCH_NAME }}" --repo ${{ github.repository }} | grep "${{ env.BRANCH_NAME }}"; then echo "pr_exists=true" >> $GITHUB_ENV else echo "pr_exists=false" >> $GITHUB_ENV @@ -47,7 +59,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} base: dev - branch: "feature-${{ github.event.issue.number }}" + branch: "${{ env.BRANCH_NAME }}" title: "🚀 Pull Request #${{ github.event.issue.number }}" body: | 🔥 **이슈 #${{ github.event.issue.number }}에 대한 PR 생성!** 🔥 From 90c2b74f24090ea0364e1a6252d47d64b0f9a9be Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:41:48 +0900 Subject: [PATCH 019/114] Update create-branch.yml --- .github/workflows/create-branch.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml index d928698..443c204 100644 --- a/.github/workflows/create-branch.yml +++ b/.github/workflows/create-branch.yml @@ -25,14 +25,13 @@ jobs: - name: Determine Branch Name Based on Issue Labels id: detect_branch run: | - LABEL=$(echo '${{ toJSON(github.event.issue.labels) }}' | grep -oE '"name":"(refactor|release|test|fix|validation)"' | head -n 1 | cut -d':' -f2 | tr -d '"') - if [[ -z "$LABEL" ]]; then + LABEL=$(echo '${{ toJSON(github.event.issue.labels) }}' | jq -r '.[0].name') + if [[ -z "$LABEL" || "$LABEL" == "null" ]]; then LABEL="feature" # 기본값 설정 fi BRANCH_NAME="${LABEL}/issue-${{ github.event.issue.number }}" echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + echo "🔹 Detected branch name: $BRANCH_NAME" - name: Create Issue Branch uses: robvanderleek/create-issue-branch@main From 8b4cf59f682891abee28ad92ae99750c6c957ef3 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Thu, 20 Feb 2025 19:48:27 +0900 Subject: [PATCH 020/114] =?UTF-8?q?Settings:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=EC=99=80=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/config/CorsMvcConfig.java | 1 + src/main/java/com/bamboo/log/config/SecurityConfig.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/com/bamboo/log/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/config/CorsMvcConfig.java index 553586b..1e9db2a 100644 --- a/src/main/java/com/bamboo/log/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/config/CorsMvcConfig.java @@ -12,6 +12,7 @@ public void addCorsMappings(CorsRegistry corsRegistry) { corsRegistry.addMapping("/**") .exposedHeaders("Set-Cookie") + //프론트 url 넣기 .allowedOrigins("http://localhost:3000"); } } diff --git a/src/main/java/com/bamboo/log/config/SecurityConfig.java b/src/main/java/com/bamboo/log/config/SecurityConfig.java index 4add644..ab1f0ac 100644 --- a/src/main/java/com/bamboo/log/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/config/SecurityConfig.java @@ -61,6 +61,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration configuration = new CorsConfiguration(); + //프론트 url 넣기 configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); From fc75f17c2c2061f3eeb5742ec99ae45a9e5868b1 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 14 Feb 2025 18:39:36 +0900 Subject: [PATCH 021/114] =?UTF-8?q?Settings:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=EC=99=80=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes .../log/common/config/CorsMvcConfig.java | 19 ++++++++ .../log/common/config/SecurityConfig.java | 46 ++++++++++++++++++ .../log/common/config/SwaggerConfig.java | 12 +++++ 4 files changed, 77 insertions(+) create mode 100644 src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java create mode 100644 src/main/java/com/bamboo/log/common/config/SecurityConfig.java create mode 100644 src/main/java/com/bamboo/log/common/config/SwaggerConfig.java diff --git a/.DS_Store b/.DS_Store index dd46a9923d3511c0ddce67c1cc0fd4f8a0b2c125..5770197c32987a55423285d0d1e1dddc77abfd3b 100644 GIT binary patch delta 71 zcmZoMXfc=|#>B)qu~2NHo+2a1#(>?7j2x4BSc)ezvYy!domGHoW5Wif&Fmcf96)88 Z1v$PmPv#eKB!ku~2NHo+2a9#(>?7iw`g}G4f32VJc>1pWMYH6UAu9z`(HVKNtX6 z3=FIcdJLHiDGaF$iJm$6$w@i+NkCBn1_s7yKpItL5<@9aZ4N^Us!9ekph~03B}|v) z*%>?-Tp4^A0)VFGqN`(kx7mVOo@p~X2R{eUotrl@e`lV|FXG6-$iTn^G=gDsgvc6Z E0A!UiL;wH) diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java new file mode 100644 index 0000000..fd813ab --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -0,0 +1,19 @@ +package com.bamboo.log.common.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("/**") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") + .allowedHeaders("*") + .allowedOriginPatterns("/**") //프론트 url 사용 + .allowCredentials(true); + } +} diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java new file mode 100644 index 0000000..66f465f --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -0,0 +1,46 @@ +package com.bamboo.log.common.config; + + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Arrays; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.csrf(csrf -> csrf.disable()) + .formLogin(form -> form.disable()) + .httpBasic(httpBasic -> httpBasic.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.addAllowedOriginPattern("/**");//프론트 url 사용 + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + configuration.setExposedHeaders(Arrays.asList("Authorization")); + configuration.setMaxAge(3600L); + + return configuration; + } + })); + return http.build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java new file mode 100644 index 0000000..dd0a87a --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java @@ -0,0 +1,12 @@ +package com.bamboo.log.common.config; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import org.springframework.context.annotation.Configuration; + +@Configuration +@OpenAPIDefinition(info = @Info(title = "ForRest", version = "v1")) +public class SwaggerConfig { +} \ No newline at end of file From 231e0f763af3605fa3b412e2f75bb0394f1fc40d Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Thu, 20 Feb 2025 20:32:25 +0900 Subject: [PATCH 022/114] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/GetTestController.java | 34 ++++++++ .../log/common/config/CorsMvcConfig.java | 10 +-- .../log/common/config/SecurityConfig.java | 62 +++++++++++---- .../com/bamboo/log/config/CorsMvcConfig.java | 19 ----- .../com/bamboo/log/config/SecurityConfig.java | 77 ------------------- .../user/controller/RefreshController.java | 10 ++- .../user/jwt/service/UserContextUtil.java | 33 ++++++++ .../user/oauth/dto/CustomOAuth2User.java | 1 - 8 files changed, 128 insertions(+), 118 deletions(-) create mode 100644 src/main/java/com/bamboo/log/GetTestController.java delete mode 100644 src/main/java/com/bamboo/log/config/CorsMvcConfig.java delete mode 100644 src/main/java/com/bamboo/log/config/SecurityConfig.java create mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java diff --git a/src/main/java/com/bamboo/log/GetTestController.java b/src/main/java/com/bamboo/log/GetTestController.java new file mode 100644 index 0000000..96fc047 --- /dev/null +++ b/src/main/java/com/bamboo/log/GetTestController.java @@ -0,0 +1,34 @@ +package com.bamboo.log; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +@RequestMapping("/api") +public class GetTestController { + + @GetMapping("/jwt") + public ResponseEntity> getJwtInfo(@AuthenticationPrincipal CustomOAuth2User userDetails) { + if (userDetails == null) { + return ResponseEntity.status(401).body(Map.of("error", "User not authenticated")); + } + + Map userInfo = Map.of( + "username", userDetails.getUsername(), + "name", userDetails.getName(), // CustomUserDetails에 name 필드가 있다고 가정 + "role", userDetails.getAuthorities().stream() + .findFirst() + .map(Object::toString) + .orElse("ROLE_USER") + ); + System.out.println("userInfo = " + userInfo); + return ResponseEntity.ok(userInfo); + } +} diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index fd813ab..a548c90 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -4,16 +4,16 @@ 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("/**") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") - .allowedHeaders("*") - .allowedOriginPatterns("/**") //프론트 url 사용 - .allowCredentials(true); + .exposedHeaders("Set-Cookie") + //프론트 url 넣기 + .allowedOrigins("http://localhost:3000"); } } + diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 66f465f..755e334 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -1,6 +1,12 @@ package com.bamboo.log.common.config; - +import com.bamboo.log.domain.user.jwt.service.JWTFilter; +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.logout.CustomLogoutFilter; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; +import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; +import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; @@ -8,39 +14,65 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; -import java.util.Arrays; +import java.util.Collections; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { + private final RefreshRepository refreshRepository; + private final CustomOAuth2UserService customOAuth2UserService; + private final CustomSuccessHandler customSuccessHandler; + private final CustomFailureHandler customFailureHandler; + private final JWTUtil jwtUtil; + + @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf(csrf -> csrf.disable()) - .formLogin(form -> form.disable()) - .httpBasic(httpBasic -> httpBasic.disable()) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf((auth) -> auth.disable()); + http.formLogin((auth) -> auth.disable()); + http.httpBasic((auth) -> auth.disable()); + http.oauth2Login((oauth2) -> oauth2 + .userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig + .userService(customOAuth2UserService)) + .successHandler(customSuccessHandler) + .failureHandler(customFailureHandler) + ); + http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); + + //이쪽에다가 각각의 엔드포인트를 넣어야함 + http.authorizeHttpRequests((auth) -> auth + .requestMatchers("/refresh").permitAll() + .requestMatchers("/logout","/api/jwt").hasAnyRole("USER") + .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() + .anyRequest().authenticated()); + + + http.sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration configuration = new CorsConfiguration(); - - configuration.addAllowedOriginPattern("/**");//프론트 url 사용 - configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); - configuration.setAllowedHeaders(Arrays.asList("*")); + //프론트 url 넣기 + configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); + configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); - configuration.setExposedHeaders(Arrays.asList("Authorization")); + configuration.setAllowedHeaders(Collections.singletonList("*")); configuration.setMaxAge(3600L); - + configuration.setExposedHeaders(Collections.singletonList("Set-Cookie")); + configuration.setExposedHeaders(Collections.singletonList("Authorization")); return configuration; } })); return http.build(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/bamboo/log/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/config/CorsMvcConfig.java deleted file mode 100644 index 1e9db2a..0000000 --- a/src/main/java/com/bamboo/log/config/CorsMvcConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.bamboo.log.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("/**") - .exposedHeaders("Set-Cookie") - //프론트 url 넣기 - .allowedOrigins("http://localhost:3000"); - } -} - diff --git a/src/main/java/com/bamboo/log/config/SecurityConfig.java b/src/main/java/com/bamboo/log/config/SecurityConfig.java deleted file mode 100644 index ab1f0ac..0000000 --- a/src/main/java/com/bamboo/log/config/SecurityConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.bamboo.log.config; - -import com.bamboo.log.domain.user.jwt.service.JWTFilter; -import com.bamboo.log.domain.user.jwt.service.JWTUtil; -import com.bamboo.log.domain.user.logout.CustomLogoutFilter; -import com.bamboo.log.domain.user.oauth.entity.RefreshToken; -import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; -import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; -import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; -import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.logout.LogoutFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; - -import java.util.Collections; - -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig { - - private final RefreshRepository refreshRepository; - private final CustomOAuth2UserService customOAuth2UserService; - private final CustomSuccessHandler customSuccessHandler; - private final CustomFailureHandler customFailureHandler; - private final JWTUtil jwtUtil; - - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.csrf((auth) -> auth.disable()); - http.formLogin((auth) -> auth.disable()); - http.httpBasic((auth) -> auth.disable()); - http.oauth2Login((oauth2) -> oauth2 - .userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig - .userService(customOAuth2UserService)) - .successHandler(customSuccessHandler) - .failureHandler(customFailureHandler) - ); - http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); - - //이쪽에다가 각각의 엔드포인트를 넣어야함 - http.authorizeHttpRequests((auth) -> auth - .requestMatchers("/refresh").permitAll() - .anyRequest().authenticated()); - - - http.sessionManagement((session) -> session - .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); - http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration configuration = new CorsConfiguration(); - //프론트 url 넣기 - 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("Set-Cookie")); - configuration.setExposedHeaders(Collections.singletonList("Authorization")); - return configuration; - } - })); - return http.build(); - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java index c02de26..3d38673 100644 --- a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java +++ b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java @@ -1,6 +1,9 @@ package com.bamboo.log.domain.user.controller; import com.bamboo.log.domain.user.refresh.service.ProcessTokenReissue; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -14,7 +17,12 @@ public class RefreshController { private final ProcessTokenReissue processTokenReissue; - + @Operation(summary = "토큰 갱신", description = "Refresh Token을 사용하여 Access Token을 재발급합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "토큰 재발급 성공"), + @ApiResponse(responseCode = "401", description = "유효하지 않은 Refresh Token"), + @ApiResponse(responseCode = "403", description = "Refresh Token이 만료됨") + }) @PostMapping("/refresh") public ResponseEntity reissue( HttpServletRequest request, diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java new file mode 100644 index 0000000..6f196f7 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java @@ -0,0 +1,33 @@ +package com.bamboo.log.domain.user.jwt.service; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class UserContextUtil { + + public Optional getCurrentUser() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !(authentication.getPrincipal() instanceof CustomOAuth2User)) { + return Optional.empty(); + } + return Optional.of((CustomOAuth2User) authentication.getPrincipal()); + } + + public String getUsername() { + return getCurrentUser() + .map(CustomOAuth2User::getUsername) + .orElseThrow(() -> new RuntimeException("User not authenticated")); + } + + public String getName() { + return getCurrentUser() + .map(CustomOAuth2User::getName) + .orElseThrow(() -> new RuntimeException("User not authenticated")); + } + +} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java index fe0da6f..707915d 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java @@ -12,7 +12,6 @@ public class CustomOAuth2User implements OAuth2User { private final UserDTO userDTO; public CustomOAuth2User(UserDTO userDTO) { - this.userDTO = userDTO; } From c262d98aec333a8d9c0f8e03d5599d122cb81df4 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Thu, 20 Feb 2025 20:35:15 +0900 Subject: [PATCH 023/114] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 31 +++++++++++-------- .../log/common/config/SecurityConfig.java | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index cff809d..e468d6b 100644 --- a/build.gradle +++ b/build.gradle @@ -24,19 +24,24 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation 'io.jsonwebtoken:jjwt-api:0.12.3' - implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' - implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' + } } tasks.named('test') { diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 755e334..38ab583 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -50,7 +50,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { //이쪽에다가 각각의 엔드포인트를 넣어야함 http.authorizeHttpRequests((auth) -> auth .requestMatchers("/refresh").permitAll() - .requestMatchers("/logout","/api/jwt").hasAnyRole("USER") + .requestMatchers("/logout").hasAnyRole("USER") .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() .anyRequest().authenticated()); From f3380687663c46a67bcd5fe8e8cdd654d9ec0e4f Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Thu, 20 Feb 2025 20:36:53 +0900 Subject: [PATCH 024/114] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 77 +++++++++--------------------- 1 file changed, 22 insertions(+), 55 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index beb25f4..c9191de 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,65 +1,32 @@ -#spring: -# datasource: -# url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} -# username: ${MYSQL_USERNAME} -# password: ${MYSQL_PASSWORD} -# driver-class-name: com.mysql.cj.jdbc.Driver -# jpa: -# show-sql: true -# open-in-view: false -# hibernate: -# ddl-auto: update -# properties: -# hibernate.dialect: org.hibernate.dialect.MySQL8Dialect -#security: -# oauth2: -# client: -# registration: -# kakao: -# client-authentication-method: client_secret_post -# client-name: kakao -# client-id: 649c1e34aedc1a7db892333d900725f2 -# client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 -# redirect-uri: http://localhost:8080/login/oauth2/code/kakao -# authorization-grant-type: authorization_code -# scope: profile_nickname,account_email -# provider: -# kakao: -# authorization-uri: https://kauth.kakao.com/oauth/authorize -# token-uri: https://kauth.kakao.com/oauth/token -# user-info-uri: https://kapi.kakao.com/v2/user/me -# user-name-attribute: id spring: datasource: - url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true - username: root - password: heodongun0922 + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true open-in-view: false hibernate: - ddl-auto: none + ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect +security: + oauth2: + client: + registration: + kakao: + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: profile_nickname,account_email + provider: + kakao: + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id - security: - oauth2: - client: - registration: - kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: http://localhost:8080/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: profile_nickname,account_email - provider: - kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id - jwt: - secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb From 4772f6680cfe7896d297f3f4d20c37579f1b04fd Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Thu, 20 Feb 2025 20:37:29 +0900 Subject: [PATCH 025/114] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/GetTestController.java | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 src/main/java/com/bamboo/log/GetTestController.java diff --git a/src/main/java/com/bamboo/log/GetTestController.java b/src/main/java/com/bamboo/log/GetTestController.java deleted file mode 100644 index 96fc047..0000000 --- a/src/main/java/com/bamboo/log/GetTestController.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.bamboo.log; - -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Map; - -@RestController -@RequestMapping("/api") -public class GetTestController { - - @GetMapping("/jwt") - public ResponseEntity> getJwtInfo(@AuthenticationPrincipal CustomOAuth2User userDetails) { - if (userDetails == null) { - return ResponseEntity.status(401).body(Map.of("error", "User not authenticated")); - } - - Map userInfo = Map.of( - "username", userDetails.getUsername(), - "name", userDetails.getName(), // CustomUserDetails에 name 필드가 있다고 가정 - "role", userDetails.getAuthorities().stream() - .findFirst() - .map(Object::toString) - .orElse("ROLE_USER") - ); - System.out.println("userInfo = " + userInfo); - return ResponseEntity.ok(userInfo); - } -} From 9d6226dd5b74e82a6131583685af6d273edadbea Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 21 Feb 2025 15:04:17 +0900 Subject: [PATCH 026/114] =?UTF-8?q?Dependency/Plugin:=20API=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - okhttp ver 4.12.0 설정 --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 1049dc1..47df65a 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,9 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' + + //okhttp + implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.12.0' } tasks.named('test') { From 8bec7f67ff3bd02d903901c1b8d609521aad13ed Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 21 Feb 2025 15:05:38 +0900 Subject: [PATCH 027/114] =?UTF-8?q?Settings:=20API=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 356817f..5324239 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,4 +10,8 @@ spring: hibernate: ddl-auto: update properties: - hibernate.dialect: org.hibernate.dialect.MySQL8Dialect \ No newline at end of file + hibernate.dialect: org.hibernate.dialect.MySQL8Dialect +face: + api: + url: ${API_URL} + token: ${API_TOKEN} \ No newline at end of file From b00a26497dbd76d2883811371354336196fdd25b Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 21 Feb 2025 15:11:50 +0900 Subject: [PATCH 028/114] =?UTF-8?q?Feat:=20=EC=96=BC=EA=B5=B4=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 반환되는 result 값에 따라 얼굴 인식 성공 여부 확인 --- .../controller/FaceDetectController.java | 25 ++++++ .../emotion/dto/FaceDetectionResponse.java | 13 +++ .../emotion/service/FaceDetectionService.java | 8 ++ .../impl/FaceDetectionServiceImpl.java | 90 +++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java create mode 100644 src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java create mode 100644 src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java create mode 100644 src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java diff --git a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java new file mode 100644 index 0000000..b8596bf --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java @@ -0,0 +1,25 @@ +package com.bamboo.log.emotion.controller; + +import com.bamboo.log.emotion.dto.FaceDetectionResponse; +import com.bamboo.log.emotion.service.FaceDetectionService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/emotion") +public class FaceDetectController { + private final FaceDetectionService faceDetectionService; + + @PostMapping("/detect") + public ResponseEntity detectFace(@RequestParam("image") MultipartFile image) { + FaceDetectionResponse response = faceDetectionService.detectFace(image); + return ResponseEntity.status(HttpStatus.valueOf(response.statusCode())).body(response); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java new file mode 100644 index 0000000..f9a2b09 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java @@ -0,0 +1,13 @@ +package com.bamboo.log.emotion.dto; + +import org.springframework.http.HttpStatus; + +public record FaceDetectionResponse( + int statusCode, + String statusMessage, + String message +) { + public FaceDetectionResponse(HttpStatus httpStatus, String message) { + this(httpStatus.value(), httpStatus.name(), message); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java b/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java new file mode 100644 index 0000000..af5f1bc --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java @@ -0,0 +1,8 @@ +package com.bamboo.log.emotion.service; + +import com.bamboo.log.emotion.dto.FaceDetectionResponse; +import org.springframework.web.multipart.MultipartFile; + +public interface FaceDetectionService { + FaceDetectionResponse detectFace(MultipartFile image); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java new file mode 100644 index 0000000..0e3fa52 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java @@ -0,0 +1,90 @@ +package com.bamboo.log.emotion.service.impl; + +import com.bamboo.log.emotion.dto.FaceDetectionResponse; +import com.bamboo.log.emotion.service.FaceDetectionService; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@Service +@RequiredArgsConstructor +public class FaceDetectionServiceImpl implements FaceDetectionService { + + @Value("${face.api.url}") + private String faceApiUrl; + + @Value("${face.api.token}") + private String faceToken; + + private final OkHttpClient client = new OkHttpClient(); + + @Override + public FaceDetectionResponse detectFace(MultipartFile image) { + if (image == null || image.isEmpty()) { + log.error("파일이 전달되지 않음."); + return new FaceDetectionResponse(HttpStatus.BAD_REQUEST, "파일이 전달되지 않았습니다."); + } + log.info("파일 이름: {}", image.getOriginalFilename()); + log.info("파일 크기: {} bytes", image.getSize()); + + try { + String contentType = image.getContentType(); + if (contentType == null) { + contentType = "application/octet-stream"; // 기본값 설정 + } + + RequestBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("image", image.getOriginalFilename(), + RequestBody.create(image.getBytes(), MediaType.parse(contentType))) + .build(); + + // API 요청 생성 (토큰 포함) + Request request = new Request.Builder() + .url(faceApiUrl) + .post(requestBody) + .addHeader("accept", "application/json") + .addHeader("Authorization", "Bearer " + faceToken) + .build(); + + try (Response response = client.newCall(request).execute()) { + log.info("API 응답 코드: {}", response.code()); + log.info("API 응답 메시지: {}", response.message()); + + if (!response.isSuccessful()) { + return new FaceDetectionResponse(HttpStatus.valueOf(response.code()), "API 요청 실패: " + response.message()); + } + + String responseBody = response.body().string(); + log.info("API 응답 본문: {}", responseBody); + + // JSON 파싱 + JsonNode jsonNode = new ObjectMapper().readTree(responseBody); + JsonNode results = jsonNode.get("results"); + + // 얼굴 인식 여부 확인 + if (results != null && results.isArray() && results.size() > 0) { + for (JsonNode result : results) { + if ("face".equals(result.get("name").asText())) { + return new FaceDetectionResponse(HttpStatus.OK, responseBody); + } + } + } + return new FaceDetectionResponse(HttpStatus.NOT_FOUND, "얼굴이 인식되지 않았습니다."); + } + } catch (IOException e) { + log.error("API 요청 중 예외 발생", e); + return new FaceDetectionResponse(HttpStatus.INTERNAL_SERVER_ERROR, "API 요청 중 오류 발생: " + e.getMessage()); + } + } + +} \ No newline at end of file From cf735ab4a23f8fd103d03c993bd637b6c70fbb55 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 21 Feb 2025 15:22:39 +0900 Subject: [PATCH 029/114] =?UTF-8?q?Feat:=20=EC=96=BC=EA=B5=B4=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=20API=20Swagger=20=EB=AC=B8=EC=84=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/emotion/controller/FaceDetectController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java index b8596bf..fee5189 100644 --- a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java +++ b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java @@ -2,6 +2,8 @@ import com.bamboo.log.emotion.dto.FaceDetectionResponse; import com.bamboo.log.emotion.service.FaceDetectionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -14,10 +16,15 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/emotion") +@Tag(name = "Face Detection", description = "얼굴 인식 관련 API") public class FaceDetectController { private final FaceDetectionService faceDetectionService; @PostMapping("/detect") + @Operation( + summary = "얼굴 인식 API", + description = "이미지를 업로드하면 얼굴 인식 여부를 반환합니다." + ) public ResponseEntity detectFace(@RequestParam("image") MultipartFile image) { FaceDetectionResponse response = faceDetectionService.detectFace(image); return ResponseEntity.status(HttpStatus.valueOf(response.statusCode())).body(response); From b3fdef559abc75621ef96bae1e9587204abf8289 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Mon, 24 Feb 2025 01:19:06 +0900 Subject: [PATCH 030/114] =?UTF-8?q?FIX=20:=20=EB=AF=BC=EA=B0=90=ED=95=9C?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=EB=93=A4=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EB=82=98=EB=88=84=EC=97=88=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c2065bc..a00fec8 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ out/ ### VS Code ### .vscode/ + +.env From 92af92955946ec501dd3706ae93901826e5be455 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Mon, 24 Feb 2025 01:20:39 +0900 Subject: [PATCH 031/114] =?UTF-8?q?FIX=20:=20=EB=AF=BC=EA=B0=90=ED=95=9C?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=EB=93=A4=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EB=82=98=EB=88=84=EC=97=88=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 ++++--- src/main/resources/application.yml | 23 +++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 2451df4..3e30949 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' } -tasks.named('test') { - useJUnitPlatform() -} + tasks.named('test') { + useJUnitPlatform() + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c9191de..c78db31 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,17 +16,16 @@ security: client: registration: kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: http://localhost:8080/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: profile_nickname,account_email + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} provider: kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id - + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} \ No newline at end of file From c473a6895d356150c975ec3258fe1f8fed0b7dea Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Mon, 24 Feb 2025 14:20:16 +0900 Subject: [PATCH 032/114] =?UTF-8?q?Fix:=20Conflict=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?in=20bulid.gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 47df65a..d6002c1 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' developmentOnly 'org.springframework.boot:spring-boot-devtools' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' testImplementation 'org.springframework.security:spring-security-test' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' From 40d597be95d966650ab475a0e9ed2d262298472b Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Mon, 24 Feb 2025 14:20:58 +0900 Subject: [PATCH 033/114] =?UTF-8?q?Fix:=20Conflict=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?in=20application.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5324239..95efcf1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,10 +8,30 @@ spring: show-sql: true open-in-view: false hibernate: - ddl-auto: update + ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + face: api: url: ${API_URL} - token: ${API_TOKEN} \ No newline at end of file + token: ${API_TOKEN} + +security: + oauth2: + client: + registration: + kakao: + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} From 03b3ccaf22672d58b135e7227fbb20635a86e4c9 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Mon, 24 Feb 2025 14:28:51 +0900 Subject: [PATCH 034/114] =?UTF-8?q?Fix:=20dependencies=20=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index 3e30949..d7e02eb 100644 --- a/build.gradle +++ b/build.gradle @@ -24,26 +24,24 @@ repositories { } dependencies { - dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - implementation 'org.springframework.boot:spring-boot-starter-security' - testImplementation 'org.springframework.security:spring-security-test' - compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation 'io.jsonwebtoken:jjwt-api:0.12.3' - implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' - implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' - } + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' +} - tasks.named('test') { - useJUnitPlatform() - } +tasks.named('test') { + useJUnitPlatform() } \ No newline at end of file From 94e18ec65308070bf84d267211c7ee2d1f60d824 Mon Sep 17 00:00:00 2001 From: HyunA <129165742+10Hyuna@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:32:00 +0900 Subject: [PATCH 035/114] =?UTF-8?q?Revert=20"[Feature]=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=83=9D=EC=84=B1=20API"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes .gitignore | 2 +- build.gradle | 41 +++------- .../bamboo/log/diary/api/DiaryController.java | 33 --------- .../com/bamboo/log/diary/domain/Diary.java | 33 --------- .../bamboo/log/diary/domain/TodaySummary.java | 29 -------- .../diary/dto/request/CreateDiaryRequest.java | 9 --- .../dto/response/CreateDiaryResponse.java | 8 -- .../log/diary/repository/DiaryRepository.java | 7 -- .../repository/TodaySummaryRepository.java | 7 -- .../log/diary/service/diary/DiaryService.java | 10 --- .../diary/service/diary/DiaryServiceImpl.java | 70 ------------------ .../diary/service/mock/MockUserService.java | 12 --- .../log/diary/service/mock/UserService.java | 5 -- .../service/summary/TodaySummaryService.java | 11 --- .../summary/TodaySummaryServiceImpl.java | 46 ------------ .../log/elice/config/RestTemplateConfig.java | 14 ---- .../elice/service/ImageGenerationService.java | 58 --------------- .../com/bamboo/log/utils/ResponseHandler.java | 51 ------------- .../com/bamboo/log/utils/SecurityConfig.java | 23 ------ .../bamboo/log/utils/dto/DetailResponse.java | 13 ---- .../bamboo/log/utils/dto/ResponseForm.java | 21 ------ src/main/resources/application.yml | 14 +--- 23 files changed, 12 insertions(+), 505 deletions(-) delete mode 100644 src/main/java/com/bamboo/log/diary/api/DiaryController.java delete mode 100644 src/main/java/com/bamboo/log/diary/domain/Diary.java delete mode 100644 src/main/java/com/bamboo/log/diary/domain/TodaySummary.java delete mode 100644 src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java delete mode 100644 src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java delete mode 100644 src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java delete mode 100644 src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/mock/UserService.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java delete mode 100644 src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java delete mode 100644 src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java delete mode 100644 src/main/java/com/bamboo/log/utils/ResponseHandler.java delete mode 100644 src/main/java/com/bamboo/log/utils/SecurityConfig.java delete mode 100644 src/main/java/com/bamboo/log/utils/dto/DetailResponse.java delete mode 100644 src/main/java/com/bamboo/log/utils/dto/ResponseForm.java diff --git a/.DS_Store b/.DS_Store index 5770197c32987a55423285d0d1e1dddc77abfd3b..dd46a9923d3511c0ddce67c1cc0fd4f8a0b2c125 100644 GIT binary patch delta 200 zcmZoMXfc=|#>B!ku~2NHo+2a9#(>?7iw`g}G4f32VJc>1pWMYH6UAu9z`(HVKNtX6 z3=FIcdJLHiDGaF$iJm$6$w@i+NkCBn1_s7yKpItL5<@9aZ4N^Us!9ekph~03B}|v) z*%>?-Tp4^A0)VFGqN`(kx7mVOo@p~X2R{eUotrl@e`lV|FXG6-$iTn^G=gDsgvc6Z E0A!UiL;wH) delta 71 zcmZoMXfc=|#>B)qu~2NHo+2a1#(>?7j2x4BSc)ezvYy!domGHoW5Wif&Fmcf96)88 Z1v$PmPv#eK { -} diff --git a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java deleted file mode 100644 index 4093d83..0000000 --- a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.bamboo.log.diary.repository; - -import com.bamboo.log.diary.domain.TodaySummary; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface TodaySummaryRepository extends JpaRepository { -} diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java deleted file mode 100644 index 50cb4ac..0000000 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.bamboo.log.diary.service.diary; - -import com.bamboo.log.diary.dto.request.CreateDiaryRequest; -import org.springframework.http.ResponseEntity; - -public interface DiaryService { - - ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); - -} diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java deleted file mode 100644 index 5c201fd..0000000 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.bamboo.log.diary.service.diary; - -import com.bamboo.log.diary.domain.Diary; -import com.bamboo.log.diary.dto.request.CreateDiaryRequest; -import com.bamboo.log.diary.dto.response.CreateDiaryResponse; -import com.bamboo.log.diary.repository.DiaryRepository; -import com.bamboo.log.diary.repository.TodaySummaryRepository; -import com.bamboo.log.diary.service.mock.UserService; -import com.bamboo.log.diary.service.summary.TodaySummaryService; -import com.bamboo.log.utils.ResponseHandler; -import com.bamboo.log.utils.dto.ResponseForm; -import jakarta.persistence.EntityNotFoundException; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.io.IOException; -import java.time.LocalDateTime; - -@Service -@Transactional -@RequiredArgsConstructor -public class DiaryServiceImpl implements DiaryService { - - private final UserService userService; - private final DiaryRepository diaryRepository; - private final TodaySummaryService todaySummaryService; - - @Override - public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { - - // User 객체 검증 로직 - // 해당 User 객체 찾을 수 없을 경우, 에러 반환 - if(userService.existsById(createDiaryRequest.userId())) { - return ResponseHandler.create404Error(new ResponseForm(), new EntityNotFoundException("잘못된 유저 정보입니다.")); - } - - // 해당 시점의 localDateTime 저장 - LocalDateTime localDateTime = LocalDateTime.now(); - - // alice api 연결 함수 호출 - - // 데이터베이스에 저장 - Diary diary = saveDiary(createDiaryRequest, localDateTime); - - byte[] todayImage = todaySummaryService.createTodaySummaryImage(createDiaryRequest.diaryDetail()); - - // todaySummaryImage 데이터베이스에 저장 - try { - todaySummaryService.saveTodaySummaryImage(todayImage, diary.getId()); - } catch (IOException e) { - return ResponseHandler.create500Error(new ResponseForm(), e); - } - - // 201 code 반환 - return ResponseHandler.create201Response(new ResponseForm(), new CreateDiaryResponse(localDateTime)); - } - - private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { - Diary diary = Diary.builder() - .userId(createDiaryRequest.userId()) - .context(createDiaryRequest.diaryDetail()) - .createdAt(localDateTime) - .build(); - - return diaryRepository.save(diary); - } - -} diff --git a/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java b/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java deleted file mode 100644 index 86824f2..0000000 --- a/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.bamboo.log.diary.service.mock; - -import org.springframework.stereotype.Service; - -@Service -public class MockUserService implements UserService { - - @Override - public boolean existsById(String userId) { - return true; - } -} diff --git a/src/main/java/com/bamboo/log/diary/service/mock/UserService.java b/src/main/java/com/bamboo/log/diary/service/mock/UserService.java deleted file mode 100644 index 0b37f9a..0000000 --- a/src/main/java/com/bamboo/log/diary/service/mock/UserService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.bamboo.log.diary.service.mock; - -public interface UserService { - boolean existsById(String userId); -} diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java deleted file mode 100644 index 598d786..0000000 --- a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.bamboo.log.diary.service.summary; - -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; - -public interface TodaySummaryService { - - void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException; - byte[] createTodaySummaryImage(String prompt); -} diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java deleted file mode 100644 index 06df2dd..0000000 --- a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.bamboo.log.diary.service.summary; - -import com.bamboo.log.diary.domain.TodaySummary; -import com.bamboo.log.diary.repository.TodaySummaryRepository; -import com.bamboo.log.elice.service.ImageGenerationService; -import com.bamboo.log.utils.ResponseHandler; -import com.bamboo.log.utils.dto.ResponseForm; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.util.Optional; - -@Service -@Transactional -@RequiredArgsConstructor -public class TodaySummaryServiceImpl implements TodaySummaryService { - - private final TodaySummaryRepository todaySummaryRepository; - private final ImageGenerationService imageGenerationService; - - @Override - public void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException { - TodaySummary todaySummary = TodaySummary.builder() - .diaryId(diaryId) - .imageData(image) - .build(); - - todaySummaryRepository.save(todaySummary); - } - - @Override - public byte[] createTodaySummaryImage(String prompt) { - try { - byte[] imageBytes = imageGenerationService.generateImage(prompt); - - return imageBytes; - } catch (IOException e) { - throw new RuntimeException("이미지 생성 중에 문제가 발생했습니다."); - } - - } - -} diff --git a/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java deleted file mode 100644 index 0ea3f07..0000000 --- a/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.bamboo.log.elice.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/bamboo/log/elice/service/ImageGenerationService.java b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java deleted file mode 100644 index d7aa11b..0000000 --- a/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.bamboo.log.elice.service; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import okhttp3.*; -import okhttp3.MediaType; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Service -public class ImageGenerationService { - - private final RestTemplate restTemplate; - private final String apiKey; - private final String apiUrl; - - public ImageGenerationService(RestTemplate restTemplate, - @Value("${elice.api.token}") String apiKey, - @Value("${elice.api.url}") String apiUrl) { - this.restTemplate = restTemplate; - this.apiKey = apiKey; - this.apiUrl = apiUrl; - } - - public byte[] generateImage(String prompt) throws IOException { - OkHttpClient client = new OkHttpClient(); - - String requestBody = String.format("{\"prompt\":\"%s\",\"style\":\"polaroid\"}", prompt); - - MediaType mediaType = MediaType.parse("application/json"); - RequestBody body = RequestBody.create(requestBody, mediaType); - - Request request = new Request.Builder() - .url(apiUrl) - .post(body) - .addHeader("accept", "application/json") - .addHeader("content-type", "application/json") - .addHeader("Authorization", "Bearer " + apiKey) - .build(); - - Response response = client.newCall(request).execute(); - - if (!response.isSuccessful()) { - throw new IOException("요청 실패: " + response.code()); - } - - byte[] imageBytes = response.body().bytes(); // ✅ 이미지 데이터를 byte[]로 변환 - response.close(); - return imageBytes; - } -} - diff --git a/src/main/java/com/bamboo/log/utils/ResponseHandler.java b/src/main/java/com/bamboo/log/utils/ResponseHandler.java deleted file mode 100644 index c483a50..0000000 --- a/src/main/java/com/bamboo/log/utils/ResponseHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.bamboo.log.utils; - -import com.bamboo.log.utils.dto.DetailResponse; -import com.bamboo.log.utils.dto.ResponseForm; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -public class ResponseHandler { - - public static ResponseEntity create500Error(ResponseForm response, Exception e) { - response.of("result", "FAIL"); - response.of("error", DetailResponse.builder().code(500).message(e.getMessage()).build()); - return new ResponseEntity(response, HttpStatus.INTERNAL_SERVER_ERROR); - } - - public static ResponseEntity create400Error(ResponseForm response, Exception e) { - response.of("result", "FAIL"); - response.of("error", DetailResponse.builder().code(400).message(e.getMessage()).build()); - - return new ResponseEntity(response, HttpStatus.BAD_REQUEST); - } - - public static ResponseEntity create404Error(ResponseForm response, Exception e) { - response.of("result", "FAIL"); - response.of("error", DetailResponse.builder().code(404).message(e.getMessage()).build()); - - return new ResponseEntity(response, HttpStatus.NOT_FOUND); - } - - public static ResponseEntity create204Response(ResponseForm response, String message) { - response.of("result", "SUCCESS"); - response.of("code", DetailResponse.builder().code(204).message(message).build()); - - return new ResponseEntity(response, HttpStatus.NO_CONTENT); - } - - public static ResponseEntity create200Response(ResponseForm response, Object object) { - response.of("result", "SUCCESS"); - response.of("code", object); - - return new ResponseEntity(response, HttpStatus.OK); - } - - public static ResponseEntity create201Response(ResponseForm response, Object object) { - response.of("result", "SUCCESS"); - response.of("code", object); - - return new ResponseEntity(response, HttpStatus.CREATED); - } - -} diff --git a/src/main/java/com/bamboo/log/utils/SecurityConfig.java b/src/main/java/com/bamboo/log/utils/SecurityConfig.java deleted file mode 100644 index 5d51eb4..0000000 --- a/src/main/java/com/bamboo/log/utils/SecurityConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.bamboo.log.utils; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.web.SecurityFilterChain; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(csrf -> csrf.disable()) // CSRF 비활성화 - .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/images/**").permitAll() // 이미지 API 인증 없이 허용 - .anyRequest().authenticated() - ); - - return http.build(); - } -} diff --git a/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java deleted file mode 100644 index 61f22cf..0000000 --- a/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.bamboo.log.utils.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DetailResponse { - - private Integer code; - private String message; - -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java deleted file mode 100644 index 04eccf6..0000000 --- a/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.bamboo.log.utils.dto; - -import lombok.Getter; - -import java.util.HashMap; -import java.util.Map; - -@Getter -public class ResponseForm { - - public ResponseForm() { - response = new HashMap<>(); - } - - public void of(String value1, Object value2) { - response.put(value1, value2); - } - - private Map response; - -} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 317cea6..356817f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,23 +1,13 @@ spring: datasource: - driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} username: ${MYSQL_USERNAME} password: ${MYSQL_PASSWORD} - + driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true open-in-view: false hibernate: ddl-auto: update properties: - hibernate.dialect: org.hibernate.dialect.MySQL8Dialect - - -elice: - api: - token: ${API_TOKEN} - url: - face: ${FACE_API_URL} - img: ${IMG_API_URL} - chat: ${CHAT_API_URL} + hibernate.dialect: org.hibernate.dialect.MySQL8Dialect \ No newline at end of file From 2ad544d918c8e753cffc2cf9adbd4af8927127d5 Mon Sep 17 00:00:00 2001 From: heodongun <162291579+heodongun@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:40:50 +0900 Subject: [PATCH 036/114] =?UTF-8?q?FIX=20:=20=EB=AF=BC=EA=B0=90=ED=95=9C?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=EB=93=A4=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EB=82=98=EB=88=84=EC=97=88=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 95efcf1..d7ccf57 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -29,9 +29,11 @@ security: redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} +jwt: + secret: ${JWT_SECRET} From cf86ff35c1f31171602bb8be1a9d1ad9d8f901bc Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Mon, 24 Feb 2025 20:48:06 +0900 Subject: [PATCH 037/114] =?UTF-8?q?FIX=20:=20spring=EC=97=90=EB=8B=A4?= =?UTF-8?q?=EA=B0=80=20security=EA=B0=80=20=EC=95=88=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EA=B0=94=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d7ccf57..bff74b3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,29 +11,29 @@ spring: ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + jwt: + secret: ${JWT_SECRET} face: api: url: ${API_URL} token: ${API_TOKEN} -security: - oauth2: - client: - registration: - kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} -jwt: - secret: ${JWT_SECRET} From dd725e9855d1b0b8a561d4d8717b348a08748287 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Tue, 25 Feb 2025 02:37:07 +0900 Subject: [PATCH 038/114] =?UTF-8?q?Fix:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 소셜로그인 들여쓰기 부분 수정 - API 연동 위한 공통 명칭 설정 --- .../impl/FaceDetectionServiceImpl.java | 4 +- src/main/resources/application.yml | 49 ++++++++++--------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java index 0e3fa52..47e6d8a 100644 --- a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java +++ b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java @@ -19,10 +19,10 @@ @RequiredArgsConstructor public class FaceDetectionServiceImpl implements FaceDetectionService { - @Value("${face.api.url}") + @Value("${elice.api.url.face}") private String faceApiUrl; - @Value("${face.api.token}") + @Value("${elice.api.token}") private String faceToken; private final OkHttpClient client = new OkHttpClient(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d7ccf57..5379dd6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -12,28 +12,31 @@ spring: properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect -face: + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + jwt: + secret: ${JWT_SECRET} + +elice: api: - url: ${API_URL} token: ${API_TOKEN} - -security: - oauth2: - client: - registration: - kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} -jwt: - secret: ${JWT_SECRET} + url: + face: ${FACE_URL} + img: + chat: \ No newline at end of file From 9f40e210095613f21a3a57f7464d0ebd7481a61b Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Tue, 25 Feb 2025 13:29:33 +0900 Subject: [PATCH 039/114] chore: Add temporary commit to trigger PR --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37b0f07..3bc95eb 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,4 @@ - **Comment:** 필요한 주석 추가 및 변경 - **Dependency/Plugin:** Add a dependency/plugin - **Docs:** 문서 수정 -- **Rename:** 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우 \ No newline at end of file +- **Rename:** 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우# Temporary commit for PR From 5c08cb8137cff63c33b4afee962b9e6086b7f015 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Tue, 25 Feb 2025 13:34:27 +0900 Subject: [PATCH 040/114] Fix: Manually merge feature/issue-10 into dev --- .../ISSUE_TEMPLATE/test-issue-template.yml | 22 - .github/workflows/auto-branch.yml | 17 + .github/workflows/close-issue.yml | 14 - .github/workflows/create-branch.yml | 68 - .github/workflows/delete-branch.yml | 14 - .gitignore | 4 +- build.gradle | 56 +- feature-issue-10.patch | 2072 +++++++++++++++++ .../log/common/config/CorsMvcConfig.java | 20 - .../log/common/config/SecurityConfig.java | 78 - .../log/common/config/SwaggerConfig.java | 12 - .../bamboo/log/diary/api/DiaryController.java | 33 + .../com/bamboo/log/diary/domain/Diary.java | 33 + .../bamboo/log/diary/domain/TodaySummary.java | 29 + .../diary/dto/request/CreateDiaryRequest.java | 9 + .../dto/response/CreateDiaryResponse.java | 8 + .../log/diary/repository/DiaryRepository.java | 7 + .../repository/TodaySummaryRepository.java | 7 + .../log/diary/service/diary/DiaryService.java | 10 + .../diary/service/diary/DiaryServiceImpl.java | 70 + .../diary/service/mock/MockUserService.java | 12 + .../log/diary/service/mock/UserService.java | 5 + .../service/summary/TodaySummaryService.java | 11 + .../summary/TodaySummaryServiceImpl.java | 46 + .../user/controller/RefreshController.java | 33 - .../domain/user/jwt/service/JWTFilter.java | 76 - .../log/domain/user/jwt/service/JWTUtil.java | 57 - .../user/jwt/service/UserContextUtil.java | 33 - .../user/logout/CustomLogoutFilter.java | 94 - .../user/oauth/dto/CustomOAuth2User.java | 48 - .../domain/user/oauth/dto/KakaoResponse.java | 40 - .../domain/user/oauth/dto/OAuth2Response.java | 11 - .../log/domain/user/oauth/dto/UserDTO.java | 14 - .../user/oauth/entity/RefreshToken.java | 40 - .../domain/user/oauth/entity/UserEntity.java | 45 - .../oauth/repository/RefreshRepository.java | 10 - .../user/oauth/repository/UserRepository.java | 8 - .../oauth/service/CustomFailureHandler.java | 20 - .../service/CustomOAuth2UserService.java | 67 - .../oauth/service/CustomSuccessHandler.java | 78 - .../refresh/service/ProcessTokenReissue.java | 9 - .../service/ProcessTokenReissueImpl.java | 103 - .../log/elice/config/RestTemplateConfig.java | 14 + .../elice/service/ImageGenerationService.java | 58 + .../controller/FaceDetectController.java | 32 - .../emotion/dto/FaceDetectionResponse.java | 13 - .../emotion/service/FaceDetectionService.java | 8 - .../impl/FaceDetectionServiceImpl.java | 90 - .../com/bamboo/log/utils/ResponseHandler.java | 51 + .../com/bamboo/log/utils/SecurityConfig.java | 23 + .../bamboo/log/utils/dto/DetailResponse.java | 13 + .../bamboo/log/utils/dto/ResponseForm.java | 21 + src/main/resources/application.yml | 31 +- 53 files changed, 2589 insertions(+), 1208 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/test-issue-template.yml create mode 100644 .github/workflows/auto-branch.yml delete mode 100644 .github/workflows/close-issue.yml delete mode 100644 .github/workflows/create-branch.yml delete mode 100644 .github/workflows/delete-branch.yml create mode 100644 feature-issue-10.patch delete mode 100644 src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java delete mode 100644 src/main/java/com/bamboo/log/common/config/SecurityConfig.java delete mode 100644 src/main/java/com/bamboo/log/common/config/SwaggerConfig.java create mode 100644 src/main/java/com/bamboo/log/diary/api/DiaryController.java create mode 100644 src/main/java/com/bamboo/log/diary/domain/Diary.java create mode 100644 src/main/java/com/bamboo/log/diary/domain/TodaySummary.java create mode 100644 src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java create mode 100644 src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java create mode 100644 src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java create mode 100644 src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java create mode 100644 src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java create mode 100644 src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java create mode 100644 src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java create mode 100644 src/main/java/com/bamboo/log/diary/service/mock/UserService.java create mode 100644 src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java create mode 100644 src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java delete mode 100644 src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java create mode 100644 src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java create mode 100644 src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java delete mode 100644 src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java delete mode 100644 src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java delete mode 100644 src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java delete mode 100644 src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java create mode 100644 src/main/java/com/bamboo/log/utils/ResponseHandler.java create mode 100644 src/main/java/com/bamboo/log/utils/SecurityConfig.java create mode 100644 src/main/java/com/bamboo/log/utils/dto/DetailResponse.java create mode 100644 src/main/java/com/bamboo/log/utils/dto/ResponseForm.java diff --git a/.github/ISSUE_TEMPLATE/test-issue-template.yml b/.github/ISSUE_TEMPLATE/test-issue-template.yml deleted file mode 100644 index 465c4ef..0000000 --- a/.github/ISSUE_TEMPLATE/test-issue-template.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: "💬 Test" -description: "코드 테스트" -labels: ["test"] -body: - - type: textarea - attributes: - label: 📄 설명 - description: 검증하고자 하는 코드 대한 설명을 작성해 주세요. - placeholder: 검증하고자 하는 코드와 검증 방식에 대해 최대한 자세히 작성해 주세요! - validations: - required: true - - type: textarea - attributes: - label: ✅ 작업할 내용 - description: 할 일을 체크박스 형태로 작성해주세요. - placeholder: 최대한 세분화 해서 적어주세요! - validations: - required: true - - type: textarea - attributes: - label: 🙋🏻 참고 자료 - description: 참고 자료가 있다면 작성해 주세요. diff --git a/.github/workflows/auto-branch.yml b/.github/workflows/auto-branch.yml new file mode 100644 index 0000000..4634d2b --- /dev/null +++ b/.github/workflows/auto-branch.yml @@ -0,0 +1,17 @@ +name: Create Issue Branch +on: + issues: + types: [ assigned ] + issue_comment: + types: [ created ] + pull_request: + types: [ closed ] + +jobs: + create_issue_branch_job: + runs-on: ubuntu-latest + steps: + - name: Create Issue Branch + uses: robvanderleek/create-issue-branch@main + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} diff --git a/.github/workflows/close-issue.yml b/.github/workflows/close-issue.yml deleted file mode 100644 index cd25d28..0000000 --- a/.github/workflows/close-issue.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Close Issue When PR Merged -on: - pull_request: - types: [closed] - -jobs: - close_issue: - runs-on: ubuntu-latest - steps: - - name: Close linked issue - uses: peter-evans/close-issue@v2 - with: - github_token: ${{ secrets.TOKEN }} - comment: "✅ PR이 머지되어 해당 이슈를 닫습니다." diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml deleted file mode 100644 index 443c204..0000000 --- a/.github/workflows/create-branch.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Create Issue Branch -on: - issues: - types: [assigned] - issue_comment: - types: [created] - pull_request: - types: [closed] - -jobs: - create_issue_branch_job: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Debug Git repository - run: | - git remote -v - git branch - git status - - - name: Determine Branch Name Based on Issue Labels - id: detect_branch - run: | - LABEL=$(echo '${{ toJSON(github.event.issue.labels) }}' | jq -r '.[0].name') - if [[ -z "$LABEL" || "$LABEL" == "null" ]]; then - LABEL="feature" # 기본값 설정 - fi - BRANCH_NAME="${LABEL}/issue-${{ github.event.issue.number }}" - echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV - echo "🔹 Detected branch name: $BRANCH_NAME" - - - name: Create Issue Branch - uses: robvanderleek/create-issue-branch@main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - default-branch: dev - branch-name: "${{ env.BRANCH_NAME }}" - overwrite-existing: true - - - name: Check if PR already exists - id: check_pr - run: | - if gh pr list --state open --base dev --head "${{ env.BRANCH_NAME }}" --repo ${{ github.repository }} | grep "${{ env.BRANCH_NAME }}"; then - echo "pr_exists=true" >> $GITHUB_ENV - else - echo "pr_exists=false" >> $GITHUB_ENV - fi - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Open Pull Request - uses: peter-evans/create-pull-request@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - base: dev - branch: "${{ env.BRANCH_NAME }}" - title: "🚀 Pull Request #${{ github.event.issue.number }}" - body: | - 🔥 **이슈 #${{ github.event.issue.number }}에 대한 PR 생성!** 🔥 - - - 이슈 링크: #${{ github.event.issue.number }} - delete-branch: false - allow-empty-commits: true diff --git a/.github/workflows/delete-branch.yml b/.github/workflows/delete-branch.yml deleted file mode 100644 index ad1416a..0000000 --- a/.github/workflows/delete-branch.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Delete Merged Branch -on: - pull_request: - types: [closed] - -jobs: - delete_merged_branch: - runs-on: ubuntu-latest - if: github.event.pull_request.merged == true - steps: - - name: Delete branch if merged - uses: github-actions/delete-merged-branch@v4 - with: - github_token: ${{ secrets.TOKEN }} diff --git a/.gitignore b/.gitignore index a00fec8..e48b6be 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,4 @@ out/ /.nb-gradle/ ### VS Code ### -.vscode/ - -.env +.vscode/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index dd9d89a..56f4f0e 100644 --- a/build.gradle +++ b/build.gradle @@ -24,30 +24,40 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' - implementation 'io.jsonwebtoken:jjwt-api:0.12.3' - implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' - implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' - - developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.mysql:mysql-connector-j' - - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.security:spring-security-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - - // okhttp - implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.0.4' + implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0' + testImplementation 'org.projectlombok:lombok:1.18.26' + compileOnly 'org.projectlombok:lombok:1.18.26' + runtimeOnly 'com.mysql:mysql-connector-j:8.0.32' + annotationProcessor 'org.projectlombok:lombok:1.18.26' + testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' + + implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' + implementation 'jakarta.validation:jakarta.validation-api:3.0.2' + + implementation 'com.squareup.okhttp3:okhttp:4.9.3' + + // Spring Doc + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' + + // Spring Security + implementation 'org.springframework.boot:spring-boot-starter-security:3.0.4' + + // JWT + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // chatGPT API + implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' + + // JSON 라이브러리 + implementation 'org.json:json:20230227' + + implementation 'org.springframework:spring-test:6.0.6' + testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' } tasks.named('test') { useJUnitPlatform() -} \ No newline at end of file +} diff --git a/feature-issue-10.patch b/feature-issue-10.patch new file mode 100644 index 0000000..4549bcd --- /dev/null +++ b/feature-issue-10.patch @@ -0,0 +1,2072 @@ +diff --git a/.github/ISSUE_TEMPLATE/test-issue-template.yml b/.github/ISSUE_TEMPLATE/test-issue-template.yml +deleted file mode 100644 +index 465c4ef..0000000 +--- a/.github/ISSUE_TEMPLATE/test-issue-template.yml ++++ /dev/null +@@ -1,22 +0,0 @@ +-name: "💬 Test" +-description: "코드 테스트" +-labels: ["test"] +-body: +- - type: textarea +- attributes: +- label: 📄 설명 +- description: 검증하고자 하는 코드 대한 설명을 작성해 주세요. +- placeholder: 검증하고자 하는 코드와 검증 방식에 대해 최대한 자세히 작성해 주세요! +- validations: +- required: true +- - type: textarea +- attributes: +- label: ✅ 작업할 내용 +- description: 할 일을 체크박스 형태로 작성해주세요. +- placeholder: 최대한 세분화 해서 적어주세요! +- validations: +- required: true +- - type: textarea +- attributes: +- label: 🙋🏻 참고 자료 +- description: 참고 자료가 있다면 작성해 주세요. +diff --git a/.github/workflows/auto-branch.yml b/.github/workflows/auto-branch.yml +new file mode 100644 +index 0000000..4634d2b +--- /dev/null ++++ b/.github/workflows/auto-branch.yml +@@ -0,0 +1,17 @@ ++name: Create Issue Branch ++on: ++ issues: ++ types: [ assigned ] ++ issue_comment: ++ types: [ created ] ++ pull_request: ++ types: [ closed ] ++ ++jobs: ++ create_issue_branch_job: ++ runs-on: ubuntu-latest ++ steps: ++ - name: Create Issue Branch ++ uses: robvanderleek/create-issue-branch@main ++ env: ++ GITHUB_TOKEN: ${{ secrets.TOKEN }} +diff --git a/.github/workflows/close-issue.yml b/.github/workflows/close-issue.yml +deleted file mode 100644 +index cd25d28..0000000 +--- a/.github/workflows/close-issue.yml ++++ /dev/null +@@ -1,14 +0,0 @@ +-name: Close Issue When PR Merged +-on: +- pull_request: +- types: [closed] +- +-jobs: +- close_issue: +- runs-on: ubuntu-latest +- steps: +- - name: Close linked issue +- uses: peter-evans/close-issue@v2 +- with: +- github_token: ${{ secrets.TOKEN }} +- comment: "✅ PR이 머지되어 해당 이슈를 닫습니다." +diff --git a/.github/workflows/create-branch.yml b/.github/workflows/create-branch.yml +deleted file mode 100644 +index 443c204..0000000 +--- a/.github/workflows/create-branch.yml ++++ /dev/null +@@ -1,68 +0,0 @@ +-name: Create Issue Branch +-on: +- issues: +- types: [assigned] +- issue_comment: +- types: [created] +- pull_request: +- types: [closed] +- +-jobs: +- create_issue_branch_job: +- runs-on: ubuntu-latest +- steps: +- - name: Checkout repository +- uses: actions/checkout@v4 +- with: +- fetch-depth: 0 +- +- - name: Debug Git repository +- run: | +- git remote -v +- git branch +- git status +- +- - name: Determine Branch Name Based on Issue Labels +- id: detect_branch +- run: | +- LABEL=$(echo '${{ toJSON(github.event.issue.labels) }}' | jq -r '.[0].name') +- if [[ -z "$LABEL" || "$LABEL" == "null" ]]; then +- LABEL="feature" # 기본값 설정 +- fi +- BRANCH_NAME="${LABEL}/issue-${{ github.event.issue.number }}" +- echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV +- echo "🔹 Detected branch name: $BRANCH_NAME" +- +- - name: Create Issue Branch +- uses: robvanderleek/create-issue-branch@main +- env: +- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +- with: +- default-branch: dev +- branch-name: "${{ env.BRANCH_NAME }}" +- overwrite-existing: true +- +- - name: Check if PR already exists +- id: check_pr +- run: | +- if gh pr list --state open --base dev --head "${{ env.BRANCH_NAME }}" --repo ${{ github.repository }} | grep "${{ env.BRANCH_NAME }}"; then +- echo "pr_exists=true" >> $GITHUB_ENV +- else +- echo "pr_exists=false" >> $GITHUB_ENV +- fi +- env: +- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +- +- - name: Open Pull Request +- uses: peter-evans/create-pull-request@v5 +- with: +- token: ${{ secrets.GITHUB_TOKEN }} +- base: dev +- branch: "${{ env.BRANCH_NAME }}" +- title: "🚀 Pull Request #${{ github.event.issue.number }}" +- body: | +- 🔥 **이슈 #${{ github.event.issue.number }}에 대한 PR 생성!** 🔥 +- +- - 이슈 링크: #${{ github.event.issue.number }} +- delete-branch: false +- allow-empty-commits: true +diff --git a/.github/workflows/delete-branch.yml b/.github/workflows/delete-branch.yml +deleted file mode 100644 +index ad1416a..0000000 +--- a/.github/workflows/delete-branch.yml ++++ /dev/null +@@ -1,14 +0,0 @@ +-name: Delete Merged Branch +-on: +- pull_request: +- types: [closed] +- +-jobs: +- delete_merged_branch: +- runs-on: ubuntu-latest +- if: github.event.pull_request.merged == true +- steps: +- - name: Delete branch if merged +- uses: github-actions/delete-merged-branch@v4 +- with: +- github_token: ${{ secrets.TOKEN }} +diff --git a/.gitignore b/.gitignore +index a00fec8..e48b6be 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -34,6 +34,4 @@ out/ + /.nb-gradle/ + + ### VS Code ### +-.vscode/ +- +-.env ++.vscode/ +\ No newline at end of file +diff --git a/build.gradle b/build.gradle +index dd9d89a..56f4f0e 100644 +--- a/build.gradle ++++ b/build.gradle +@@ -24,30 +24,40 @@ repositories { + } + + dependencies { +- implementation 'org.springframework.boot:spring-boot-starter-web' +- implementation 'org.springframework.boot:spring-boot-starter-data-jpa' +- implementation 'org.springframework.boot:spring-boot-starter-validation' +- implementation 'org.springframework.boot:spring-boot-starter-security' +- implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' +- implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' +- implementation 'io.jsonwebtoken:jjwt-api:0.12.3' +- implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' +- implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' +- +- developmentOnly 'org.springframework.boot:spring-boot-devtools' +- runtimeOnly 'com.mysql:mysql-connector-j' +- +- compileOnly 'org.projectlombok:lombok' +- annotationProcessor 'org.projectlombok:lombok' +- +- testImplementation 'org.springframework.boot:spring-boot-starter-test' +- testImplementation 'org.springframework.security:spring-security-test' +- testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +- +- // okhttp +- implementation 'com.squareup.okhttp3:okhttp:4.12.0' ++ implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.0.4' ++ implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0' ++ testImplementation 'org.projectlombok:lombok:1.18.26' ++ compileOnly 'org.projectlombok:lombok:1.18.26' ++ runtimeOnly 'com.mysql:mysql-connector-j:8.0.32' ++ annotationProcessor 'org.projectlombok:lombok:1.18.26' ++ testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' ++ ++ implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' ++ implementation 'jakarta.validation:jakarta.validation-api:3.0.2' ++ ++ implementation 'com.squareup.okhttp3:okhttp:4.9.3' ++ ++ // Spring Doc ++ implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' ++ ++ // Spring Security ++ implementation 'org.springframework.boot:spring-boot-starter-security:3.0.4' ++ ++ // JWT ++ implementation 'io.jsonwebtoken:jjwt-api:0.11.5' ++ implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' ++ implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' ++ ++ // chatGPT API ++ implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' ++ ++ // JSON 라이브러리 ++ implementation 'org.json:json:20230227' ++ ++ implementation 'org.springframework:spring-test:6.0.6' ++ testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' + } + + tasks.named('test') { + useJUnitPlatform() +-} +\ No newline at end of file ++} +diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +deleted file mode 100644 +index 2f3565b..0000000 +--- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java ++++ /dev/null +@@ -1,20 +0,0 @@ +-package com.bamboo.log.common.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("/**") +- .exposedHeaders("Set-Cookie") +- //프론트 url 넣기 +- .allowedOrigins("http://localhost:3000"); +- } +-} +- +- +diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +deleted file mode 100644 +index 38ab583..0000000 +--- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java ++++ /dev/null +@@ -1,78 +0,0 @@ +-package com.bamboo.log.common.config; +- +-import com.bamboo.log.domain.user.jwt.service.JWTFilter; +-import com.bamboo.log.domain.user.jwt.service.JWTUtil; +-import com.bamboo.log.domain.user.logout.CustomLogoutFilter; +-import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +-import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; +-import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; +-import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; +-import jakarta.servlet.http.HttpServletRequest; +-import lombok.RequiredArgsConstructor; +-import org.springframework.context.annotation.Bean; +-import org.springframework.context.annotation.Configuration; +-import org.springframework.security.config.annotation.web.builders.HttpSecurity; +-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +-import org.springframework.security.config.http.SessionCreationPolicy; +-import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; +-import org.springframework.security.web.SecurityFilterChain; +-import org.springframework.security.web.authentication.logout.LogoutFilter; +-import org.springframework.web.cors.CorsConfiguration; +-import org.springframework.web.cors.CorsConfigurationSource; +- +-import java.util.Collections; +- +-@Configuration +-@EnableWebSecurity +-@RequiredArgsConstructor +-public class SecurityConfig { +- +- private final RefreshRepository refreshRepository; +- private final CustomOAuth2UserService customOAuth2UserService; +- private final CustomSuccessHandler customSuccessHandler; +- private final CustomFailureHandler customFailureHandler; +- private final JWTUtil jwtUtil; +- +- +- @Bean +- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { +- http.csrf((auth) -> auth.disable()); +- http.formLogin((auth) -> auth.disable()); +- http.httpBasic((auth) -> auth.disable()); +- http.oauth2Login((oauth2) -> oauth2 +- .userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig +- .userService(customOAuth2UserService)) +- .successHandler(customSuccessHandler) +- .failureHandler(customFailureHandler) +- ); +- http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); +- +- //이쪽에다가 각각의 엔드포인트를 넣어야함 +- http.authorizeHttpRequests((auth) -> auth +- .requestMatchers("/refresh").permitAll() +- .requestMatchers("/logout").hasAnyRole("USER") +- .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() +- .anyRequest().authenticated()); +- +- +- http.sessionManagement((session) -> session +- .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); +- http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); +- http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { +- @Override +- public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { +- CorsConfiguration configuration = new CorsConfiguration(); +- //프론트 url 넣기 +- 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("Set-Cookie")); +- configuration.setExposedHeaders(Collections.singletonList("Authorization")); +- return configuration; +- } +- })); +- return http.build(); +- } +-} +diff --git a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java +deleted file mode 100644 +index dd0a87a..0000000 +--- a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java ++++ /dev/null +@@ -1,12 +0,0 @@ +-package com.bamboo.log.common.config; +- +-import io.swagger.v3.oas.annotations.OpenAPIDefinition; +-import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +-import io.swagger.v3.oas.annotations.info.Info; +-import io.swagger.v3.oas.annotations.security.SecurityScheme; +-import org.springframework.context.annotation.Configuration; +- +-@Configuration +-@OpenAPIDefinition(info = @Info(title = "ForRest", version = "v1")) +-public class SwaggerConfig { +-} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java +new file mode 100644 +index 0000000..0b4e1c5 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java +@@ -0,0 +1,33 @@ ++package com.bamboo.log.diary.api; ++ ++import com.bamboo.log.diary.dto.request.CreateDiaryRequest; ++import com.bamboo.log.diary.service.diary.DiaryService; ++import com.bamboo.log.utils.ResponseHandler; ++import com.bamboo.log.utils.dto.ResponseForm; ++import io.swagger.v3.oas.annotations.Operation; ++import lombok.RequiredArgsConstructor; ++import org.springframework.http.ResponseEntity; ++import org.springframework.web.bind.annotation.*; ++ ++import io.swagger.v3.oas.annotations.tags.Tag; ++ ++import java.util.List; ++ ++@RestController ++@RequiredArgsConstructor ++@RequestMapping("/diary") ++@Tag(name = "Diary API", description = "일기 관련 API") ++public class DiaryController { ++ ++ private final DiaryService diaryService; ++ ++ @Operation(summary = "랜덤 주제 조회") ++ @PostMapping("/create") ++ public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createDiaryRequest) { ++ try { ++ return diaryService.createDiary(createDiaryRequest); ++ } catch (Exception e) { ++ return ResponseHandler.create500Error(new ResponseForm(), e); ++ } ++ } ++} +diff --git a/src/main/java/com/bamboo/log/diary/domain/Diary.java b/src/main/java/com/bamboo/log/diary/domain/Diary.java +new file mode 100644 +index 0000000..c820a2c +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/domain/Diary.java +@@ -0,0 +1,33 @@ ++package com.bamboo.log.diary.domain; ++ ++import jakarta.persistence.*; ++import lombok.AllArgsConstructor; ++import lombok.Builder; ++import lombok.Getter; ++import lombok.NoArgsConstructor; ++ ++import java.time.LocalDateTime; ++ ++@Entity ++@Getter ++@Builder ++@AllArgsConstructor ++@NoArgsConstructor ++@Table(name = "diaries") ++public class Diary { ++ ++ @Id ++ @GeneratedValue(strategy = GenerationType.AUTO) ++ @Column(name = "diary_id") ++ private Long id; ++ ++ @Column(nullable = false) ++ private String userId; ++ ++ @Column(columnDefinition = "TEXT", nullable = false) ++ private String context; ++ ++ @Column(nullable = false) ++ private LocalDateTime createdAt; ++ ++} +diff --git a/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java b/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java +new file mode 100644 +index 0000000..24af228 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java +@@ -0,0 +1,29 @@ ++package com.bamboo.log.diary.domain; ++ ++import jakarta.persistence.*; ++import lombok.AllArgsConstructor; ++import lombok.Getter; ++import lombok.Builder; ++import lombok.NoArgsConstructor; ++ ++@Entity ++@Getter ++@Builder ++@NoArgsConstructor ++@AllArgsConstructor ++@Table(name = "today_summaries") ++public class TodaySummary { ++ ++ @Id ++ @GeneratedValue(strategy = GenerationType.AUTO) ++ private Long id; ++ ++ @Column(nullable = false) ++ private Long diaryId; ++ ++ @Lob ++ @Column(columnDefinition = "LONGBLOB", nullable = false) ++ private byte[] imageData; ++ ++} ++ +diff --git a/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java b/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java +new file mode 100644 +index 0000000..084708d +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java +@@ -0,0 +1,9 @@ ++package com.bamboo.log.diary.dto.request; ++ ++import jakarta.validation.constraints.NotEmpty; ++ ++import java.util.List; ++ ++public record CreateDiaryRequest(@NotEmpty(message = "User's Id shouldn't be empty") String userId, ++ @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDetail) { ++} +diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java +new file mode 100644 +index 0000000..9e994c0 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java +@@ -0,0 +1,8 @@ ++package com.bamboo.log.diary.dto.response; ++ ++import jakarta.validation.constraints.NotEmpty; ++ ++import java.time.LocalDateTime; ++ ++public record CreateDiaryResponse(@NotEmpty(message = "created shouldn't be empty") LocalDateTime created) { ++} +diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +new file mode 100644 +index 0000000..416db42 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +@@ -0,0 +1,7 @@ ++package com.bamboo.log.diary.repository; ++ ++import com.bamboo.log.diary.domain.Diary; ++import org.springframework.data.jpa.repository.JpaRepository; ++ ++public interface DiaryRepository extends JpaRepository { ++} +diff --git a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java +new file mode 100644 +index 0000000..4093d83 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java +@@ -0,0 +1,7 @@ ++package com.bamboo.log.diary.repository; ++ ++import com.bamboo.log.diary.domain.TodaySummary; ++import org.springframework.data.jpa.repository.JpaRepository; ++ ++public interface TodaySummaryRepository extends JpaRepository { ++} +diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +new file mode 100644 +index 0000000..50cb4ac +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +@@ -0,0 +1,10 @@ ++package com.bamboo.log.diary.service.diary; ++ ++import com.bamboo.log.diary.dto.request.CreateDiaryRequest; ++import org.springframework.http.ResponseEntity; ++ ++public interface DiaryService { ++ ++ ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); ++ ++} +diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +new file mode 100644 +index 0000000..5c201fd +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +@@ -0,0 +1,70 @@ ++package com.bamboo.log.diary.service.diary; ++ ++import com.bamboo.log.diary.domain.Diary; ++import com.bamboo.log.diary.dto.request.CreateDiaryRequest; ++import com.bamboo.log.diary.dto.response.CreateDiaryResponse; ++import com.bamboo.log.diary.repository.DiaryRepository; ++import com.bamboo.log.diary.repository.TodaySummaryRepository; ++import com.bamboo.log.diary.service.mock.UserService; ++import com.bamboo.log.diary.service.summary.TodaySummaryService; ++import com.bamboo.log.utils.ResponseHandler; ++import com.bamboo.log.utils.dto.ResponseForm; ++import jakarta.persistence.EntityNotFoundException; ++import lombok.RequiredArgsConstructor; ++import org.springframework.http.ResponseEntity; ++import org.springframework.stereotype.Service; ++import org.springframework.transaction.annotation.Transactional; ++ ++import java.io.IOException; ++import java.time.LocalDateTime; ++ ++@Service ++@Transactional ++@RequiredArgsConstructor ++public class DiaryServiceImpl implements DiaryService { ++ ++ private final UserService userService; ++ private final DiaryRepository diaryRepository; ++ private final TodaySummaryService todaySummaryService; ++ ++ @Override ++ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { ++ ++ // User 객체 검증 로직 ++ // 해당 User 객체 찾을 수 없을 경우, 에러 반환 ++ if(userService.existsById(createDiaryRequest.userId())) { ++ return ResponseHandler.create404Error(new ResponseForm(), new EntityNotFoundException("잘못된 유저 정보입니다.")); ++ } ++ ++ // 해당 시점의 localDateTime 저장 ++ LocalDateTime localDateTime = LocalDateTime.now(); ++ ++ // alice api 연결 함수 호출 ++ ++ // 데이터베이스에 저장 ++ Diary diary = saveDiary(createDiaryRequest, localDateTime); ++ ++ byte[] todayImage = todaySummaryService.createTodaySummaryImage(createDiaryRequest.diaryDetail()); ++ ++ // todaySummaryImage 데이터베이스에 저장 ++ try { ++ todaySummaryService.saveTodaySummaryImage(todayImage, diary.getId()); ++ } catch (IOException e) { ++ return ResponseHandler.create500Error(new ResponseForm(), e); ++ } ++ ++ // 201 code 반환 ++ return ResponseHandler.create201Response(new ResponseForm(), new CreateDiaryResponse(localDateTime)); ++ } ++ ++ private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { ++ Diary diary = Diary.builder() ++ .userId(createDiaryRequest.userId()) ++ .context(createDiaryRequest.diaryDetail()) ++ .createdAt(localDateTime) ++ .build(); ++ ++ return diaryRepository.save(diary); ++ } ++ ++} +diff --git a/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java b/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java +new file mode 100644 +index 0000000..86824f2 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java +@@ -0,0 +1,12 @@ ++package com.bamboo.log.diary.service.mock; ++ ++import org.springframework.stereotype.Service; ++ ++@Service ++public class MockUserService implements UserService { ++ ++ @Override ++ public boolean existsById(String userId) { ++ return true; ++ } ++} +diff --git a/src/main/java/com/bamboo/log/diary/service/mock/UserService.java b/src/main/java/com/bamboo/log/diary/service/mock/UserService.java +new file mode 100644 +index 0000000..0b37f9a +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/service/mock/UserService.java +@@ -0,0 +1,5 @@ ++package com.bamboo.log.diary.service.mock; ++ ++public interface UserService { ++ boolean existsById(String userId); ++} +diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java +new file mode 100644 +index 0000000..598d786 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java +@@ -0,0 +1,11 @@ ++package com.bamboo.log.diary.service.summary; ++ ++import org.springframework.web.multipart.MultipartFile; ++ ++import java.io.IOException; ++ ++public interface TodaySummaryService { ++ ++ void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException; ++ byte[] createTodaySummaryImage(String prompt); ++} +diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java +new file mode 100644 +index 0000000..06df2dd +--- /dev/null ++++ b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java +@@ -0,0 +1,46 @@ ++package com.bamboo.log.diary.service.summary; ++ ++import com.bamboo.log.diary.domain.TodaySummary; ++import com.bamboo.log.diary.repository.TodaySummaryRepository; ++import com.bamboo.log.elice.service.ImageGenerationService; ++import com.bamboo.log.utils.ResponseHandler; ++import com.bamboo.log.utils.dto.ResponseForm; ++import lombok.RequiredArgsConstructor; ++import org.springframework.stereotype.Service; ++import org.springframework.transaction.annotation.Transactional; ++import org.springframework.web.multipart.MultipartFile; ++ ++import java.io.IOException; ++import java.util.Optional; ++ ++@Service ++@Transactional ++@RequiredArgsConstructor ++public class TodaySummaryServiceImpl implements TodaySummaryService { ++ ++ private final TodaySummaryRepository todaySummaryRepository; ++ private final ImageGenerationService imageGenerationService; ++ ++ @Override ++ public void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException { ++ TodaySummary todaySummary = TodaySummary.builder() ++ .diaryId(diaryId) ++ .imageData(image) ++ .build(); ++ ++ todaySummaryRepository.save(todaySummary); ++ } ++ ++ @Override ++ public byte[] createTodaySummaryImage(String prompt) { ++ try { ++ byte[] imageBytes = imageGenerationService.generateImage(prompt); ++ ++ return imageBytes; ++ } catch (IOException e) { ++ throw new RuntimeException("이미지 생성 중에 문제가 발생했습니다."); ++ } ++ ++ } ++ ++} +diff --git a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java +deleted file mode 100644 +index 3d38673..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java ++++ /dev/null +@@ -1,33 +0,0 @@ +-package com.bamboo.log.domain.user.controller; +- +-import com.bamboo.log.domain.user.refresh.service.ProcessTokenReissue; +-import io.swagger.v3.oas.annotations.Operation; +-import io.swagger.v3.oas.annotations.responses.ApiResponse; +-import io.swagger.v3.oas.annotations.responses.ApiResponses; +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import lombok.RequiredArgsConstructor; +-import org.springframework.http.ResponseEntity; +-import org.springframework.web.bind.annotation.PostMapping; +-import org.springframework.web.bind.annotation.RestController; +- +-@RestController +-@RequiredArgsConstructor +-public class RefreshController { +- +- private final ProcessTokenReissue processTokenReissue; +- +- @Operation(summary = "토큰 갱신", description = "Refresh Token을 사용하여 Access Token을 재발급합니다.") +- @ApiResponses({ +- @ApiResponse(responseCode = "200", description = "토큰 재발급 성공"), +- @ApiResponse(responseCode = "401", description = "유효하지 않은 Refresh Token"), +- @ApiResponse(responseCode = "403", description = "Refresh Token이 만료됨") +- }) +- @PostMapping("/refresh") +- public ResponseEntity reissue( +- HttpServletRequest request, +- HttpServletResponse response) { +- return processTokenReissue.reissue(request, response); +- } +-} +- +diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java +deleted file mode 100644 +index 36c936f..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java ++++ /dev/null +@@ -1,76 +0,0 @@ +-package com.bamboo.log.domain.user.jwt.service; +- +-import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +-import com.bamboo.log.domain.user.oauth.dto.UserDTO; +-import io.jsonwebtoken.ExpiredJwtException; +-import jakarta.servlet.FilterChain; +-import jakarta.servlet.ServletException; +-import jakarta.servlet.http.Cookie; +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import lombok.RequiredArgsConstructor; +-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +-import org.springframework.security.core.Authentication; +-import org.springframework.security.core.context.SecurityContextHolder; +-import org.springframework.web.filter.OncePerRequestFilter; +- +-import java.io.IOException; +-import java.io.PrintWriter; +- +-@RequiredArgsConstructor +-public class JWTFilter extends OncePerRequestFilter { +- +- private final JWTUtil jwtUtil; +- +- +- @Override +- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { +- // 헤더에서 Authorization에 담긴 토큰을 꺼냄 +- String accessToken = request.getHeader("Authorization"); +- +- if (accessToken != null && accessToken.startsWith("Bearer ")) { +- accessToken = accessToken.substring(7); // "Bearer "의 길이는 7 +- } +- +- // 토큰이 없다면 다음 필터로 넘김 +- if (accessToken == null|| accessToken.isEmpty()) { +- filterChain.doFilter(request, response); +- return; +- } +- +- // 토큰 만료 여부 확인, 만료시 다음 필터로 넘기지 않음 +- try { +- jwtUtil.isExpired(accessToken); +- } catch (ExpiredJwtException e) { +- // 만료된 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 +- PrintWriter writer = response.getWriter(); +- writer.print("access token expired"); +- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); +- return; +- } +- +- // 토큰이 access인지 확인 (발급시 페이로드에 명시) +- String category = jwtUtil.getCategory(accessToken); +- if (!category.equals("access")) { +- // 유효하지 않은 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 +- PrintWriter writer = response.getWriter(); +- writer.print("invalid access token"); +- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); +- return; +- } +- +- +- String username = jwtUtil.getUsername(accessToken); +- String role = jwtUtil.getRole(accessToken); +- String name=jwtUtil.getName(accessToken); +- UserDTO userDTO = UserDTO.builder() +- .name(name) +- .username(username) +- .role(role) +- .build(); +- CustomOAuth2User customOAuth2User = new CustomOAuth2User(userDTO); +- Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, customOAuth2User.getAuthorities()); +- SecurityContextHolder.getContext().setAuthentication(authToken); +- filterChain.doFilter(request, response); +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java +deleted file mode 100644 +index d7f3a78..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java ++++ /dev/null +@@ -1,57 +0,0 @@ +-package com.bamboo.log.domain.user.jwt.service; +- +-import io.jsonwebtoken.JwtException; +-import io.jsonwebtoken.Jwts; +-import org.springframework.beans.factory.annotation.Value; +-import org.springframework.stereotype.Component; +- +-import javax.crypto.SecretKey; +-import javax.crypto.spec.SecretKeySpec; +-import java.nio.charset.StandardCharsets; +-import java.util.Date; +- +-@Component +-public class JWTUtil { +- +- private SecretKey secretKey; +- +- public JWTUtil(@Value("${spring.jwt.secret}")String secret) { +- secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); +- } +- +- public String getUsername(String token) { +- return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class); +- } +- +- public String getRole(String token) { +- return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); +- } +- +- public Boolean isExpired(String token) { +- return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); +- } +- +- public String getCategory(String token) { +- try { +- // 토큰에서 카테고리 정보를 추출합니다. +- return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("category", String.class); +- } catch (JwtException e) { +- throw new RuntimeException("토큰에서 카테고리를 추출하는 중 오류가 발생했습니다."); +- } +- } +- +- public String getName(String token){ +- return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("name", String.class); +- } +- public String createJwt(String category,String name,String username, String role, Long expiredMs) { +- return Jwts.builder() +- .claim("category",category) +- .claim("username", username) +- .claim("name",name) +- .claim("role", role) +- .issuedAt(new Date(System.currentTimeMillis())) +- .expiration(new Date(System.currentTimeMillis() + expiredMs)) +- .signWith(secretKey) +- .compact(); +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java +deleted file mode 100644 +index 6f196f7..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java ++++ /dev/null +@@ -1,33 +0,0 @@ +-package com.bamboo.log.domain.user.jwt.service; +- +-import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +-import org.springframework.security.core.Authentication; +-import org.springframework.security.core.context.SecurityContextHolder; +-import org.springframework.stereotype.Component; +- +-import java.util.Optional; +- +-@Component +-public class UserContextUtil { +- +- public Optional getCurrentUser() { +- Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +- if (authentication == null || !(authentication.getPrincipal() instanceof CustomOAuth2User)) { +- return Optional.empty(); +- } +- return Optional.of((CustomOAuth2User) authentication.getPrincipal()); +- } +- +- public String getUsername() { +- return getCurrentUser() +- .map(CustomOAuth2User::getUsername) +- .orElseThrow(() -> new RuntimeException("User not authenticated")); +- } +- +- public String getName() { +- return getCurrentUser() +- .map(CustomOAuth2User::getName) +- .orElseThrow(() -> new RuntimeException("User not authenticated")); +- } +- +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java b/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java +deleted file mode 100644 +index 3ff12c5..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java ++++ /dev/null +@@ -1,94 +0,0 @@ +-package com.bamboo.log.domain.user.logout; +- +-import com.bamboo.log.domain.user.jwt.service.JWTUtil; +-import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +-import io.jsonwebtoken.ExpiredJwtException; +-import jakarta.servlet.FilterChain; +-import jakarta.servlet.ServletException; +-import jakarta.servlet.ServletRequest; +-import jakarta.servlet.ServletResponse; +-import jakarta.servlet.http.Cookie; +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import lombok.RequiredArgsConstructor; +-import lombok.extern.slf4j.Slf4j; +-import org.springframework.transaction.annotation.Transactional; +-import org.springframework.web.filter.GenericFilterBean; +- +-import java.io.IOException; +- +-@RequiredArgsConstructor +-@Transactional +-@Slf4j +-public class CustomLogoutFilter extends GenericFilterBean { +- +- private final JWTUtil jwtUtil; +- private final RefreshRepository refreshRepository; +- +- @Override +- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { +- doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); +- } +- +- private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { +- +- String requestUri = request.getRequestURI(); +- if (!requestUri.matches("^\\/logout$")) { +- filterChain.doFilter(request, response); +- return; +- } +- +- String requestMethod = request.getMethod(); +- if (!requestMethod.equals("POST")) { +- filterChain.doFilter(request, response); +- return; +- } +- +- String refresh = null; +- Cookie[] cookies = request.getCookies(); +- for (Cookie cookie : cookies) { +- if (cookie.getName().equals("refresh")) { +- refresh = cookie.getValue(); +- } +- } +- +- if (refresh == null) { +- log.error("로그아웃 시 리프레시 토큰이 없습니다."); +- response.setStatus(HttpServletResponse.SC_BAD_REQUEST); +- return; +- } +- +- try { +- jwtUtil.isExpired(refresh); +- } catch (ExpiredJwtException e) { +- log.error("리프레시 토큰이 만료되었습니다. refresh token: {}", refresh); +- response.setStatus(HttpServletResponse.SC_BAD_REQUEST); +- return; +- } +- +- String category = jwtUtil.getCategory(refresh); +- if (!category.equals("refresh")) { +- log.error("유효하지 않은 리프레시 토큰입니다. refresh token: {}", refresh); +- response.setStatus(HttpServletResponse.SC_BAD_REQUEST); +- return; +- } +- +- Boolean isExist = refreshRepository.existsByToken(refresh); +- if (!isExist) { +- log.error("리프레시 토큰이 데이터베이스에 존재하지 않습니다. refresh token: {}", refresh); +- response.setStatus(HttpServletResponse.SC_BAD_REQUEST); +- return; +- } +- +- refreshRepository.deleteByToken(refresh); +- log.info("리프레시 토큰이 데이터베이스에서 삭제되었습니다. refresh token: {}", refresh); +- +- Cookie cookie = new Cookie("refresh", null); +- cookie.setMaxAge(0); +- cookie.setPath("/"); +- +- response.addCookie(cookie); +- log.info("리프레시 토큰 쿠키가 삭제되었습니다."); +- response.setStatus(HttpServletResponse.SC_OK); +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java +deleted file mode 100644 +index 707915d..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java ++++ /dev/null +@@ -1,48 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.dto; +- +-import org.springframework.security.core.GrantedAuthority; +-import org.springframework.security.oauth2.core.user.OAuth2User; +- +-import java.util.ArrayList; +-import java.util.Collection; +-import java.util.Map; +- +-public class CustomOAuth2User implements OAuth2User { +- +- private final UserDTO userDTO; +- +- public CustomOAuth2User(UserDTO userDTO) { +- this.userDTO = userDTO; +- } +- +- @Override +- public Map getAttributes() { +- return null; +- } +- +- @Override +- public Collection getAuthorities() { +- +- Collection collection = new ArrayList<>(); +- +- collection.add(new GrantedAuthority() { +- +- @Override +- public String getAuthority() { +- +- return userDTO.getRole(); +- } +- }); +- +- return collection; +- } +- +- @Override +- public String getName() { +- return userDTO.getName(); +- } +- +- public String getUsername() { +- return userDTO.getUsername(); +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java +deleted file mode 100644 +index 2e16ed0..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java ++++ /dev/null +@@ -1,40 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.dto; +- +-import java.util.Map; +- +-public class KakaoResponse implements OAuth2Response { +- private final Map attributes; +- private final Map kakaoAccount; +- private final Map profile; +- +- public KakaoResponse(Map attributes) { +- this.attributes = attributes; +- this.kakaoAccount = (Map) attributes.get("kakao_account"); +- this.profile = (Map) kakaoAccount.get("profile"); +- } +- +- @Override +- public String getProvider() { +- return "kakao"; +- } +- +- @Override +- public String getProviderId() { +- return attributes.get("id").toString(); +- } +- +- @Override +- public String getEmail() { +- return kakaoAccount.get("email") != null ? kakaoAccount.get("email").toString() : "N/A"; +- } +- +- @Override +- public String getName() { +- return profile.get("nickname").toString(); +- } +- +- @Override +- public Map getAttributes() { +- return attributes; +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java +deleted file mode 100644 +index 28f415d..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java ++++ /dev/null +@@ -1,11 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.dto; +- +-import java.util.Map; +- +-public interface OAuth2Response { +- String getProvider(); +- String getProviderId(); +- String getEmail(); +- String getName(); +- Map getAttributes(); +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java +deleted file mode 100644 +index f76f9a2..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java ++++ /dev/null +@@ -1,14 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.dto; +- +-import lombok.AllArgsConstructor; +-import lombok.Builder; +-import lombok.Getter; +- +-@AllArgsConstructor +-@Builder +-@Getter +-public class UserDTO { +- private String name; +- private String username; +- private String role; +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java +deleted file mode 100644 +index e02ceb4..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java ++++ /dev/null +@@ -1,40 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.entity; +- +-import jakarta.persistence.*; +-import lombok.AllArgsConstructor; +-import lombok.Builder; +-import lombok.Getter; +-import lombok.NoArgsConstructor; +- +-import java.util.Date; +-@Entity +-@Table(name = "refresh_token") +-@Getter +-@NoArgsConstructor +-@AllArgsConstructor +-@Builder +-public class RefreshToken { +- +- @Id +- @GeneratedValue(strategy = GenerationType.IDENTITY) +- private Long id; +- +- @Column(nullable = false) +- private String name; +- +- @Column(nullable = false) +- private String username; +- +- @Column(nullable = false) +- private String token; +- +- @Column(name = "expires_at", nullable = false) +- @Temporal(TemporalType.TIMESTAMP) +- private Date expiresAt; +- +- @Column(name = "created_at", nullable = false) +- @Temporal(TemporalType.TIMESTAMP) +- private Date createdAt; +- +-} +- +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java +deleted file mode 100644 +index fcf2836..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java ++++ /dev/null +@@ -1,45 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.entity; +- +-import jakarta.persistence.*; +-import lombok.Builder; +-import lombok.Getter; +-import lombok.NoArgsConstructor; +- +-@Getter +-@NoArgsConstructor +-@Entity +-@Table(name="users") +-public class UserEntity { +- +- @Id +- @GeneratedValue(strategy = GenerationType.IDENTITY) +- private Long id; +- +- @Column(unique = true, nullable = false) +- private String username; +- +- +- private String name; +- private String email; +- private String role; +- private String profile_img_url; +- +- @Builder +- public UserEntity(String username, String name, String email, String role,String profile_img_url) { +- this.username = username; +- this.name = name; +- this.email = email; +- this.role = role; +- this.profile_img_url = profile_img_url; +- } +- +- public UserEntity updateEmailAndName(String email, String name) { +- return UserEntity.builder() +- .username(this.username) +- .name(name) +- .email(email) +- .role(this.role) +- .profile_img_url(this.profile_img_url) +- .build(); +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java +deleted file mode 100644 +index 8bbe809..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java ++++ /dev/null +@@ -1,10 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.repository; +- +-import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +-import org.springframework.data.jpa.repository.JpaRepository; +- +-public interface RefreshRepository extends JpaRepository { +- Boolean existsByToken(String token); +- void deleteByToken(String token); +- RefreshToken findByToken(String token); +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java +deleted file mode 100644 +index 645a385..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java ++++ /dev/null +@@ -1,8 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.repository; +- +-import com.bamboo.log.domain.user.oauth.entity.UserEntity; +-import org.springframework.data.jpa.repository.JpaRepository; +- +-public interface UserRepository extends JpaRepository { +- UserEntity findByUsername(String username); +-} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java +deleted file mode 100644 +index 1dac05d..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java ++++ /dev/null +@@ -1,20 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.service; +- +-import jakarta.servlet.ServletException; +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import org.springframework.security.core.AuthenticationException; +-import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +-import org.springframework.stereotype.Component; +- +-import java.io.IOException; +- +-@Component +-public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler { +- +- @Override +- public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { +- response.sendRedirect("http://localhost:3000"); +- } +-} +- +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java +deleted file mode 100644 +index 8de41c2..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java ++++ /dev/null +@@ -1,67 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.service; +- +-import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +-import com.bamboo.log.domain.user.oauth.dto.KakaoResponse; +-import com.bamboo.log.domain.user.oauth.dto.OAuth2Response; +-import com.bamboo.log.domain.user.oauth.dto.UserDTO; +-import com.bamboo.log.domain.user.oauth.entity.UserEntity; +-import com.bamboo.log.domain.user.oauth.repository.UserRepository; +-import lombok.RequiredArgsConstructor; +-import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +-import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +-import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +-import org.springframework.security.oauth2.core.user.OAuth2User; +-import org.springframework.stereotype.Service; +- +- +-@Service +-@RequiredArgsConstructor +-public class CustomOAuth2UserService extends DefaultOAuth2UserService { +- +- private final UserRepository userRepository; +- +- @Override +- public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { +- OAuth2User oAuth2User = super.loadUser(userRequest); +- String registrationId = userRequest.getClientRegistration().getRegistrationId(); +- +- OAuth2Response oAuth2Response = null; +- if (registrationId.equals("kakao")) { +- oAuth2Response = new KakaoResponse(oAuth2User.getAttributes()); +- } else { +- return null; +- } +- +- System.out.println("OAuth2Response: " + oAuth2Response); +- String username = oAuth2Response.getProvider() + " " + oAuth2Response.getProviderId(); +- UserEntity existData = userRepository.findByUsername(username); +- System.out.println("username = " + username); +- System.out.println("Existing User: " + existData); +- +- if (existData == null) { +- UserEntity userEntity = UserEntity.builder() +- .username(username) +- .name(oAuth2Response.getName()) +- .email(oAuth2Response.getEmail()) +- .role("ROLE_USER") +- .build(); +- +- userRepository.save(userEntity); +- +- UserDTO userDTO = UserDTO.builder() +- .username(username) +- .name(oAuth2Response.getName()) +- .role("ROLE_USER") +- .build(); +- +- return new CustomOAuth2User(userDTO); +- } else { +- UserDTO userDTO = UserDTO.builder() +- .username(existData.getUsername()) +- .name(existData.getName()) +- .role(existData.getRole()) +- .build(); +- return new CustomOAuth2User(userDTO); +- } +- } +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +deleted file mode 100644 +index 069d271..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java ++++ /dev/null +@@ -1,78 +0,0 @@ +-package com.bamboo.log.domain.user.oauth.service; +- +-import com.bamboo.log.domain.user.jwt.service.JWTUtil; +-import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +-import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +-import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +-import jakarta.servlet.ServletException; +-import jakarta.servlet.http.Cookie; +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import lombok.RequiredArgsConstructor; +-import org.springframework.security.core.Authentication; +-import org.springframework.security.core.GrantedAuthority; +-import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +-import org.springframework.stereotype.Component; +- +-import java.io.IOException; +-import java.util.Collection; +-import java.util.Date; +-import java.util.Iterator; +- +-@Component +-@RequiredArgsConstructor +-public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { +- +- private final JWTUtil jwtUtil; +- private final RefreshRepository refreshRepository; +- +- +- @Override +- public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { +- CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); +- String name=customUserDetails.getName(); +- String username = customUserDetails.getUsername(); +- Collection authorities = authentication.getAuthorities(); +- Iterator iterator = authorities.iterator(); +- GrantedAuthority auth = iterator.next(); +- String role = auth.getAuthority(); +- +- String refreshToken = jwtUtil.createJwt("refresh",name,username, role, 1800000L); +- String accessToken = jwtUtil.createJwt("access",name,username, role, 1209600000L); +- addRefreshEntity(name, username, refreshToken, 1209600000L); +- response.addCookie(createCookie("refresh", refreshToken)); +- response.addCookie(UnScretCreateCookie("access", accessToken)); +- response.sendRedirect("http://localhost:3000/"); +- } +- +- private Cookie createCookie(String key, String value) { +- +- Cookie cookie = new Cookie(key, value); +- cookie.setMaxAge(24 * 60 * 60 * 14); +- cookie.setPath("/"); +- cookie.setHttpOnly(true); +- +- return cookie; +- } +- private Cookie UnScretCreateCookie(String key, String value) { +- +- Cookie cookie = new Cookie(key, value); +- cookie.setMaxAge(60*60); +- cookie.setPath("/"); +- return cookie; +- } +- private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { +- Date CrDate = new Date(System.currentTimeMillis()); +- Date ExDate = new Date(System.currentTimeMillis() + expiredMs); +- refreshRepository.save( +- RefreshToken.builder() +- .name(name) +- .username(username) +- .token(refresh) +- .createdAt(CrDate) +- .expiresAt(ExDate) +- .build() +- ); +- } +-} +- +diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java +deleted file mode 100644 +index 73dcdd3..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java ++++ /dev/null +@@ -1,9 +0,0 @@ +-package com.bamboo.log.domain.user.refresh.service; +- +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import org.springframework.http.ResponseEntity; +- +-public interface ProcessTokenReissue { +- public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response); +-} +diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java +deleted file mode 100644 +index e808c3f..0000000 +--- a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java ++++ /dev/null +@@ -1,103 +0,0 @@ +-package com.bamboo.log.domain.user.refresh.service; +- +-import com.bamboo.log.domain.user.jwt.service.JWTUtil; +-import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +-import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +-import io.jsonwebtoken.ExpiredJwtException; +-import jakarta.servlet.http.Cookie; +-import jakarta.servlet.http.HttpServletRequest; +-import jakarta.servlet.http.HttpServletResponse; +-import lombok.RequiredArgsConstructor; +-import lombok.extern.slf4j.Slf4j; +-import org.springframework.http.HttpStatus; +-import org.springframework.http.ResponseEntity; +-import org.springframework.stereotype.Service; +-import org.springframework.transaction.annotation.Transactional; +- +-import java.util.Date; +- +-@Service +-@RequiredArgsConstructor +-@Transactional +-@Slf4j +-public class ProcessTokenReissueImpl implements ProcessTokenReissue { +- +- private final JWTUtil jwtUtil; +- private final RefreshRepository refreshRepository; +- +- public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response) { +- +- String refresh = null; +- Cookie[] cookies = request.getCookies(); +- for (Cookie cookie : cookies) { +- if (cookie.getName().equals("refresh")) { +- refresh = cookie.getValue(); +- } +- } +- +- if (refresh == null) { +- log.error("리프레시 토큰이 없습니다."); +- return new ResponseEntity<>("refresh token null", HttpStatus.BAD_REQUEST); +- } +- +- try { +- jwtUtil.isExpired(refresh); +- } catch (ExpiredJwtException e) { +- log.error("리프레시 토큰이 만료되었습니다. token: {}", refresh); +- return new ResponseEntity<>("refresh token expired", HttpStatus.BAD_REQUEST); +- } +- +- String category = jwtUtil.getCategory(refresh); +- if (!category.equals("refresh")) { +- log.error("유효하지 않은 리프레시 토큰입니다. token: {}", refresh); +- return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); +- } +- +- if (!refreshRepository.existsByToken(refresh)) { +- log.error("리프레시 토큰이 존재하지 않습니다. token: {}", refresh); +- return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); +- } +- +- String name = jwtUtil.getName(refresh); +- String username = jwtUtil.getUsername(refresh); +- String role = jwtUtil.getRole(refresh); +- +- String newAccess = jwtUtil.createJwt("access", name, username , role, 1800000L); +- String newRefresh = jwtUtil.createJwt("refresh", name, username, role, 1209600000L); +- +- refreshRepository.deleteByToken(refresh); +- addRefreshEntity(name,username,role,1209600000L); +- +- log.info("새로운 액세스 토큰과 리프레시 토큰을 발급했습니다. 새로운 리프레시 토큰: {}", newRefresh); +- +- response.setHeader("Authorization", newAccess); +- response.addCookie(createCookie("refresh", newRefresh)); +- +- log.info("리프레시 토큰 재발급 완료"); +- +- return new ResponseEntity<>(HttpStatus.OK); +- } +- +- private Cookie createCookie(String key, String value) { +- log.info("쿠키 생성 시작"); +- Cookie cookie = new Cookie(key, value); +- cookie.setMaxAge(24 * 60 * 60 * 14); // 14일 유효 +- cookie.setHttpOnly(true); // 클라이언트 측에서 접근 불가 +- log.info("쿠키 생성 끝"); +- return cookie; +- } +- private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { +- Date CrDate = new Date(System.currentTimeMillis()); +- Date ExDate = new Date(System.currentTimeMillis() + expiredMs); +- refreshRepository.save( +- RefreshToken.builder() +- .name(name) +- .username(username) +- .token(refresh) +- .createdAt(CrDate) +- .expiresAt(ExDate) +- .build() +- ); +- } +-} +- +diff --git a/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java +new file mode 100644 +index 0000000..0ea3f07 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java +@@ -0,0 +1,14 @@ ++package com.bamboo.log.elice.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/bamboo/log/elice/service/ImageGenerationService.java b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java +new file mode 100644 +index 0000000..d7aa11b +--- /dev/null ++++ b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java +@@ -0,0 +1,58 @@ ++package com.bamboo.log.elice.service; ++ ++import org.springframework.beans.factory.annotation.Value; ++import org.springframework.http.*; ++import org.springframework.stereotype.Service; ++import org.springframework.web.client.RestTemplate; ++ ++import okhttp3.*; ++import okhttp3.MediaType; ++ ++import java.io.IOException; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++@Service ++public class ImageGenerationService { ++ ++ private final RestTemplate restTemplate; ++ private final String apiKey; ++ private final String apiUrl; ++ ++ public ImageGenerationService(RestTemplate restTemplate, ++ @Value("${elice.api.token}") String apiKey, ++ @Value("${elice.api.url}") String apiUrl) { ++ this.restTemplate = restTemplate; ++ this.apiKey = apiKey; ++ this.apiUrl = apiUrl; ++ } ++ ++ public byte[] generateImage(String prompt) throws IOException { ++ OkHttpClient client = new OkHttpClient(); ++ ++ String requestBody = String.format("{\"prompt\":\"%s\",\"style\":\"polaroid\"}", prompt); ++ ++ MediaType mediaType = MediaType.parse("application/json"); ++ RequestBody body = RequestBody.create(requestBody, mediaType); ++ ++ Request request = new Request.Builder() ++ .url(apiUrl) ++ .post(body) ++ .addHeader("accept", "application/json") ++ .addHeader("content-type", "application/json") ++ .addHeader("Authorization", "Bearer " + apiKey) ++ .build(); ++ ++ Response response = client.newCall(request).execute(); ++ ++ if (!response.isSuccessful()) { ++ throw new IOException("요청 실패: " + response.code()); ++ } ++ ++ byte[] imageBytes = response.body().bytes(); // ✅ 이미지 데이터를 byte[]로 변환 ++ response.close(); ++ return imageBytes; ++ } ++} ++ +diff --git a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java +deleted file mode 100644 +index fee5189..0000000 +--- a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java ++++ /dev/null +@@ -1,32 +0,0 @@ +-package com.bamboo.log.emotion.controller; +- +-import com.bamboo.log.emotion.dto.FaceDetectionResponse; +-import com.bamboo.log.emotion.service.FaceDetectionService; +-import io.swagger.v3.oas.annotations.Operation; +-import io.swagger.v3.oas.annotations.tags.Tag; +-import lombok.RequiredArgsConstructor; +-import org.springframework.http.HttpStatus; +-import org.springframework.http.ResponseEntity; +-import org.springframework.web.bind.annotation.PostMapping; +-import org.springframework.web.bind.annotation.RequestMapping; +-import org.springframework.web.bind.annotation.RequestParam; +-import org.springframework.web.bind.annotation.RestController; +-import org.springframework.web.multipart.MultipartFile; +- +-@RestController +-@RequiredArgsConstructor +-@RequestMapping("/api/emotion") +-@Tag(name = "Face Detection", description = "얼굴 인식 관련 API") +-public class FaceDetectController { +- private final FaceDetectionService faceDetectionService; +- +- @PostMapping("/detect") +- @Operation( +- summary = "얼굴 인식 API", +- description = "이미지를 업로드하면 얼굴 인식 여부를 반환합니다." +- ) +- public ResponseEntity detectFace(@RequestParam("image") MultipartFile image) { +- FaceDetectionResponse response = faceDetectionService.detectFace(image); +- return ResponseEntity.status(HttpStatus.valueOf(response.statusCode())).body(response); +- } +-} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java +deleted file mode 100644 +index f9a2b09..0000000 +--- a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java ++++ /dev/null +@@ -1,13 +0,0 @@ +-package com.bamboo.log.emotion.dto; +- +-import org.springframework.http.HttpStatus; +- +-public record FaceDetectionResponse( +- int statusCode, +- String statusMessage, +- String message +-) { +- public FaceDetectionResponse(HttpStatus httpStatus, String message) { +- this(httpStatus.value(), httpStatus.name(), message); +- } +-} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java b/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java +deleted file mode 100644 +index af5f1bc..0000000 +--- a/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java ++++ /dev/null +@@ -1,8 +0,0 @@ +-package com.bamboo.log.emotion.service; +- +-import com.bamboo.log.emotion.dto.FaceDetectionResponse; +-import org.springframework.web.multipart.MultipartFile; +- +-public interface FaceDetectionService { +- FaceDetectionResponse detectFace(MultipartFile image); +-} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java +deleted file mode 100644 +index 47e6d8a..0000000 +--- a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java ++++ /dev/null +@@ -1,90 +0,0 @@ +-package com.bamboo.log.emotion.service.impl; +- +-import com.bamboo.log.emotion.dto.FaceDetectionResponse; +-import com.bamboo.log.emotion.service.FaceDetectionService; +-import com.fasterxml.jackson.databind.JsonNode; +-import com.fasterxml.jackson.databind.ObjectMapper; +-import lombok.RequiredArgsConstructor; +-import lombok.extern.slf4j.Slf4j; +-import okhttp3.*; +-import org.springframework.beans.factory.annotation.Value; +-import org.springframework.http.HttpStatus; +-import org.springframework.stereotype.Service; +-import org.springframework.web.multipart.MultipartFile; +- +-import java.io.IOException; +- +-@Slf4j +-@Service +-@RequiredArgsConstructor +-public class FaceDetectionServiceImpl implements FaceDetectionService { +- +- @Value("${elice.api.url.face}") +- private String faceApiUrl; +- +- @Value("${elice.api.token}") +- private String faceToken; +- +- private final OkHttpClient client = new OkHttpClient(); +- +- @Override +- public FaceDetectionResponse detectFace(MultipartFile image) { +- if (image == null || image.isEmpty()) { +- log.error("파일이 전달되지 않음."); +- return new FaceDetectionResponse(HttpStatus.BAD_REQUEST, "파일이 전달되지 않았습니다."); +- } +- log.info("파일 이름: {}", image.getOriginalFilename()); +- log.info("파일 크기: {} bytes", image.getSize()); +- +- try { +- String contentType = image.getContentType(); +- if (contentType == null) { +- contentType = "application/octet-stream"; // 기본값 설정 +- } +- +- RequestBody requestBody = new MultipartBody.Builder() +- .setType(MultipartBody.FORM) +- .addFormDataPart("image", image.getOriginalFilename(), +- RequestBody.create(image.getBytes(), MediaType.parse(contentType))) +- .build(); +- +- // API 요청 생성 (토큰 포함) +- Request request = new Request.Builder() +- .url(faceApiUrl) +- .post(requestBody) +- .addHeader("accept", "application/json") +- .addHeader("Authorization", "Bearer " + faceToken) +- .build(); +- +- try (Response response = client.newCall(request).execute()) { +- log.info("API 응답 코드: {}", response.code()); +- log.info("API 응답 메시지: {}", response.message()); +- +- if (!response.isSuccessful()) { +- return new FaceDetectionResponse(HttpStatus.valueOf(response.code()), "API 요청 실패: " + response.message()); +- } +- +- String responseBody = response.body().string(); +- log.info("API 응답 본문: {}", responseBody); +- +- // JSON 파싱 +- JsonNode jsonNode = new ObjectMapper().readTree(responseBody); +- JsonNode results = jsonNode.get("results"); +- +- // 얼굴 인식 여부 확인 +- if (results != null && results.isArray() && results.size() > 0) { +- for (JsonNode result : results) { +- if ("face".equals(result.get("name").asText())) { +- return new FaceDetectionResponse(HttpStatus.OK, responseBody); +- } +- } +- } +- return new FaceDetectionResponse(HttpStatus.NOT_FOUND, "얼굴이 인식되지 않았습니다."); +- } +- } catch (IOException e) { +- log.error("API 요청 중 예외 발생", e); +- return new FaceDetectionResponse(HttpStatus.INTERNAL_SERVER_ERROR, "API 요청 중 오류 발생: " + e.getMessage()); +- } +- } +- +-} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/utils/ResponseHandler.java b/src/main/java/com/bamboo/log/utils/ResponseHandler.java +new file mode 100644 +index 0000000..c483a50 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/utils/ResponseHandler.java +@@ -0,0 +1,51 @@ ++package com.bamboo.log.utils; ++ ++import com.bamboo.log.utils.dto.DetailResponse; ++import com.bamboo.log.utils.dto.ResponseForm; ++import org.springframework.http.HttpStatus; ++import org.springframework.http.ResponseEntity; ++ ++public class ResponseHandler { ++ ++ public static ResponseEntity create500Error(ResponseForm response, Exception e) { ++ response.of("result", "FAIL"); ++ response.of("error", DetailResponse.builder().code(500).message(e.getMessage()).build()); ++ return new ResponseEntity(response, HttpStatus.INTERNAL_SERVER_ERROR); ++ } ++ ++ public static ResponseEntity create400Error(ResponseForm response, Exception e) { ++ response.of("result", "FAIL"); ++ response.of("error", DetailResponse.builder().code(400).message(e.getMessage()).build()); ++ ++ return new ResponseEntity(response, HttpStatus.BAD_REQUEST); ++ } ++ ++ public static ResponseEntity create404Error(ResponseForm response, Exception e) { ++ response.of("result", "FAIL"); ++ response.of("error", DetailResponse.builder().code(404).message(e.getMessage()).build()); ++ ++ return new ResponseEntity(response, HttpStatus.NOT_FOUND); ++ } ++ ++ public static ResponseEntity create204Response(ResponseForm response, String message) { ++ response.of("result", "SUCCESS"); ++ response.of("code", DetailResponse.builder().code(204).message(message).build()); ++ ++ return new ResponseEntity(response, HttpStatus.NO_CONTENT); ++ } ++ ++ public static ResponseEntity create200Response(ResponseForm response, Object object) { ++ response.of("result", "SUCCESS"); ++ response.of("code", object); ++ ++ return new ResponseEntity(response, HttpStatus.OK); ++ } ++ ++ public static ResponseEntity create201Response(ResponseForm response, Object object) { ++ response.of("result", "SUCCESS"); ++ response.of("code", object); ++ ++ return new ResponseEntity(response, HttpStatus.CREATED); ++ } ++ ++} +diff --git a/src/main/java/com/bamboo/log/utils/SecurityConfig.java b/src/main/java/com/bamboo/log/utils/SecurityConfig.java +new file mode 100644 +index 0000000..5d51eb4 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/utils/SecurityConfig.java +@@ -0,0 +1,23 @@ ++package com.bamboo.log.utils; ++ ++import org.springframework.context.annotation.Bean; ++import org.springframework.context.annotation.Configuration; ++import org.springframework.security.config.annotation.web.builders.HttpSecurity; ++import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; ++import org.springframework.security.web.SecurityFilterChain; ++ ++@Configuration ++@EnableWebSecurity ++public class SecurityConfig { ++ @Bean ++ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { ++ http ++ .csrf(csrf -> csrf.disable()) // CSRF 비활성화 ++ .authorizeHttpRequests(auth -> auth ++ .requestMatchers("/api/images/**").permitAll() // 이미지 API 인증 없이 허용 ++ .anyRequest().authenticated() ++ ); ++ ++ return http.build(); ++ } ++} +diff --git a/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java +new file mode 100644 +index 0000000..61f22cf +--- /dev/null ++++ b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java +@@ -0,0 +1,13 @@ ++package com.bamboo.log.utils.dto; ++ ++import lombok.Builder; ++import lombok.Getter; ++ ++@Builder ++@Getter ++public class DetailResponse { ++ ++ private Integer code; ++ private String message; ++ ++} +\ No newline at end of file +diff --git a/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java +new file mode 100644 +index 0000000..04eccf6 +--- /dev/null ++++ b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java +@@ -0,0 +1,21 @@ ++package com.bamboo.log.utils.dto; ++ ++import lombok.Getter; ++ ++import java.util.HashMap; ++import java.util.Map; ++ ++@Getter ++public class ResponseForm { ++ ++ public ResponseForm() { ++ response = new HashMap<>(); ++ } ++ ++ public void of(String value1, Object value2) { ++ response.put(value1, value2); ++ } ++ ++ private Map response; ++ ++} +\ No newline at end of file +diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml +index 5379dd6..317cea6 100644 +--- a/src/main/resources/application.yml ++++ b/src/main/resources/application.yml +@@ -1,42 +1,23 @@ + spring: + datasource: ++ driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} +- driver-class-name: com.mysql.cj.jdbc.Driver ++ + jpa: + show-sql: true + open-in-view: false + hibernate: +- ddl-auto: update ++ ddl-auto: update + properties: + hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + +- security: +- oauth2: +- client: +- registration: +- kakao: +- client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} +- client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} +- client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} +- client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} +- redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} +- authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} +- scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} +- provider: +- kakao: +- authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} +- token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} +- user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} +- user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} +- jwt: +- secret: ${JWT_SECRET} + + elice: + api: + token: ${API_TOKEN} + url: +- face: ${FACE_URL} +- img: +- chat: +\ No newline at end of file ++ face: ${FACE_API_URL} ++ img: ${IMG_API_URL} ++ chat: ${CHAT_API_URL} diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java deleted file mode 100644 index 2f3565b..0000000 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.bamboo.log.common.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("/**") - .exposedHeaders("Set-Cookie") - //프론트 url 넣기 - .allowedOrigins("http://localhost:3000"); - } -} - - diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java deleted file mode 100644 index 38ab583..0000000 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.bamboo.log.common.config; - -import com.bamboo.log.domain.user.jwt.service.JWTFilter; -import com.bamboo.log.domain.user.jwt.service.JWTUtil; -import com.bamboo.log.domain.user.logout.CustomLogoutFilter; -import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; -import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; -import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; -import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.logout.LogoutFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; - -import java.util.Collections; - -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig { - - private final RefreshRepository refreshRepository; - private final CustomOAuth2UserService customOAuth2UserService; - private final CustomSuccessHandler customSuccessHandler; - private final CustomFailureHandler customFailureHandler; - private final JWTUtil jwtUtil; - - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.csrf((auth) -> auth.disable()); - http.formLogin((auth) -> auth.disable()); - http.httpBasic((auth) -> auth.disable()); - http.oauth2Login((oauth2) -> oauth2 - .userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig - .userService(customOAuth2UserService)) - .successHandler(customSuccessHandler) - .failureHandler(customFailureHandler) - ); - http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); - - //이쪽에다가 각각의 엔드포인트를 넣어야함 - http.authorizeHttpRequests((auth) -> auth - .requestMatchers("/refresh").permitAll() - .requestMatchers("/logout").hasAnyRole("USER") - .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() - .anyRequest().authenticated()); - - - http.sessionManagement((session) -> session - .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); - http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration configuration = new CorsConfiguration(); - //프론트 url 넣기 - 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("Set-Cookie")); - configuration.setExposedHeaders(Collections.singletonList("Authorization")); - return configuration; - } - })); - return http.build(); - } -} diff --git a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java deleted file mode 100644 index dd0a87a..0000000 --- a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.bamboo.log.common.config; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.security.SecurityScheme; -import org.springframework.context.annotation.Configuration; - -@Configuration -@OpenAPIDefinition(info = @Info(title = "ForRest", version = "v1")) -public class SwaggerConfig { -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java new file mode 100644 index 0000000..0b4e1c5 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -0,0 +1,33 @@ +package com.bamboo.log.diary.api; + +import com.bamboo.log.diary.dto.request.CreateDiaryRequest; +import com.bamboo.log.diary.service.diary.DiaryService; +import com.bamboo.log.utils.ResponseHandler; +import com.bamboo.log.utils.dto.ResponseForm; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import io.swagger.v3.oas.annotations.tags.Tag; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/diary") +@Tag(name = "Diary API", description = "일기 관련 API") +public class DiaryController { + + private final DiaryService diaryService; + + @Operation(summary = "랜덤 주제 조회") + @PostMapping("/create") + public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createDiaryRequest) { + try { + return diaryService.createDiary(createDiaryRequest); + } catch (Exception e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } + } +} diff --git a/src/main/java/com/bamboo/log/diary/domain/Diary.java b/src/main/java/com/bamboo/log/diary/domain/Diary.java new file mode 100644 index 0000000..c820a2c --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/domain/Diary.java @@ -0,0 +1,33 @@ +package com.bamboo.log.diary.domain; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "diaries") +public class Diary { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "diary_id") + private Long id; + + @Column(nullable = false) + private String userId; + + @Column(columnDefinition = "TEXT", nullable = false) + private String context; + + @Column(nullable = false) + private LocalDateTime createdAt; + +} diff --git a/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java b/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java new file mode 100644 index 0000000..24af228 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java @@ -0,0 +1,29 @@ +package com.bamboo.log.diary.domain; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Builder; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "today_summaries") +public class TodaySummary { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(nullable = false) + private Long diaryId; + + @Lob + @Column(columnDefinition = "LONGBLOB", nullable = false) + private byte[] imageData; + +} + diff --git a/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java b/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java new file mode 100644 index 0000000..084708d --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java @@ -0,0 +1,9 @@ +package com.bamboo.log.diary.dto.request; + +import jakarta.validation.constraints.NotEmpty; + +import java.util.List; + +public record CreateDiaryRequest(@NotEmpty(message = "User's Id shouldn't be empty") String userId, + @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDetail) { +} diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java new file mode 100644 index 0000000..9e994c0 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java @@ -0,0 +1,8 @@ +package com.bamboo.log.diary.dto.response; + +import jakarta.validation.constraints.NotEmpty; + +import java.time.LocalDateTime; + +public record CreateDiaryResponse(@NotEmpty(message = "created shouldn't be empty") LocalDateTime created) { +} diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java new file mode 100644 index 0000000..416db42 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -0,0 +1,7 @@ +package com.bamboo.log.diary.repository; + +import com.bamboo.log.diary.domain.Diary; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DiaryRepository extends JpaRepository { +} diff --git a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java new file mode 100644 index 0000000..4093d83 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java @@ -0,0 +1,7 @@ +package com.bamboo.log.diary.repository; + +import com.bamboo.log.diary.domain.TodaySummary; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TodaySummaryRepository extends JpaRepository { +} diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java new file mode 100644 index 0000000..50cb4ac --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java @@ -0,0 +1,10 @@ +package com.bamboo.log.diary.service.diary; + +import com.bamboo.log.diary.dto.request.CreateDiaryRequest; +import org.springframework.http.ResponseEntity; + +public interface DiaryService { + + ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); + +} diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java new file mode 100644 index 0000000..5c201fd --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -0,0 +1,70 @@ +package com.bamboo.log.diary.service.diary; + +import com.bamboo.log.diary.domain.Diary; +import com.bamboo.log.diary.dto.request.CreateDiaryRequest; +import com.bamboo.log.diary.dto.response.CreateDiaryResponse; +import com.bamboo.log.diary.repository.DiaryRepository; +import com.bamboo.log.diary.repository.TodaySummaryRepository; +import com.bamboo.log.diary.service.mock.UserService; +import com.bamboo.log.diary.service.summary.TodaySummaryService; +import com.bamboo.log.utils.ResponseHandler; +import com.bamboo.log.utils.dto.ResponseForm; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.time.LocalDateTime; + +@Service +@Transactional +@RequiredArgsConstructor +public class DiaryServiceImpl implements DiaryService { + + private final UserService userService; + private final DiaryRepository diaryRepository; + private final TodaySummaryService todaySummaryService; + + @Override + public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { + + // User 객체 검증 로직 + // 해당 User 객체 찾을 수 없을 경우, 에러 반환 + if(userService.existsById(createDiaryRequest.userId())) { + return ResponseHandler.create404Error(new ResponseForm(), new EntityNotFoundException("잘못된 유저 정보입니다.")); + } + + // 해당 시점의 localDateTime 저장 + LocalDateTime localDateTime = LocalDateTime.now(); + + // alice api 연결 함수 호출 + + // 데이터베이스에 저장 + Diary diary = saveDiary(createDiaryRequest, localDateTime); + + byte[] todayImage = todaySummaryService.createTodaySummaryImage(createDiaryRequest.diaryDetail()); + + // todaySummaryImage 데이터베이스에 저장 + try { + todaySummaryService.saveTodaySummaryImage(todayImage, diary.getId()); + } catch (IOException e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } + + // 201 code 반환 + return ResponseHandler.create201Response(new ResponseForm(), new CreateDiaryResponse(localDateTime)); + } + + private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { + Diary diary = Diary.builder() + .userId(createDiaryRequest.userId()) + .context(createDiaryRequest.diaryDetail()) + .createdAt(localDateTime) + .build(); + + return diaryRepository.save(diary); + } + +} diff --git a/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java b/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java new file mode 100644 index 0000000..86824f2 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java @@ -0,0 +1,12 @@ +package com.bamboo.log.diary.service.mock; + +import org.springframework.stereotype.Service; + +@Service +public class MockUserService implements UserService { + + @Override + public boolean existsById(String userId) { + return true; + } +} diff --git a/src/main/java/com/bamboo/log/diary/service/mock/UserService.java b/src/main/java/com/bamboo/log/diary/service/mock/UserService.java new file mode 100644 index 0000000..0b37f9a --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/mock/UserService.java @@ -0,0 +1,5 @@ +package com.bamboo.log.diary.service.mock; + +public interface UserService { + boolean existsById(String userId); +} diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java new file mode 100644 index 0000000..598d786 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java @@ -0,0 +1,11 @@ +package com.bamboo.log.diary.service.summary; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +public interface TodaySummaryService { + + void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException; + byte[] createTodaySummaryImage(String prompt); +} diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java new file mode 100644 index 0000000..06df2dd --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java @@ -0,0 +1,46 @@ +package com.bamboo.log.diary.service.summary; + +import com.bamboo.log.diary.domain.TodaySummary; +import com.bamboo.log.diary.repository.TodaySummaryRepository; +import com.bamboo.log.elice.service.ImageGenerationService; +import com.bamboo.log.utils.ResponseHandler; +import com.bamboo.log.utils.dto.ResponseForm; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Optional; + +@Service +@Transactional +@RequiredArgsConstructor +public class TodaySummaryServiceImpl implements TodaySummaryService { + + private final TodaySummaryRepository todaySummaryRepository; + private final ImageGenerationService imageGenerationService; + + @Override + public void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException { + TodaySummary todaySummary = TodaySummary.builder() + .diaryId(diaryId) + .imageData(image) + .build(); + + todaySummaryRepository.save(todaySummary); + } + + @Override + public byte[] createTodaySummaryImage(String prompt) { + try { + byte[] imageBytes = imageGenerationService.generateImage(prompt); + + return imageBytes; + } catch (IOException e) { + throw new RuntimeException("이미지 생성 중에 문제가 발생했습니다."); + } + + } + +} diff --git a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java deleted file mode 100644 index 3d38673..0000000 --- a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.bamboo.log.domain.user.controller; - -import com.bamboo.log.domain.user.refresh.service.ProcessTokenReissue; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -public class RefreshController { - - private final ProcessTokenReissue processTokenReissue; - - @Operation(summary = "토큰 갱신", description = "Refresh Token을 사용하여 Access Token을 재발급합니다.") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "토큰 재발급 성공"), - @ApiResponse(responseCode = "401", description = "유효하지 않은 Refresh Token"), - @ApiResponse(responseCode = "403", description = "Refresh Token이 만료됨") - }) - @PostMapping("/refresh") - public ResponseEntity reissue( - HttpServletRequest request, - HttpServletResponse response) { - return processTokenReissue.reissue(request, response); - } -} - diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java deleted file mode 100644 index 36c936f..0000000 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.bamboo.log.domain.user.jwt.service; - -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; -import com.bamboo.log.domain.user.oauth.dto.UserDTO; -import io.jsonwebtoken.ExpiredJwtException; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.io.PrintWriter; - -@RequiredArgsConstructor -public class JWTFilter extends OncePerRequestFilter { - - private final JWTUtil jwtUtil; - - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - // 헤더에서 Authorization에 담긴 토큰을 꺼냄 - String accessToken = request.getHeader("Authorization"); - - if (accessToken != null && accessToken.startsWith("Bearer ")) { - accessToken = accessToken.substring(7); // "Bearer "의 길이는 7 - } - - // 토큰이 없다면 다음 필터로 넘김 - if (accessToken == null|| accessToken.isEmpty()) { - filterChain.doFilter(request, response); - return; - } - - // 토큰 만료 여부 확인, 만료시 다음 필터로 넘기지 않음 - try { - jwtUtil.isExpired(accessToken); - } catch (ExpiredJwtException e) { - // 만료된 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 - PrintWriter writer = response.getWriter(); - writer.print("access token expired"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - - // 토큰이 access인지 확인 (발급시 페이로드에 명시) - String category = jwtUtil.getCategory(accessToken); - if (!category.equals("access")) { - // 유효하지 않은 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 - PrintWriter writer = response.getWriter(); - writer.print("invalid access token"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - - - String username = jwtUtil.getUsername(accessToken); - String role = jwtUtil.getRole(accessToken); - String name=jwtUtil.getName(accessToken); - UserDTO userDTO = UserDTO.builder() - .name(name) - .username(username) - .role(role) - .build(); - CustomOAuth2User customOAuth2User = new CustomOAuth2User(userDTO); - Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, customOAuth2User.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authToken); - filterChain.doFilter(request, response); - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java deleted file mode 100644 index d7f3a78..0000000 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.bamboo.log.domain.user.jwt.service; - -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.Jwts; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.StandardCharsets; -import java.util.Date; - -@Component -public class JWTUtil { - - private SecretKey secretKey; - - public JWTUtil(@Value("${spring.jwt.secret}")String secret) { - secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); - } - - public String getUsername(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class); - } - - public String getRole(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); - } - - public Boolean isExpired(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); - } - - public String getCategory(String token) { - try { - // 토큰에서 카테고리 정보를 추출합니다. - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("category", String.class); - } catch (JwtException e) { - throw new RuntimeException("토큰에서 카테고리를 추출하는 중 오류가 발생했습니다."); - } - } - - public String getName(String token){ - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("name", String.class); - } - public String createJwt(String category,String name,String username, String role, Long expiredMs) { - return Jwts.builder() - .claim("category",category) - .claim("username", username) - .claim("name",name) - .claim("role", role) - .issuedAt(new Date(System.currentTimeMillis())) - .expiration(new Date(System.currentTimeMillis() + expiredMs)) - .signWith(secretKey) - .compact(); - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java deleted file mode 100644 index 6f196f7..0000000 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.bamboo.log.domain.user.jwt.service; - -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; - -import java.util.Optional; - -@Component -public class UserContextUtil { - - public Optional getCurrentUser() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null || !(authentication.getPrincipal() instanceof CustomOAuth2User)) { - return Optional.empty(); - } - return Optional.of((CustomOAuth2User) authentication.getPrincipal()); - } - - public String getUsername() { - return getCurrentUser() - .map(CustomOAuth2User::getUsername) - .orElseThrow(() -> new RuntimeException("User not authenticated")); - } - - public String getName() { - return getCurrentUser() - .map(CustomOAuth2User::getName) - .orElseThrow(() -> new RuntimeException("User not authenticated")); - } - -} diff --git a/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java b/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java deleted file mode 100644 index 3ff12c5..0000000 --- a/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.bamboo.log.domain.user.logout; - -import com.bamboo.log.domain.user.jwt.service.JWTUtil; -import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; -import io.jsonwebtoken.ExpiredJwtException; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.filter.GenericFilterBean; - -import java.io.IOException; - -@RequiredArgsConstructor -@Transactional -@Slf4j -public class CustomLogoutFilter extends GenericFilterBean { - - private final JWTUtil jwtUtil; - private final RefreshRepository refreshRepository; - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); - } - - private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { - - String requestUri = request.getRequestURI(); - if (!requestUri.matches("^\\/logout$")) { - filterChain.doFilter(request, response); - return; - } - - String requestMethod = request.getMethod(); - if (!requestMethod.equals("POST")) { - filterChain.doFilter(request, response); - return; - } - - String refresh = null; - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("refresh")) { - refresh = cookie.getValue(); - } - } - - if (refresh == null) { - log.error("로그아웃 시 리프레시 토큰이 없습니다."); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - try { - jwtUtil.isExpired(refresh); - } catch (ExpiredJwtException e) { - log.error("리프레시 토큰이 만료되었습니다. refresh token: {}", refresh); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - String category = jwtUtil.getCategory(refresh); - if (!category.equals("refresh")) { - log.error("유효하지 않은 리프레시 토큰입니다. refresh token: {}", refresh); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - Boolean isExist = refreshRepository.existsByToken(refresh); - if (!isExist) { - log.error("리프레시 토큰이 데이터베이스에 존재하지 않습니다. refresh token: {}", refresh); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - refreshRepository.deleteByToken(refresh); - log.info("리프레시 토큰이 데이터베이스에서 삭제되었습니다. refresh token: {}", refresh); - - Cookie cookie = new Cookie("refresh", null); - cookie.setMaxAge(0); - cookie.setPath("/"); - - response.addCookie(cookie); - log.info("리프레시 토큰 쿠키가 삭제되었습니다."); - response.setStatus(HttpServletResponse.SC_OK); - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java deleted file mode 100644 index 707915d..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.bamboo.log.domain.user.oauth.dto; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.core.user.OAuth2User; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -public class CustomOAuth2User implements OAuth2User { - - private final UserDTO userDTO; - - public CustomOAuth2User(UserDTO userDTO) { - this.userDTO = userDTO; - } - - @Override - public Map getAttributes() { - return null; - } - - @Override - public Collection getAuthorities() { - - Collection collection = new ArrayList<>(); - - collection.add(new GrantedAuthority() { - - @Override - public String getAuthority() { - - return userDTO.getRole(); - } - }); - - return collection; - } - - @Override - public String getName() { - return userDTO.getName(); - } - - public String getUsername() { - return userDTO.getUsername(); - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java deleted file mode 100644 index 2e16ed0..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.bamboo.log.domain.user.oauth.dto; - -import java.util.Map; - -public class KakaoResponse implements OAuth2Response { - private final Map attributes; - private final Map kakaoAccount; - private final Map profile; - - public KakaoResponse(Map attributes) { - this.attributes = attributes; - this.kakaoAccount = (Map) attributes.get("kakao_account"); - this.profile = (Map) kakaoAccount.get("profile"); - } - - @Override - public String getProvider() { - return "kakao"; - } - - @Override - public String getProviderId() { - return attributes.get("id").toString(); - } - - @Override - public String getEmail() { - return kakaoAccount.get("email") != null ? kakaoAccount.get("email").toString() : "N/A"; - } - - @Override - public String getName() { - return profile.get("nickname").toString(); - } - - @Override - public Map getAttributes() { - return attributes; - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java deleted file mode 100644 index 28f415d..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.bamboo.log.domain.user.oauth.dto; - -import java.util.Map; - -public interface OAuth2Response { - String getProvider(); - String getProviderId(); - String getEmail(); - String getName(); - Map getAttributes(); -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java deleted file mode 100644 index f76f9a2..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.bamboo.log.domain.user.oauth.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -@AllArgsConstructor -@Builder -@Getter -public class UserDTO { - private String name; - private String username; - private String role; -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java deleted file mode 100644 index e02ceb4..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.bamboo.log.domain.user.oauth.entity; - -import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.util.Date; -@Entity -@Table(name = "refresh_token") -@Getter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class RefreshToken { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(nullable = false) - private String name; - - @Column(nullable = false) - private String username; - - @Column(nullable = false) - private String token; - - @Column(name = "expires_at", nullable = false) - @Temporal(TemporalType.TIMESTAMP) - private Date expiresAt; - - @Column(name = "created_at", nullable = false) - @Temporal(TemporalType.TIMESTAMP) - private Date createdAt; - -} - diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java deleted file mode 100644 index fcf2836..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.bamboo.log.domain.user.oauth.entity; - -import jakarta.persistence.*; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@Entity -@Table(name="users") -public class UserEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(unique = true, nullable = false) - private String username; - - - private String name; - private String email; - private String role; - private String profile_img_url; - - @Builder - public UserEntity(String username, String name, String email, String role,String profile_img_url) { - this.username = username; - this.name = name; - this.email = email; - this.role = role; - this.profile_img_url = profile_img_url; - } - - public UserEntity updateEmailAndName(String email, String name) { - return UserEntity.builder() - .username(this.username) - .name(name) - .email(email) - .role(this.role) - .profile_img_url(this.profile_img_url) - .build(); - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java deleted file mode 100644 index 8bbe809..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.bamboo.log.domain.user.oauth.repository; - -import com.bamboo.log.domain.user.oauth.entity.RefreshToken; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface RefreshRepository extends JpaRepository { - Boolean existsByToken(String token); - void deleteByToken(String token); - RefreshToken findByToken(String token); -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java deleted file mode 100644 index 645a385..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bamboo.log.domain.user.oauth.repository; - -import com.bamboo.log.domain.user.oauth.entity.UserEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface UserRepository extends JpaRepository { - UserEntity findByUsername(String username); -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java deleted file mode 100644 index 1dac05d..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.bamboo.log.domain.user.oauth.service; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -@Component -public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler { - - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - response.sendRedirect("http://localhost:3000"); - } -} - diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java deleted file mode 100644 index 8de41c2..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.bamboo.log.domain.user.oauth.service; - -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; -import com.bamboo.log.domain.user.oauth.dto.KakaoResponse; -import com.bamboo.log.domain.user.oauth.dto.OAuth2Response; -import com.bamboo.log.domain.user.oauth.dto.UserDTO; -import com.bamboo.log.domain.user.oauth.entity.UserEntity; -import com.bamboo.log.domain.user.oauth.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Service; - - -@Service -@RequiredArgsConstructor -public class CustomOAuth2UserService extends DefaultOAuth2UserService { - - private final UserRepository userRepository; - - @Override - public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { - OAuth2User oAuth2User = super.loadUser(userRequest); - String registrationId = userRequest.getClientRegistration().getRegistrationId(); - - OAuth2Response oAuth2Response = null; - if (registrationId.equals("kakao")) { - oAuth2Response = new KakaoResponse(oAuth2User.getAttributes()); - } else { - return null; - } - - System.out.println("OAuth2Response: " + oAuth2Response); - String username = oAuth2Response.getProvider() + " " + oAuth2Response.getProviderId(); - UserEntity existData = userRepository.findByUsername(username); - System.out.println("username = " + username); - System.out.println("Existing User: " + existData); - - if (existData == null) { - UserEntity userEntity = UserEntity.builder() - .username(username) - .name(oAuth2Response.getName()) - .email(oAuth2Response.getEmail()) - .role("ROLE_USER") - .build(); - - userRepository.save(userEntity); - - UserDTO userDTO = UserDTO.builder() - .username(username) - .name(oAuth2Response.getName()) - .role("ROLE_USER") - .build(); - - return new CustomOAuth2User(userDTO); - } else { - UserDTO userDTO = UserDTO.builder() - .username(existData.getUsername()) - .name(existData.getName()) - .role(existData.getRole()) - .build(); - return new CustomOAuth2User(userDTO); - } - } -} diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java deleted file mode 100644 index 069d271..0000000 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.bamboo.log.domain.user.oauth.service; - -import com.bamboo.log.domain.user.jwt.service.JWTUtil; -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; -import com.bamboo.log.domain.user.oauth.entity.RefreshToken; -import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.stereotype.Component; - -import java.io.IOException; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; - -@Component -@RequiredArgsConstructor -public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - - private final JWTUtil jwtUtil; - private final RefreshRepository refreshRepository; - - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); - String name=customUserDetails.getName(); - String username = customUserDetails.getUsername(); - Collection authorities = authentication.getAuthorities(); - Iterator iterator = authorities.iterator(); - GrantedAuthority auth = iterator.next(); - String role = auth.getAuthority(); - - String refreshToken = jwtUtil.createJwt("refresh",name,username, role, 1800000L); - String accessToken = jwtUtil.createJwt("access",name,username, role, 1209600000L); - addRefreshEntity(name, username, refreshToken, 1209600000L); - response.addCookie(createCookie("refresh", refreshToken)); - response.addCookie(UnScretCreateCookie("access", accessToken)); - response.sendRedirect("http://localhost:3000/"); - } - - private Cookie createCookie(String key, String value) { - - Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(24 * 60 * 60 * 14); - cookie.setPath("/"); - cookie.setHttpOnly(true); - - return cookie; - } - private Cookie UnScretCreateCookie(String key, String value) { - - Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(60*60); - cookie.setPath("/"); - return cookie; - } - private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { - Date CrDate = new Date(System.currentTimeMillis()); - Date ExDate = new Date(System.currentTimeMillis() + expiredMs); - refreshRepository.save( - RefreshToken.builder() - .name(name) - .username(username) - .token(refresh) - .createdAt(CrDate) - .expiresAt(ExDate) - .build() - ); - } -} - diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java deleted file mode 100644 index 73dcdd3..0000000 --- a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.bamboo.log.domain.user.refresh.service; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.ResponseEntity; - -public interface ProcessTokenReissue { - public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response); -} diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java deleted file mode 100644 index e808c3f..0000000 --- a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.bamboo.log.domain.user.refresh.service; - -import com.bamboo.log.domain.user.jwt.service.JWTUtil; -import com.bamboo.log.domain.user.oauth.entity.RefreshToken; -import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; -import io.jsonwebtoken.ExpiredJwtException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Date; - -@Service -@RequiredArgsConstructor -@Transactional -@Slf4j -public class ProcessTokenReissueImpl implements ProcessTokenReissue { - - private final JWTUtil jwtUtil; - private final RefreshRepository refreshRepository; - - public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response) { - - String refresh = null; - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("refresh")) { - refresh = cookie.getValue(); - } - } - - if (refresh == null) { - log.error("리프레시 토큰이 없습니다."); - return new ResponseEntity<>("refresh token null", HttpStatus.BAD_REQUEST); - } - - try { - jwtUtil.isExpired(refresh); - } catch (ExpiredJwtException e) { - log.error("리프레시 토큰이 만료되었습니다. token: {}", refresh); - return new ResponseEntity<>("refresh token expired", HttpStatus.BAD_REQUEST); - } - - String category = jwtUtil.getCategory(refresh); - if (!category.equals("refresh")) { - log.error("유효하지 않은 리프레시 토큰입니다. token: {}", refresh); - return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); - } - - if (!refreshRepository.existsByToken(refresh)) { - log.error("리프레시 토큰이 존재하지 않습니다. token: {}", refresh); - return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); - } - - String name = jwtUtil.getName(refresh); - String username = jwtUtil.getUsername(refresh); - String role = jwtUtil.getRole(refresh); - - String newAccess = jwtUtil.createJwt("access", name, username , role, 1800000L); - String newRefresh = jwtUtil.createJwt("refresh", name, username, role, 1209600000L); - - refreshRepository.deleteByToken(refresh); - addRefreshEntity(name,username,role,1209600000L); - - log.info("새로운 액세스 토큰과 리프레시 토큰을 발급했습니다. 새로운 리프레시 토큰: {}", newRefresh); - - response.setHeader("Authorization", newAccess); - response.addCookie(createCookie("refresh", newRefresh)); - - log.info("리프레시 토큰 재발급 완료"); - - return new ResponseEntity<>(HttpStatus.OK); - } - - private Cookie createCookie(String key, String value) { - log.info("쿠키 생성 시작"); - Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(24 * 60 * 60 * 14); // 14일 유효 - cookie.setHttpOnly(true); // 클라이언트 측에서 접근 불가 - log.info("쿠키 생성 끝"); - return cookie; - } - private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { - Date CrDate = new Date(System.currentTimeMillis()); - Date ExDate = new Date(System.currentTimeMillis() + expiredMs); - refreshRepository.save( - RefreshToken.builder() - .name(name) - .username(username) - .token(refresh) - .createdAt(CrDate) - .expiresAt(ExDate) - .build() - ); - } -} - diff --git a/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java new file mode 100644 index 0000000..0ea3f07 --- /dev/null +++ b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package com.bamboo.log.elice.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/bamboo/log/elice/service/ImageGenerationService.java b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java new file mode 100644 index 0000000..d7aa11b --- /dev/null +++ b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java @@ -0,0 +1,58 @@ +package com.bamboo.log.elice.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import okhttp3.*; +import okhttp3.MediaType; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class ImageGenerationService { + + private final RestTemplate restTemplate; + private final String apiKey; + private final String apiUrl; + + public ImageGenerationService(RestTemplate restTemplate, + @Value("${elice.api.token}") String apiKey, + @Value("${elice.api.url}") String apiUrl) { + this.restTemplate = restTemplate; + this.apiKey = apiKey; + this.apiUrl = apiUrl; + } + + public byte[] generateImage(String prompt) throws IOException { + OkHttpClient client = new OkHttpClient(); + + String requestBody = String.format("{\"prompt\":\"%s\",\"style\":\"polaroid\"}", prompt); + + MediaType mediaType = MediaType.parse("application/json"); + RequestBody body = RequestBody.create(requestBody, mediaType); + + Request request = new Request.Builder() + .url(apiUrl) + .post(body) + .addHeader("accept", "application/json") + .addHeader("content-type", "application/json") + .addHeader("Authorization", "Bearer " + apiKey) + .build(); + + Response response = client.newCall(request).execute(); + + if (!response.isSuccessful()) { + throw new IOException("요청 실패: " + response.code()); + } + + byte[] imageBytes = response.body().bytes(); // ✅ 이미지 데이터를 byte[]로 변환 + response.close(); + return imageBytes; + } +} + diff --git a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java deleted file mode 100644 index fee5189..0000000 --- a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.bamboo.log.emotion.controller; - -import com.bamboo.log.emotion.dto.FaceDetectionResponse; -import com.bamboo.log.emotion.service.FaceDetectionService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/emotion") -@Tag(name = "Face Detection", description = "얼굴 인식 관련 API") -public class FaceDetectController { - private final FaceDetectionService faceDetectionService; - - @PostMapping("/detect") - @Operation( - summary = "얼굴 인식 API", - description = "이미지를 업로드하면 얼굴 인식 여부를 반환합니다." - ) - public ResponseEntity detectFace(@RequestParam("image") MultipartFile image) { - FaceDetectionResponse response = faceDetectionService.detectFace(image); - return ResponseEntity.status(HttpStatus.valueOf(response.statusCode())).body(response); - } -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java deleted file mode 100644 index f9a2b09..0000000 --- a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.bamboo.log.emotion.dto; - -import org.springframework.http.HttpStatus; - -public record FaceDetectionResponse( - int statusCode, - String statusMessage, - String message -) { - public FaceDetectionResponse(HttpStatus httpStatus, String message) { - this(httpStatus.value(), httpStatus.name(), message); - } -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java b/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java deleted file mode 100644 index af5f1bc..0000000 --- a/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bamboo.log.emotion.service; - -import com.bamboo.log.emotion.dto.FaceDetectionResponse; -import org.springframework.web.multipart.MultipartFile; - -public interface FaceDetectionService { - FaceDetectionResponse detectFace(MultipartFile image); -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java deleted file mode 100644 index 47e6d8a..0000000 --- a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.bamboo.log.emotion.service.impl; - -import com.bamboo.log.emotion.dto.FaceDetectionResponse; -import com.bamboo.log.emotion.service.FaceDetectionService; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import okhttp3.*; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; - -@Slf4j -@Service -@RequiredArgsConstructor -public class FaceDetectionServiceImpl implements FaceDetectionService { - - @Value("${elice.api.url.face}") - private String faceApiUrl; - - @Value("${elice.api.token}") - private String faceToken; - - private final OkHttpClient client = new OkHttpClient(); - - @Override - public FaceDetectionResponse detectFace(MultipartFile image) { - if (image == null || image.isEmpty()) { - log.error("파일이 전달되지 않음."); - return new FaceDetectionResponse(HttpStatus.BAD_REQUEST, "파일이 전달되지 않았습니다."); - } - log.info("파일 이름: {}", image.getOriginalFilename()); - log.info("파일 크기: {} bytes", image.getSize()); - - try { - String contentType = image.getContentType(); - if (contentType == null) { - contentType = "application/octet-stream"; // 기본값 설정 - } - - RequestBody requestBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("image", image.getOriginalFilename(), - RequestBody.create(image.getBytes(), MediaType.parse(contentType))) - .build(); - - // API 요청 생성 (토큰 포함) - Request request = new Request.Builder() - .url(faceApiUrl) - .post(requestBody) - .addHeader("accept", "application/json") - .addHeader("Authorization", "Bearer " + faceToken) - .build(); - - try (Response response = client.newCall(request).execute()) { - log.info("API 응답 코드: {}", response.code()); - log.info("API 응답 메시지: {}", response.message()); - - if (!response.isSuccessful()) { - return new FaceDetectionResponse(HttpStatus.valueOf(response.code()), "API 요청 실패: " + response.message()); - } - - String responseBody = response.body().string(); - log.info("API 응답 본문: {}", responseBody); - - // JSON 파싱 - JsonNode jsonNode = new ObjectMapper().readTree(responseBody); - JsonNode results = jsonNode.get("results"); - - // 얼굴 인식 여부 확인 - if (results != null && results.isArray() && results.size() > 0) { - for (JsonNode result : results) { - if ("face".equals(result.get("name").asText())) { - return new FaceDetectionResponse(HttpStatus.OK, responseBody); - } - } - } - return new FaceDetectionResponse(HttpStatus.NOT_FOUND, "얼굴이 인식되지 않았습니다."); - } - } catch (IOException e) { - log.error("API 요청 중 예외 발생", e); - return new FaceDetectionResponse(HttpStatus.INTERNAL_SERVER_ERROR, "API 요청 중 오류 발생: " + e.getMessage()); - } - } - -} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/utils/ResponseHandler.java b/src/main/java/com/bamboo/log/utils/ResponseHandler.java new file mode 100644 index 0000000..c483a50 --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/ResponseHandler.java @@ -0,0 +1,51 @@ +package com.bamboo.log.utils; + +import com.bamboo.log.utils.dto.DetailResponse; +import com.bamboo.log.utils.dto.ResponseForm; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class ResponseHandler { + + public static ResponseEntity create500Error(ResponseForm response, Exception e) { + response.of("result", "FAIL"); + response.of("error", DetailResponse.builder().code(500).message(e.getMessage()).build()); + return new ResponseEntity(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + + public static ResponseEntity create400Error(ResponseForm response, Exception e) { + response.of("result", "FAIL"); + response.of("error", DetailResponse.builder().code(400).message(e.getMessage()).build()); + + return new ResponseEntity(response, HttpStatus.BAD_REQUEST); + } + + public static ResponseEntity create404Error(ResponseForm response, Exception e) { + response.of("result", "FAIL"); + response.of("error", DetailResponse.builder().code(404).message(e.getMessage()).build()); + + return new ResponseEntity(response, HttpStatus.NOT_FOUND); + } + + public static ResponseEntity create204Response(ResponseForm response, String message) { + response.of("result", "SUCCESS"); + response.of("code", DetailResponse.builder().code(204).message(message).build()); + + return new ResponseEntity(response, HttpStatus.NO_CONTENT); + } + + public static ResponseEntity create200Response(ResponseForm response, Object object) { + response.of("result", "SUCCESS"); + response.of("code", object); + + return new ResponseEntity(response, HttpStatus.OK); + } + + public static ResponseEntity create201Response(ResponseForm response, Object object) { + response.of("result", "SUCCESS"); + response.of("code", object); + + return new ResponseEntity(response, HttpStatus.CREATED); + } + +} diff --git a/src/main/java/com/bamboo/log/utils/SecurityConfig.java b/src/main/java/com/bamboo/log/utils/SecurityConfig.java new file mode 100644 index 0000000..5d51eb4 --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/SecurityConfig.java @@ -0,0 +1,23 @@ +package com.bamboo.log.utils; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) // CSRF 비활성화 + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/images/**").permitAll() // 이미지 API 인증 없이 허용 + .anyRequest().authenticated() + ); + + return http.build(); + } +} diff --git a/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java new file mode 100644 index 0000000..61f22cf --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java @@ -0,0 +1,13 @@ +package com.bamboo.log.utils.dto; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class DetailResponse { + + private Integer code; + private String message; + +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java new file mode 100644 index 0000000..04eccf6 --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java @@ -0,0 +1,21 @@ +package com.bamboo.log.utils.dto; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + +@Getter +public class ResponseForm { + + public ResponseForm() { + response = new HashMap<>(); + } + + public void of(String value1, Object value2) { + response.put(value1, value2); + } + + private Map response; + +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5379dd6..317cea6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,42 +1,23 @@ spring: datasource: + driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} username: ${MYSQL_USERNAME} password: ${MYSQL_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver + jpa: show-sql: true open-in-view: false hibernate: - ddl-auto: update + ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect - security: - oauth2: - client: - registration: - kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} - jwt: - secret: ${JWT_SECRET} elice: api: token: ${API_TOKEN} url: - face: ${FACE_URL} - img: - chat: \ No newline at end of file + face: ${FACE_API_URL} + img: ${IMG_API_URL} + chat: ${CHAT_API_URL} From 26d32802fbee3eb134324181a8ee6563352db34b Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Tue, 25 Feb 2025 13:59:57 +0900 Subject: [PATCH 041/114] =?UTF-8?q?Feat:=201=EC=B0=A8=20Merge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/common/config/CorsMvcConfig.java | 18 ++++ .../log/common/config/SecurityConfig.java | 78 ++++++++++++++ .../log/common/config/SwaggerConfig.java | 12 +++ .../user/controller/RefreshController.java | 32 ++++++ .../domain/user/jwt/service/JWTFilter.java | 76 +++++++++++++ .../log/domain/user/jwt/service/JWTUtil.java | 57 ++++++++++ .../user/jwt/service/UserContextUtil.java | 33 ++++++ .../user/logout/CustomLogoutFilter.java | 94 ++++++++++++++++ .../user/oauth/dto/CustomOAuth2User.java | 48 +++++++++ .../domain/user/oauth/dto/KakaoResponse.java | 40 +++++++ .../domain/user/oauth/dto/OAuth2Response.java | 11 ++ .../log/domain/user/oauth/dto/UserDTO.java | 14 +++ .../user/oauth/entity/RefreshToken.java | 39 +++++++ .../domain/user/oauth/entity/UserEntity.java | 45 ++++++++ .../oauth/repository/RefreshRepository.java | 10 ++ .../user/oauth/repository/UserRepository.java | 8 ++ .../oauth/service/CustomFailureHandler.java | 19 ++++ .../service/CustomOAuth2UserService.java | 67 ++++++++++++ .../oauth/service/CustomSuccessHandler.java | 77 +++++++++++++ .../refresh/service/ProcessTokenReissue.java | 9 ++ .../service/ProcessTokenReissueImpl.java | 102 ++++++++++++++++++ .../controller/FaceDetectController.java | 32 ++++++ .../emotion/dto/FaceDetectionResponse.java | 13 +++ .../emotion/service/FaceDetectionService.java | 8 ++ .../impl/FaceDetectionServiceImpl.java | 90 ++++++++++++++++ 25 files changed, 1032 insertions(+) create mode 100644 src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java create mode 100644 src/main/java/com/bamboo/log/common/config/SecurityConfig.java create mode 100644 src/main/java/com/bamboo/log/common/config/SwaggerConfig.java create mode 100644 src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java create mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java create mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java create mode 100644 src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java create mode 100644 src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java create mode 100644 src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java create mode 100644 src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java create mode 100644 src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java create mode 100644 src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java create mode 100644 src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java create mode 100644 src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java create mode 100644 src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java new file mode 100644 index 0000000..38df386 --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -0,0 +1,18 @@ +package com.bamboo.log.common.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("/**") + .exposedHeaders("Set-Cookie") + //프론트 url 넣기 + .allowedOrigins("http://localhost:3000"); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java new file mode 100644 index 0000000..f36c518 --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -0,0 +1,78 @@ +package com.bamboo.log.common.config; + +import com.bamboo.log.domain.user.jwt.service.JWTFilter; +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.logout.CustomLogoutFilter; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; +import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; +import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Collections; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final RefreshRepository refreshRepository; + private final CustomOAuth2UserService customOAuth2UserService; + private final CustomSuccessHandler customSuccessHandler; + private final CustomFailureHandler customFailureHandler; + private final JWTUtil jwtUtil; + + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf((auth) -> auth.disable()); + http.formLogin((auth) -> auth.disable()); + http.httpBasic((auth) -> auth.disable()); + http.oauth2Login((oauth2) -> oauth2 + .userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig + .userService(customOAuth2UserService)) + .successHandler(customSuccessHandler) + .failureHandler(customFailureHandler) + ); + http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); + + //이쪽에다가 각각의 엔드포인트를 넣어야함 + http.authorizeHttpRequests((auth) -> auth + .requestMatchers("/refresh").permitAll() + .requestMatchers("/logout").hasAnyRole("USER") + .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() + .anyRequest().authenticated()); + + + http.sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); + http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + CorsConfiguration configuration = new CorsConfiguration(); + //프론트 url 넣기 + 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("Set-Cookie")); + configuration.setExposedHeaders(Collections.singletonList("Authorization")); + return configuration; + } + })); + return http.build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java new file mode 100644 index 0000000..dd0a87a --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/SwaggerConfig.java @@ -0,0 +1,12 @@ +package com.bamboo.log.common.config; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import org.springframework.context.annotation.Configuration; + +@Configuration +@OpenAPIDefinition(info = @Info(title = "ForRest", version = "v1")) +public class SwaggerConfig { +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java new file mode 100644 index 0000000..05cd4d9 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/controller/RefreshController.java @@ -0,0 +1,32 @@ +package com.bamboo.log.domain.user.controller; + +import com.bamboo.log.domain.user.refresh.service.ProcessTokenReissue; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class RefreshController { + + private final ProcessTokenReissue processTokenReissue; + + @Operation(summary = "토큰 갱신", description = "Refresh Token을 사용하여 Access Token을 재발급합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "토큰 재발급 성공"), + @ApiResponse(responseCode = "401", description = "유효하지 않은 Refresh Token"), + @ApiResponse(responseCode = "403", description = "Refresh Token이 만료됨") + }) + @PostMapping("/refresh") + public ResponseEntity reissue( + HttpServletRequest request, + HttpServletResponse response) { + return processTokenReissue.reissue(request, response); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java new file mode 100644 index 0000000..1698608 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java @@ -0,0 +1,76 @@ +package com.bamboo.log.domain.user.jwt.service; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.dto.UserDTO; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.io.PrintWriter; + +@RequiredArgsConstructor +public class JWTFilter extends OncePerRequestFilter { + + private final JWTUtil jwtUtil; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // 헤더에서 Authorization에 담긴 토큰을 꺼냄 + String accessToken = request.getHeader("Authorization"); + + if (accessToken != null && accessToken.startsWith("Bearer ")) { + accessToken = accessToken.substring(7); // "Bearer "의 길이는 7 + } + + // 토큰이 없다면 다음 필터로 넘김 + if (accessToken == null|| accessToken.isEmpty()) { + filterChain.doFilter(request, response); + return; + } + + // 토큰 만료 여부 확인, 만료시 다음 필터로 넘기지 않음 + try { + jwtUtil.isExpired(accessToken); + } catch (ExpiredJwtException e) { + // 만료된 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 + PrintWriter writer = response.getWriter(); + writer.print("access token expired"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + // 토큰이 access인지 확인 (발급시 페이로드에 명시) + String category = jwtUtil.getCategory(accessToken); + if (!category.equals("access")) { + // 유효하지 않은 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 + PrintWriter writer = response.getWriter(); + writer.print("invalid access token"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + + String username = jwtUtil.getUsername(accessToken); + String role = jwtUtil.getRole(accessToken); + String name=jwtUtil.getName(accessToken); + UserDTO userDTO = UserDTO.builder() + .name(name) + .username(username) + .role(role) + .build(); + CustomOAuth2User customOAuth2User = new CustomOAuth2User(userDTO); + Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, customOAuth2User.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java new file mode 100644 index 0000000..88ee472 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java @@ -0,0 +1,57 @@ +package com.bamboo.log.domain.user.jwt.service; + +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +@Component +public class JWTUtil { + + private SecretKey secretKey; + + public JWTUtil(@Value("${spring.jwt.secret}")String secret) { + secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); + } + + public String getUsername(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class); + } + + public String getRole(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); + } + + public Boolean isExpired(String token) { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + } + + public String getCategory(String token) { + try { + // 토큰에서 카테고리 정보를 추출합니다. + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("category", String.class); + } catch (JwtException e) { + throw new RuntimeException("토큰에서 카테고리를 추출하는 중 오류가 발생했습니다."); + } + } + + public String getName(String token){ + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("name", String.class); + } + public String createJwt(String category,String name,String username, String role, Long expiredMs) { + return Jwts.builder() + .claim("category",category) + .claim("username", username) + .claim("name",name) + .claim("role", role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiredMs)) + .signWith(secretKey) + .compact(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java new file mode 100644 index 0000000..77ae9d6 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java @@ -0,0 +1,33 @@ +package com.bamboo.log.domain.user.jwt.service; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class UserContextUtil { + + public Optional getCurrentUser() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !(authentication.getPrincipal() instanceof CustomOAuth2User)) { + return Optional.empty(); + } + return Optional.of((CustomOAuth2User) authentication.getPrincipal()); + } + + public String getUsername() { + return getCurrentUser() + .map(CustomOAuth2User::getUsername) + .orElseThrow(() -> new RuntimeException("User not authenticated")); + } + + public String getName() { + return getCurrentUser() + .map(CustomOAuth2User::getName) + .orElseThrow(() -> new RuntimeException("User not authenticated")); + } + +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java b/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java new file mode 100644 index 0000000..d663e5e --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/logout/CustomLogoutFilter.java @@ -0,0 +1,94 @@ +package com.bamboo.log.domain.user.logout; + +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.filter.GenericFilterBean; + +import java.io.IOException; + +@RequiredArgsConstructor +@Transactional +@Slf4j +public class CustomLogoutFilter extends GenericFilterBean { + + private final JWTUtil jwtUtil; + private final RefreshRepository refreshRepository; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); + } + + private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + + String requestUri = request.getRequestURI(); + if (!requestUri.matches("^\\/logout$")) { + filterChain.doFilter(request, response); + return; + } + + String requestMethod = request.getMethod(); + if (!requestMethod.equals("POST")) { + filterChain.doFilter(request, response); + return; + } + + String refresh = null; + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + if (cookie.getName().equals("refresh")) { + refresh = cookie.getValue(); + } + } + + if (refresh == null) { + log.error("로그아웃 시 리프레시 토큰이 없습니다."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + try { + jwtUtil.isExpired(refresh); + } catch (ExpiredJwtException e) { + log.error("리프레시 토큰이 만료되었습니다. refresh token: {}", refresh); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + String category = jwtUtil.getCategory(refresh); + if (!category.equals("refresh")) { + log.error("유효하지 않은 리프레시 토큰입니다. refresh token: {}", refresh); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + Boolean isExist = refreshRepository.existsByToken(refresh); + if (!isExist) { + log.error("리프레시 토큰이 데이터베이스에 존재하지 않습니다. refresh token: {}", refresh); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + refreshRepository.deleteByToken(refresh); + log.info("리프레시 토큰이 데이터베이스에서 삭제되었습니다. refresh token: {}", refresh); + + Cookie cookie = new Cookie("refresh", null); + cookie.setMaxAge(0); + cookie.setPath("/"); + + response.addCookie(cookie); + log.info("리프레시 토큰 쿠키가 삭제되었습니다."); + response.setStatus(HttpServletResponse.SC_OK); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java new file mode 100644 index 0000000..3e33b94 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java @@ -0,0 +1,48 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +public class CustomOAuth2User implements OAuth2User { + + private final UserDTO userDTO; + + public CustomOAuth2User(UserDTO userDTO) { + this.userDTO = userDTO; + } + + @Override + public Map getAttributes() { + return null; + } + + @Override + public Collection getAuthorities() { + + Collection collection = new ArrayList<>(); + + collection.add(new GrantedAuthority() { + + @Override + public String getAuthority() { + + return userDTO.getRole(); + } + }); + + return collection; + } + + @Override + public String getName() { + return userDTO.getName(); + } + + public String getUsername() { + return userDTO.getUsername(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java new file mode 100644 index 0000000..a8d8f79 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/KakaoResponse.java @@ -0,0 +1,40 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import java.util.Map; + +public class KakaoResponse implements OAuth2Response { + private final Map attributes; + private final Map kakaoAccount; + private final Map profile; + + public KakaoResponse(Map attributes) { + this.attributes = attributes; + this.kakaoAccount = (Map) attributes.get("kakao_account"); + this.profile = (Map) kakaoAccount.get("profile"); + } + + @Override + public String getProvider() { + return "kakao"; + } + + @Override + public String getProviderId() { + return attributes.get("id").toString(); + } + + @Override + public String getEmail() { + return kakaoAccount.get("email") != null ? kakaoAccount.get("email").toString() : "N/A"; + } + + @Override + public String getName() { + return profile.get("nickname").toString(); + } + + @Override + public Map getAttributes() { + return attributes; + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java new file mode 100644 index 0000000..126b2ea --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/OAuth2Response.java @@ -0,0 +1,11 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import java.util.Map; + +public interface OAuth2Response { + String getProvider(); + String getProviderId(); + String getEmail(); + String getName(); + Map getAttributes(); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java new file mode 100644 index 0000000..efda413 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java @@ -0,0 +1,14 @@ +package com.bamboo.log.domain.user.oauth.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@AllArgsConstructor +@Builder +@Getter +public class UserDTO { + private String name; + private String username; + private String role; +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java new file mode 100644 index 0000000..ae4b22c --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/entity/RefreshToken.java @@ -0,0 +1,39 @@ +package com.bamboo.log.domain.user.oauth.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Date; +@Entity +@Table(name = "refresh_token") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class RefreshToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String username; + + @Column(nullable = false) + private String token; + + @Column(name = "expires_at", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date expiresAt; + + @Column(name = "created_at", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date createdAt; + +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java new file mode 100644 index 0000000..2a3be77 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java @@ -0,0 +1,45 @@ +package com.bamboo.log.domain.user.oauth.entity; + +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@Entity +@Table(name="users") +public class UserEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true, nullable = false) + private String username; + + + private String name; + private String email; + private String role; + private String profile_img_url; + + @Builder + public UserEntity(String username, String name, String email, String role,String profile_img_url) { + this.username = username; + this.name = name; + this.email = email; + this.role = role; + this.profile_img_url = profile_img_url; + } + + public UserEntity updateEmailAndName(String email, String name) { + return UserEntity.builder() + .username(this.username) + .name(name) + .email(email) + .role(this.role) + .profile_img_url(this.profile_img_url) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java new file mode 100644 index 0000000..0075488 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/repository/RefreshRepository.java @@ -0,0 +1,10 @@ +package com.bamboo.log.domain.user.oauth.repository; + +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RefreshRepository extends JpaRepository { + Boolean existsByToken(String token); + void deleteByToken(String token); + RefreshToken findByToken(String token); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java b/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java new file mode 100644 index 0000000..645a385 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/repository/UserRepository.java @@ -0,0 +1,8 @@ +package com.bamboo.log.domain.user.oauth.repository; + +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + UserEntity findByUsername(String username); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java new file mode 100644 index 0000000..da4890c --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java @@ -0,0 +1,19 @@ +package com.bamboo.log.domain.user.oauth.service; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + response.sendRedirect("http://localhost:3000"); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java new file mode 100644 index 0000000..c7d1e1e --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java @@ -0,0 +1,67 @@ +package com.bamboo.log.domain.user.oauth.service; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.dto.KakaoResponse; +import com.bamboo.log.domain.user.oauth.dto.OAuth2Response; +import com.bamboo.log.domain.user.oauth.dto.UserDTO; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + + +@Service +@RequiredArgsConstructor +public class CustomOAuth2UserService extends DefaultOAuth2UserService { + + private final UserRepository userRepository; + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + OAuth2User oAuth2User = super.loadUser(userRequest); + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + + OAuth2Response oAuth2Response = null; + if (registrationId.equals("kakao")) { + oAuth2Response = new KakaoResponse(oAuth2User.getAttributes()); + } else { + return null; + } + + System.out.println("OAuth2Response: " + oAuth2Response); + String username = oAuth2Response.getProvider() + " " + oAuth2Response.getProviderId(); + UserEntity existData = userRepository.findByUsername(username); + System.out.println("username = " + username); + System.out.println("Existing User: " + existData); + + if (existData == null) { + UserEntity userEntity = UserEntity.builder() + .username(username) + .name(oAuth2Response.getName()) + .email(oAuth2Response.getEmail()) + .role("ROLE_USER") + .build(); + + userRepository.save(userEntity); + + UserDTO userDTO = UserDTO.builder() + .username(username) + .name(oAuth2Response.getName()) + .role("ROLE_USER") + .build(); + + return new CustomOAuth2User(userDTO); + } else { + UserDTO userDTO = UserDTO.builder() + .username(existData.getUsername()) + .name(existData.getName()) + .role(existData.getRole()) + .build(); + return new CustomOAuth2User(userDTO); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java new file mode 100644 index 0000000..f82af92 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -0,0 +1,77 @@ +package com.bamboo.log.domain.user.oauth.service; + +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; + +@Component +@RequiredArgsConstructor +public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + private final JWTUtil jwtUtil; + private final RefreshRepository refreshRepository; + + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); + String name=customUserDetails.getName(); + String username = customUserDetails.getUsername(); + Collection authorities = authentication.getAuthorities(); + Iterator iterator = authorities.iterator(); + GrantedAuthority auth = iterator.next(); + String role = auth.getAuthority(); + + String refreshToken = jwtUtil.createJwt("refresh",name,username, role, 1800000L); + String accessToken = jwtUtil.createJwt("access",name,username, role, 1209600000L); + addRefreshEntity(name, username, refreshToken, 1209600000L); + response.addCookie(createCookie("refresh", refreshToken)); + response.addCookie(UnScretCreateCookie("access", accessToken)); + response.sendRedirect("http://localhost:3000/"); + } + + private Cookie createCookie(String key, String value) { + + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(24 * 60 * 60 * 14); + cookie.setPath("/"); + cookie.setHttpOnly(true); + + return cookie; + } + private Cookie UnScretCreateCookie(String key, String value) { + + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(60*60); + cookie.setPath("/"); + return cookie; + } + private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { + Date CrDate = new Date(System.currentTimeMillis()); + Date ExDate = new Date(System.currentTimeMillis() + expiredMs); + refreshRepository.save( + RefreshToken.builder() + .name(name) + .username(username) + .token(refresh) + .createdAt(CrDate) + .expiresAt(ExDate) + .build() + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java new file mode 100644 index 0000000..8069876 --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissue.java @@ -0,0 +1,9 @@ +package com.bamboo.log.domain.user.refresh.service; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.ResponseEntity; + +public interface ProcessTokenReissue { + public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java new file mode 100644 index 0000000..0e123ae --- /dev/null +++ b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java @@ -0,0 +1,102 @@ +package com.bamboo.log.domain.user.refresh.service; + +import com.bamboo.log.domain.user.jwt.service.JWTUtil; +import com.bamboo.log.domain.user.oauth.entity.RefreshToken; +import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +@Service +@RequiredArgsConstructor +@Transactional +@Slf4j +public class ProcessTokenReissueImpl implements ProcessTokenReissue { + + private final JWTUtil jwtUtil; + private final RefreshRepository refreshRepository; + + public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse response) { + + String refresh = null; + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + if (cookie.getName().equals("refresh")) { + refresh = cookie.getValue(); + } + } + + if (refresh == null) { + log.error("리프레시 토큰이 없습니다."); + return new ResponseEntity<>("refresh token null", HttpStatus.BAD_REQUEST); + } + + try { + jwtUtil.isExpired(refresh); + } catch (ExpiredJwtException e) { + log.error("리프레시 토큰이 만료되었습니다. token: {}", refresh); + return new ResponseEntity<>("refresh token expired", HttpStatus.BAD_REQUEST); + } + + String category = jwtUtil.getCategory(refresh); + if (!category.equals("refresh")) { + log.error("유효하지 않은 리프레시 토큰입니다. token: {}", refresh); + return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); + } + + if (!refreshRepository.existsByToken(refresh)) { + log.error("리프레시 토큰이 존재하지 않습니다. token: {}", refresh); + return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); + } + + String name = jwtUtil.getName(refresh); + String username = jwtUtil.getUsername(refresh); + String role = jwtUtil.getRole(refresh); + + String newAccess = jwtUtil.createJwt("access", name, username , role, 1800000L); + String newRefresh = jwtUtil.createJwt("refresh", name, username, role, 1209600000L); + + refreshRepository.deleteByToken(refresh); + addRefreshEntity(name,username,role,1209600000L); + + log.info("새로운 액세스 토큰과 리프레시 토큰을 발급했습니다. 새로운 리프레시 토큰: {}", newRefresh); + + response.setHeader("Authorization", newAccess); + response.addCookie(createCookie("refresh", newRefresh)); + + log.info("리프레시 토큰 재발급 완료"); + + return new ResponseEntity<>(HttpStatus.OK); + } + + private Cookie createCookie(String key, String value) { + log.info("쿠키 생성 시작"); + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(24 * 60 * 60 * 14); // 14일 유효 + cookie.setHttpOnly(true); // 클라이언트 측에서 접근 불가 + log.info("쿠키 생성 끝"); + return cookie; + } + private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { + Date CrDate = new Date(System.currentTimeMillis()); + Date ExDate = new Date(System.currentTimeMillis() + expiredMs); + refreshRepository.save( + RefreshToken.builder() + .name(name) + .username(username) + .token(refresh) + .createdAt(CrDate) + .expiresAt(ExDate) + .build() + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java new file mode 100644 index 0000000..fee5189 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java @@ -0,0 +1,32 @@ +package com.bamboo.log.emotion.controller; + +import com.bamboo.log.emotion.dto.FaceDetectionResponse; +import com.bamboo.log.emotion.service.FaceDetectionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/emotion") +@Tag(name = "Face Detection", description = "얼굴 인식 관련 API") +public class FaceDetectController { + private final FaceDetectionService faceDetectionService; + + @PostMapping("/detect") + @Operation( + summary = "얼굴 인식 API", + description = "이미지를 업로드하면 얼굴 인식 여부를 반환합니다." + ) + public ResponseEntity detectFace(@RequestParam("image") MultipartFile image) { + FaceDetectionResponse response = faceDetectionService.detectFace(image); + return ResponseEntity.status(HttpStatus.valueOf(response.statusCode())).body(response); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java new file mode 100644 index 0000000..f9a2b09 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java @@ -0,0 +1,13 @@ +package com.bamboo.log.emotion.dto; + +import org.springframework.http.HttpStatus; + +public record FaceDetectionResponse( + int statusCode, + String statusMessage, + String message +) { + public FaceDetectionResponse(HttpStatus httpStatus, String message) { + this(httpStatus.value(), httpStatus.name(), message); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java b/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java new file mode 100644 index 0000000..af5f1bc --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/service/FaceDetectionService.java @@ -0,0 +1,8 @@ +package com.bamboo.log.emotion.service; + +import com.bamboo.log.emotion.dto.FaceDetectionResponse; +import org.springframework.web.multipart.MultipartFile; + +public interface FaceDetectionService { + FaceDetectionResponse detectFace(MultipartFile image); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java new file mode 100644 index 0000000..0e3fa52 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java @@ -0,0 +1,90 @@ +package com.bamboo.log.emotion.service.impl; + +import com.bamboo.log.emotion.dto.FaceDetectionResponse; +import com.bamboo.log.emotion.service.FaceDetectionService; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@Service +@RequiredArgsConstructor +public class FaceDetectionServiceImpl implements FaceDetectionService { + + @Value("${face.api.url}") + private String faceApiUrl; + + @Value("${face.api.token}") + private String faceToken; + + private final OkHttpClient client = new OkHttpClient(); + + @Override + public FaceDetectionResponse detectFace(MultipartFile image) { + if (image == null || image.isEmpty()) { + log.error("파일이 전달되지 않음."); + return new FaceDetectionResponse(HttpStatus.BAD_REQUEST, "파일이 전달되지 않았습니다."); + } + log.info("파일 이름: {}", image.getOriginalFilename()); + log.info("파일 크기: {} bytes", image.getSize()); + + try { + String contentType = image.getContentType(); + if (contentType == null) { + contentType = "application/octet-stream"; // 기본값 설정 + } + + RequestBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("image", image.getOriginalFilename(), + RequestBody.create(image.getBytes(), MediaType.parse(contentType))) + .build(); + + // API 요청 생성 (토큰 포함) + Request request = new Request.Builder() + .url(faceApiUrl) + .post(requestBody) + .addHeader("accept", "application/json") + .addHeader("Authorization", "Bearer " + faceToken) + .build(); + + try (Response response = client.newCall(request).execute()) { + log.info("API 응답 코드: {}", response.code()); + log.info("API 응답 메시지: {}", response.message()); + + if (!response.isSuccessful()) { + return new FaceDetectionResponse(HttpStatus.valueOf(response.code()), "API 요청 실패: " + response.message()); + } + + String responseBody = response.body().string(); + log.info("API 응답 본문: {}", responseBody); + + // JSON 파싱 + JsonNode jsonNode = new ObjectMapper().readTree(responseBody); + JsonNode results = jsonNode.get("results"); + + // 얼굴 인식 여부 확인 + if (results != null && results.isArray() && results.size() > 0) { + for (JsonNode result : results) { + if ("face".equals(result.get("name").asText())) { + return new FaceDetectionResponse(HttpStatus.OK, responseBody); + } + } + } + return new FaceDetectionResponse(HttpStatus.NOT_FOUND, "얼굴이 인식되지 않았습니다."); + } + } catch (IOException e) { + log.error("API 요청 중 예외 발생", e); + return new FaceDetectionResponse(HttpStatus.INTERNAL_SERVER_ERROR, "API 요청 중 오류 발생: " + e.getMessage()); + } + } + +} \ No newline at end of file From 63ef269f5e00a859d97f46db4c165311a5033d33 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Tue, 25 Feb 2025 21:49:45 +0900 Subject: [PATCH 042/114] =?UTF-8?q?Settings:=20application.yml,=20bulid.gr?= =?UTF-8?q?adle=20=EA=B8=B0=EC=A1=B4=20=EC=83=81=ED=83=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 54 ++++++++----------- .../elice/service/ImageGenerationService.java | 2 +- .../impl/FaceDetectionServiceImpl.java | 4 +- .../com/bamboo/log/utils/SecurityConfig.java | 23 -------- src/main/resources/application.yml | 31 ++++++++--- 5 files changed, 50 insertions(+), 64 deletions(-) delete mode 100644 src/main/java/com/bamboo/log/utils/SecurityConfig.java diff --git a/build.gradle b/build.gradle index 56f4f0e..53c709f 100644 --- a/build.gradle +++ b/build.gradle @@ -24,38 +24,28 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.0.4' - implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0' - testImplementation 'org.projectlombok:lombok:1.18.26' - compileOnly 'org.projectlombok:lombok:1.18.26' - runtimeOnly 'com.mysql:mysql-connector-j:8.0.32' - annotationProcessor 'org.projectlombok:lombok:1.18.26' - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' - - implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' - implementation 'jakarta.validation:jakarta.validation-api:3.0.2' - - implementation 'com.squareup.okhttp3:okhttp:4.9.3' - - // Spring Doc - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' - - // Spring Security - implementation 'org.springframework.boot:spring-boot-starter-security:3.0.4' - - // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' - implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' - - // chatGPT API - implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' - - // JSON 라이브러리 - implementation 'org.json:json:20230227' - - implementation 'org.springframework:spring-test:6.0.6' - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + + developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.mysql:mysql-connector-j' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // okhttp + implementation 'com.squareup.okhttp3:okhttp:4.12.0' } tasks.named('test') { diff --git a/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java index d7aa11b..d318cce 100644 --- a/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java +++ b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java @@ -22,7 +22,7 @@ public class ImageGenerationService { public ImageGenerationService(RestTemplate restTemplate, @Value("${elice.api.token}") String apiKey, - @Value("${elice.api.url}") String apiUrl) { + @Value("${elice.api.url.img}") String apiUrl) { this.restTemplate = restTemplate; this.apiKey = apiKey; this.apiUrl = apiUrl; diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java index 0e3fa52..47e6d8a 100644 --- a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java +++ b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java @@ -19,10 +19,10 @@ @RequiredArgsConstructor public class FaceDetectionServiceImpl implements FaceDetectionService { - @Value("${face.api.url}") + @Value("${elice.api.url.face}") private String faceApiUrl; - @Value("${face.api.token}") + @Value("${elice.api.token}") private String faceToken; private final OkHttpClient client = new OkHttpClient(); diff --git a/src/main/java/com/bamboo/log/utils/SecurityConfig.java b/src/main/java/com/bamboo/log/utils/SecurityConfig.java deleted file mode 100644 index 5d51eb4..0000000 --- a/src/main/java/com/bamboo/log/utils/SecurityConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.bamboo.log.utils; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.web.SecurityFilterChain; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(csrf -> csrf.disable()) // CSRF 비활성화 - .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/images/**").permitAll() // 이미지 API 인증 없이 허용 - .anyRequest().authenticated() - ); - - return http.build(); - } -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 317cea6..746ba36 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,23 +1,42 @@ spring: datasource: - driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} username: ${MYSQL_USERNAME} password: ${MYSQL_PASSWORD} - + driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true open-in-view: false hibernate: - ddl-auto: update + ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + jwt: + secret: ${JWT_SECRET} elice: api: token: ${API_TOKEN} url: - face: ${FACE_API_URL} - img: ${IMG_API_URL} - chat: ${CHAT_API_URL} + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} \ No newline at end of file From 03e7a83660035db40dd6230f29fd89bea168d5bb Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Tue, 25 Feb 2025 22:00:30 +0900 Subject: [PATCH 043/114] =?UTF-8?q?F=E3=85=91=E3=85=8C=20:=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=EC=9D=B4=20=EC=95=88=EB=90=98=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- build.gradle | 7 +-- .../log/common/config/SecurityConfig.java | 1 + .../elice/service/ImageGenerationService.java | 2 +- .../impl/FaceDetectionServiceImpl.java | 4 +- .../com/bamboo/log/utils/SecurityConfig.java | 23 ---------- src/main/resources/application.txt | 40 +++++++++++++++++ src/main/resources/application.yml | 45 +++++++++---------- 8 files changed, 69 insertions(+), 55 deletions(-) delete mode 100644 src/main/java/com/bamboo/log/utils/SecurityConfig.java create mode 100644 src/main/resources/application.txt diff --git a/.env b/.env index 3f466ec..983dc22 100644 --- a/.env +++ b/.env @@ -13,7 +13,7 @@ SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD=client_se SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=kakao SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=649c1e34aedc1a7db892333d900725f2 SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET=FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=http://localhost:8080/login/oauth2/code/kakao +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=authorization_code SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=profile_nickname,account_email diff --git a/build.gradle b/build.gradle index 56f4f0e..25ad86f 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { compileOnly 'org.projectlombok:lombok:1.18.26' runtimeOnly 'com.mysql:mysql-connector-j:8.0.32' annotationProcessor 'org.projectlombok:lombok:1.18.26' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' @@ -44,9 +45,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security:3.0.4' // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' - implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' // chatGPT API implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index f36c518..d40a6fe 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -52,6 +52,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/refresh").permitAll() .requestMatchers("/logout").hasAnyRole("USER") .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() + .requestMatchers("/api/images/**").hasAnyRole("USER") .anyRequest().authenticated()); diff --git a/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java index d7aa11b..d318cce 100644 --- a/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java +++ b/src/main/java/com/bamboo/log/elice/service/ImageGenerationService.java @@ -22,7 +22,7 @@ public class ImageGenerationService { public ImageGenerationService(RestTemplate restTemplate, @Value("${elice.api.token}") String apiKey, - @Value("${elice.api.url}") String apiUrl) { + @Value("${elice.api.url.img}") String apiUrl) { this.restTemplate = restTemplate; this.apiKey = apiKey; this.apiUrl = apiUrl; diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java index 0e3fa52..47e6d8a 100644 --- a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java +++ b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java @@ -19,10 +19,10 @@ @RequiredArgsConstructor public class FaceDetectionServiceImpl implements FaceDetectionService { - @Value("${face.api.url}") + @Value("${elice.api.url.face}") private String faceApiUrl; - @Value("${face.api.token}") + @Value("${elice.api.token}") private String faceToken; private final OkHttpClient client = new OkHttpClient(); diff --git a/src/main/java/com/bamboo/log/utils/SecurityConfig.java b/src/main/java/com/bamboo/log/utils/SecurityConfig.java deleted file mode 100644 index 5d51eb4..0000000 --- a/src/main/java/com/bamboo/log/utils/SecurityConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.bamboo.log.utils; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.web.SecurityFilterChain; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(csrf -> csrf.disable()) // CSRF 비활성화 - .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/images/**").permitAll() // 이미지 API 인증 없이 허용 - .anyRequest().authenticated() - ); - - return http.build(); - } -} diff --git a/src/main/resources/application.txt b/src/main/resources/application.txt new file mode 100644 index 0000000..f930f54 --- /dev/null +++ b/src/main/resources/application.txt @@ -0,0 +1,40 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} + jpa: + show-sql: true + open-in-view: false + hibernate: + ddl-auto: update + properties: + hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + jwt: + secret: ${JWT_SECRET} +elice: + api: + token: ${API_TOKEN} + url: + face: ${FACE_API_URL} + img: ${IMG_API_URL} + chat: ${CHAT_API_URL} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 99a2856..d73895c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,15 +1,14 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} - + url: jdbc:mysql://localhost:3306/OAuth2 + username: root + password: heodongun0922 jpa: show-sql: true open-in-view: false hibernate: - ddl-auto: update + ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect security: @@ -17,30 +16,26 @@ spring: client: registration: kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: profile_nickname,account_email provider: kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: - secret: ${JWT_SECRET} - + secret: fkldjfklsdjfaslkjflaksdjklfjaljewofijaenndlksjajojf -<<<<<<< HEAD -======= elice: api: - token: ${API_TOKEN} + token: yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms url: - face: ${FACE_API_URL} - img: ${IMG_API_URL} - chat: ${CHAT_API_URL} ->>>>>>> 26d32802fbee3eb134324181a8ee6563352db34b + face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict + img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + chat: https://api-cloud-function.elice.io/9f071d94-a459-429d-a375-9601e521b079/v1/chat/completions From 70e0b7c587a4f0d6be1db9f217d1e7daeac7bf4f Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Tue, 25 Feb 2025 22:06:36 +0900 Subject: [PATCH 044/114] =?UTF-8?q?Fix=20:=20=EC=8B=A4=ED=96=89=EC=9D=B4?= =?UTF-8?q?=20=EC=95=88=EB=90=98=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 30 ------------------------- src/main/resources/application.txt | 24 +++++++++++--------- src/main/resources/application.yml | 36 ------------------------------ 3 files changed, 13 insertions(+), 77 deletions(-) diff --git a/build.gradle b/build.gradle index 44ef86e..53c709f 100644 --- a/build.gradle +++ b/build.gradle @@ -24,16 +24,6 @@ repositories { } dependencies { -<<<<<<< HEAD - implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.0.4' - implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0' - testImplementation 'org.projectlombok:lombok:1.18.26' - compileOnly 'org.projectlombok:lombok:1.18.26' - runtimeOnly 'com.mysql:mysql-connector-j:8.0.32' - annotationProcessor 'org.projectlombok:lombok:1.18.26' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' -======= implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' @@ -43,7 +33,6 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.12.3' implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' ->>>>>>> 63ef269f5e00a859d97f46db4c165311a5033d33 developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' @@ -55,27 +44,8 @@ dependencies { testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' -<<<<<<< HEAD - // Spring Security - implementation 'org.springframework.boot:spring-boot-starter-security:3.0.4' - - // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.12.3' - implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' - implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' - - // chatGPT API - implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4' - - // JSON 라이브러리 - implementation 'org.json:json:20230227' - - implementation 'org.springframework:spring-test:6.0.6' - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.0' -======= // okhttp implementation 'com.squareup.okhttp3:okhttp:4.12.0' ->>>>>>> 63ef269f5e00a859d97f46db4c165311a5033d33 } tasks.named('test') { diff --git a/src/main/resources/application.txt b/src/main/resources/application.txt index f930f54..746ba36 100644 --- a/src/main/resources/application.txt +++ b/src/main/resources/application.txt @@ -1,16 +1,17 @@ spring: datasource: - driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} username: ${MYSQL_USERNAME} password: ${MYSQL_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true open-in-view: false hibernate: - ddl-auto: update + ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + security: oauth2: client: @@ -23,18 +24,19 @@ spring: redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} jwt: secret: ${JWT_SECRET} + elice: api: token: ${API_TOKEN} url: - face: ${FACE_API_URL} - img: ${IMG_API_URL} - chat: ${CHAT_API_URL} + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8b1e288..e7731bb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,16 +1,9 @@ spring: datasource: -<<<<<<< HEAD driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/OAuth2 username: root password: heodongun0922 -======= - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver ->>>>>>> 63ef269f5e00a859d97f46db4c165311a5033d33 jpa: show-sql: true open-in-view: false @@ -18,16 +11,11 @@ spring: ddl-auto: update properties: hibernate.dialect: org.hibernate.dialect.MySQL8Dialect -<<<<<<< HEAD -======= - ->>>>>>> 63ef269f5e00a859d97f46db4c165311a5033d33 security: oauth2: client: registration: kakao: -<<<<<<< HEAD client-authentication-method: client_secret_post client-name: kakao client-id: 649c1e34aedc1a7db892333d900725f2 @@ -43,34 +31,10 @@ spring: user-name-attribute: id jwt: secret: fkldjfklsdjfaslkjflaksdjklfjaljewofijaenndlksjajojf -======= - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} - jwt: - secret: ${JWT_SECRET} ->>>>>>> 63ef269f5e00a859d97f46db4c165311a5033d33 - elice: api: token: yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms url: -<<<<<<< HEAD face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate chat: https://api-cloud-function.elice.io/9f071d94-a459-429d-a375-9601e521b079/v1/chat/completions -======= - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} ->>>>>>> 63ef269f5e00a859d97f46db4c165311a5033d33 From 7045e3b60e436c364dc034fa8108dbe186bda178 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Wed, 26 Feb 2025 00:51:27 +0900 Subject: [PATCH 045/114] =?UTF-8?q?Fix=20:=20yml=20=EC=97=90=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/diary/repository/DiaryRepository.java | 1 + .../log/domain/user/oauth/dto/CustomOAuth2User.java | 4 ++++ src/main/resources/application.yml | 12 ++++++------ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java index 416db42..2ef3b85 100644 --- a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -4,4 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface DiaryRepository extends JpaRepository { + Diary findByContext(String diaryContent); } diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java index 3e33b94..dcabe8e 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java @@ -45,4 +45,8 @@ public String getName() { public String getUsername() { return userDTO.getUsername(); } + + public String userId(){ + return userDTO.getId + } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e7731bb..cecbe40 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,12 +23,12 @@ spring: redirect-uri: https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao authorization-grant-type: authorization_code scope: profile_nickname,account_email - provider: - kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id + provider: + kakao: + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: secret: fkldjfklsdjfaslkjflaksdjklfjaljewofijaenndlksjajojf elice: From be1d417f3d5d972e0724d52df07e89857249ee3b Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Wed, 26 Feb 2025 00:53:04 +0900 Subject: [PATCH 046/114] =?UTF-8?q?Fix=20:=20yml=20=EC=97=90=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 25 ------------------ src/main/resources/application.txt | 42 ------------------------------ src/main/resources/application.yml | 40 ++++++++++++++-------------- 3 files changed, 21 insertions(+), 86 deletions(-) delete mode 100644 .env delete mode 100644 src/main/resources/application.txt diff --git a/.env b/.env deleted file mode 100644 index 983dc22..0000000 --- a/.env +++ /dev/null @@ -1,25 +0,0 @@ -MYSQL_HOST=localhost -MYSQL_PORT=3306 -MYSQL_NAME=OAuth2 -MYSQL_USERNAME=root -MYSQL_PASSWORD=heodongun0922 - -SPRING_JPA_SHOW_SQL=true -SPRING_JPA_OPEN_IN_VIEW=false -SPRING_JPA_HIBERNATE_DDL_AUTO=update -SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT=org.hibernate.dialect.MySQL8Dialect - -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD=client_secret_post -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=kakao -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=649c1e34aedc1a7db892333d900725f2 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET=FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=authorization_code -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=profile_nickname,account_email - -SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI=https://kauth.kakao.com/oauth/authorize -SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI=https://kauth.kakao.com/oauth/token -SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI=https://kapi.kakao.com/v2/user/me -SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=id - -JWT_SECRET=fkldjfklsdjfaslkjflaksdjklfjaljewofijaenndlksjajojf \ No newline at end of file diff --git a/src/main/resources/application.txt b/src/main/resources/application.txt deleted file mode 100644 index 746ba36..0000000 --- a/src/main/resources/application.txt +++ /dev/null @@ -1,42 +0,0 @@ -spring: - datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver - jpa: - show-sql: true - open-in-view: false - hibernate: - ddl-auto: update - properties: - hibernate.dialect: org.hibernate.dialect.MySQL8Dialect - - security: - oauth2: - client: - registration: - kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} - jwt: - secret: ${JWT_SECRET} - -elice: - api: - token: ${API_TOKEN} - url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index cecbe40..17527e6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,9 +1,11 @@ +# application.yml + spring: datasource: + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/OAuth2 - username: root - password: heodongun0922 jpa: show-sql: true open-in-view: false @@ -16,25 +18,25 @@ spring: client: registration: kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: profile_nickname,account_email + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} provider: kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} jwt: - secret: fkldjfklsdjfaslkjflaksdjklfjaljewofijaenndlksjajojf + secret: ${JWT_SECRET} elice: api: - token: yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms + token: ${API_TOKEN} url: - face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict - img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - chat: https://api-cloud-function.elice.io/9f071d94-a459-429d-a375-9601e521b079/v1/chat/completions + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} \ No newline at end of file From e4dc9a0b1461dffa42bbf64f6303576956f9c488 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Wed, 26 Feb 2025 00:53:46 +0900 Subject: [PATCH 047/114] =?UTF-8?q?Fix=20:=20yml=20=EC=97=90=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bamboo/log/diary/repository/DiaryRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java index 2ef3b85..416db42 100644 --- a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -4,5 +4,4 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface DiaryRepository extends JpaRepository { - Diary findByContext(String diaryContent); } From b851b11e6561760e041a31611f08e48c976e7deb Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 01:37:38 +0900 Subject: [PATCH 048/114] =?UTF-8?q?fix:=20UserEntity=20=EA=B8=B0=EB=B0=98?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/diary/domain/Diary.java | 6 +++-- .../diary/dto/request/CreateDiaryRequest.java | 3 +-- .../diary/service/diary/DiaryServiceImpl.java | 25 ++++++++----------- .../diary/service/mock/MockUserService.java | 12 --------- .../log/diary/service/mock/UserService.java | 5 ---- .../user/jwt/service/UserContextUtil.java | 12 +++++++++ .../service/CustomOAuth2UserService.java | 1 + 7 files changed, 29 insertions(+), 35 deletions(-) delete mode 100644 src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java delete mode 100644 src/main/java/com/bamboo/log/diary/service/mock/UserService.java diff --git a/src/main/java/com/bamboo/log/diary/domain/Diary.java b/src/main/java/com/bamboo/log/diary/domain/Diary.java index c820a2c..20f071d 100644 --- a/src/main/java/com/bamboo/log/diary/domain/Diary.java +++ b/src/main/java/com/bamboo/log/diary/domain/Diary.java @@ -1,5 +1,6 @@ package com.bamboo.log.diary.domain; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -21,8 +22,9 @@ public class Diary { @Column(name = "diary_id") private Long id; - @Column(nullable = false) - private String userId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private UserEntity user; @Column(columnDefinition = "TEXT", nullable = false) private String context; diff --git a/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java b/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java index 084708d..8e5425b 100644 --- a/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java +++ b/src/main/java/com/bamboo/log/diary/dto/request/CreateDiaryRequest.java @@ -4,6 +4,5 @@ import java.util.List; -public record CreateDiaryRequest(@NotEmpty(message = "User's Id shouldn't be empty") String userId, - @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDetail) { +public record CreateDiaryRequest(@NotEmpty(message = "Diary Description shouldn't be empty") String diaryDetail) { } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 5c201fd..7aa7b33 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -4,12 +4,16 @@ import com.bamboo.log.diary.dto.request.CreateDiaryRequest; import com.bamboo.log.diary.dto.response.CreateDiaryResponse; import com.bamboo.log.diary.repository.DiaryRepository; -import com.bamboo.log.diary.repository.TodaySummaryRepository; -import com.bamboo.log.diary.service.mock.UserService; +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; +import org.springframework.security.core.GrantedAuthority; + import com.bamboo.log.diary.service.summary.TodaySummaryService; +import com.bamboo.log.domain.user.jwt.service.UserContextUtil; +import com.bamboo.log.domain.user.oauth.dto.KakaoResponse; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; import com.bamboo.log.utils.ResponseHandler; import com.bamboo.log.utils.dto.ResponseForm; -import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -23,24 +27,15 @@ @RequiredArgsConstructor public class DiaryServiceImpl implements DiaryService { - private final UserService userService; + private final UserContextUtil userContextUtil; private final DiaryRepository diaryRepository; private final TodaySummaryService todaySummaryService; @Override public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { - - // User 객체 검증 로직 - // 해당 User 객체 찾을 수 없을 경우, 에러 반환 - if(userService.existsById(createDiaryRequest.userId())) { - return ResponseHandler.create404Error(new ResponseForm(), new EntityNotFoundException("잘못된 유저 정보입니다.")); - } - // 해당 시점의 localDateTime 저장 LocalDateTime localDateTime = LocalDateTime.now(); - // alice api 연결 함수 호출 - // 데이터베이스에 저장 Diary diary = saveDiary(createDiaryRequest, localDateTime); @@ -58,8 +53,10 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { } private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { + UserEntity user = userContextUtil.getUserEntity(); + Diary diary = Diary.builder() - .userId(createDiaryRequest.userId()) + .user(user) .context(createDiaryRequest.diaryDetail()) .createdAt(localDateTime) .build(); diff --git a/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java b/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java deleted file mode 100644 index 86824f2..0000000 --- a/src/main/java/com/bamboo/log/diary/service/mock/MockUserService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.bamboo.log.diary.service.mock; - -import org.springframework.stereotype.Service; - -@Service -public class MockUserService implements UserService { - - @Override - public boolean existsById(String userId) { - return true; - } -} diff --git a/src/main/java/com/bamboo/log/diary/service/mock/UserService.java b/src/main/java/com/bamboo/log/diary/service/mock/UserService.java deleted file mode 100644 index 0b37f9a..0000000 --- a/src/main/java/com/bamboo/log/diary/service/mock/UserService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.bamboo.log.diary.service.mock; - -public interface UserService { - boolean existsById(String userId); -} diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java index 77ae9d6..19162c0 100644 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/UserContextUtil.java @@ -1,6 +1,9 @@ package com.bamboo.log.domain.user.jwt.service; import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; +import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @@ -8,8 +11,11 @@ import java.util.Optional; @Component +@RequiredArgsConstructor public class UserContextUtil { + private final UserRepository userRepository; + public Optional getCurrentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !(authentication.getPrincipal() instanceof CustomOAuth2User)) { @@ -30,4 +36,10 @@ public String getName() { .orElseThrow(() -> new RuntimeException("User not authenticated")); } + public UserEntity getUserEntity() { + String username = getUsername(); + + return userRepository.findByUsername(username); + } + } \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java index c7d1e1e..b7f7c02 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java @@ -64,4 +64,5 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic return new CustomOAuth2User(userDTO); } } + } \ No newline at end of file From 723b8d58a3b9032ddeb08d05fd154dbaf2ab6c05 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Wed, 26 Feb 2025 02:08:58 +0900 Subject: [PATCH 049/114] =?UTF-8?q?Fix=20:=20=EC=9D=B4=EC=83=81=ED=95=9C?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java index dcabe8e..bb7502a 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java @@ -46,7 +46,4 @@ public String getUsername() { return userDTO.getUsername(); } - public String userId(){ - return userDTO.getId - } } \ No newline at end of file From f29fe5e8773770055c202dbff00dd0f6fe3887ac Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 11:18:21 +0900 Subject: [PATCH 050/114] =?UTF-8?q?Feat:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20API=20Request=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/diary/dto/request/CheckDiaryRequest.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java diff --git a/src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java b/src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java new file mode 100644 index 0000000..19b87dd --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java @@ -0,0 +1,6 @@ +package com.bamboo.log.diary.dto.request; + +import jakarta.validation.constraints.NotEmpty; + +public record CheckDiaryRequest(@NotEmpty(message = "Date shouldn't be empty") String date) { +} From 6ac3e54d89ad905eb371ae7ac779c5754d5251cc Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 11:22:14 +0900 Subject: [PATCH 051/114] =?UTF-8?q?Feat:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20API=20Response=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/diary/dto/response/CheckDiaryResponse.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java new file mode 100644 index 0000000..4cf3a55 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java @@ -0,0 +1,9 @@ +package com.bamboo.log.diary.dto.response; + +import jakarta.validation.constraints.NotEmpty; + +public record CheckDiaryResponse(@NotEmpty(message = "Date shouldn't be empty") String date, + @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDescription, + @NotEmpty(message = "Summary Image shouldn't be empty") byte[] summaryImage + ) { +} From b6696653fb93f4494600902e4cbb143b09de2ced Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 11:25:47 +0900 Subject: [PATCH 052/114] =?UTF-8?q?Feat:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20Controller=20?= =?UTF-8?q?=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/diary/api/DiaryController.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 0b4e1c5..efe0b04 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -21,13 +21,23 @@ public class DiaryController { private final DiaryService diaryService; - @Operation(summary = "랜덤 주제 조회") + @Operation(summary = "일기 생성") @PostMapping("/create") - public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createDiaryRequest) { + public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryRequest) { try { return diaryService.createDiary(createDiaryRequest); } catch (Exception e) { return ResponseHandler.create500Error(new ResponseForm(), e); } } + + @Operation(summary = "날짜별 일기 조회") + @GetMapping("/diariy") + public ResponseEntity getDiaryByDate(@RequestParam String date) { + try { + + } catch (Exception e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } + } } From 67ea19d3661580e6d7887c7fa532e0eeeb427b2c Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 11:32:38 +0900 Subject: [PATCH 053/114] =?UTF-8?q?Refactor:=20=EB=82=A0=EC=A7=9C=EB=B3=84?= =?UTF-8?q?=20=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20DTO=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=A4=91=20Date=20=EC=9E=90=EB=A3=8C=ED=98=95=20Lo?= =?UTF-8?q?calDateTime=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/diary/api/DiaryController.java | 2 +- .../bamboo/log/diary/dto/response/CheckDiaryResponse.java | 4 +++- .../com/bamboo/log/diary/service/diary/DiaryService.java | 2 ++ .../com/bamboo/log/diary/service/diary/DiaryServiceImpl.java | 5 +++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index efe0b04..00e19e5 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -35,7 +35,7 @@ public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryReq @GetMapping("/diariy") public ResponseEntity getDiaryByDate(@RequestParam String date) { try { - + return diaryService.getDiaryByDate(date); } catch (Exception e) { return ResponseHandler.create500Error(new ResponseForm(), e); } diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java index 4cf3a55..99a9a9c 100644 --- a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java +++ b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java @@ -2,7 +2,9 @@ import jakarta.validation.constraints.NotEmpty; -public record CheckDiaryResponse(@NotEmpty(message = "Date shouldn't be empty") String date, +import java.time.LocalDateTime; + +public record CheckDiaryResponse(@NotEmpty(message = "Date shouldn't be empty") LocalDateTime date, @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDescription, @NotEmpty(message = "Summary Image shouldn't be empty") byte[] summaryImage ) { diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java index 50cb4ac..f513e75 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java @@ -7,4 +7,6 @@ public interface DiaryService { ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); + ResponseEntity getDiaryByDate(String date); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 7aa7b33..cf659be 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -52,6 +52,11 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { return ResponseHandler.create201Response(new ResponseForm(), new CreateDiaryResponse(localDateTime)); } + @Override + public ResponseEntity getDiaryByDate(String date) { + return null; + } + private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { UserEntity user = userContextUtil.getUserEntity(); From 4a24cfdd3a5b6a334a2c437c30cc98aca04da2b2 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 11:49:17 +0900 Subject: [PATCH 054/114] Settings: dotenv Config --- .env | 24 +++++++++++++++++++ .gitignore | 4 +++- build.gradle | 2 ++ .../bamboo/log/common/config/EnvConfig.java | 13 ++++++++++ .../bamboo/log/diary/api/DiaryController.java | 4 ++-- .../diary/dto/request/CheckDiaryRequest.java | 6 ----- .../dto/response/CheckDiaryResponse.java | 2 ++ .../log/diary/repository/DiaryRepository.java | 7 ++++++ .../log/diary/service/diary/DiaryService.java | 4 +++- .../diary/service/diary/DiaryServiceImpl.java | 9 +++++-- 10 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 .env create mode 100644 src/main/java/com/bamboo/log/common/config/EnvConfig.java delete mode 100644 src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java diff --git a/.env b/.env new file mode 100644 index 0000000..74a0ff2 --- /dev/null +++ b/.env @@ -0,0 +1,24 @@ +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_NAME=forrest +MYSQL_USERNAME=root +MYSQL_PASSWORD=ensharp2005* + +API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms; +FACE_URL=https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict +IMG_URL=https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + +JWT_SECRET=vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD=client_secret_post +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=kakao +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=649c1e34aedc1a7db892333d900725f2 +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET=FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=authorization_code +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=profile_nickname,account_email + +SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI=https://kauth.kakao.com/oauth/authorize +SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI=https://kauth.kakao.com/oauth/token +SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI=https://kapi.kakao.com/v2/user/me +SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=id \ No newline at end of file diff --git a/.gitignore b/.gitignore index e48b6be..4c75e42 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,6 @@ out/ /.nb-gradle/ ### VS Code ### -.vscode/ \ No newline at end of file +.vscode/ + +.env \ No newline at end of file diff --git a/build.gradle b/build.gradle index 53c709f..c0649cd 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,8 @@ dependencies { // okhttp implementation 'com.squareup.okhttp3:okhttp:4.12.0' + + implementation 'io.github.cdimascio:java-dotenv:5.2.2' } tasks.named('test') { diff --git a/src/main/java/com/bamboo/log/common/config/EnvConfig.java b/src/main/java/com/bamboo/log/common/config/EnvConfig.java new file mode 100644 index 0000000..e531e99 --- /dev/null +++ b/src/main/java/com/bamboo/log/common/config/EnvConfig.java @@ -0,0 +1,13 @@ +package com.bamboo.log.common.config; + +import io.github.cdimascio.dotenv.Dotenv; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class EnvConfig { + @Bean + public Dotenv dotenv() { + return Dotenv.configure().ignoreIfMissing().load(); + } +} diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 00e19e5..bcd933e 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -11,7 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; +import java.time.LocalDateTime; @RestController @RequiredArgsConstructor @@ -33,7 +33,7 @@ public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryReq @Operation(summary = "날짜별 일기 조회") @GetMapping("/diariy") - public ResponseEntity getDiaryByDate(@RequestParam String date) { + public ResponseEntity getDiaryByDate(@RequestParam LocalDateTime date) { try { return diaryService.getDiaryByDate(date); } catch (Exception e) { diff --git a/src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java b/src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java deleted file mode 100644 index 19b87dd..0000000 --- a/src/main/java/com/bamboo/log/diary/dto/request/CheckDiaryRequest.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.bamboo.log.diary.dto.request; - -import jakarta.validation.constraints.NotEmpty; - -public record CheckDiaryRequest(@NotEmpty(message = "Date shouldn't be empty") String date) { -} diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java index 99a9a9c..b15ff89 100644 --- a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java +++ b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java @@ -1,9 +1,11 @@ package com.bamboo.log.diary.dto.response; import jakarta.validation.constraints.NotEmpty; +import lombok.Builder; import java.time.LocalDateTime; +@Builder public record CheckDiaryResponse(@NotEmpty(message = "Date shouldn't be empty") LocalDateTime date, @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDescription, @NotEmpty(message = "Summary Image shouldn't be empty") byte[] summaryImage diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java index 416db42..922fcfd 100644 --- a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -1,7 +1,14 @@ package com.bamboo.log.diary.repository; import com.bamboo.log.diary.domain.Diary; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; +import java.util.List; + public interface DiaryRepository extends JpaRepository { + + List findByUserAAndCreatedAtBetween(UserEntity user, LocalDateTime start, LocalDateTime end); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java index f513e75..57d08fc 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java @@ -3,10 +3,12 @@ import com.bamboo.log.diary.dto.request.CreateDiaryRequest; import org.springframework.http.ResponseEntity; +import java.time.LocalDateTime; + public interface DiaryService { ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); - ResponseEntity getDiaryByDate(String date); + ResponseEntity getDiaryByDate(LocalDateTime date); } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index cf659be..eeea9cc 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -53,8 +53,13 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { } @Override - public ResponseEntity getDiaryByDate(String date) { - return null; + public ResponseEntity getDiaryByDate(LocalDateTime date) { + // 해당 유저가 입력받은 날짜에 일기를 작성했는지 확인 + + // 확인 후 얻어낸 일기 정보를 Response에 담아 반환 + + + return ResponseHandler.create200Response(new ResponseForm(), new CreateDiaryResponse[]); } private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { From c9f5f984e5b146b06accf5c1a2b9da0d9007626f Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 11:58:42 +0900 Subject: [PATCH 055/114] =?UTF-8?q?Feat:=20=EC=9D=BC=EA=B8=B0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20Service=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 24 ---------------- .../repository/TodaySummaryRepository.java | 5 ++++ .../diary/service/diary/DiaryServiceImpl.java | 28 +++++++++++++++---- 3 files changed, 28 insertions(+), 29 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 74a0ff2..0000000 --- a/.env +++ /dev/null @@ -1,24 +0,0 @@ -MYSQL_HOST=localhost -MYSQL_PORT=3306 -MYSQL_NAME=forrest -MYSQL_USERNAME=root -MYSQL_PASSWORD=ensharp2005* - -API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms; -FACE_URL=https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict -IMG_URL=https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - -JWT_SECRET=vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb - -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD=client_secret_post -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=kakao -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=649c1e34aedc1a7db892333d900725f2 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET=FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=authorization_code -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=profile_nickname,account_email - -SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI=https://kauth.kakao.com/oauth/authorize -SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI=https://kauth.kakao.com/oauth/token -SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI=https://kapi.kakao.com/v2/user/me -SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=id \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java index 4093d83..2442924 100644 --- a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java @@ -3,5 +3,10 @@ import com.bamboo.log.diary.domain.TodaySummary; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface TodaySummaryRepository extends JpaRepository { + + Optional findByDiaryId(Long diaryId); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index eeea9cc..1a546a7 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -1,10 +1,14 @@ package com.bamboo.log.diary.service.diary; import com.bamboo.log.diary.domain.Diary; +import com.bamboo.log.diary.domain.TodaySummary; import com.bamboo.log.diary.dto.request.CreateDiaryRequest; +import com.bamboo.log.diary.dto.response.CheckDiaryResponse; import com.bamboo.log.diary.dto.response.CreateDiaryResponse; import com.bamboo.log.diary.repository.DiaryRepository; +import com.bamboo.log.diary.repository.TodaySummaryRepository; import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; import org.springframework.security.core.GrantedAuthority; @@ -21,6 +25,8 @@ import java.io.IOException; import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; @Service @Transactional @@ -28,8 +34,10 @@ public class DiaryServiceImpl implements DiaryService { private final UserContextUtil userContextUtil; + private final UserRepository userRepository; private final DiaryRepository diaryRepository; private final TodaySummaryService todaySummaryService; + private final TodaySummaryRepository todaySummaryRepository; @Override public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { @@ -54,12 +62,22 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { @Override public ResponseEntity getDiaryByDate(LocalDateTime date) { - // 해당 유저가 입력받은 날짜에 일기를 작성했는지 확인 + UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); - // 확인 후 얻어낸 일기 정보를 Response에 담아 반환 - - - return ResponseHandler.create200Response(new ResponseForm(), new CreateDiaryResponse[]); + try { + List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, date, date); + Diary diaryByDate = diaries.get(0); + Optional summaryImage = todaySummaryRepository.findByDiaryId(diaryByDate.getId()); + + return ResponseHandler.create200Response(new ResponseForm(), + CheckDiaryResponse.builder() + .date(diaryByDate.getCreatedAt()) + .diaryDescription(diaryByDate.getContext()) + .summaryImage(summaryImage.get().getImageData())); + + } catch (Exception e) { + return ResponseHandler.create404Error(new ResponseForm(), new IllegalArgumentException("해당 날짜에 작성된 일기가 없습니다.")); + } } private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { From 5f1d37ab15ab65477fd068083c9a28fecc59d849 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 13:45:18 +0900 Subject: [PATCH 056/114] =?UTF-8?q?Refactor:=20=EB=82=A0=EC=A7=9C=EB=B3=84?= =?UTF-8?q?=20=EC=9D=BC=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20api=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/diary/api/DiaryController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index bcd933e..776728f 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -32,7 +32,7 @@ public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryReq } @Operation(summary = "날짜별 일기 조회") - @GetMapping("/diariy") + @GetMapping("/date") public ResponseEntity getDiaryByDate(@RequestParam LocalDateTime date) { try { return diaryService.getDiaryByDate(date); From 462cb785abc1d1a626a1cb4ee3b88906763a8344 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 15:45:18 +0900 Subject: [PATCH 057/114] =?UTF-8?q?Feat:=20=EC=9B=94=EB=B3=84=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20API=20controller=20=EA=B5=AC?= =?UTF-8?q?=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 24 +++++++++++++++++++ .../bamboo/log/diary/api/DiaryController.java | 10 ++++++++ 2 files changed, 34 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..74a0ff2 --- /dev/null +++ b/.env @@ -0,0 +1,24 @@ +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_NAME=forrest +MYSQL_USERNAME=root +MYSQL_PASSWORD=ensharp2005* + +API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms; +FACE_URL=https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict +IMG_URL=https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + +JWT_SECRET=vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD=client_secret_post +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=kakao +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=649c1e34aedc1a7db892333d900725f2 +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET=FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=authorization_code +SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=profile_nickname,account_email + +SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI=https://kauth.kakao.com/oauth/authorize +SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI=https://kauth.kakao.com/oauth/token +SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI=https://kapi.kakao.com/v2/user/me +SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=id \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 0b4e1c5..7d24b93 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -30,4 +30,14 @@ public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createD return ResponseHandler.create500Error(new ResponseForm(), e); } } + + @Operation(summary = "월별 일기 조회") + @GetMapping("/month") + public ResponseEntity getDiariesByMonth(@RequestParam String date) { + try { + + } catch (Exception e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } + } } From f6a223556bf4addbd400973c68b1331cad41dba1 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 15:48:49 +0900 Subject: [PATCH 058/114] Settings: Remove .env from Git tracking --- .env | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 74a0ff2..0000000 --- a/.env +++ /dev/null @@ -1,24 +0,0 @@ -MYSQL_HOST=localhost -MYSQL_PORT=3306 -MYSQL_NAME=forrest -MYSQL_USERNAME=root -MYSQL_PASSWORD=ensharp2005* - -API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mzg4MzQxNDgsIm5iZiI6MTczODgzNDE0OCwia2V5X2lkIjoiZTdmNTUxN2MtYzNlZC00ZmVmLWFkNzYtNTUxZjA3ZDJmMjNiIn0.sMIASpWdac5l3GdIXshb7NnuB-PQlHMFyNurcpu4Cms; -FACE_URL=https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict -IMG_URL=https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - -JWT_SECRET=vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb - -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD=client_secret_post -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=kakao -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=649c1e34aedc1a7db892333d900725f2 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET=FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=https://qbdffmpbayqfbgja.tunnel-pt.elice.io/login/oauth2/code/kakao -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=authorization_code -SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=profile_nickname,account_email - -SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI=https://kauth.kakao.com/oauth/authorize -SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI=https://kauth.kakao.com/oauth/token -SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI=https://kapi.kakao.com/v2/user/me -SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=id \ No newline at end of file From a775946c036167840ea0d06077d16f22486f8009 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 15:50:20 +0900 Subject: [PATCH 059/114] =?UTF-8?q?Feat:=20=EC=9B=94=EB=B3=84=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20Service=20=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- .../com/bamboo/log/diary/service/diary/DiaryService.java | 2 ++ .../com/bamboo/log/diary/service/diary/DiaryServiceImpl.java | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e48b6be..4c75e42 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,6 @@ out/ /.nb-gradle/ ### VS Code ### -.vscode/ \ No newline at end of file +.vscode/ + +.env \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java index 50cb4ac..c6d3ba7 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java @@ -7,4 +7,6 @@ public interface DiaryService { ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); + ResponseEntity getDiariesByMonth(String date); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 7aa7b33..cbef192 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -52,6 +52,11 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { return ResponseHandler.create201Response(new ResponseForm(), new CreateDiaryResponse(localDateTime)); } + @Override + public ResponseEntity getDiariesByMonth(String date) { + return null; + } + private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { UserEntity user = userContextUtil.getUserEntity(); From d6529a33c651a986d29deae381c96d38e23da2b5 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 15:54:00 +0900 Subject: [PATCH 060/114] =?UTF-8?q?Feat:=20=EC=9B=94=EB=B3=84=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20Response=20Form=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/diary/api/DiaryController.java | 2 +- .../response/GetDiariesOfMonthResponse.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 7d24b93..ffd3851 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -35,7 +35,7 @@ public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createD @GetMapping("/month") public ResponseEntity getDiariesByMonth(@RequestParam String date) { try { - + return diaryService.getDiariesByMonth(date); } catch (Exception e) { return ResponseHandler.create500Error(new ResponseForm(), e); } diff --git a/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java new file mode 100644 index 0000000..96285a3 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java @@ -0,0 +1,22 @@ +package com.bamboo.log.diary.dto.response; + +import lombok.Builder; + +import java.time.LocalDateTime; +import java.util.List; + +@Builder +public record GetDiariesOfMonthResponse( + int diariesCount, + String date, + List diaries +) { + + @Builder + public record DiaryOfMonth( + LocalDateTime createdAt, + String context, + byte[] summaryImage + ) {} + +} From 7e908b2c614f3a6b416a2d9c038f86f7c5874104 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 16:01:36 +0900 Subject: [PATCH 061/114] =?UTF-8?q?Feat:=20String=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=EB=B0=9B=EC=9D=80=20=EB=85=84=EB=8F=84?= =?UTF-8?q?=EC=99=80=20=EC=9B=94=EC=9D=84=20LocalDateTime=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=ED=98=95=EC=9C=BC=EB=A1=9C=20Convert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/diary/dto/ParseYearMonth.java | 17 +++++++++++++++++ .../diary/service/diary/DiaryServiceImpl.java | 15 +++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/com/bamboo/log/diary/dto/ParseYearMonth.java diff --git a/src/main/java/com/bamboo/log/diary/dto/ParseYearMonth.java b/src/main/java/com/bamboo/log/diary/dto/ParseYearMonth.java new file mode 100644 index 0000000..3811fea --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/ParseYearMonth.java @@ -0,0 +1,17 @@ +package com.bamboo.log.diary.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; +import java.time.YearMonth; + +@Getter +@Builder +public class ParseYearMonth { + + private YearMonth parsedYearMonth; + private LocalDateTime startOfMonth; + private LocalDateTime endOfMonth; + +} diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index cbef192..0dc1739 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -1,6 +1,7 @@ package com.bamboo.log.diary.service.diary; import com.bamboo.log.diary.domain.Diary; +import com.bamboo.log.diary.dto.ParseYearMonth; import com.bamboo.log.diary.dto.request.CreateDiaryRequest; import com.bamboo.log.diary.dto.response.CreateDiaryResponse; import com.bamboo.log.diary.repository.DiaryRepository; @@ -21,6 +22,8 @@ import java.io.IOException; import java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; @Service @Transactional @@ -69,4 +72,16 @@ private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime loc return diaryRepository.save(diary); } + private ParseYearMonth getParsedDate(String date) { + YearMonth parsedYearMonth = YearMonth.parse(date, DateTimeFormatter.ofPattern("yyyy-MM")); + LocalDateTime startOfMonth = parsedYearMonth.atDay(1).atStartOfDay(); + LocalDateTime endOfMonth = parsedYearMonth.atEndOfMonth().atTime(23, 59, 59); + + return ParseYearMonth.builder() + .parsedYearMonth(parsedYearMonth) + .startOfMonth(startOfMonth) + .endOfMonth(endOfMonth) + .build(); + } + } From b3c1538321beefd96676b045e0e75fa399868b83 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 16:05:34 +0900 Subject: [PATCH 062/114] =?UTF-8?q?Feat:=20=EC=9C=A0=EC=A0=80=20=EC=8B=9D?= =?UTF-8?q?=EB=B3=84=EC=9E=90=EC=99=80=20=EB=82=A0=EC=A7=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A7=A4=EC=B9=AD=EB=90=98=EB=8A=94=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=ED=95=98=EB=8A=94=20Respository=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ .../bamboo/log/diary/repository/DiaryRepository.java | 7 +++++++ .../log/diary/service/diary/DiaryServiceImpl.java | 10 ++++++++++ 3 files changed, 20 insertions(+) diff --git a/build.gradle b/build.gradle index 53c709f..0e6598d 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,9 @@ dependencies { // okhttp implementation 'com.squareup.okhttp3:okhttp:4.12.0' + + // mapper + implementation 'org.modelmapper:modelmapper:3.0.0' } tasks.named('test') { diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java index 416db42..27411bc 100644 --- a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -1,7 +1,14 @@ package com.bamboo.log.diary.repository; import com.bamboo.log.diary.domain.Diary; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; +import java.util.List; + public interface DiaryRepository extends JpaRepository { + + List findByUserAndCreatedAtBetween(UserEntity user, LocalDateTime start, LocalDateTime end); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 0dc1739..d3bf887 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -6,6 +6,7 @@ import com.bamboo.log.diary.dto.response.CreateDiaryResponse; import com.bamboo.log.diary.repository.DiaryRepository; import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; import org.springframework.security.core.GrantedAuthority; @@ -24,6 +25,7 @@ import java.time.LocalDateTime; import java.time.YearMonth; import java.time.format.DateTimeFormatter; +import java.util.List; @Service @Transactional @@ -31,6 +33,7 @@ public class DiaryServiceImpl implements DiaryService { private final UserContextUtil userContextUtil; + private final UserRepository userRepository; private final DiaryRepository diaryRepository; private final TodaySummaryService todaySummaryService; @@ -57,6 +60,13 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { @Override public ResponseEntity getDiariesByMonth(String date) { + ParseYearMonth parseYearMonth = getParsedDate(date); + UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); + + try { + List diaries = diaryRepository. + } + return null; } From 464294e2f0506bf87fabb052d98ff0089c9a2181 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 16:07:54 +0900 Subject: [PATCH 063/114] =?UTF-8?q?Feat:=20=EB=8B=A4=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EB=A6=AC=20=EC=95=84=EC=9D=B4=EB=94=94=EC=99=80=20=EC=99=B8?= =?UTF-8?q?=EB=9E=98=ED=82=A4=EB=A1=9C=20=EB=A7=A4=ED=95=91=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EB=8A=94=20=ED=95=98=EB=A3=A8=20=EC=9A=94?= =?UTF-8?q?=EC=95=BD=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20Repository=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/diary/repository/TodaySummaryRepository.java | 5 +++++ .../com/bamboo/log/diary/service/diary/DiaryServiceImpl.java | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java index 4093d83..2442924 100644 --- a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java @@ -3,5 +3,10 @@ import com.bamboo.log.diary.domain.TodaySummary; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface TodaySummaryRepository extends JpaRepository { + + Optional findByDiaryId(Long diaryId); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index d3bf887..29b9e85 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -64,7 +64,10 @@ public ResponseEntity getDiariesByMonth(String date) { UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); try { - List diaries = diaryRepository. + List diaries = + diaryRepository.findByUserAndCreatedAtBetween(user, + parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); + } return null; From ccd12d878fd2cdf4454a4671ea594bee1e4f9241 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 16:18:12 +0900 Subject: [PATCH 064/114] =?UTF-8?q?Feat:=20=EC=9B=94=EB=B3=84=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=20API=20Service=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/TodaySummaryRepository.java | 3 ++ .../diary/service/diary/DiaryServiceImpl.java | 32 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java index 2442924..195266f 100644 --- a/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/TodaySummaryRepository.java @@ -3,10 +3,13 @@ import com.bamboo.log.diary.domain.TodaySummary; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface TodaySummaryRepository extends JpaRepository { Optional findByDiaryId(Long diaryId); + List findByDiaryIdIn(List diaryIds); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 29b9e85..671073a 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -4,11 +4,11 @@ import com.bamboo.log.diary.dto.ParseYearMonth; import com.bamboo.log.diary.dto.request.CreateDiaryRequest; import com.bamboo.log.diary.dto.response.CreateDiaryResponse; +import com.bamboo.log.diary.dto.response.GetDiariesOfMonthResponse; +import com.bamboo.log.diary.dto.response.GetDiariesOfMonthResponse.DiaryOfMonth; import com.bamboo.log.diary.repository.DiaryRepository; -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.diary.repository.TodaySummaryRepository; import com.bamboo.log.domain.user.oauth.repository.UserRepository; -import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; -import org.springframework.security.core.GrantedAuthority; import com.bamboo.log.diary.service.summary.TodaySummaryService; import com.bamboo.log.domain.user.jwt.service.UserContextUtil; @@ -26,6 +26,8 @@ import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Service @Transactional @@ -36,6 +38,7 @@ public class DiaryServiceImpl implements DiaryService { private final UserRepository userRepository; private final DiaryRepository diaryRepository; private final TodaySummaryService todaySummaryService; + private final TodaySummaryRepository todaySummaryRepository; @Override public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { @@ -68,9 +71,28 @@ public ResponseEntity getDiariesByMonth(String date) { diaryRepository.findByUserAndCreatedAtBetween(user, parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); + List diaryIds = diaries.stream().map(Diary::getId).toList(); + Map summaryImageMap = todaySummaryRepository.findByDiaryIdIn(diaryIds) + .stream() + .collect(Collectors.toMap(summary -> summary.getDiaryId(), summary -> summary.getImageData())); + + List diaryOfMonthList = diaries.stream() + .map(diary -> new DiaryOfMonth( + diary.getCreatedAt(), + diary.getContext(), + summaryImageMap.getOrDefault(diary.getId(), null) + )) + .toList(); + + return ResponseHandler.create200Response(new ResponseForm(), + GetDiariesOfMonthResponse.builder() + .diariesCount(diaryOfMonthList.size()) + .date(date) + .diaries(diaryOfMonthList) + .build()); + } catch (RuntimeException e) { + return ResponseHandler.create500Error(new ResponseForm(), e); } - - return null; } private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { From 9826965d03fcaa4ffd76f01d318dc3cdaa388854 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 16:20:51 +0900 Subject: [PATCH 065/114] =?UTF-8?q?Refactor:=20API=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=8B=A8=EC=97=90=EC=84=9C=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/diary/api/DiaryController.java | 12 ++---------- .../log/diary/service/diary/DiaryServiceImpl.java | 12 ++++++++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index ffd3851..8760aa9 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -24,20 +24,12 @@ public class DiaryController { @Operation(summary = "랜덤 주제 조회") @PostMapping("/create") public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createDiaryRequest) { - try { - return diaryService.createDiary(createDiaryRequest); - } catch (Exception e) { - return ResponseHandler.create500Error(new ResponseForm(), e); - } + return diaryService.createDiary(createDiaryRequest); } @Operation(summary = "월별 일기 조회") @GetMapping("/month") public ResponseEntity getDiariesByMonth(@RequestParam String date) { - try { - return diaryService.getDiariesByMonth(date); - } catch (Exception e) { - return ResponseHandler.create500Error(new ResponseForm(), e); - } + return diaryService.getDiariesByMonth(date); } } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 671073a..b738c9a 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -53,6 +53,8 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { // todaySummaryImage 데이터베이스에 저장 try { todaySummaryService.saveTodaySummaryImage(todayImage, diary.getId()); + } catch (RuntimeException e) { + return ResponseHandler.create404Error(new ResponseForm(), e); } catch (IOException e) { return ResponseHandler.create500Error(new ResponseForm(), e); } @@ -86,11 +88,13 @@ public ResponseEntity getDiariesByMonth(String date) { return ResponseHandler.create200Response(new ResponseForm(), GetDiariesOfMonthResponse.builder() - .diariesCount(diaryOfMonthList.size()) - .date(date) - .diaries(diaryOfMonthList) - .build()); + .diariesCount(diaryOfMonthList.size()) + .date(date) + .diaries(diaryOfMonthList) + .build()); } catch (RuntimeException e) { + return ResponseHandler.create404Error(new ResponseForm(), e); + } catch (Exception e) { return ResponseHandler.create500Error(new ResponseForm(), e); } } From b8a3c630dd16e2d59f2fba4496d39fbefde48049 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 16:22:22 +0900 Subject: [PATCH 066/114] =?UTF-8?q?Refactor:=20API=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=8B=A8=EC=97=90=EC=84=9C=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/diary/api/DiaryController.java | 6 +----- .../bamboo/log/diary/service/diary/DiaryServiceImpl.java | 5 ++++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 776728f..bdde0d2 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -34,10 +34,6 @@ public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryReq @Operation(summary = "날짜별 일기 조회") @GetMapping("/date") public ResponseEntity getDiaryByDate(@RequestParam LocalDateTime date) { - try { - return diaryService.getDiaryByDate(date); - } catch (Exception e) { - return ResponseHandler.create500Error(new ResponseForm(), e); - } + return diaryService.getDiaryByDate(date); } } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 1a546a7..66a2fe6 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -75,9 +75,12 @@ public ResponseEntity getDiaryByDate(LocalDateTime date) { .diaryDescription(diaryByDate.getContext()) .summaryImage(summaryImage.get().getImageData())); - } catch (Exception e) { + } catch (RuntimeException e) { return ResponseHandler.create404Error(new ResponseForm(), new IllegalArgumentException("해당 날짜에 작성된 일기가 없습니다.")); } + catch (Exception e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } } private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { From 8807986333d4f635c160d7887f6d39cf74ed8aab Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 22:00:49 +0900 Subject: [PATCH 067/114] =?UTF-8?q?Fix:=20Merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20in=20build.gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0e6598d..b5e78e7 100644 --- a/build.gradle +++ b/build.gradle @@ -47,8 +47,11 @@ dependencies { // okhttp implementation 'com.squareup.okhttp3:okhttp:4.12.0' - // mapper + // mapper (ModelMapper) implementation 'org.modelmapper:modelmapper:3.0.0' + + // dotenv (환경변수 관리) + implementation 'io.github.cdimascio:java-dotenv:5.2.2' } tasks.named('test') { From f1f943cce745718f03448a25bb78924fe0137a06 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 22:01:57 +0900 Subject: [PATCH 068/114] =?UTF-8?q?Fix:=20Merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20in=20DiaryController?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/diary/api/DiaryController.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 8760aa9..6268ae9 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -11,7 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.List; +import java.time.LocalDateTime; @RestController @RequiredArgsConstructor @@ -21,10 +21,14 @@ public class DiaryController { private final DiaryService diaryService; - @Operation(summary = "랜덤 주제 조회") + @Operation(summary = "일기 생성") @PostMapping("/create") - public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createDiaryRequest) { - return diaryService.createDiary(createDiaryRequest); + public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryRequest) { + try { + return diaryService.createDiary(createDiaryRequest); + } catch (Exception e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } } @Operation(summary = "월별 일기 조회") @@ -32,4 +36,10 @@ public ResponseEntity lookupRandomTopics(@RequestBody CreateDiaryRequest createD public ResponseEntity getDiariesByMonth(@RequestParam String date) { return diaryService.getDiariesByMonth(date); } + + @Operation(summary = "날짜별 일기 조회") + @GetMapping("/date") + public ResponseEntity getDiaryByDate(@RequestParam LocalDateTime date) { + return diaryService.getDiaryByDate(date); + } } From eba68bf43956d9d463bf012de2817db5be27db62 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 22:04:05 +0900 Subject: [PATCH 069/114] =?UTF-8?q?Fix:=20Merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20in=20DiaryRepository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bamboo/log/diary/repository/DiaryRepository.java | 2 +- .../com/bamboo/log/diary/service/diary/DiaryServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java index 27411bc..922fcfd 100644 --- a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -9,6 +9,6 @@ public interface DiaryRepository extends JpaRepository { - List findByUserAndCreatedAtBetween(UserEntity user, LocalDateTime start, LocalDateTime end); + List findByUserAAndCreatedAtBetween(UserEntity user, LocalDateTime start, LocalDateTime end); } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index b738c9a..cce47ec 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -70,7 +70,7 @@ public ResponseEntity getDiariesByMonth(String date) { try { List diaries = - diaryRepository.findByUserAndCreatedAtBetween(user, + diaryRepository.findByUserAAndCreatedAtBetween(user, parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); List diaryIds = diaries.stream().map(Diary::getId).toList(); From 0af3cc295bd07534f1f3bee9597f36050f9ee24c Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 22:07:29 +0900 Subject: [PATCH 070/114] =?UTF-8?q?Fix:=20Merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20in=20DiaryService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/diary/service/diary/DiaryService.java | 3 ++ .../diary/service/diary/DiaryServiceImpl.java | 47 +++++++++++++------ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java index c6d3ba7..85392f0 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java @@ -3,10 +3,13 @@ import com.bamboo.log.diary.dto.request.CreateDiaryRequest; import org.springframework.http.ResponseEntity; +import java.time.LocalDateTime; + public interface DiaryService { ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest); ResponseEntity getDiariesByMonth(String date); + ResponseEntity getDiaryByDate(LocalDateTime date); } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index cce47ec..3b302bc 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -1,6 +1,7 @@ package com.bamboo.log.diary.service.diary; import com.bamboo.log.diary.domain.Diary; +import com.bamboo.log.diary.domain.TodaySummary; import com.bamboo.log.diary.dto.ParseYearMonth; import com.bamboo.log.diary.dto.request.CreateDiaryRequest; import com.bamboo.log.diary.dto.response.CreateDiaryResponse; @@ -8,12 +9,10 @@ import com.bamboo.log.diary.dto.response.GetDiariesOfMonthResponse.DiaryOfMonth; import com.bamboo.log.diary.repository.DiaryRepository; import com.bamboo.log.diary.repository.TodaySummaryRepository; -import com.bamboo.log.domain.user.oauth.repository.UserRepository; - import com.bamboo.log.diary.service.summary.TodaySummaryService; import com.bamboo.log.domain.user.jwt.service.UserContextUtil; -import com.bamboo.log.domain.user.oauth.dto.KakaoResponse; import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; import com.bamboo.log.utils.ResponseHandler; import com.bamboo.log.utils.dto.ResponseForm; import lombok.RequiredArgsConstructor; @@ -27,6 +26,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -42,15 +42,10 @@ public class DiaryServiceImpl implements DiaryService { @Override public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { - // 해당 시점의 localDateTime 저장 LocalDateTime localDateTime = LocalDateTime.now(); - - // 데이터베이스에 저장 Diary diary = saveDiary(createDiaryRequest, localDateTime); - byte[] todayImage = todaySummaryService.createTodaySummaryImage(createDiaryRequest.diaryDetail()); - // todaySummaryImage 데이터베이스에 저장 try { todaySummaryService.saveTodaySummaryImage(todayImage, diary.getId()); } catch (RuntimeException e) { @@ -59,7 +54,6 @@ public ResponseEntity createDiary(CreateDiaryRequest createDiaryRequest) { return ResponseHandler.create500Error(new ResponseForm(), e); } - // 201 code 반환 return ResponseHandler.create201Response(new ResponseForm(), new CreateDiaryResponse(localDateTime)); } @@ -69,14 +63,13 @@ public ResponseEntity getDiariesByMonth(String date) { UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); try { - List diaries = - diaryRepository.findByUserAAndCreatedAtBetween(user, - parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); + List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, + parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); List diaryIds = diaries.stream().map(Diary::getId).toList(); Map summaryImageMap = todaySummaryRepository.findByDiaryIdIn(diaryIds) .stream() - .collect(Collectors.toMap(summary -> summary.getDiaryId(), summary -> summary.getImageData())); + .collect(Collectors.toMap(TodaySummary::getDiaryId, TodaySummary::getImageData)); List diaryOfMonthList = diaries.stream() .map(diary -> new DiaryOfMonth( @@ -99,6 +92,33 @@ public ResponseEntity getDiariesByMonth(String date) { } } + @Override + public ResponseEntity getDiaryByDate(LocalDateTime date) { + UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); + + try { + List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, date, date); + if (diaries.isEmpty()) { + throw new RuntimeException("해당 날짜에 작성된 일기가 없습니다."); + } + + Diary diaryByDate = diaries.get(0); + Optional summaryImage = todaySummaryRepository.findByDiaryId(diaryByDate.getId()); + + return ResponseHandler.create200Response(new ResponseForm(), + CheckDiaryResponse.builder() + .date(diaryByDate.getCreatedAt()) + .diaryDescription(diaryByDate.getContext()) + .summaryImage(summaryImage.map(TodaySummary::getImageData).orElse(null)) + .build()); + + } catch (RuntimeException e) { + return ResponseHandler.create404Error(new ResponseForm(), new IllegalArgumentException("해당 날짜에 작성된 일기가 없습니다.")); + } catch (Exception e) { + return ResponseHandler.create500Error(new ResponseForm(), e); + } + } + private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { UserEntity user = userContextUtil.getUserEntity(); @@ -122,5 +142,4 @@ private ParseYearMonth getParsedDate(String date) { .endOfMonth(endOfMonth) .build(); } - } From a2d810b387cbf9b72676214eb22e214f1cab74f5 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 22:10:00 +0900 Subject: [PATCH 071/114] =?UTF-8?q?Fix:=20Merge=20Conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20in=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/diary/dto/response/CheckDiaryResponse.java | 13 +++++++++++++ .../log/diary/service/diary/DiaryServiceImpl.java | 1 + 2 files changed, 14 insertions(+) create mode 100644 src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java new file mode 100644 index 0000000..ad365f7 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java @@ -0,0 +1,13 @@ +package com.bamboo.log.diary.dto.response; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Builder; + +import java.time.LocalDateTime; + +@Builder +public record CheckDiaryResponse(@NotEmpty(message = "Date shouldn't be empty") LocalDateTime date, + @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDescription, + @NotEmpty(message = "Summary Image shouldn't be empty") byte[] summaryImage +) { +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 3b302bc..462588a 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -4,6 +4,7 @@ import com.bamboo.log.diary.domain.TodaySummary; import com.bamboo.log.diary.dto.ParseYearMonth; import com.bamboo.log.diary.dto.request.CreateDiaryRequest; +import com.bamboo.log.diary.dto.response.CheckDiaryResponse; import com.bamboo.log.diary.dto.response.CreateDiaryResponse; import com.bamboo.log.diary.dto.response.GetDiariesOfMonthResponse; import com.bamboo.log.diary.dto.response.GetDiariesOfMonthResponse.DiaryOfMonth; From 8a3b4c0c14206fa6458e64673aac8c94d516ee86 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Wed, 26 Feb 2025 22:16:26 +0900 Subject: [PATCH 072/114] =?UTF-8?q?Styles:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC,=20=ED=8F=AC=EB=A7=B7=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/diary/api/DiaryController.java | 7 ++----- .../log/diary/repository/DiaryRepository.java | 2 +- .../log/diary/service/diary/DiaryService.java | 1 + .../log/diary/service/diary/DiaryServiceImpl.java | 13 ++++--------- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/api/DiaryController.java b/src/main/java/com/bamboo/log/diary/api/DiaryController.java index 6268ae9..747dea9 100644 --- a/src/main/java/com/bamboo/log/diary/api/DiaryController.java +++ b/src/main/java/com/bamboo/log/diary/api/DiaryController.java @@ -24,11 +24,7 @@ public class DiaryController { @Operation(summary = "일기 생성") @PostMapping("/create") public ResponseEntity createDiary(@RequestBody CreateDiaryRequest createDiaryRequest) { - try { - return diaryService.createDiary(createDiaryRequest); - } catch (Exception e) { - return ResponseHandler.create500Error(new ResponseForm(), e); - } + return diaryService.createDiary(createDiaryRequest); } @Operation(summary = "월별 일기 조회") @@ -42,4 +38,5 @@ public ResponseEntity getDiariesByMonth(@RequestParam String date) { public ResponseEntity getDiaryByDate(@RequestParam LocalDateTime date) { return diaryService.getDiaryByDate(date); } + } diff --git a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java index 922fcfd..27411bc 100644 --- a/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java +++ b/src/main/java/com/bamboo/log/diary/repository/DiaryRepository.java @@ -9,6 +9,6 @@ public interface DiaryRepository extends JpaRepository { - List findByUserAAndCreatedAtBetween(UserEntity user, LocalDateTime start, LocalDateTime end); + List findByUserAndCreatedAtBetween(UserEntity user, LocalDateTime start, LocalDateTime end); } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java index 85392f0..76342cc 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryService.java @@ -12,4 +12,5 @@ public interface DiaryService { ResponseEntity getDiariesByMonth(String date); ResponseEntity getDiaryByDate(LocalDateTime date); + } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index b00bb68..315ad07 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -11,15 +11,11 @@ import com.bamboo.log.diary.repository.DiaryRepository; import com.bamboo.log.diary.repository.TodaySummaryRepository; -import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; import com.bamboo.log.domain.user.oauth.repository.UserRepository; -import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; -import org.springframework.security.core.GrantedAuthority; import com.bamboo.log.diary.service.summary.TodaySummaryService; import com.bamboo.log.domain.user.jwt.service.UserContextUtil; import com.bamboo.log.domain.user.oauth.entity.UserEntity; -import com.bamboo.log.domain.user.oauth.repository.UserRepository; import com.bamboo.log.utils.ResponseHandler; import com.bamboo.log.utils.dto.ResponseForm; import lombok.RequiredArgsConstructor; @@ -35,8 +31,7 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import java.util.List; -import java.util.Optional; + @Service @Transactional @RequiredArgsConstructor @@ -71,7 +66,7 @@ public ResponseEntity getDiariesByMonth(String date) { UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); try { - List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, + List diaries = diaryRepository.findByUserAndCreatedAtBetween(user, parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); List diaryIds = diaries.stream().map(Diary::getId).toList(); @@ -105,7 +100,7 @@ public ResponseEntity getDiaryByDate(LocalDateTime date) { UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); try { - List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, date, date); + List diaries = diaryRepository.findByUserAndCreatedAtBetween(user, date, date); if (diaries.isEmpty()) { throw new RuntimeException("해당 날짜에 작성된 일기가 없습니다."); @@ -118,7 +113,6 @@ public ResponseEntity getDiaryByDate(LocalDateTime date) { CheckDiaryResponse.builder() .date(diaryByDate.getCreatedAt()) .diaryDescription(diaryByDate.getContext()) - .summaryImage(summaryImage.map(TodaySummary::getImageData).orElse(null)) .build()); @@ -152,4 +146,5 @@ private ParseYearMonth getParsedDate(String date) { .endOfMonth(endOfMonth) .build(); } + } From a0e1391528bdb33217c339c18b3434ef027f26cc Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:01:36 +0900 Subject: [PATCH 073/114] =?UTF-8?q?Feat:=20=EC=96=BC=EA=B5=B4=20=EC=98=81?= =?UTF-8?q?=EC=97=AD=20=EC=A2=8C=ED=91=9C=20dto=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추후 감정 분석 시 얼굴 영역 crop 시 사용 --- src/main/java/com/bamboo/log/emotion/dto/BoundingBox.java | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/main/java/com/bamboo/log/emotion/dto/BoundingBox.java diff --git a/src/main/java/com/bamboo/log/emotion/dto/BoundingBox.java b/src/main/java/com/bamboo/log/emotion/dto/BoundingBox.java new file mode 100644 index 0000000..7fcdb2a --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/dto/BoundingBox.java @@ -0,0 +1,3 @@ +package com.bamboo.log.emotion.dto; + +public record BoundingBox(double x1, double y1, double x2, double y2) {} From 8ba6864561715d67efaa006b7b45161356919b08 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:02:55 +0900 Subject: [PATCH 074/114] =?UTF-8?q?Fix:=20=ED=91=9C=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=9C=84=ED=95=9C=20response=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 좌표 값 고려하여 List으로 변경 --- .../com/bamboo/log/emotion/dto/FaceDetectionResponse.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java index f9a2b09..16551a0 100644 --- a/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java +++ b/src/main/java/com/bamboo/log/emotion/dto/FaceDetectionResponse.java @@ -2,12 +2,14 @@ import org.springframework.http.HttpStatus; +import java.util.List; + public record FaceDetectionResponse( int statusCode, String statusMessage, - String message + List faceBox ) { - public FaceDetectionResponse(HttpStatus httpStatus, String message) { - this(httpStatus.value(), httpStatus.name(), message); + public FaceDetectionResponse(HttpStatus httpStatus, List faceBox) { + this(httpStatus.value(), httpStatus.name(), faceBox); } } \ No newline at end of file From 62ffc96d23c47a7e5cc3178da38698df17e66bfd Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:03:59 +0900 Subject: [PATCH 075/114] =?UTF-8?q?Fix:=20=EC=88=98=EC=A0=95=EB=90=9C=20re?= =?UTF-8?q?sponse=20=EB=A7=9E=EA=B2=8C=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HttpStatus와 함께 얼굴 영역 좌표 값 리턴 --- .../impl/FaceDetectionServiceImpl.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java index 47e6d8a..0175ac0 100644 --- a/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java +++ b/src/main/java/com/bamboo/log/emotion/service/impl/FaceDetectionServiceImpl.java @@ -1,5 +1,6 @@ package com.bamboo.log.emotion.service.impl; +import com.bamboo.log.emotion.dto.BoundingBox; import com.bamboo.log.emotion.dto.FaceDetectionResponse; import com.bamboo.log.emotion.service.FaceDetectionService; import com.fasterxml.jackson.databind.JsonNode; @@ -13,6 +14,8 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; @Slf4j @Service @@ -31,7 +34,7 @@ public class FaceDetectionServiceImpl implements FaceDetectionService { public FaceDetectionResponse detectFace(MultipartFile image) { if (image == null || image.isEmpty()) { log.error("파일이 전달되지 않음."); - return new FaceDetectionResponse(HttpStatus.BAD_REQUEST, "파일이 전달되지 않았습니다."); + return new FaceDetectionResponse(HttpStatus.BAD_REQUEST, List.of()); } log.info("파일 이름: {}", image.getOriginalFilename()); log.info("파일 크기: {} bytes", image.getSize()); @@ -61,7 +64,7 @@ public FaceDetectionResponse detectFace(MultipartFile image) { log.info("API 응답 메시지: {}", response.message()); if (!response.isSuccessful()) { - return new FaceDetectionResponse(HttpStatus.valueOf(response.code()), "API 요청 실패: " + response.message()); + return new FaceDetectionResponse(HttpStatus.valueOf(response.code()), List.of()); } String responseBody = response.body().string(); @@ -72,18 +75,27 @@ public FaceDetectionResponse detectFace(MultipartFile image) { JsonNode results = jsonNode.get("results"); // 얼굴 인식 여부 확인 - if (results != null && results.isArray() && results.size() > 0) { + List boxList = new ArrayList<>(); + if (results != null && results.isArray()) { for (JsonNode result : results) { if ("face".equals(result.get("name").asText())) { - return new FaceDetectionResponse(HttpStatus.OK, responseBody); + JsonNode boxNode = result.get("box"); + if (boxNode != null) { + boxList.add(new BoundingBox( + boxNode.get("x1").asDouble(), + boxNode.get("y1").asDouble(), + boxNode.get("x2").asDouble(), + boxNode.get("y2").asDouble() + )); + } } } } - return new FaceDetectionResponse(HttpStatus.NOT_FOUND, "얼굴이 인식되지 않았습니다."); + return new FaceDetectionResponse(HttpStatus.OK, boxList); } } catch (IOException e) { log.error("API 요청 중 예외 발생", e); - return new FaceDetectionResponse(HttpStatus.INTERNAL_SERVER_ERROR, "API 요청 중 오류 발생: " + e.getMessage()); + return new FaceDetectionResponse(HttpStatus.INTERNAL_SERVER_ERROR, List.of()); } } From 06f9be4b765bffff51aa9fd232db07ee110fc83e Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:05:32 +0900 Subject: [PATCH 076/114] =?UTF-8?q?Feat:=20EmotionType=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서버 오류 시, NONE 리턴 --- .../com/bamboo/log/emotion/domain/EmotionType.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/bamboo/log/emotion/domain/EmotionType.java diff --git a/src/main/java/com/bamboo/log/emotion/domain/EmotionType.java b/src/main/java/com/bamboo/log/emotion/domain/EmotionType.java new file mode 100644 index 0000000..8fbbe0e --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/domain/EmotionType.java @@ -0,0 +1,10 @@ +package com.bamboo.log.emotion.domain; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum EmotionType { + ANGRY, HAPPY, NEUTRAL, SAD, NONE ; +} \ No newline at end of file From d1951ac636a7ebaee97f3a716fa6c1599ec4c558 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:07:03 +0900 Subject: [PATCH 077/114] =?UTF-8?q?Settings:=20FastAPI=20URL=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 17527e6..3ab1365 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -39,4 +39,7 @@ elice: url: face: ${FACE_URL} img: ${IMG_URL} - chat: ${CHAT_URL} \ No newline at end of file + chat: ${CHAT_URL} +emotion: + api: + url: ${EMOTION_URL} \ No newline at end of file From 6ee757074daa7fcc319a86bd000858fb7b82fefc Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:07:26 +0900 Subject: [PATCH 078/114] =?UTF-8?q?Feat:=20=EA=B0=90=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20req/res=20DTO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bamboo/log/emotion/dto/EmotionAnalysisResponse.java | 6 ++++++ .../bamboo/log/emotion/dto/req/EmotionAnalysisRequest.java | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 src/main/java/com/bamboo/log/emotion/dto/EmotionAnalysisResponse.java create mode 100644 src/main/java/com/bamboo/log/emotion/dto/req/EmotionAnalysisRequest.java diff --git a/src/main/java/com/bamboo/log/emotion/dto/EmotionAnalysisResponse.java b/src/main/java/com/bamboo/log/emotion/dto/EmotionAnalysisResponse.java new file mode 100644 index 0000000..cf925a5 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/dto/EmotionAnalysisResponse.java @@ -0,0 +1,6 @@ +package com.bamboo.log.emotion.dto; + +import com.bamboo.log.emotion.domain.EmotionType; +import org.springframework.http.HttpStatus; + +public record EmotionAnalysisResponse (HttpStatus status, EmotionType emotion) {} diff --git a/src/main/java/com/bamboo/log/emotion/dto/req/EmotionAnalysisRequest.java b/src/main/java/com/bamboo/log/emotion/dto/req/EmotionAnalysisRequest.java new file mode 100644 index 0000000..7961010 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/dto/req/EmotionAnalysisRequest.java @@ -0,0 +1,6 @@ +package com.bamboo.log.emotion.dto.req; + +import com.bamboo.log.emotion.dto.BoundingBox; +import org.springframework.web.multipart.MultipartFile; + +public record EmotionAnalysisRequest(MultipartFile image, BoundingBox faceBox) {} From e1205d13667806d0e8f47b134f88aea59302d2bb Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:08:40 +0900 Subject: [PATCH 079/114] =?UTF-8?q?Feat:=20=EA=B0=90=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D(=EC=9A=94=EC=B2=AD)=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FASTAPI 요청을 통해 감정 분석 - HttpStatus code 와 enum 타입의 감정 결과 return --- .../service/EmotionAnalysisService.java | 8 ++ .../impl/EmotionAnalysisServiceImpl.java | 84 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/main/java/com/bamboo/log/emotion/service/EmotionAnalysisService.java create mode 100644 src/main/java/com/bamboo/log/emotion/service/impl/EmotionAnalysisServiceImpl.java diff --git a/src/main/java/com/bamboo/log/emotion/service/EmotionAnalysisService.java b/src/main/java/com/bamboo/log/emotion/service/EmotionAnalysisService.java new file mode 100644 index 0000000..7e4300d --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/service/EmotionAnalysisService.java @@ -0,0 +1,8 @@ +package com.bamboo.log.emotion.service; + +import com.bamboo.log.emotion.dto.EmotionAnalysisResponse; +import com.bamboo.log.emotion.dto.req.EmotionAnalysisRequest; + +public interface EmotionAnalysisService { + EmotionAnalysisResponse analyzeEmotion(EmotionAnalysisRequest request); +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/emotion/service/impl/EmotionAnalysisServiceImpl.java b/src/main/java/com/bamboo/log/emotion/service/impl/EmotionAnalysisServiceImpl.java new file mode 100644 index 0000000..c707b8e --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/service/impl/EmotionAnalysisServiceImpl.java @@ -0,0 +1,84 @@ +package com.bamboo.log.emotion.service.impl; + +import com.bamboo.log.emotion.domain.EmotionType; +import com.bamboo.log.emotion.dto.BoundingBox; +import com.bamboo.log.emotion.dto.EmotionAnalysisResponse; +import com.bamboo.log.emotion.dto.req.EmotionAnalysisRequest; +import com.bamboo.log.emotion.service.EmotionAnalysisService; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@Service +@RequiredArgsConstructor +public class EmotionAnalysisServiceImpl implements EmotionAnalysisService { + @Value("${emotion.api.url}") + private String emotionApiUrl; + + private final OkHttpClient client = new OkHttpClient(); + + @Override + public EmotionAnalysisResponse analyzeEmotion(EmotionAnalysisRequest request) { + MultipartFile image = request.image(); + BoundingBox faceBox = request.faceBox(); + + log.info("Received EmotionAnalysisRequest: {}", request); + log.info("Received BoundingBox: {}", (faceBox != null) ? faceBox.toString() : "NULL"); + + if (faceBox == null) { + log.warn("얼굴 영역이 감지되지 않음. 감정 분석 불가능"); + return new EmotionAnalysisResponse(HttpStatus.OK, EmotionType.NONE); + } + + EmotionAnalysisResponse response = callFastAPI(image, request); + + log.info("Received response: {}", response); + return response; + } + + private EmotionAnalysisResponse callFastAPI(MultipartFile image, EmotionAnalysisRequest request) { + try { + RequestBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("image", image.getOriginalFilename(), + RequestBody.create(image.getBytes(), MediaType.parse(image.getContentType()))) + .addFormDataPart("x1", String.valueOf(request.faceBox().x1())) + .addFormDataPart("y1", String.valueOf(request.faceBox().y1())) + .addFormDataPart("x2", String.valueOf(request.faceBox().x2())) + .addFormDataPart("y2", String.valueOf(request.faceBox().y2())) + .build(); + + Request fastApiRequest = new Request.Builder() + .url(emotionApiUrl) + .post(requestBody) + .addHeader("accept", "application/json") + .build(); + + try (Response response = client.newCall(fastApiRequest).execute()) { + if (!response.isSuccessful()) { + return new EmotionAnalysisResponse(HttpStatus.valueOf(response.code()), EmotionType.NONE); + } + + String responseBody = response.body().string(); + JsonNode jsonNode = new ObjectMapper().readTree(responseBody); + String emotionString = jsonNode.get(0).get("emotion").asText(); + EmotionType emotionType = EmotionType.valueOf(emotionString.toUpperCase()); + + log.info("Received response about api: {}", response); + return new EmotionAnalysisResponse(HttpStatus.OK, emotionType); + } + } catch (IOException e) { + log.error("FASTAPI 호출 중 오류 발생", e); + return new EmotionAnalysisResponse(HttpStatus.INTERNAL_SERVER_ERROR, EmotionType.NONE); + } + } +} \ No newline at end of file From 2bab740ffa958df0fbcbd6a6b3a8600dbf7c9390 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:12:09 +0900 Subject: [PATCH 080/114] =?UTF-8?q?Refactor:=20=EC=96=BC=EA=B5=B4=20?= =?UTF-8?q?=EC=9D=B8=EC=8B=9D=20API=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 감정 분석 API 구분을 위한 api 변경 --- .../bamboo/log/emotion/controller/FaceDetectController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java index fee5189..62fd28f 100644 --- a/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java +++ b/src/main/java/com/bamboo/log/emotion/controller/FaceDetectController.java @@ -15,14 +15,14 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/api/emotion") -@Tag(name = "Face Detection", description = "얼굴 인식 관련 API") +@RequestMapping("/api/face") +@Tag(name = "Face Detection", description = "얼굴 인식 API") public class FaceDetectController { private final FaceDetectionService faceDetectionService; @PostMapping("/detect") @Operation( - summary = "얼굴 인식 API", + summary = "얼굴 인식", description = "이미지를 업로드하면 얼굴 인식 여부를 반환합니다." ) public ResponseEntity detectFace(@RequestParam("image") MultipartFile image) { From c05ed162872bdfb09212000b33eb1dde11f0c27e Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Wed, 26 Feb 2025 23:16:51 +0900 Subject: [PATCH 081/114] =?UTF-8?q?Feat:=20=EA=B0=90=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../emotion/controller/EmotionController.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/com/bamboo/log/emotion/controller/EmotionController.java diff --git a/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java b/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java new file mode 100644 index 0000000..b801e59 --- /dev/null +++ b/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java @@ -0,0 +1,50 @@ +package com.bamboo.log.emotion.controller; + +import com.bamboo.log.emotion.dto.BoundingBox; +import com.bamboo.log.emotion.dto.EmotionAnalysisResponse; +import com.bamboo.log.emotion.dto.req.EmotionAnalysisRequest; +import com.bamboo.log.emotion.service.EmotionAnalysisService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/api/emotion") +@RequiredArgsConstructor +@Tag(name = "Emotion Analysis", description = "얼굴 감정(표정)분석 API") +public class EmotionController { + + private final EmotionAnalysisService emotionAnalysisService; + + @PostMapping(value = "/result", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation( + summary = "감정 분석", + description = "반환받은 얼굴 영역 좌표와 이미지를 분석하여 감정을 반환합니다." + ) + public ResponseEntity analyzeEmotion( + + @RequestPart("image") + @Parameter(description = "감정 분석할 얼굴 이미지", required = true) + MultipartFile image, + + @RequestPart("faceBox") + @Parameter(description = "얼굴 영역 좌표 (JSON 형식)", required = true, example = "{\"x1\": 12.34, \"y1\": 56.78, \"x2\": 123.456, \"y2\": 789.87}") + BoundingBox faceBox) { + + EmotionAnalysisRequest request = new EmotionAnalysisRequest(image, faceBox); + EmotionAnalysisResponse response = emotionAnalysisService.analyzeEmotion(request); + + HttpStatus status = HttpStatus.resolve(response.status().value()); + if (status == null) { + status = HttpStatus.INTERNAL_SERVER_ERROR; + } + + return ResponseEntity.status(status).body(response); + } +} From 84c8ea8dd51f7c61d51634fa59412c3923f58b39 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Thu, 27 Feb 2025 22:00:34 +0900 Subject: [PATCH 082/114] =?UTF-8?q?FEAUTE=20:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=81=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/common/config/CorsMvcConfig.java | 11 +++-- .../log/common/config/SecurityConfig.java | 3 +- .../oauth/service/CustomFailureHandler.java | 2 +- .../oauth/service/CustomSuccessHandler.java | 2 +- src/main/resources/application.txt | 44 ++++++++++++++++++ src/main/resources/application.yml | 46 ++++++++++--------- 6 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 src/main/resources/application.txt diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index 38df386..311084f 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -9,10 +9,11 @@ public class CorsMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry corsRegistry) { - corsRegistry.addMapping("/**") - .exposedHeaders("Set-Cookie") - //프론트 url 넣기 - .allowedOrigins("http://localhost:3000"); + .allowedOrigins("*") // 모든 Origin 허용 + .allowedMethods("*") // 모든 HTTP 메서드 허용 + .allowedHeaders("*") // 모든 헤더 허용 + .allowCredentials(true) // 쿠키 허용 + .exposedHeaders("Set-Cookie"); } -} \ No newline at end of file +} diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index d40a6fe..b899dcc 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -52,8 +52,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/refresh").permitAll() .requestMatchers("/logout").hasAnyRole("USER") .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() - .requestMatchers("/api/images/**").hasAnyRole("USER") - .anyRequest().authenticated()); + .anyRequest().hasAnyRole("USER")); http.sessionManagement((session) -> session diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java index da4890c..492f364 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomFailureHandler.java @@ -14,6 +14,6 @@ public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - response.sendRedirect("http://localhost:3000"); + response.sendRedirect("http://localhost:3000/login"); } } \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index f82af92..4dbab0a 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -42,7 +42,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo addRefreshEntity(name, username, refreshToken, 1209600000L); response.addCookie(createCookie("refresh", refreshToken)); response.addCookie(UnScretCreateCookie("access", accessToken)); - response.sendRedirect("http://localhost:3000/"); + response.sendRedirect("http://localhost:3000/welcome"); } private Cookie createCookie(String key, String value) { diff --git a/src/main/resources/application.txt b/src/main/resources/application.txt new file mode 100644 index 0000000..4f0ed06 --- /dev/null +++ b/src/main/resources/application.txt @@ -0,0 +1,44 @@ + +spring: + datasource: + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + show-sql: true + open-in-view: false + hibernate: + ddl-auto: update + properties: + hibernate.dialect: org.hibernate.dialect.MySQL8Dialect + security: + oauth2: + client: + registration: + kakao: + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + provider: + kakao: + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + jwt: + secret: ${JWT_SECRET} +elice: + api: + token: ${API_TOKEN} + url: + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} +emotion: + api: + url: ${EMOTION_URL} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3ab1365..5b9161e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,10 +1,8 @@ -# application.yml - spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} + url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + username: root + password: heodongun0922 driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -18,28 +16,32 @@ spring: client: registration: kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: + - profile_nickname + - account_email provider: kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: - secret: ${JWT_SECRET} + secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + elice: api: - token: ${API_TOKEN} + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} + face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict + img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + chat: null + emotion: api: - url: ${EMOTION_URL} \ No newline at end of file + url: null From 6ee84ae9ead8b5271829acfc68b44e8b23469bb3 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Thu, 27 Feb 2025 23:51:05 +0900 Subject: [PATCH 083/114] =?UTF-8?q?Fix:=20JWT=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20Id=20?= =?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 정보 조회 시, 원활함을 위해 Id값을 추가 --- .../com/bamboo/log/domain/user/jwt/service/JWTFilter.java | 6 +++--- .../com/bamboo/log/domain/user/jwt/service/JWTUtil.java | 7 ++++++- .../bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java | 2 ++ .../java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java | 1 + .../domain/user/oauth/service/CustomOAuth2UserService.java | 2 ++ .../domain/user/oauth/service/CustomSuccessHandler.java | 5 +++-- .../user/refresh/service/ProcessTokenReissueImpl.java | 6 +++--- 7 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java index 1698608..9f7e2c9 100644 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java @@ -5,7 +5,6 @@ import io.jsonwebtoken.ExpiredJwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -58,12 +57,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } - - + Long userId = jwtUtil.getId(accessToken); String username = jwtUtil.getUsername(accessToken); String role = jwtUtil.getRole(accessToken); String name=jwtUtil.getName(accessToken); + UserDTO userDTO = UserDTO.builder() + .id(userId) .name(name) .username(username) .role(role) diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java index 88ee472..c2dd261 100644 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTUtil.java @@ -43,8 +43,13 @@ public String getCategory(String token) { public String getName(String token){ return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("name", String.class); } - public String createJwt(String category,String name,String username, String role, Long expiredMs) { + public Long getId(String token){ + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("id", Long.class); + } + + public String createJwt(Long id, String category,String name,String username, String role, Long expiredMs) { return Jwts.builder() + .claim("id", id) .claim("category",category) .claim("username", username) .claim("name",name) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java index bb7502a..29e2adb 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/CustomOAuth2User.java @@ -46,4 +46,6 @@ public String getUsername() { return userDTO.getUsername(); } + public Long getId() { return userDTO.getId(); } + } \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java index efda413..54f3499 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/dto/UserDTO.java @@ -8,6 +8,7 @@ @Builder @Getter public class UserDTO { + private Long id; private String name; private String username; private String role; diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java index b7f7c02..93fcb76 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomOAuth2UserService.java @@ -49,6 +49,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic userRepository.save(userEntity); UserDTO userDTO = UserDTO.builder() + .id(userEntity.getId()) .username(username) .name(oAuth2Response.getName()) .role("ROLE_USER") @@ -57,6 +58,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic return new CustomOAuth2User(userDTO); } else { UserDTO userDTO = UserDTO.builder() + .id(existData.getId()) .username(existData.getUsername()) .name(existData.getName()) .role(existData.getRole()) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index f82af92..c91ce65 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -30,6 +30,7 @@ public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); + Long userId = customUserDetails.getId(); String name=customUserDetails.getName(); String username = customUserDetails.getUsername(); Collection authorities = authentication.getAuthorities(); @@ -37,8 +38,8 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo GrantedAuthority auth = iterator.next(); String role = auth.getAuthority(); - String refreshToken = jwtUtil.createJwt("refresh",name,username, role, 1800000L); - String accessToken = jwtUtil.createJwt("access",name,username, role, 1209600000L); + String refreshToken = jwtUtil.createJwt(userId,"refresh", name, username, role, 1800000L); + String accessToken = jwtUtil.createJwt(userId,"access", name, username, role, 1209600000L); addRefreshEntity(name, username, refreshToken, 1209600000L); response.addCookie(createCookie("refresh", refreshToken)); response.addCookie(UnScretCreateCookie("access", accessToken)); diff --git a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java index 0e123ae..477002c 100644 --- a/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java +++ b/src/main/java/com/bamboo/log/domain/user/refresh/service/ProcessTokenReissueImpl.java @@ -57,13 +57,13 @@ public ResponseEntity reissue(HttpServletRequest request, HttpServletResponse log.error("리프레시 토큰이 존재하지 않습니다. token: {}", refresh); return new ResponseEntity<>("invalid refresh token", HttpStatus.BAD_REQUEST); } - + Long userId = jwtUtil.getId(refresh); String name = jwtUtil.getName(refresh); String username = jwtUtil.getUsername(refresh); String role = jwtUtil.getRole(refresh); - String newAccess = jwtUtil.createJwt("access", name, username , role, 1800000L); - String newRefresh = jwtUtil.createJwt("refresh", name, username, role, 1209600000L); + String newAccess = jwtUtil.createJwt(userId,"access", name, username , role, 1800000L); + String newRefresh = jwtUtil.createJwt(userId,"refresh", name, username, role, 1209600000L); refreshRepository.deleteByToken(refresh); addRefreshEntity(name,username,role,1209600000L); From 751e2e5426ad7031b25f0ae422f9176b92b330c0 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Thu, 27 Feb 2025 23:52:58 +0900 Subject: [PATCH 084/114] =?UTF-8?q?Feat:=20=EC=A3=BD=EC=88=9C=EC=9D=B4=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유저<->죽순이 1:1 연관관계 매핑 --- .../domain/user/oauth/entity/UserEntity.java | 4 +++ .../bamboo/log/juksoon/domain/Juksoon.java | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java index 2a3be77..03b88bc 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/entity/UserEntity.java @@ -1,5 +1,6 @@ package com.bamboo.log.domain.user.oauth.entity; +import com.bamboo.log.juksoon.domain.Juksoon; import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; @@ -24,6 +25,9 @@ public class UserEntity { private String role; private String profile_img_url; + @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private Juksoon juksooni; + @Builder public UserEntity(String username, String name, String email, String role,String profile_img_url) { this.username = username; diff --git a/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java b/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java new file mode 100644 index 0000000..267b79e --- /dev/null +++ b/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java @@ -0,0 +1,25 @@ +package com.bamboo.log.juksoon.domain; + +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "juksooni") +@Builder +public class Juksoon { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "user_id", nullable = false, unique = true) + private UserEntity user; + + public static Juksoon create(UserEntity user) { + return Juksoon.builder().user(user).build(); + } +} From caa13947831a684c16b31d90f867eeec325e084b Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Thu, 27 Feb 2025 23:53:48 +0900 Subject: [PATCH 085/114] =?UTF-8?q?Feat:=20=EC=A3=BD=EC=88=9C=EC=9D=B4=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../juksoon/controller/JuksoonController.java | 30 +++++++++++++++++++ .../log/juksoon/dto/JuksooniResponse.java | 8 +++++ .../juksoon/repository/JuksoonRepository.java | 9 ++++++ .../log/juksoon/service/JuksoonService.java | 7 +++++ .../juksoon/service/JuksoonServiceImpl.java | 22 ++++++++++++++ 5 files changed, 76 insertions(+) create mode 100644 src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java create mode 100644 src/main/java/com/bamboo/log/juksoon/dto/JuksooniResponse.java create mode 100644 src/main/java/com/bamboo/log/juksoon/repository/JuksoonRepository.java create mode 100644 src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java create mode 100644 src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java diff --git a/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java b/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java new file mode 100644 index 0000000..78c6b6f --- /dev/null +++ b/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java @@ -0,0 +1,30 @@ +package com.bamboo.log.juksoon.controller; + +import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; +import com.bamboo.log.juksoon.dto.JuksooniResponse; +import com.bamboo.log.juksoon.service.JuksoonService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/api/juksooni") +@RequiredArgsConstructor +public class JuksoonController { + private final JuksoonService juksoonService; + + @GetMapping("/status") + public ResponseEntity checkJuksooniStatus( + @AuthenticationPrincipal CustomOAuth2User customOAuth2User) { + + Long userId = customOAuth2User.getId(); + JuksooniResponse response = juksoonService.checkJuksooniStatus(userId); + + return ResponseEntity.status(HttpStatus.OK).body(response); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/juksoon/dto/JuksooniResponse.java b/src/main/java/com/bamboo/log/juksoon/dto/JuksooniResponse.java new file mode 100644 index 0000000..cd4fe9b --- /dev/null +++ b/src/main/java/com/bamboo/log/juksoon/dto/JuksooniResponse.java @@ -0,0 +1,8 @@ +package com.bamboo.log.juksoon.dto; + +import org.springframework.http.HttpStatus; + +public record JuksooniResponse( + HttpStatus status, + boolean hasJuksooni +) { } diff --git a/src/main/java/com/bamboo/log/juksoon/repository/JuksoonRepository.java b/src/main/java/com/bamboo/log/juksoon/repository/JuksoonRepository.java new file mode 100644 index 0000000..8bf6a64 --- /dev/null +++ b/src/main/java/com/bamboo/log/juksoon/repository/JuksoonRepository.java @@ -0,0 +1,9 @@ +package com.bamboo.log.juksoon.repository; + +import com.bamboo.log.juksoon.domain.Juksoon; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface JuksoonRepository extends JpaRepository { + boolean existsByUserId(Long userId); + +} diff --git a/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java b/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java new file mode 100644 index 0000000..a391cf9 --- /dev/null +++ b/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java @@ -0,0 +1,7 @@ +package com.bamboo.log.juksoon.service; + +import com.bamboo.log.juksoon.dto.JuksooniResponse; + +public interface JuksoonService { + JuksooniResponse checkJuksooniStatus(Long userId); +} diff --git a/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java b/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java new file mode 100644 index 0000000..dacf9db --- /dev/null +++ b/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java @@ -0,0 +1,22 @@ +package com.bamboo.log.juksoon.service; + +import com.bamboo.log.juksoon.dto.JuksooniResponse; +import com.bamboo.log.juksoon.repository.JuksoonRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class JuksoonServiceImpl implements JuksoonService{ + + private final JuksoonRepository juksoonRepository; + + @Override + public JuksooniResponse checkJuksooniStatus(Long userId) { + boolean hasJuksooni = juksoonRepository.existsByUserId(userId); + return new JuksooniResponse(HttpStatus.OK, hasJuksooni); + } +} From f1956086114da9ec9be1a95512f825cc5714e89b Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 01:58:46 +0900 Subject: [PATCH 086/114] =?UTF-8?q?Refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java b/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java index 267b79e..bd8dfcc 100644 --- a/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java +++ b/src/main/java/com/bamboo/log/juksoon/domain/Juksoon.java @@ -19,7 +19,4 @@ public class Juksoon { @JoinColumn(name = "user_id", nullable = false, unique = true) private UserEntity user; - public static Juksoon create(UserEntity user) { - return Juksoon.builder().user(user).build(); - } } From 28ec3637436159633c89f469c4ba321fc23c5e6a Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 01:59:28 +0900 Subject: [PATCH 087/114] =?UTF-8?q?Feat:=20=EC=A3=BD=EC=88=9C=EC=9D=B4=20?= =?UTF-8?q?=EB=B6=84=EC=96=91=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../juksoon/controller/JuksoonController.java | 18 +++++++++- .../log/juksoon/service/JuksoonService.java | 1 + .../juksoon/service/JuksoonServiceImpl.java | 34 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java b/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java index 78c6b6f..b96dd38 100644 --- a/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java +++ b/src/main/java/com/bamboo/log/juksoon/controller/JuksoonController.java @@ -3,11 +3,14 @@ import com.bamboo.log.domain.user.oauth.dto.CustomOAuth2User; import com.bamboo.log.juksoon.dto.JuksooniResponse; import com.bamboo.log.juksoon.service.JuksoonService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -15,16 +18,29 @@ @RestController @RequestMapping("/api/juksooni") @RequiredArgsConstructor +@Tag(name = "Juksooni", description = "죽순이 분양 관련 API") public class JuksoonController { private final JuksoonService juksoonService; + @PostMapping("/adopt") + @Operation(summary = "죽순이 분양", description = "JWT 토큰이 필요합니다.") + public ResponseEntity adoptJuksooni( + @AuthenticationPrincipal CustomOAuth2User customOAuth2User) { + + Long userId = customOAuth2User.getId(); + JuksooniResponse response = juksoonService.adoptJuksooni(userId); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + @GetMapping("/status") + @Operation(summary = "죽순이 분양 여부 조회", description = "JWT 토큰이 필요합니다.") public ResponseEntity checkJuksooniStatus( @AuthenticationPrincipal CustomOAuth2User customOAuth2User) { Long userId = customOAuth2User.getId(); JuksooniResponse response = juksoonService.checkJuksooniStatus(userId); - return ResponseEntity.status(HttpStatus.OK).body(response); + return ResponseEntity.ok(response); + } } \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java b/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java index a391cf9..daf9999 100644 --- a/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java +++ b/src/main/java/com/bamboo/log/juksoon/service/JuksoonService.java @@ -3,5 +3,6 @@ import com.bamboo.log.juksoon.dto.JuksooniResponse; public interface JuksoonService { + JuksooniResponse adoptJuksooni(Long userId); JuksooniResponse checkJuksooniStatus(Long userId); } diff --git a/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java b/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java index dacf9db..f866361 100644 --- a/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java +++ b/src/main/java/com/bamboo/log/juksoon/service/JuksoonServiceImpl.java @@ -1,11 +1,15 @@ package com.bamboo.log.juksoon.service; +import com.bamboo.log.domain.user.oauth.entity.UserEntity; +import com.bamboo.log.domain.user.oauth.repository.UserRepository; +import com.bamboo.log.juksoon.domain.Juksoon; import com.bamboo.log.juksoon.dto.JuksooniResponse; import com.bamboo.log.juksoon.repository.JuksoonRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @@ -13,10 +17,40 @@ public class JuksoonServiceImpl implements JuksoonService{ private final JuksoonRepository juksoonRepository; + private final UserRepository userRepository; + // 죽순이 분양 로직 @Override + @Transactional + public JuksooniResponse adoptJuksooni(Long userId) { + if (userId == null) { + throw new IllegalArgumentException("유저 ID가 null입니다."); + } + + // 이미 죽순이를 분양받았는지 확인 + if (juksoonRepository.existsByUserId(userId)) { + return new JuksooniResponse(HttpStatus.CONFLICT, false); + } + + // 유저 조회 + UserEntity user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("해당 유저를 찾을 수 없습니다.")); + + // 죽순이 생성 및 저장 + Juksoon juksoon = Juksoon.builder() + .user(user) + .build(); + juksoonRepository.save(juksoon); + + return new JuksooniResponse(HttpStatus.CREATED, true); + } + + // 죽순이 분양 여부 조회 로직 + @Override + @Transactional(readOnly = true) public JuksooniResponse checkJuksooniStatus(Long userId) { boolean hasJuksooni = juksoonRepository.existsByUserId(userId); + return new JuksooniResponse(HttpStatus.OK, hasJuksooni); } } From f8899d275f4ef54eb8a4fa3b9e0bc3763bc2162c Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 03:12:30 +0900 Subject: [PATCH 088/114] =?UTF-8?q?feature=20:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=81=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.txt | 44 ----------------------------- src/main/resources/application.yml | 45 ++++++++++++++---------------- 2 files changed, 21 insertions(+), 68 deletions(-) delete mode 100644 src/main/resources/application.txt diff --git a/src/main/resources/application.txt b/src/main/resources/application.txt deleted file mode 100644 index 4f0ed06..0000000 --- a/src/main/resources/application.txt +++ /dev/null @@ -1,44 +0,0 @@ - -spring: - datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} - driver-class-name: com.mysql.cj.jdbc.Driver - jpa: - show-sql: true - open-in-view: false - hibernate: - ddl-auto: update - properties: - hibernate.dialect: org.hibernate.dialect.MySQL8Dialect - security: - oauth2: - client: - registration: - kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} - provider: - kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} - jwt: - secret: ${JWT_SECRET} -elice: - api: - token: ${API_TOKEN} - url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} -emotion: - api: - url: ${EMOTION_URL} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5b9161e..4f0ed06 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,9 @@ + spring: datasource: - url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true - username: root - password: heodongun0922 + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -16,32 +17,28 @@ spring: client: registration: kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: http://localhost:8080/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: - - profile_nickname - - account_email + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} provider: kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} jwt: - secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb - + secret: ${JWT_SECRET} elice: api: - token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk + token: ${API_TOKEN} url: - face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict - img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - chat: null - + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} emotion: api: - url: null + url: ${EMOTION_URL} \ No newline at end of file From 73f80ab63182f46491e382bb6b9f95c3b5faf7b3 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 04:31:48 +0900 Subject: [PATCH 089/114] =?UTF-8?q?feature=20:=20=EC=A3=84=EC=86=A1?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java | 2 +- src/main/java/com/bamboo/log/common/config/SecurityConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index 311084f..b41b27a 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -10,7 +10,7 @@ public class CorsMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry corsRegistry) { corsRegistry.addMapping("/**") - .allowedOrigins("*") // 모든 Origin 허용 + .allowedOriginPatterns("*") .allowedMethods("*") // 모든 HTTP 메서드 허용 .allowedHeaders("*") // 모든 헤더 허용 .allowCredentials(true) // 쿠키 허용 diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index b899dcc..c97e60e 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -63,7 +63,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration configuration = new CorsConfiguration(); //프론트 url 넣기 - configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); + configuration.setAllowedOrigins(Collections.singletonList("*")); configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Collections.singletonList("*")); From 50ae8a59964149c162fc701fe9331a6495e8700e Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 05:26:28 +0900 Subject: [PATCH 090/114] =?UTF-8?q?feature=20:=20=EC=BF=A0=ED=82=A4?= =?UTF-8?q?=EA=B0=80=20=EB=8B=A4=EB=A5=B8=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9E=98=EA=B0=80=EC=A7=80=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/service/CustomSuccessHandler.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index f92af5d..9c97ffd 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -9,6 +9,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseCookie; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; @@ -26,42 +27,48 @@ public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler private final JWTUtil jwtUtil; private final RefreshRepository refreshRepository; - @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); Long userId = customUserDetails.getId(); - String name=customUserDetails.getName(); + String name = customUserDetails.getName(); String username = customUserDetails.getUsername(); Collection authorities = authentication.getAuthorities(); Iterator iterator = authorities.iterator(); GrantedAuthority auth = iterator.next(); String role = auth.getAuthority(); - String refreshToken = jwtUtil.createJwt(userId,"refresh", name, username, role, 1800000L); - String accessToken = jwtUtil.createJwt(userId,"access", name, username, role, 1209600000L); + String refreshToken = jwtUtil.createJwt(userId, "refresh", name, username, role, 1800000L); + String accessToken = jwtUtil.createJwt(userId, "access", name, username, role, 1209600000L); addRefreshEntity(name, username, refreshToken, 1209600000L); - response.addCookie(createCookie("refresh", refreshToken)); - response.addCookie(UnScretCreateCookie("access", accessToken)); + + // SameSite 속성을 추가한 쿠키 설정 + response.addHeader("Set-Cookie", createCookie("refresh", refreshToken)); + response.addHeader("Set-Cookie", UnScretCreateCookie("access", accessToken)); response.sendRedirect("http://localhost:3000/welcome"); } - private Cookie createCookie(String key, String value) { - - Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(24 * 60 * 60 * 14); - cookie.setPath("/"); - cookie.setHttpOnly(true); - - return cookie; + private String createCookie(String key, String value) { + ResponseCookie cookie = ResponseCookie.from(key, value) + .maxAge(24 * 60 * 60 * 14) // 14일 + .path("/") + .httpOnly(true) + .secure(true) // HTTPS에서만 전송하도록 설정 + .sameSite("None") // SameSite=None 설정 + .build(); + return cookie.toString(); } - private Cookie UnScretCreateCookie(String key, String value) { - Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(60*60); - cookie.setPath("/"); - return cookie; + private String UnScretCreateCookie(String key, String value) { + ResponseCookie cookie = ResponseCookie.from(key, value) + .maxAge(60 * 60*12) // 12시간 + .path("/") + .secure(true) // HTTPS에서만 전송하도록 설정 + .sameSite("None") // SameSite=None 설정 + .build(); + return cookie.toString(); } + private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { Date CrDate = new Date(System.currentTimeMillis()); Date ExDate = new Date(System.currentTimeMillis() + expiredMs); @@ -75,4 +82,4 @@ private void addRefreshEntity(String name, String username, String refresh, Long .build() ); } -} \ No newline at end of file +} From 86c031edc441606985cd945c0ef8487bbdb49f6d Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 11:24:23 +0900 Subject: [PATCH 091/114] =?UTF-8?q?Feat:=20=ED=8F=AC=ED=8A=B8=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4f0ed06..9e8b474 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,5 @@ +server: + port: ${SERVER_PORT} spring: datasource: From fc03b8c9478ae75258b75f9bd4700adab7f60920 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 15:29:59 +0900 Subject: [PATCH 092/114] =?UTF-8?q?fix:=20cors=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/common/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index c97e60e..a37e947 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -63,7 +63,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration configuration = new CorsConfiguration(); //프론트 url 넣기 - configuration.setAllowedOrigins(Collections.singletonList("*")); + configuration.setAllowedOriginPatterns(Collections.singletonList("*")); configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Collections.singletonList("*")); From 9afb08d9b9137d32c87ab44195843eea0a50afed Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 15:54:40 +0900 Subject: [PATCH 093/114] =?UTF-8?q?fix:=20cors=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 중복 Cors 설정 수정 --- .../log/common/config/CorsMvcConfig.java | 38 +++++++-------- .../log/common/config/SecurityConfig.java | 46 +++++++++++-------- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index b41b27a..91cb2f9 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -1,19 +1,19 @@ -package com.bamboo.log.common.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("/**") - .allowedOriginPatterns("*") - .allowedMethods("*") // 모든 HTTP 메서드 허용 - .allowedHeaders("*") // 모든 헤더 허용 - .allowCredentials(true) // 쿠키 허용 - .exposedHeaders("Set-Cookie"); - } -} +//package com.bamboo.log.common.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("/**") +// .allowedOriginPatterns("*") +// .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH") +// .allowedHeaders("*") // 모든 헤더 허용 +// .allowCredentials(true) // 쿠키 허용 +// .exposedHeaders("Set-Cookie"); +// } +//} diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index a37e947..34daa06 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -7,10 +7,10 @@ import com.bamboo.log.domain.user.oauth.service.CustomFailureHandler; import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; -import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -19,8 +19,10 @@ import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Collections; +import java.util.List; @Configuration @EnableWebSecurity @@ -51,28 +53,36 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((auth) -> auth .requestMatchers("/refresh").permitAll() .requestMatchers("/logout").hasAnyRole("USER") - .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() - .anyRequest().hasAnyRole("USER")); + .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() + .anyRequest().authenticated()); + // .anyRequest().hasAnyRole("USER")); http.sessionManagement((session) -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); - http.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration configuration = new CorsConfiguration(); - //프론트 url 넣기 - configuration.setAllowedOriginPatterns(Collections.singletonList("*")); - configuration.setAllowedMethods(Collections.singletonList("*")); - configuration.setAllowCredentials(true); - configuration.setAllowedHeaders(Collections.singletonList("*")); - configuration.setMaxAge(3600L); - configuration.setExposedHeaders(Collections.singletonList("Set-Cookie")); - configuration.setExposedHeaders(Collections.singletonList("Authorization")); - return configuration; - } - })); + http.cors(Customizer.withDefaults()); + return http.build(); } + + @Bean + public CorsConfigurationSource getCorsConfiguration() { + CorsConfiguration configuration = new CorsConfiguration(); + // configuration.setAllowedOriginPatterns(Collections.singletonList("*")); + configuration.setAllowedOrigins(List.of("http://localhost:3000")); // 허용 프론트엔드 url 추가 필요 + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + //configuration.setAllowedMethods(Collections.singletonList("*")); + configuration.setAllowCredentials(true); + configuration.setAllowedHeaders(Collections.singletonList("*")); + configuration.setMaxAge(3600L); + + configuration.setExposedHeaders(List.of("Set-Cookie", "Authorization")); // 한 번에 설정 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + + return source; + //return configuration; + } } \ No newline at end of file From 5d5f8aa716719ac5b61190554b754c4564fbc4a5 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 16:15:48 +0900 Subject: [PATCH 094/114] =?UTF-8?q?fix:=20cors=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bamboo/log/common/config/SecurityConfig.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 34daa06..cbb3234 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -69,8 +69,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Bean public CorsConfigurationSource getCorsConfiguration() { CorsConfiguration configuration = new CorsConfiguration(); - // configuration.setAllowedOriginPatterns(Collections.singletonList("*")); - configuration.setAllowedOrigins(List.of("http://localhost:3000")); // 허용 프론트엔드 url 추가 필요 + + configuration.setAllowedOrigins(List.of( + "http://localhost:3000", + "https://qbdffmpbayqfbgja.tunnel-pt.elice.io")); // 허용 프론트엔드 url 추가 필요 + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); //configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); From aebdc66049678b8ad9ba924dd8b56ca7b5edddbd Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 16:40:41 +0900 Subject: [PATCH 095/114] =?UTF-8?q?fix:=20cors=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - header 재설정 - allowOrigin 메서드 수정 --- .../java/com/bamboo/log/common/config/SecurityConfig.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index cbb3234..94ebf3a 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -70,14 +70,16 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public CorsConfigurationSource getCorsConfiguration() { CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(List.of( + configuration.setAllowedOriginPatterns(List.of( "http://localhost:3000", "https://qbdffmpbayqfbgja.tunnel-pt.elice.io")); // 허용 프론트엔드 url 추가 필요 configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); //configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); - configuration.setAllowedHeaders(Collections.singletonList("*")); + configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type")); + + // configuration.setAllowedHeaders(Collections.singletonList("*")); configuration.setMaxAge(3600L); configuration.setExposedHeaders(List.of("Set-Cookie", "Authorization")); // 한 번에 설정 From 0296775967b2eb1b2d125d24134b88f297c8b969 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Fri, 28 Feb 2025 17:03:13 +0900 Subject: [PATCH 096/114] =?UTF-8?q?Fix:=20cors=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bamboo/log/common/config/SecurityConfig.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 94ebf3a..5a37e4d 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -61,8 +61,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.sessionManagement((session) -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshRepository), LogoutFilter.class); - http.cors(Customizer.withDefaults()); - + //http.cors(Customizer.withDefaults()); + http.cors(cors -> cors.configurationSource(getCorsConfiguration())); return http.build(); } @@ -77,7 +77,8 @@ public CorsConfigurationSource getCorsConfiguration() { configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); //configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); - configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type")); + configuration.setAllowedHeaders(Collections.singletonList("*")); + // configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type")); // configuration.setAllowedHeaders(Collections.singletonList("*")); configuration.setMaxAge(3600L); From f95e937aa9820a5be29e0ec87961a32fd338440d Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 17:41:36 +0900 Subject: [PATCH 097/114] =?UTF-8?q?feature=20:=20=EC=BF=A0=ED=82=A4?= =?UTF-8?q?=EA=B0=80=20=EB=8B=A4=EB=A5=B8=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9E=98=EA=B0=80=EC=A7=80=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/common/config/SecurityConfig.java | 9 +++- src/main/resources/application.yml | 45 ++++++++++--------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index c97e60e..0887c6a 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -8,6 +8,7 @@ import com.bamboo.log.domain.user.oauth.service.CustomOAuth2UserService; import com.bamboo.log.domain.user.oauth.service.CustomSuccessHandler; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -63,7 +64,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration configuration = new CorsConfiguration(); //프론트 url 넣기 - configuration.setAllowedOrigins(Collections.singletonList("*")); + configuration.setAllowedOriginPatterns(Collections.singletonList("*")); configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Collections.singletonList("*")); @@ -73,6 +74,12 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return configuration; } })); + http.exceptionHandling((exceptionHandling) -> + exceptionHandling.authenticationEntryPoint((request, response, authException) -> + response.sendError(HttpServletResponse.SC_UNAUTHORIZED) // 401 반환 + ) + ); + return http.build(); } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4f0ed06..5b9161e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,9 +1,8 @@ - spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} + url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + username: root + password: heodongun0922 driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -17,28 +16,32 @@ spring: client: registration: kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: + - profile_nickname + - account_email provider: kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: - secret: ${JWT_SECRET} + secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + elice: api: - token: ${API_TOKEN} + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} + face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict + img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + chat: null + emotion: api: - url: ${EMOTION_URL} \ No newline at end of file + url: null From c47748457c8db7995b4bca3024080779266610dd Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 18:49:46 +0900 Subject: [PATCH 098/114] =?UTF-8?q?feature=20:=20=EC=84=9C=EB=93=9C?= =?UTF-8?q?=ED=8C=8C=ED=8B=B0=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=EB=90=98=EB=8A=94=EA=B1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/common/config/CorsMvcConfig.java | 7 +-- .../log/common/config/SecurityConfig.java | 12 +--- .../domain/user/jwt/service/JWTFilter.java | 55 +++++++++---------- .../oauth/service/CustomSuccessHandler.java | 34 ++++-------- src/main/resources/application.yml | 47 ++++++++-------- 5 files changed, 67 insertions(+), 88 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index b41b27a..022ddd9 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -10,10 +10,7 @@ public class CorsMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry corsRegistry) { corsRegistry.addMapping("/**") - .allowedOriginPatterns("*") - .allowedMethods("*") // 모든 HTTP 메서드 허용 - .allowedHeaders("*") // 모든 헤더 허용 - .allowCredentials(true) // 쿠키 허용 - .exposedHeaders("Set-Cookie"); + .exposedHeaders("Set-Cookie") + .allowedOrigins("http://localhost:3000"); } } diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 0887c6a..5763e88 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -22,6 +22,7 @@ import org.springframework.web.cors.CorsConfigurationSource; import java.util.Collections; +import java.util.List; @Configuration @EnableWebSecurity @@ -51,9 +52,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { //이쪽에다가 각각의 엔드포인트를 넣어야함 http.authorizeHttpRequests((auth) -> auth .requestMatchers("/refresh").permitAll() - .requestMatchers("/logout").hasAnyRole("USER") .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() - .anyRequest().hasAnyRole("USER")); + .anyRequest().authenticated()); http.sessionManagement((session) -> session @@ -63,8 +63,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration configuration = new CorsConfiguration(); - //프론트 url 넣기 - configuration.setAllowedOriginPatterns(Collections.singletonList("*")); + configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Collections.singletonList("*")); @@ -74,11 +73,6 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return configuration; } })); - http.exceptionHandling((exceptionHandling) -> - exceptionHandling.authenticationEntryPoint((request, response, authException) -> - response.sendError(HttpServletResponse.SC_UNAUTHORIZED) // 401 반환 - ) - ); return http.build(); } diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java index 9f7e2c9..429d1b1 100644 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java @@ -5,6 +5,7 @@ import io.jsonwebtoken.ExpiredJwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -24,44 +25,42 @@ public class JWTFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - // 헤더에서 Authorization에 담긴 토큰을 꺼냄 - String accessToken = request.getHeader("Authorization"); + String requestUri = request.getRequestURI(); - if (accessToken != null && accessToken.startsWith("Bearer ")) { - accessToken = accessToken.substring(7); // "Bearer "의 길이는 7 - } + System.out.println("requestUri = " + requestUri); + if (requestUri.matches("^\\/login(?:\\/.*)?$")) { - // 토큰이 없다면 다음 필터로 넘김 - if (accessToken == null|| accessToken.isEmpty()) { filterChain.doFilter(request, response); return; } + if (requestUri.matches("^\\/oauth2(?:\\/.*)?$")) { - // 토큰 만료 여부 확인, 만료시 다음 필터로 넘기지 않음 - try { - jwtUtil.isExpired(accessToken); - } catch (ExpiredJwtException e) { - // 만료된 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 - PrintWriter writer = response.getWriter(); - writer.print("access token expired"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + filterChain.doFilter(request, response); return; } - - // 토큰이 access인지 확인 (발급시 페이로드에 명시) - String category = jwtUtil.getCategory(accessToken); - if (!category.equals("access")) { - // 유효하지 않은 토큰일 경우 응답 본문에 메시지 출력 및 상태 코드 설정 - PrintWriter writer = response.getWriter(); - writer.print("invalid access token"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + String authorization = null; + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + System.out.println(cookie.getName()); + if (cookie.getName().equals("Authorization")) { + authorization = cookie.getValue(); + } + } + if (authorization == null) { + System.out.println("token null"); + filterChain.doFilter(request, response); return; } - Long userId = jwtUtil.getId(accessToken); - String username = jwtUtil.getUsername(accessToken); - String role = jwtUtil.getRole(accessToken); - String name=jwtUtil.getName(accessToken); - + String token = authorization; + if (jwtUtil.isExpired(token)) { + System.out.println("token expired"); + filterChain.doFilter(request, response); + return; + } + Long userId = jwtUtil.getId(token); + String username = jwtUtil.getUsername(token); + String role = jwtUtil.getRole(token); + String name=jwtUtil.getName(token); UserDTO userDTO = UserDTO.builder() .id(userId) .name(name) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index 9c97ffd..38a89a0 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -38,35 +38,23 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo GrantedAuthority auth = iterator.next(); String role = auth.getAuthority(); - String refreshToken = jwtUtil.createJwt(userId, "refresh", name, username, role, 1800000L); - String accessToken = jwtUtil.createJwt(userId, "access", name, username, role, 1209600000L); + String refreshToken = jwtUtil.createJwt(userId, "refresh", name, username, role, 1209600000L); addRefreshEntity(name, username, refreshToken, 1209600000L); - // SameSite 속성을 추가한 쿠키 설정 - response.addHeader("Set-Cookie", createCookie("refresh", refreshToken)); - response.addHeader("Set-Cookie", UnScretCreateCookie("access", accessToken)); + response.addCookie(createCookie("Authorization", refreshToken)); response.sendRedirect("http://localhost:3000/welcome"); } - private String createCookie(String key, String value) { - ResponseCookie cookie = ResponseCookie.from(key, value) - .maxAge(24 * 60 * 60 * 14) // 14일 - .path("/") - .httpOnly(true) - .secure(true) // HTTPS에서만 전송하도록 설정 - .sameSite("None") // SameSite=None 설정 - .build(); - return cookie.toString(); - } - private String UnScretCreateCookie(String key, String value) { - ResponseCookie cookie = ResponseCookie.from(key, value) - .maxAge(60 * 60*12) // 12시간 - .path("/") - .secure(true) // HTTPS에서만 전송하도록 설정 - .sameSite("None") // SameSite=None 설정 - .build(); - return cookie.toString(); + + private Cookie createCookie(String key, String value) { + + Cookie cookie = new Cookie(key, value); + cookie.setMaxAge(60*60*60); + cookie.setPath("/"); + cookie.setHttpOnly(true); + + return cookie; } private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9e8b474..5b9161e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,11 +1,8 @@ -server: - port: ${SERVER_PORT} - spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} + url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + username: root + password: heodongun0922 driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -19,28 +16,32 @@ spring: client: registration: kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: + - profile_nickname + - account_email provider: kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: - secret: ${JWT_SECRET} + secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + elice: api: - token: ${API_TOKEN} + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} + face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict + img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + chat: null + emotion: api: - url: ${EMOTION_URL} \ No newline at end of file + url: null From 7ca9771f21723aa083fe0c62fb906f2e25e9cc54 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 18:57:19 +0900 Subject: [PATCH 099/114] =?UTF-8?q?feature=20:=20=EC=84=9C=EB=93=9C?= =?UTF-8?q?=ED=8C=8C=ED=8B=B0=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=EB=90=98=EB=8A=94=EA=B1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java | 2 +- src/main/java/com/bamboo/log/common/config/SecurityConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index 022ddd9..76fec8b 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -11,6 +11,6 @@ public class CorsMvcConfig implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry corsRegistry) { corsRegistry.addMapping("/**") .exposedHeaders("Set-Cookie") - .allowedOrigins("http://localhost:3000"); + .allowedOriginPatterns("*"); } } diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 5763e88..3b98a7c 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -63,7 +63,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); + configuration.addAllowedOriginPattern("*"); configuration.setAllowedMethods(Collections.singletonList("*")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Collections.singletonList("*")); From 2d560c142106868b6aa8cfc94860e79fd5741f72 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 18:58:02 +0900 Subject: [PATCH 100/114] =?UTF-8?q?feature=20:=20=EC=84=9C=EB=93=9C?= =?UTF-8?q?=ED=8C=8C=ED=8B=B0=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=EC=8B=9D=EB=90=98=EB=8A=94=EA=B1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 47 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5b9161e..9e8b474 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,11 @@ +server: + port: ${SERVER_PORT} + spring: datasource: - url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true - username: root - password: heodongun0922 + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -16,32 +19,28 @@ spring: client: registration: kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: http://localhost:8080/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: - - profile_nickname - - account_email + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} provider: kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} jwt: - secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb - + secret: ${JWT_SECRET} elice: api: - token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk + token: ${API_TOKEN} url: - face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict - img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - chat: null - + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} emotion: api: - url: null + url: ${EMOTION_URL} \ No newline at end of file From b382ef3172dcec6e439bd41841655fabde424658 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 19:00:36 +0900 Subject: [PATCH 101/114] =?UTF-8?q?feature=20:=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9D=98=20=EA=B0=B1=EC=8B=A0=EC=8B=9C=EA=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/domain/user/oauth/service/CustomSuccessHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index 38a89a0..5ef9833 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -50,7 +50,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo private Cookie createCookie(String key, String value) { Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(60*60*60); + cookie.setMaxAge(1209600000 / 1000); cookie.setPath("/"); cookie.setHttpOnly(true); From 60ac91a3fe5e44310d9f06dc36f6d49105a0a69b Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 19:02:55 +0900 Subject: [PATCH 102/114] =?UTF-8?q?feature=20:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/domain/user/oauth/service/CustomSuccessHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index 5ef9833..3cb25ee 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -53,7 +53,8 @@ private Cookie createCookie(String key, String value) { cookie.setMaxAge(1209600000 / 1000); cookie.setPath("/"); cookie.setHttpOnly(true); - + cookie.setSecure(true); + cookie.setComment("SameSite=None; Secure"); return cookie; } From 019530882c36be365854a6b2a8a4c923ea0dec98 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 19:25:34 +0900 Subject: [PATCH 103/114] =?UTF-8?q?feature=20:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/service/CustomSuccessHandler.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java index 3cb25ee..14b0310 100644 --- a/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java +++ b/src/main/java/com/bamboo/log/domain/user/oauth/service/CustomSuccessHandler.java @@ -5,7 +5,6 @@ import com.bamboo.log.domain.user.oauth.entity.RefreshToken; import com.bamboo.log.domain.user.oauth.repository.RefreshRepository; import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -41,21 +40,24 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo String refreshToken = jwtUtil.createJwt(userId, "refresh", name, username, role, 1209600000L); addRefreshEntity(name, username, refreshToken, 1209600000L); - response.addCookie(createCookie("Authorization", refreshToken)); + // Create and set the cookie + createCookie(response, "Authorization", refreshToken); + response.sendRedirect("http://localhost:3000/welcome"); } + private void createCookie(HttpServletResponse response, String key, String value) { + // ResponseCookie 사용 + ResponseCookie cookie = ResponseCookie.from(key, value) + .maxAge(1209600000 / 1000) + .path("/") + .httpOnly(true) + .secure(true) + .sameSite("None") // SameSite=None 설정 + .build(); - - private Cookie createCookie(String key, String value) { - - Cookie cookie = new Cookie(key, value); - cookie.setMaxAge(1209600000 / 1000); - cookie.setPath("/"); - cookie.setHttpOnly(true); - cookie.setSecure(true); - cookie.setComment("SameSite=None; Secure"); - return cookie; + // Response에 쿠키 추가 + response.addHeader("Set-Cookie", cookie.toString()); } private void addRefreshEntity(String name, String username, String refresh, Long expiredMs) { From c546c374f31ea579e89f2b452a97897fc7ed42d4 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 20:00:05 +0900 Subject: [PATCH 104/114] =?UTF-8?q?feature=20:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/bamboo/log/common/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java index 3b98a7c..f85103a 100644 --- a/src/main/java/com/bamboo/log/common/config/SecurityConfig.java +++ b/src/main/java/com/bamboo/log/common/config/SecurityConfig.java @@ -53,7 +53,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((auth) -> auth .requestMatchers("/refresh").permitAll() .requestMatchers("/swagger-ui/**","/v3/api-docs/**","/swagger-resources/**","/webjars/**").permitAll() - .anyRequest().authenticated()); + .anyRequest().permitAll()); http.sessionManagement((session) -> session From 912dff7a632d95b887ea4ad019841c41559d1c4a Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 20:03:11 +0900 Subject: [PATCH 105/114] =?UTF-8?q?feature=20:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/domain/user/jwt/service/JWTFilter.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java index 429d1b1..1d4cdf5 100644 --- a/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java +++ b/src/main/java/com/bamboo/log/domain/user/jwt/service/JWTFilter.java @@ -28,16 +28,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String requestUri = request.getRequestURI(); System.out.println("requestUri = " + requestUri); - if (requestUri.matches("^\\/login(?:\\/.*)?$")) { - filterChain.doFilter(request, response); - return; - } - if (requestUri.matches("^\\/oauth2(?:\\/.*)?$")) { - - filterChain.doFilter(request, response); - return; - } String authorization = null; Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { From 5fae57458c86e56ebab02799ea7db00d315957de Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 20:20:47 +0900 Subject: [PATCH 106/114] =?UTF-8?q?feature=20:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bamboo/log/MyController.java | 17 +++++++ src/main/resources/application.yml | 47 ++++++++++--------- 2 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/bamboo/log/MyController.java diff --git a/src/main/java/com/bamboo/log/MyController.java b/src/main/java/com/bamboo/log/MyController.java new file mode 100644 index 0000000..a00d01d --- /dev/null +++ b/src/main/java/com/bamboo/log/MyController.java @@ -0,0 +1,17 @@ +package com.bamboo.log; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@Slf4j +public class MyController { + @GetMapping("/my") + @ResponseBody + public String myPI() { + log.info("myPI"); + return "my route"; + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9e8b474..5b9161e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,11 +1,8 @@ -server: - port: ${SERVER_PORT} - spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} + url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + username: root + password: heodongun0922 driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -19,28 +16,32 @@ spring: client: registration: kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: + - profile_nickname + - account_email provider: kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: - secret: ${JWT_SECRET} + secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + elice: api: - token: ${API_TOKEN} + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} + face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict + img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + chat: null + emotion: api: - url: ${EMOTION_URL} \ No newline at end of file + url: null From b4c046a939781ed89492c1a7d3a0edb05658dfba Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Fri, 28 Feb 2025 20:23:59 +0900 Subject: [PATCH 107/114] =?UTF-8?q?feature=20:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 47 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5b9161e..9e8b474 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,11 @@ +server: + port: ${SERVER_PORT} + spring: datasource: - url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true - username: root - password: heodongun0922 + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -16,32 +19,28 @@ spring: client: registration: kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: http://localhost:8080/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: - - profile_nickname - - account_email + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} provider: kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} jwt: - secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb - + secret: ${JWT_SECRET} elice: api: - token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk + token: ${API_TOKEN} url: - face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict - img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - chat: null - + face: ${FACE_URL} + img: ${IMG_URL} + chat: ${CHAT_URL} emotion: api: - url: null + url: ${EMOTION_URL} \ No newline at end of file From 372deb252405b01f09a1414aa8ff95e2bacaa912 Mon Sep 17 00:00:00 2001 From: 10Hyuna Date: Sat, 1 Mar 2025 03:59:08 +0900 Subject: [PATCH 108/114] =?UTF-8?q?Fix:=20image=20base64=20=EC=9D=B8?= =?UTF-8?q?=EC=BD=94=EB=94=A9=20=ED=9B=84=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/CheckDiaryResponse.java | 19 ++++++++++++----- .../response/GetDiariesOfMonthResponse.java | 5 +---- .../diary/service/diary/DiaryServiceImpl.java | 21 ++++++++++--------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java index ad365f7..152e4db 100644 --- a/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java +++ b/src/main/java/com/bamboo/log/diary/dto/response/CheckDiaryResponse.java @@ -1,13 +1,22 @@ package com.bamboo.log.diary.dto.response; +import com.bamboo.log.diary.domain.Diary; import jakarta.validation.constraints.NotEmpty; import lombok.Builder; - import java.time.LocalDateTime; +import java.util.Base64; @Builder -public record CheckDiaryResponse(@NotEmpty(message = "Date shouldn't be empty") LocalDateTime date, - @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDescription, - @NotEmpty(message = "Summary Image shouldn't be empty") byte[] summaryImage +public record CheckDiaryResponse( + @NotEmpty(message = "Date shouldn't be empty") LocalDateTime date, + @NotEmpty(message = "Diary Description shouldn't be empty") String diaryDescription, + String summaryImage // 👈 Base64 문자열로 변경 (byte[] 대신 String) ) { -} \ No newline at end of file + public static CheckDiaryResponse from(Diary diary, byte[] imageData) { + return new CheckDiaryResponse( + diary.getCreatedAt(), + diary.getContext(), + imageData != null ? Base64.getEncoder().encodeToString(imageData) : null + ); + } +} diff --git a/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java index 96285a3..6440971 100644 --- a/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java +++ b/src/main/java/com/bamboo/log/diary/dto/response/GetDiariesOfMonthResponse.java @@ -1,7 +1,6 @@ package com.bamboo.log.diary.dto.response; import lombok.Builder; - import java.time.LocalDateTime; import java.util.List; @@ -11,12 +10,10 @@ public record GetDiariesOfMonthResponse( String date, List diaries ) { - @Builder public record DiaryOfMonth( LocalDateTime createdAt, String context, - byte[] summaryImage + String summaryImage ) {} - } diff --git a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java index 462588a..2857652 100644 --- a/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java +++ b/src/main/java/com/bamboo/log/diary/service/diary/DiaryServiceImpl.java @@ -25,6 +25,7 @@ import java.time.LocalDateTime; import java.time.YearMonth; import java.time.format.DateTimeFormatter; +import java.util.Base64; import java.util.List; import java.util.Map; import java.util.Optional; @@ -64,19 +65,21 @@ public ResponseEntity getDiariesByMonth(String date) { UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); try { - List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, + List diaries = diaryRepository.findByUserAndCreatedAtBetween(user, parseYearMonth.getStartOfMonth(), parseYearMonth.getEndOfMonth()); List diaryIds = diaries.stream().map(Diary::getId).toList(); - Map summaryImageMap = todaySummaryRepository.findByDiaryIdIn(diaryIds) + Map summaryImageMap = todaySummaryRepository.findByDiaryIdIn(diaryIds) .stream() - .collect(Collectors.toMap(TodaySummary::getDiaryId, TodaySummary::getImageData)); + .collect(Collectors.toMap( + TodaySummary::getDiaryId, + summary -> Base64.getEncoder().encodeToString(summary.getImageData()))); // ✅ Base64 변환 List diaryOfMonthList = diaries.stream() .map(diary -> new DiaryOfMonth( diary.getCreatedAt(), diary.getContext(), - summaryImageMap.getOrDefault(diary.getId(), null) + summaryImageMap.getOrDefault(diary.getId(), null) // Base64 문자열 전달 )) .toList(); @@ -98,7 +101,8 @@ public ResponseEntity getDiaryByDate(LocalDateTime date) { UserEntity user = userRepository.findByUsername(userContextUtil.getUsername()); try { - List diaries = diaryRepository.findByUserAAndCreatedAtBetween(user, date, date); + List diaries = diaryRepository.findByUserAndCreatedAtBetween(user, date, date); + if (diaries.isEmpty()) { throw new RuntimeException("해당 날짜에 작성된 일기가 없습니다."); } @@ -107,11 +111,7 @@ public ResponseEntity getDiaryByDate(LocalDateTime date) { Optional summaryImage = todaySummaryRepository.findByDiaryId(diaryByDate.getId()); return ResponseHandler.create200Response(new ResponseForm(), - CheckDiaryResponse.builder() - .date(diaryByDate.getCreatedAt()) - .diaryDescription(diaryByDate.getContext()) - .summaryImage(summaryImage.map(TodaySummary::getImageData).orElse(null)) - .build()); + CheckDiaryResponse.from(diaryByDate, summaryImage.map(TodaySummary::getImageData).orElse(null))); } catch (RuntimeException e) { return ResponseHandler.create404Error(new ResponseForm(), new IllegalArgumentException("해당 날짜에 작성된 일기가 없습니다.")); @@ -120,6 +120,7 @@ public ResponseEntity getDiaryByDate(LocalDateTime date) { } } + private Diary saveDiary(CreateDiaryRequest createDiaryRequest, LocalDateTime localDateTime) { UserEntity user = userContextUtil.getUserEntity(); From a059853fb9707d3da32e1af5c610d681b11add59 Mon Sep 17 00:00:00 2001 From: heodongun <“heodongun0922@gmail.com”> Date: Sat, 1 Mar 2025 04:41:00 +0900 Subject: [PATCH 109/114] =?UTF-8?q?feature=20:=20=EC=BF=A0=ED=82=A4=20cors?= =?UTF-8?q?=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/common/config/CorsMvcConfig.java | 5 +- src/main/resources/application.yml | 47 ++++++++++--------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java index 76fec8b..daa5f81 100644 --- a/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java +++ b/src/main/java/com/bamboo/log/common/config/CorsMvcConfig.java @@ -10,7 +10,8 @@ public class CorsMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry corsRegistry) { corsRegistry.addMapping("/**") - .exposedHeaders("Set-Cookie") - .allowedOriginPatterns("*"); + .allowedOriginPatterns("*") + .allowCredentials(true) + .exposedHeaders("Set-Cookie"); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9e8b474..5b9161e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,11 +1,8 @@ -server: - port: ${SERVER_PORT} - spring: datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} - username: ${MYSQL_USERNAME} - password: ${MYSQL_PASSWORD} + url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true + username: root + password: heodongun0922 driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -19,28 +16,32 @@ spring: client: registration: kakao: - client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} - client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} - client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} - client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} - redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} - authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} - scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} + client-authentication-method: client_secret_post + client-name: kakao + client-id: 649c1e34aedc1a7db892333d900725f2 + client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: + - profile_nickname + - account_email provider: kakao: - authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} - token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} - user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} - user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} + authorization-uri: https://kauth.kakao.com/oauth/authorize + token-uri: https://kauth.kakao.com/oauth/token + user-info-uri: https://kapi.kakao.com/v2/user/me + user-name-attribute: id jwt: - secret: ${JWT_SECRET} + secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb + elice: api: - token: ${API_TOKEN} + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk url: - face: ${FACE_URL} - img: ${IMG_URL} - chat: ${CHAT_URL} + face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict + img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate + chat: null + emotion: api: - url: ${EMOTION_URL} \ No newline at end of file + url: null From df33612c4a9241891a2089c7b7dc7e6e670ed986 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Sat, 1 Mar 2025 05:13:21 +0900 Subject: [PATCH 110/114] =?UTF-8?q?Fix:=20yml=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 46 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5b9161e..b8974c2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,11 @@ +sever: + port: ${SERVER_PORT} + spring: datasource: - url: jdbc:mysql://localhost:3306/OAuth2?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true - username: root - password: heodongun0922 + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME} + username: ${MYSQL_USERNAME} + password: ${MYSQL_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver jpa: show-sql: true @@ -16,32 +19,27 @@ spring: client: registration: kakao: - client-authentication-method: client_secret_post - client-name: kakao - client-id: 649c1e34aedc1a7db892333d900725f2 - client-secret: FqJEAF5XKaO0n4Bv3KVXWCpUcHQWahY8 - redirect-uri: http://localhost:8080/login/oauth2/code/kakao - authorization-grant-type: authorization_code - scope: - - profile_nickname - - account_email + client-authentication-method: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_AUTHENTICATION_METHOD} + client-name: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME} + client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID} + client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_SECRET} + redirect-uri: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI} + authorization-grant-type: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE} + scope: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE} provider: kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id + authorization-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_AUTHORIZATION_URI} + token-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_TOKEN_URI} + user-info-uri: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_INFO_URI} + user-name-attribute: ${SECURITY_OAUTH2_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE} jwt: - secret: vmfhaltmskdlstkfkdgodyroqkfwkdbalroqkfwkdbalaaaaaaaaaaaaaaaabbbbb - + secret: ${JWT_SECRET} elice: api: - token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDA1NDYyMjgsIm5iZiI6MTc0MDU0NjIyOCwiZXhwIjoxNzUwNDYzOTk5LCJrZXlfaWQiOiJjYzI2YjZjMi0zNWExLTQ0NmEtODhkYS02ZjdkZGExNDMzYWMifQ.tqRvmEibKaRZUJK9m7pp7ZOWsdzy2rGg8YxjQ1MrSDk + token: ${API_TOKEN} url: - face: https://api-cloud-function.elice.io/d5ff1aef-c59f-433c-8e1c-f9b52ac4ab6a/predict - img: https://api-cloud-function.elice.io/0133c2f7-9f3f-44b6-a3d6-c24ba8ef4510/generate - chat: null - + face: ${FACE_URL} + img: ${IMG_URL} emotion: api: - url: null + url: ${EMOTION_URL} \ No newline at end of file From 7a680dce311a48e812ca84ceede47ca788eaf9a8 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Sat, 1 Mar 2025 05:18:15 +0900 Subject: [PATCH 111/114] =?UTF-8?q?Fix:=20=EC=96=BC=EA=B5=B4=20=EC=98=81?= =?UTF-8?q?=EC=97=AD=20=EB=B6=80=EB=B6=84=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bamboo/log/emotion/controller/EmotionController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java b/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java index b801e59..4bfcba7 100644 --- a/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java +++ b/src/main/java/com/bamboo/log/emotion/controller/EmotionController.java @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + @RestController @RequestMapping("/api/emotion") @RequiredArgsConstructor @@ -34,9 +36,10 @@ public ResponseEntity analyzeEmotion( MultipartFile image, @RequestPart("faceBox") - @Parameter(description = "얼굴 영역 좌표 (JSON 형식)", required = true, example = "{\"x1\": 12.34, \"y1\": 56.78, \"x2\": 123.456, \"y2\": 789.87}") - BoundingBox faceBox) { + @Parameter(description = "얼굴 영역 좌표 (JSON 형식)", required = true) + List faceBoxList) { + BoundingBox faceBox = faceBoxList.get(0); // 첫 번째 얼굴만 사용 EmotionAnalysisRequest request = new EmotionAnalysisRequest(image, faceBox); EmotionAnalysisResponse response = emotionAnalysisService.analyzeEmotion(request); From 4f7df9ed173218ff0a592df497cf417bf73e1463 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Mon, 10 Mar 2025 16:15:42 +0900 Subject: [PATCH 112/114] =?UTF-8?q?Docs:=20gitignore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4c75e42..c25652d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,7 @@ out/ ### VS Code ### .vscode/ -.env \ No newline at end of file +.env + +### Mac ### +.DS_store \ No newline at end of file From b8093f2d47263547ba221c9ff02894b661432d8b Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Mon, 10 Mar 2025 16:18:43 +0900 Subject: [PATCH 113/114] Refactor: Resolve merge conflict --- .../bamboo/log/diary/domain/TodaySummary.java | 29 +++++++++++ .../dto/response/CreateDiaryResponse.java | 8 +++ .../service/summary/TodaySummaryService.java | 11 ++++ .../summary/TodaySummaryServiceImpl.java | 46 +++++++++++++++++ .../log/elice/config/RestTemplateConfig.java | 14 +++++ .../com/bamboo/log/utils/ResponseHandler.java | 51 +++++++++++++++++++ .../bamboo/log/utils/dto/DetailResponse.java | 13 +++++ .../bamboo/log/utils/dto/ResponseForm.java | 21 ++++++++ 8 files changed, 193 insertions(+) create mode 100644 src/main/java/com/bamboo/log/diary/domain/TodaySummary.java create mode 100644 src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java create mode 100644 src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java create mode 100644 src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java create mode 100644 src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java create mode 100644 src/main/java/com/bamboo/log/utils/ResponseHandler.java create mode 100644 src/main/java/com/bamboo/log/utils/dto/DetailResponse.java create mode 100644 src/main/java/com/bamboo/log/utils/dto/ResponseForm.java diff --git a/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java b/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java new file mode 100644 index 0000000..24af228 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/domain/TodaySummary.java @@ -0,0 +1,29 @@ +package com.bamboo.log.diary.domain; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Builder; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "today_summaries") +public class TodaySummary { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(nullable = false) + private Long diaryId; + + @Lob + @Column(columnDefinition = "LONGBLOB", nullable = false) + private byte[] imageData; + +} + diff --git a/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java b/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java new file mode 100644 index 0000000..9e994c0 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/dto/response/CreateDiaryResponse.java @@ -0,0 +1,8 @@ +package com.bamboo.log.diary.dto.response; + +import jakarta.validation.constraints.NotEmpty; + +import java.time.LocalDateTime; + +public record CreateDiaryResponse(@NotEmpty(message = "created shouldn't be empty") LocalDateTime created) { +} diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java new file mode 100644 index 0000000..598d786 --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryService.java @@ -0,0 +1,11 @@ +package com.bamboo.log.diary.service.summary; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +public interface TodaySummaryService { + + void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException; + byte[] createTodaySummaryImage(String prompt); +} diff --git a/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java new file mode 100644 index 0000000..06df2dd --- /dev/null +++ b/src/main/java/com/bamboo/log/diary/service/summary/TodaySummaryServiceImpl.java @@ -0,0 +1,46 @@ +package com.bamboo.log.diary.service.summary; + +import com.bamboo.log.diary.domain.TodaySummary; +import com.bamboo.log.diary.repository.TodaySummaryRepository; +import com.bamboo.log.elice.service.ImageGenerationService; +import com.bamboo.log.utils.ResponseHandler; +import com.bamboo.log.utils.dto.ResponseForm; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Optional; + +@Service +@Transactional +@RequiredArgsConstructor +public class TodaySummaryServiceImpl implements TodaySummaryService { + + private final TodaySummaryRepository todaySummaryRepository; + private final ImageGenerationService imageGenerationService; + + @Override + public void saveTodaySummaryImage(byte[] image, Long diaryId) throws IOException { + TodaySummary todaySummary = TodaySummary.builder() + .diaryId(diaryId) + .imageData(image) + .build(); + + todaySummaryRepository.save(todaySummary); + } + + @Override + public byte[] createTodaySummaryImage(String prompt) { + try { + byte[] imageBytes = imageGenerationService.generateImage(prompt); + + return imageBytes; + } catch (IOException e) { + throw new RuntimeException("이미지 생성 중에 문제가 발생했습니다."); + } + + } + +} diff --git a/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java new file mode 100644 index 0000000..0ea3f07 --- /dev/null +++ b/src/main/java/com/bamboo/log/elice/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package com.bamboo.log.elice.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/bamboo/log/utils/ResponseHandler.java b/src/main/java/com/bamboo/log/utils/ResponseHandler.java new file mode 100644 index 0000000..c483a50 --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/ResponseHandler.java @@ -0,0 +1,51 @@ +package com.bamboo.log.utils; + +import com.bamboo.log.utils.dto.DetailResponse; +import com.bamboo.log.utils.dto.ResponseForm; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class ResponseHandler { + + public static ResponseEntity create500Error(ResponseForm response, Exception e) { + response.of("result", "FAIL"); + response.of("error", DetailResponse.builder().code(500).message(e.getMessage()).build()); + return new ResponseEntity(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + + public static ResponseEntity create400Error(ResponseForm response, Exception e) { + response.of("result", "FAIL"); + response.of("error", DetailResponse.builder().code(400).message(e.getMessage()).build()); + + return new ResponseEntity(response, HttpStatus.BAD_REQUEST); + } + + public static ResponseEntity create404Error(ResponseForm response, Exception e) { + response.of("result", "FAIL"); + response.of("error", DetailResponse.builder().code(404).message(e.getMessage()).build()); + + return new ResponseEntity(response, HttpStatus.NOT_FOUND); + } + + public static ResponseEntity create204Response(ResponseForm response, String message) { + response.of("result", "SUCCESS"); + response.of("code", DetailResponse.builder().code(204).message(message).build()); + + return new ResponseEntity(response, HttpStatus.NO_CONTENT); + } + + public static ResponseEntity create200Response(ResponseForm response, Object object) { + response.of("result", "SUCCESS"); + response.of("code", object); + + return new ResponseEntity(response, HttpStatus.OK); + } + + public static ResponseEntity create201Response(ResponseForm response, Object object) { + response.of("result", "SUCCESS"); + response.of("code", object); + + return new ResponseEntity(response, HttpStatus.CREATED); + } + +} diff --git a/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java new file mode 100644 index 0000000..61f22cf --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/dto/DetailResponse.java @@ -0,0 +1,13 @@ +package com.bamboo.log.utils.dto; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class DetailResponse { + + private Integer code; + private String message; + +} \ No newline at end of file diff --git a/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java new file mode 100644 index 0000000..04eccf6 --- /dev/null +++ b/src/main/java/com/bamboo/log/utils/dto/ResponseForm.java @@ -0,0 +1,21 @@ +package com.bamboo.log.utils.dto; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + +@Getter +public class ResponseForm { + + public ResponseForm() { + response = new HashMap<>(); + } + + public void of(String value1, Object value2) { + response.put(value1, value2); + } + + private Map response; + +} \ No newline at end of file From 2f3d62286104e8a6a8474c5e954ee82b1ef9df56 Mon Sep 17 00:00:00 2001 From: qhdudedi Date: Mon, 10 Mar 2025 16:19:19 +0900 Subject: [PATCH 114/114] remove: .DS_Store files --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index dd46a9923d3511c0ddce67c1cc0fd4f8a0b2c125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKyH3L}6g_SWt=bAYGUf}AU|>X1)gq7>koW|;CU+P){U9Ro1K?d${?0O&9Ywz|x|FnKSjVh!KYCmP$w1RFjpq(~}k zZBPYNf#0Tptla|~W5G4ML2>xVU#fbPyWWgib%a`n#x zpSQrLZGBc8ZH6ploAZFZM|Mi)9Fuat=DEU5uq8Lctjxq2nWu$W_qjiL=67?k(H8yo z8Dq`7AyF2&?IIVCV#H3Bb*xaif!CC0eM4f*$B)aE^4A7V`vWrt4r zr53Eocdv=3aS?bo$zNewo>Ai^V6m$9M-p&92&bgsr zXM1GnFtJ0;RRL9?tU%LM2Xg+8>+k<%C#|Uhs=&Wez_doUqaj1`XKN)mIcp=PJ0>yV n